1 /*
2  * Copyright (C) 2017 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 android.cts.statsdatom.statsd;
17 
18 import static com.google.common.truth.Truth.assertThat;
19 import static com.google.common.truth.Truth.assertWithMessage;
20 
21 import android.cts.statsdatom.lib.AtomTestUtils;
22 import android.cts.statsdatom.lib.ConfigUtils;
23 import android.cts.statsdatom.lib.DeviceUtils;
24 import android.cts.statsdatom.lib.ReportUtils;
25 import android.os.BatteryPluggedStateEnum;
26 import android.os.BatteryStatusEnum;
27 import android.os.StatsDataDumpProto;
28 import android.platform.test.annotations.RestrictedBuildTest;
29 import android.server.DeviceIdleModeEnum;
30 import android.view.DisplayStateEnum;
31 
32 import com.android.os.AtomsProto.AppBreadcrumbReported;
33 import com.android.os.AtomsProto.Atom;
34 import com.android.os.AtomsProto.BatterySaverModeStateChanged;
35 import com.android.os.AtomsProto.BuildInformation;
36 import com.android.os.AtomsProto.ConnectivityStateChanged;
37 import com.android.os.StatsLog.ConfigMetricsReportList;
38 import com.android.os.StatsLog.EventMetricData;
39 import com.android.tradefed.build.IBuildInfo;
40 import com.android.tradefed.device.DeviceNotAvailableException;
41 import com.android.tradefed.log.LogUtil;
42 import com.android.tradefed.testtype.DeviceTestCase;
43 import com.android.tradefed.testtype.IBuildReceiver;
44 import com.android.tradefed.util.RunUtil;
45 
46 import com.google.common.collect.Range;
47 import com.google.protobuf.ByteString;
48 import com.google.protobuf.ExtensionRegistry;
49 
50 import java.util.ArrayList;
51 import java.util.Arrays;
52 import java.util.HashSet;
53 import java.util.List;
54 import java.util.Set;
55 import java.util.stream.Collectors;
56 
57 /**
58  * Statsd atom tests that are done via adb (hostside).
59  */
60 public class HostAtomTests extends DeviceTestCase implements IBuildReceiver {
61 
62     private static final String TAG = "Statsd.HostAtomTests";
63 
64     private static final String DUMPSYS_STATS_CMD = "dumpsys stats";
65 
66     // Either file must exist to read kernel wake lock stats.
67     private static final String WAKE_LOCK_FILE = "/proc/wakelocks";
68     private static final String WAKE_SOURCES_FILE = "/d/wakeup_sources";
69 
70     private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
71     private static final String FEATURE_WATCH = "android.hardware.type.watch";
72     private static final String FEATURE_TWM = "com.google.clockwork.hardware.traditional_watch_mode";
73     private static final String FEATURE_WIFI = "android.hardware.wifi";
74     private static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
75 
76     private IBuildInfo mCtsBuild;
77 
78     @Override
setUp()79     protected void setUp() throws Exception {
80         super.setUp();
81         assertThat(mCtsBuild).isNotNull();
82         ConfigUtils.removeConfig(getDevice());
83         ReportUtils.clearReports(getDevice());
84         DeviceUtils.installStatsdTestApp(getDevice(), mCtsBuild);
85         DeviceUtils.turnBatteryStatsAutoResetOff(getDevice());
86         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
87     }
88 
89     @Override
tearDown()90     protected void tearDown() throws Exception {
91         ConfigUtils.removeConfig(getDevice());
92         ReportUtils.clearReports(getDevice());
93         DeviceUtils.uninstallStatsdTestApp(getDevice());
94         DeviceUtils.turnBatteryStatsAutoResetOn(getDevice());
95         super.tearDown();
96     }
97 
98     @Override
setBuild(IBuildInfo buildInfo)99     public void setBuild(IBuildInfo buildInfo) {
100         mCtsBuild = buildInfo;
101     }
102 
testScreenStateChangedAtom()103     public void testScreenStateChangedAtom() throws Exception {
104         // Setup, make sure the screen is off and turn off AoD if it is on.
105         // AoD needs to be turned off because the screen should go into an off state. But, if AoD is
106         // on and the device doesn't support STATE_DOZE, the screen sadly goes back to STATE_ON.
107         String aodState = getAodState();
108         setAodState("0");
109         DeviceUtils.turnScreenOn(getDevice());
110         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
111         DeviceUtils.turnScreenOff(getDevice());
112         // Ensure that the screen on/off atoms are pushed before the config is uploaded.
113         RunUtil.getDefault().sleep(5_000);
114 
115         final int atomTag = Atom.SCREEN_STATE_CHANGED_FIELD_NUMBER;
116 
117         Set<Integer> screenOnStates = new HashSet<>(
118                 Arrays.asList(DisplayStateEnum.DISPLAY_STATE_ON_VALUE,
119                         DisplayStateEnum.DISPLAY_STATE_ON_SUSPEND_VALUE,
120                         DisplayStateEnum.DISPLAY_STATE_VR_VALUE));
121         Set<Integer> screenOffStates = new HashSet<>(
122                 Arrays.asList(DisplayStateEnum.DISPLAY_STATE_OFF_VALUE,
123                         DisplayStateEnum.DISPLAY_STATE_DOZE_VALUE,
124                         DisplayStateEnum.DISPLAY_STATE_DOZE_SUSPEND_VALUE,
125                         DisplayStateEnum.DISPLAY_STATE_UNKNOWN_VALUE));
126 
127         // Add state sets to the list in order.
128         List<Set<Integer>> stateSet = Arrays.asList(screenOnStates, screenOffStates);
129 
130         ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
131                 atomTag);
132 
133         // Trigger events in same order.
134         DeviceUtils.turnScreenOn(getDevice());
135         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
136         DeviceUtils.turnScreenOff(getDevice());
137         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
138 
139         // Sorted list of events in order in which they occurred.
140         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
141         // reset screen to on
142         DeviceUtils.turnScreenOn(getDevice());
143         // Restores AoD to initial state.
144         setAodState(aodState);
145         // Assert that the events happened in the expected order.
146         AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, AtomTestUtils.WAIT_TIME_LONG,
147                 atom -> atom.getScreenStateChanged().getState().getNumber());
148     }
149 
testChargingStateChangedAtom()150     public void testChargingStateChangedAtom() throws Exception {
151         if (DeviceUtils.hasFeature(getDevice(), FEATURE_AUTOMOTIVE)) return;
152         // Setup, set charging state to full.
153         DeviceUtils.setChargingState(getDevice(), 5);
154         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
155 
156         final int atomTag = Atom.CHARGING_STATE_CHANGED_FIELD_NUMBER;
157 
158         Set<Integer> batteryUnknownStates = new HashSet<>(
159                 Arrays.asList(BatteryStatusEnum.BATTERY_STATUS_UNKNOWN_VALUE));
160         Set<Integer> batteryChargingStates = new HashSet<>(
161                 Arrays.asList(BatteryStatusEnum.BATTERY_STATUS_CHARGING_VALUE));
162         Set<Integer> batteryDischargingStates = new HashSet<>(
163                 Arrays.asList(BatteryStatusEnum.BATTERY_STATUS_DISCHARGING_VALUE));
164         Set<Integer> batteryNotChargingStates = new HashSet<>(
165                 Arrays.asList(BatteryStatusEnum.BATTERY_STATUS_NOT_CHARGING_VALUE));
166         Set<Integer> batteryFullStates = new HashSet<>(
167                 Arrays.asList(BatteryStatusEnum.BATTERY_STATUS_FULL_VALUE));
168 
169         // Add state sets to the list in order.
170         List<Set<Integer>> stateSet = Arrays.asList(batteryUnknownStates, batteryChargingStates,
171                 batteryDischargingStates, batteryNotChargingStates, batteryFullStates);
172 
173         ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
174                 atomTag);
175 
176         // Trigger events in same order.
177         DeviceUtils.setChargingState(getDevice(), 1);
178         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
179         DeviceUtils.setChargingState(getDevice(), 2);
180         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
181         DeviceUtils.setChargingState(getDevice(), 3);
182         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
183         DeviceUtils.setChargingState(getDevice(), 4);
184         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
185         DeviceUtils.setChargingState(getDevice(), 5);
186         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
187 
188         // Sorted list of events in order in which they occurred.
189         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
190 
191         // Unfreeze battery state after test
192         DeviceUtils.resetBatteryStatus(getDevice());
193         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
194 
195         // Assert that the events happened in the expected order.
196         AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, AtomTestUtils.WAIT_TIME_SHORT,
197                 atom -> atom.getChargingStateChanged().getState().getNumber());
198     }
199 
testPluggedStateChangedAtom()200     public void testPluggedStateChangedAtom() throws Exception {
201         if (DeviceUtils.hasFeature(getDevice(), FEATURE_AUTOMOTIVE)) return;
202         // Setup, unplug device.
203         DeviceUtils.unplugDevice(getDevice());
204         DeviceUtils.flushBatteryStatsHandlers(getDevice());
205         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
206 
207         final int atomTag = Atom.PLUGGED_STATE_CHANGED_FIELD_NUMBER;
208 
209         Set<Integer> unpluggedStates = new HashSet<>(
210                 Arrays.asList(BatteryPluggedStateEnum.BATTERY_PLUGGED_NONE_VALUE));
211         Set<Integer> acStates = new HashSet<>(
212                 Arrays.asList(BatteryPluggedStateEnum.BATTERY_PLUGGED_AC_VALUE));
213         Set<Integer> usbStates = new HashSet<>(
214                 Arrays.asList(BatteryPluggedStateEnum.BATTERY_PLUGGED_USB_VALUE));
215         Set<Integer> wirelessStates = new HashSet<>(
216                 Arrays.asList(BatteryPluggedStateEnum.BATTERY_PLUGGED_WIRELESS_VALUE));
217 
218         // Add state sets to the list in order.
219         List<Set<Integer>> stateSet = Arrays.asList(acStates, unpluggedStates, usbStates,
220                 unpluggedStates, wirelessStates, unpluggedStates);
221 
222         ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
223                 atomTag);
224 
225         // Trigger events in same order.
226         DeviceUtils.plugInAc(getDevice());
227         DeviceUtils.flushBatteryStatsHandlers(getDevice());
228         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
229         DeviceUtils.unplugDevice(getDevice());
230         DeviceUtils.flushBatteryStatsHandlers(getDevice());
231         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
232         plugInUsb();
233         DeviceUtils.flushBatteryStatsHandlers(getDevice());
234         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
235         DeviceUtils.unplugDevice(getDevice());
236         DeviceUtils.flushBatteryStatsHandlers(getDevice());
237         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
238         plugInWireless();
239         DeviceUtils.flushBatteryStatsHandlers(getDevice());
240         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
241         DeviceUtils.unplugDevice(getDevice());
242         DeviceUtils.flushBatteryStatsHandlers(getDevice());
243 
244         // Sorted list of events in order in which they occurred.
245         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
246 
247         // Unfreeze battery state after test
248         DeviceUtils.resetBatteryStatus(getDevice());
249         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
250 
251         // Assert that the events happened in the expected order.
252         AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, AtomTestUtils.WAIT_TIME_LONG,
253                 atom -> atom.getPluggedStateChanged().getState().getNumber());
254     }
255 
testBatteryLevelChangedAtom()256     public void testBatteryLevelChangedAtom() throws Exception {
257         if (DeviceUtils.hasFeature(getDevice(), FEATURE_AUTOMOTIVE)) return;
258         // Setup, set battery level to full.
259         setBatteryLevel(100);
260         DeviceUtils.flushBatteryStatsHandlers(getDevice());
261         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
262 
263         final int atomTag = Atom.BATTERY_LEVEL_CHANGED_FIELD_NUMBER;
264 
265         Set<Integer> batteryLow = new HashSet<>(Arrays.asList(2));
266         Set<Integer> battery25p = new HashSet<>(Arrays.asList(25));
267         Set<Integer> battery50p = new HashSet<>(Arrays.asList(50));
268         Set<Integer> battery75p = new HashSet<>(Arrays.asList(75));
269         Set<Integer> batteryFull = new HashSet<>(Arrays.asList(100));
270 
271         // Add state sets to the list in order.
272         List<Set<Integer>> stateSet = Arrays.asList(batteryLow, battery25p, battery50p,
273                 battery75p, batteryFull);
274 
275         ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
276                 atomTag);
277 
278         // Trigger events in same order.
279         setBatteryLevel(2);
280         DeviceUtils.flushBatteryStatsHandlers(getDevice());
281         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
282         setBatteryLevel(25);
283         DeviceUtils.flushBatteryStatsHandlers(getDevice());
284         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
285         setBatteryLevel(50);
286         DeviceUtils.flushBatteryStatsHandlers(getDevice());
287         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
288         setBatteryLevel(75);
289         DeviceUtils.flushBatteryStatsHandlers(getDevice());
290         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
291         setBatteryLevel(100);
292         DeviceUtils.flushBatteryStatsHandlers(getDevice());
293         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
294 
295         // Sorted list of events in order in which they occurred.
296         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
297 
298         // Unfreeze battery state after test
299         DeviceUtils.resetBatteryStatus(getDevice());
300         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
301 
302         // Assert that the events happened in the expected order.
303         AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, AtomTestUtils.WAIT_TIME_LONG,
304                 atom -> atom.getBatteryLevelChanged().getBatteryLevel());
305     }
306 
testDeviceIdleModeStateChangedAtom()307     public void testDeviceIdleModeStateChangedAtom() throws Exception {
308         // Setup, leave doze mode.
309         leaveDozeMode();
310         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
311 
312         final int atomTag = Atom.DEVICE_IDLE_MODE_STATE_CHANGED_FIELD_NUMBER;
313 
314         Set<Integer> dozeOff = new HashSet<>(
315                 Arrays.asList(DeviceIdleModeEnum.DEVICE_IDLE_MODE_OFF_VALUE));
316         Set<Integer> dozeLight = new HashSet<>(
317                 Arrays.asList(DeviceIdleModeEnum.DEVICE_IDLE_MODE_LIGHT_VALUE));
318         Set<Integer> dozeDeep = new HashSet<>(
319                 Arrays.asList(DeviceIdleModeEnum.DEVICE_IDLE_MODE_DEEP_VALUE));
320 
321         // Add state sets to the list in order.
322         List<Set<Integer>> stateSet = Arrays.asList(dozeLight, dozeDeep, dozeOff);
323 
324         ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
325                 atomTag);
326 
327         // Trigger events in same order.
328         enterDozeModeLight();
329         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
330         enterDozeModeDeep();
331         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
332         leaveDozeMode();
333         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
334 
335         // Sorted list of events in order in which they occurred.
336         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
337 
338         // Assert that the events happened in the expected order.
339         AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, AtomTestUtils.WAIT_TIME_SHORT,
340                 atom -> atom.getDeviceIdleModeStateChanged().getState().getNumber());
341     }
342 
testBatterySaverModeStateChangedAtom()343     public void testBatterySaverModeStateChangedAtom() throws Exception {
344         if (DeviceUtils.hasFeature(getDevice(), FEATURE_TWM)) return;
345         if (DeviceUtils.hasFeature(getDevice(), FEATURE_AUTOMOTIVE)) return;
346         if (DeviceUtils.hasFeature(getDevice(), FEATURE_WATCH)) return;
347         // Setup, turn off battery saver.
348         turnBatterySaverOff();
349         DeviceUtils.flushBatteryStatsHandlers(getDevice());
350 
351         final int atomTag = Atom.BATTERY_SAVER_MODE_STATE_CHANGED_FIELD_NUMBER;
352 
353         Set<Integer> batterySaverOn = new HashSet<>(
354                 Arrays.asList(BatterySaverModeStateChanged.State.ON_VALUE));
355         Set<Integer> batterySaverOff = new HashSet<>(
356                 Arrays.asList(BatterySaverModeStateChanged.State.OFF_VALUE));
357 
358         // Add state sets to the list in order.
359         List<Set<Integer>> stateSet = Arrays.asList(batterySaverOn, batterySaverOff);
360 
361         ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
362                 atomTag);
363 
364         // Trigger events in same order.
365         turnBatterySaverOn();
366         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
367         turnBatterySaverOff();
368         DeviceUtils.flushBatteryStatsHandlers(getDevice());
369 
370         // Sorted list of events in order in which they occurred.
371         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
372 
373         // Assert that the events happened in the expected order.
374         AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, AtomTestUtils.WAIT_TIME_LONG,
375                 atom -> atom.getBatterySaverModeStateChanged().getState().getNumber());
376     }
377 
378     @RestrictedBuildTest
testRemainingBatteryCapacity()379     public void testRemainingBatteryCapacity() throws Exception {
380         if (DeviceUtils.hasFeature(getDevice(), FEATURE_WATCH)) return;
381         if (DeviceUtils.hasFeature(getDevice(), FEATURE_AUTOMOTIVE)) return;
382 
383         ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
384                 Atom.REMAINING_BATTERY_CAPACITY_FIELD_NUMBER);
385 
386         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice());
387         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
388 
389         List<Atom> data = ReportUtils.getGaugeMetricAtoms(getDevice());
390 
391         assertThat(data).isNotEmpty();
392         Atom atom = data.get(0);
393         assertThat(atom.getRemainingBatteryCapacity().hasChargeMicroAmpereHour()).isTrue();
394         if (DeviceUtils.hasBattery(getDevice())) {
395             assertThat(atom.getRemainingBatteryCapacity().getChargeMicroAmpereHour())
396                     .isGreaterThan(0);
397         }
398     }
399 
400     @RestrictedBuildTest
testFullBatteryCapacity()401     public void testFullBatteryCapacity() throws Exception {
402         if (DeviceUtils.hasFeature(getDevice(), FEATURE_WATCH)) return;
403         if (DeviceUtils.hasFeature(getDevice(), FEATURE_AUTOMOTIVE)) return;
404 
405         ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
406                 Atom.FULL_BATTERY_CAPACITY_FIELD_NUMBER);
407 
408         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice());
409         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
410 
411         List<Atom> data = ReportUtils.getGaugeMetricAtoms(getDevice());
412 
413         assertThat(data).isNotEmpty();
414         Atom atom = data.get(0);
415         assertThat(atom.getFullBatteryCapacity().hasCapacityMicroAmpereHour()).isTrue();
416         if (DeviceUtils.hasBattery(getDevice())) {
417             assertThat(atom.getFullBatteryCapacity().getCapacityMicroAmpereHour()).isGreaterThan(0);
418         }
419     }
420 
testBatteryVoltage()421     public void testBatteryVoltage() throws Exception {
422         if (DeviceUtils.hasFeature(getDevice(), FEATURE_WATCH)) return;
423 
424         ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
425                 Atom.BATTERY_VOLTAGE_FIELD_NUMBER);
426 
427         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice());
428         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
429 
430         List<Atom> data = ReportUtils.getGaugeMetricAtoms(getDevice());
431 
432         assertThat(data).isNotEmpty();
433         Atom atom = data.get(0);
434         assertThat(atom.getBatteryVoltage().hasVoltageMillivolt()).isTrue();
435         if (DeviceUtils.hasBattery(getDevice())) {
436             assertThat(atom.getBatteryVoltage().getVoltageMillivolt()).isGreaterThan(0);
437         }
438     }
439 
440     // This test is for the pulled battery level atom.
testBatteryLevel()441     public void testBatteryLevel() throws Exception {
442         if (DeviceUtils.hasFeature(getDevice(), FEATURE_WATCH)) return;
443 
444         ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
445                 Atom.BATTERY_LEVEL_FIELD_NUMBER);
446 
447         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice());
448         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
449 
450         List<Atom> data = ReportUtils.getGaugeMetricAtoms(getDevice());
451 
452         assertThat(data).isNotEmpty();
453         Atom atom = data.get(0);
454         assertThat(atom.getBatteryLevel().hasBatteryLevel()).isTrue();
455         if (DeviceUtils.hasBattery(getDevice())) {
456             assertThat(atom.getBatteryLevel().getBatteryLevel()).isIn(Range.openClosed(0, 100));
457         }
458     }
459 
testKernelWakelock()460     public void testKernelWakelock() throws Exception {
461         if (!kernelWakelockStatsExist()) {
462             return;
463         }
464 
465         ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
466                 Atom.KERNEL_WAKELOCK_FIELD_NUMBER);
467 
468         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice());
469         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
470 
471         List<Atom> data = ReportUtils.getGaugeMetricAtoms(getDevice());
472 
473         assertThat(data).isNotEmpty();
474         for (Atom atom : data) {
475             assertThat(atom.getKernelWakelock().hasName()).isTrue();
476             assertThat(atom.getKernelWakelock().hasCount()).isTrue();
477             assertThat(atom.getKernelWakelock().hasVersion()).isTrue();
478             assertThat(atom.getKernelWakelock().getVersion()).isGreaterThan(0);
479             assertThat(atom.getKernelWakelock().hasTimeMicros()).isTrue();
480         }
481     }
482 
483     // Returns true iff either |WAKE_LOCK_FILE| or |WAKE_SOURCES_FILE| exists.
kernelWakelockStatsExist()484     private boolean kernelWakelockStatsExist() {
485       try {
486         return doesFileExist(WAKE_LOCK_FILE) || doesFileExist(WAKE_SOURCES_FILE);
487       } catch(Exception e) {
488         return false;
489       }
490     }
491 
testWifiActivityInfo()492     public void testWifiActivityInfo() throws Exception {
493         if (!DeviceUtils.hasFeature(getDevice(), FEATURE_WIFI)) return;
494         if (DeviceUtils.hasFeature(getDevice(), FEATURE_WATCH)) return;
495         if (!DeviceUtils.checkDeviceFor(getDevice(), "checkWifiEnhancedPowerReportingSupported")) {
496             return;
497         }
498 
499         ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
500                 Atom.WIFI_ACTIVITY_INFO_FIELD_NUMBER);
501 
502         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice());
503         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
504 
505         List<Atom> dataList = ReportUtils.getGaugeMetricAtoms(getDevice());
506 
507         for (Atom atom : dataList) {
508             assertThat(atom.getWifiActivityInfo().getTimestampMillis()).isGreaterThan(0L);
509             assertThat(atom.getWifiActivityInfo().getStackState()).isAtLeast(0);
510             assertThat(atom.getWifiActivityInfo().getControllerIdleTimeMillis()).isGreaterThan(0L);
511             assertThat(atom.getWifiActivityInfo().getControllerTxTimeMillis()).isAtLeast(0L);
512             assertThat(atom.getWifiActivityInfo().getControllerRxTimeMillis()).isAtLeast(0L);
513             assertThat(atom.getWifiActivityInfo().getControllerEnergyUsed()).isAtLeast(0L);
514         }
515     }
516 
testBuildInformation()517     public void testBuildInformation() throws Exception {
518         ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
519                 Atom.BUILD_INFORMATION_FIELD_NUMBER);
520 
521         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice());
522         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_LONG);
523 
524         List<Atom> data = ReportUtils.getGaugeMetricAtoms(getDevice());
525 
526         assertThat(data).isNotEmpty();
527         BuildInformation atom = data.get(0).getBuildInformation();
528         assertThat(DeviceUtils.getProperty(getDevice(), "ro.product.brand")).isEqualTo(
529                 atom.getBrand());
530         assertThat(DeviceUtils.getProperty(getDevice(), "ro.product.name")).isEqualTo(
531                 atom.getProduct());
532         assertThat(DeviceUtils.getProperty(getDevice(), "ro.product.device")).isEqualTo(
533                 atom.getDevice());
534         assertThat(DeviceUtils.getProperty(getDevice(),
535                 "ro.build.version.release_or_codename")).isEqualTo(
536                 atom.getVersionRelease());
537         assertThat(DeviceUtils.getProperty(getDevice(), "ro.build.id")).isEqualTo(atom.getId());
538         assertThat(DeviceUtils.getProperty(getDevice(), "ro.build.version.incremental"))
539                 .isEqualTo(atom.getVersionIncremental());
540         assertThat(DeviceUtils.getProperty(getDevice(), "ro.build.type")).isEqualTo(atom.getType());
541         assertThat(DeviceUtils.getProperty(getDevice(), "ro.build.tags")).isEqualTo(atom.getTags());
542     }
543 
544     // Explicitly tests if the adb command to log a breadcrumb is working.
testBreadcrumbAdb()545     public void testBreadcrumbAdb() throws Exception {
546         final int atomTag = Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER;
547         ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
548                 atomTag);
549 
550         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice());
551         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
552 
553         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
554         AppBreadcrumbReported atom = data.get(0).getAtom().getAppBreadcrumbReported();
555         assertThat(atom.getLabel()).isEqualTo(1);
556         assertThat(atom.getState().getNumber()).isEqualTo(AppBreadcrumbReported.State.START_VALUE);
557     }
558 
559     // Test dumpsys stats --proto.
testDumpsysStats()560     public void testDumpsysStats() throws Exception {
561         final int atomTag = Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER;
562         ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
563                 atomTag);
564 
565         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice());
566         RunUtil.getDefault().sleep(AtomTestUtils.WAIT_TIME_SHORT);
567 
568         // Get the stats incident section.
569         List<ConfigMetricsReportList> listList = getReportsFromStatsDataDumpProto();
570         assertThat(listList).isNotEmpty();
571 
572         // Extract the relevant report from the incident section.
573         ConfigMetricsReportList ourList = null;
574         int hostUid = 2000; // Shell UID is always used in ConfigUtils.uploadConfig
575         for (ConfigMetricsReportList list : listList) {
576             ConfigMetricsReportList.ConfigKey configKey = list.getConfigKey();
577             if (configKey.getUid() == hostUid && configKey.getId() == ConfigUtils.CONFIG_ID) {
578                 ourList = list;
579                 break;
580             }
581         }
582         assertWithMessage(String.format("Could not find list for uid=%d id=%d", hostUid,
583                 ConfigUtils.CONFIG_ID))
584                 .that(ourList).isNotNull();
585 
586         // Make sure that the report is correct.
587         List<EventMetricData> data = ReportUtils.getEventMetricDataList(ourList);
588         AppBreadcrumbReported atom = data.get(0).getAtom().getAppBreadcrumbReported();
589         assertThat(atom.getLabel()).isEqualTo(1);
590         assertThat(atom.getState().getNumber()).isEqualTo(AppBreadcrumbReported.State.START_VALUE);
591     }
592 
testConnectivityStateChange()593     public void testConnectivityStateChange() throws Exception {
594         if (!DeviceUtils.hasFeature(getDevice(), FEATURE_WIFI)) return;
595         if (DeviceUtils.hasFeature(getDevice(), FEATURE_WATCH)) return;
596         if (DeviceUtils.hasFeature(getDevice(), FEATURE_LEANBACK_ONLY)) return;
597 
598         final int atomTag = Atom.CONNECTIVITY_STATE_CHANGED_FIELD_NUMBER;
599         ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
600                 atomTag);
601 
602         turnOnAirplaneMode();
603         // wait long enough for airplane mode events to propagate.
604         RunUtil.getDefault().sleep(1_200);
605         turnOffAirplaneMode();
606         // wait long enough for the device to restore connection
607         RunUtil.getDefault().sleep(13_000);
608 
609         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
610         // at least 1 disconnect and 1 connect
611         assertThat(data.size()).isAtLeast(2);
612         boolean foundDisconnectEvent = false;
613         boolean foundConnectEvent = false;
614         for (EventMetricData d : data) {
615             ConnectivityStateChanged atom = d.getAtom().getConnectivityStateChanged();
616             if (atom.getState().getNumber()
617                     == ConnectivityStateChanged.State.DISCONNECTED_VALUE) {
618                 foundDisconnectEvent = true;
619             }
620             if (atom.getState().getNumber()
621                     == ConnectivityStateChanged.State.CONNECTED_VALUE) {
622                 foundConnectEvent = true;
623             }
624         }
625         assertThat(foundConnectEvent).isTrue();
626         assertThat(foundDisconnectEvent).isTrue();
627     }
628 
testAtomsLoggedOnBoot()629     public void testAtomsLoggedOnBoot() throws Exception {
630         ConfigUtils.uploadConfigForPushedAtoms(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
631                 new int[] {
632                     Atom.DEVICE_IDLE_MODE_STATE_CHANGED_FIELD_NUMBER,
633                     Atom.SCREEN_STATE_CHANGED_FIELD_NUMBER,
634                     Atom.BATTERY_LEVEL_CHANGED_FIELD_NUMBER,
635                     Atom.CHARGING_STATE_CHANGED_FIELD_NUMBER,
636                     Atom.PLUGGED_STATE_CHANGED_FIELD_NUMBER
637                 }
638         );
639 
640         try {
641             DeviceUtils.rebootDeviceAndWaitUntilReady(getDevice());
642         } catch (DeviceNotAvailableException e) {
643             // Ignore test if reboot fails.
644             return;
645         }
646 
647         RunUtil.getDefault().sleep(10_000);
648 
649         // Get events from the report after boot.
650         List<Atom> atoms = ReportUtils
651                 .getEventMetricDataList(
652                         getDevice(), ExtensionRegistry.getEmptyRegistry(), /*reportIndex*/ 1)
653                 .stream()
654                 .map(EventMetricData::getAtom)
655                 .collect(Collectors.toList());
656 
657         assertThat(atoms.stream().anyMatch(Atom::hasDeviceIdleModeStateChanged)).isTrue();
658         assertThat(atoms.stream().anyMatch(Atom::hasScreenStateChanged)).isTrue();
659         if (!DeviceUtils.hasFeature(getDevice(), FEATURE_AUTOMOTIVE)) {
660             assertThat(atoms.stream().anyMatch(Atom::hasBatteryLevelChanged)).isTrue();
661             assertThat(atoms.stream().anyMatch(Atom::hasChargingStateChanged)).isTrue();
662             assertThat(atoms.stream().anyMatch(Atom::hasPluggedStateChanged)).isTrue();
663         }
664     }
665 
666     // Gets whether "Always on Display" setting is enabled.
667     // In rare cases, this is different from whether the device can enter SCREEN_STATE_DOZE.
getAodState()668     private String getAodState() throws Exception {
669         return getDevice().executeShellCommand("settings get secure doze_always_on");
670     }
671 
setAodState(String state)672     private void setAodState(String state) throws Exception {
673         getDevice().executeShellCommand("settings put secure doze_always_on " + state);
674     }
675 
plugInUsb()676     private void plugInUsb() throws Exception {
677         getDevice().executeShellCommand("cmd battery set usb 1");
678     }
679 
plugInWireless()680     private void plugInWireless() throws Exception {
681         getDevice().executeShellCommand("cmd battery set wireless 1");
682     }
683 
684     /**
685      * Determines if the device has |file|.
686      */
doesFileExist(String file)687     private boolean doesFileExist(String file) throws Exception {
688         return getDevice().doesFileExist(file);
689     }
690 
setBatteryLevel(int level)691     private void setBatteryLevel(int level) throws Exception {
692         getDevice().executeShellCommand("cmd battery set level " + level);
693     }
694 
leaveDozeMode()695     private void leaveDozeMode() throws Exception {
696         getDevice().executeShellCommand("dumpsys deviceidle unforce");
697         getDevice().executeShellCommand("dumpsys deviceidle disable");
698         getDevice().executeShellCommand("dumpsys deviceidle enable");
699     }
700 
enterDozeModeLight()701     private void enterDozeModeLight() throws Exception {
702         getDevice().executeShellCommand("dumpsys deviceidle force-idle light");
703     }
704 
enterDozeModeDeep()705     private void enterDozeModeDeep() throws Exception {
706         getDevice().executeShellCommand("dumpsys deviceidle force-idle deep");
707     }
708 
turnBatterySaverOff()709     private void turnBatterySaverOff() throws Exception {
710         getDevice().executeShellCommand("settings put global low_power 0");
711         getDevice().executeShellCommand("cmd battery reset");
712     }
713 
turnBatterySaverOn()714     private void turnBatterySaverOn() throws Exception {
715         DeviceUtils.unplugDevice(getDevice());
716         getDevice().executeShellCommand("settings put global low_power 1");
717     }
718 
turnOnAirplaneMode()719     private void turnOnAirplaneMode() throws Exception {
720         getDevice().executeShellCommand("cmd connectivity airplane-mode enable");
721     }
722 
turnOffAirplaneMode()723     private void turnOffAirplaneMode() throws Exception {
724         getDevice().executeShellCommand("cmd connectivity airplane-mode disable");
725     }
726 
727     /** Gets reports from the statsd data incident section from the stats dumpsys. */
getReportsFromStatsDataDumpProto()728     private List<ConfigMetricsReportList> getReportsFromStatsDataDumpProto() throws Exception {
729         try {
730             StatsDataDumpProto statsProto = DeviceUtils.getShellCommandOutput(
731                     getDevice(),
732                     StatsDataDumpProto.parser(),
733                     String.join(" ", DUMPSYS_STATS_CMD, "--proto"));
734             // statsProto holds repeated bytes, which we must parse into ConfigMetricsReportLists.
735             List<ConfigMetricsReportList> reports
736                     = new ArrayList<>(statsProto.getConfigMetricsReportListCount());
737             for (ByteString reportListBytes : statsProto.getConfigMetricsReportListList()) {
738                 reports.add(ConfigMetricsReportList.parseFrom(reportListBytes));
739             }
740             LogUtil.CLog.d("Got dumpsys stats output:\n " + reports.toString());
741             return reports;
742         } catch (com.google.protobuf.InvalidProtocolBufferException e) {
743             LogUtil.CLog.e("Failed to dumpsys stats proto");
744             throw (e);
745         }
746     }
747 }
748