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.app.AppOpEnum;
22 import android.cts.statsdatom.lib.AtomTestUtils;
23 import android.cts.statsdatom.lib.ConfigUtils;
24 import android.cts.statsdatom.lib.DeviceUtils;
25 import android.cts.statsdatom.lib.ReportUtils;
26 import android.os.WakeLockLevelEnum;
27 import android.server.ErrorSource;
28 
29 import com.android.compatibility.common.util.PropertyUtil;
30 import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
31 import com.android.internal.os.StatsdConfigProto.StatsdConfig;
32 import com.android.os.AtomsProto.ANROccurred;
33 import com.android.os.AtomsProto.AppBreadcrumbReported;
34 import com.android.os.AtomsProto.AppCrashOccurred;
35 import com.android.os.AtomsProto.AppUsageEventOccurred;
36 import com.android.os.AtomsProto.Atom;
37 import com.android.os.AtomsProto.AttributionNode;
38 import com.android.os.AtomsProto.AudioStateChanged;
39 import com.android.os.AtomsProto.CameraStateChanged;
40 import com.android.os.AtomsProto.DeviceCalculatedPowerBlameUid;
41 import com.android.os.AtomsProto.FlashlightStateChanged;
42 import com.android.os.AtomsProto.ForegroundServiceAppOpSessionEnded;
43 import com.android.os.AtomsProto.ForegroundServiceStateChanged;
44 import com.android.os.AtomsProto.GpsScanStateChanged;
45 import com.android.os.AtomsProto.LmkKillOccurred;
46 import com.android.os.AtomsProto.MediaCodecStateChanged;
47 import com.android.os.AtomsProto.OverlayStateChanged;
48 import com.android.os.AtomsProto.SyncStateChanged;
49 import com.android.os.AtomsProto.TestAtomReported;
50 import com.android.os.AtomsProto.UiEventReported;
51 import com.android.os.AtomsProto.VibratorStateChanged;
52 import com.android.os.AtomsProto.WakelockStateChanged;
53 import com.android.os.StatsLog.EventMetricData;
54 import com.android.tradefed.build.IBuildInfo;
55 import com.android.tradefed.log.LogUtil;
56 import com.android.tradefed.testtype.DeviceTestCase;
57 import com.android.tradefed.testtype.IBuildReceiver;
58 import com.android.tradefed.util.Pair;
59 
60 import java.util.Arrays;
61 import java.util.Collections;
62 import java.util.HashSet;
63 import java.util.List;
64 import java.util.Set;
65 import java.util.function.Function;
66 import java.util.stream.Collectors;
67 
68 /**
69  * Statsd atom tests that are done via app, for atoms that report a uid.
70  */
71 public class UidAtomTests extends DeviceTestCase implements IBuildReceiver {
72 
73     private static final String TAG = "Statsd.UidAtomTests";
74 
75     private static final String TEST_PACKAGE_NAME = "com.android.server.cts.device.statsd";
76 
77     private static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
78 
79     private static final String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output";
80     private static final String FEATURE_CAMERA = "android.hardware.camera";
81     private static final String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash";
82     private static final String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
83     private static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
84     private static final String FEATURE_LOCATION_GPS = "android.hardware.location.gps";
85     private static final String FEATURE_PICTURE_IN_PICTURE = "android.software.picture_in_picture";
86     private static final String FEATURE_TV = "android.hardware.type.television";
87 
88     private IBuildInfo mCtsBuild;
89 
90     @Override
setUp()91     protected void setUp() throws Exception {
92         super.setUp();
93         assertThat(mCtsBuild).isNotNull();
94         ConfigUtils.removeConfig(getDevice());
95         ReportUtils.clearReports(getDevice());
96         DeviceUtils.installStatsdTestApp(getDevice(), mCtsBuild);
97         Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
98     }
99 
100     @Override
tearDown()101     protected void tearDown() throws Exception {
102         ConfigUtils.removeConfig(getDevice());
103         ReportUtils.clearReports(getDevice());
104         DeviceUtils.uninstallStatsdTestApp(getDevice());
105         super.tearDown();
106     }
107 
108     @Override
setBuild(IBuildInfo buildInfo)109     public void setBuild(IBuildInfo buildInfo) {
110         mCtsBuild = buildInfo;
111     }
112 
113     /**
114      * Tests that statsd correctly maps isolated uids to host uids by verifying that atoms logged
115      * from an isolated process are seen as coming from their host process.
116      */
testIsolatedToHostUidMapping()117     public void testIsolatedToHostUidMapping() throws Exception {
118         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
119                 Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, /*uidInAttributionChain=*/false);
120 
121         // Create an isolated service from which an AppBreadcrumbReported atom is logged.
122         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests",
123                 "testIsolatedProcessService");
124 
125         // Verify correctness of data.
126         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
127         assertThat(data).hasSize(1);
128         AppBreadcrumbReported atom = data.get(0).getAtom().getAppBreadcrumbReported();
129         assertThat(atom.getUid()).isEqualTo(DeviceUtils.getStatsdTestAppUid(getDevice()));
130         assertThat(atom.getLabel()).isEqualTo(0);
131         assertThat(atom.getState()).isEqualTo(AppBreadcrumbReported.State.START);
132     }
133 
shouldTestLmkdStats()134     private boolean shouldTestLmkdStats() throws Exception {
135         boolean hasKernel = DeviceUtils.isKernelGreaterEqual(getDevice(), Pair.create(4, 19));
136         boolean hasFirstApiLevel = PropertyUtil.getFirstApiLevel(getDevice()) > 30;
137         return (hasKernel && hasFirstApiLevel)
138                 || "true".equals(DeviceUtils.getProperty(getDevice(), "ro.lmk.log_stats"));
139     }
140 
testLmkKillOccurred()141     public void testLmkKillOccurred() throws Exception {
142         if (!shouldTestLmkdStats()) {
143             LogUtil.CLog.d("Skipping lmkd stats test.");
144             return;
145         }
146 
147         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
148                 Atom.LMK_KILL_OCCURRED_FIELD_NUMBER,  /*uidInAttributionChain=*/false);
149         int appUid = DeviceUtils.getStatsdTestAppUid(getDevice());
150 
151         // Start the victim process (service running in process :lmk_victim)
152         // We rely on a victim process (instead of expecting the allocating process to die)
153         // because it can be flaky and dependent on lmkd configuration
154         // (e.g. the OOM reaper can get to it first, depending on the allocation timings)
155         DeviceUtils.executeServiceAction(getDevice(), "LmkVictimBackgroundService",
156                 "action.end_immediately");
157         // Start fg activity and allocate
158         try (AutoCloseable a = DeviceUtils.withActivity(
159                 getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
160                 "StatsdCtsForegroundActivity", "action", "action.lmk")) {
161             // Sorted list of events in order in which they occurred.
162             List<EventMetricData> data = null;
163             for (int i = 0; i < 60; ++i) {
164                 Thread.sleep(1_000);
165                 data = ReportUtils.getEventMetricDataList(getDevice());
166                 if (!data.isEmpty()) {
167                   break;
168                 }
169             }
170 
171             assertThat(data).isNotEmpty();
172             // Even though both processes might have died, the non-fg one (victim)
173             // must have been first.
174             assertThat(data.get(0).getAtom().hasLmkKillOccurred()).isTrue();
175             LmkKillOccurred atom = data.get(0).getAtom().getLmkKillOccurred();
176             assertThat(atom.getUid()).isEqualTo(appUid);
177             assertThat(atom.getProcessName())
178                     .isEqualTo(DeviceUtils.STATSD_ATOM_TEST_PKG + ":lmk_victim");
179             assertThat(atom.getOomAdjScore()).isAtLeast(500);
180             assertThat(atom.getRssInBytes() + atom.getSwapInBytes()).isGreaterThan(0);
181       }
182     }
183 
testAppCrashOccurred()184     public void testAppCrashOccurred() throws Exception {
185         final int atomTag = Atom.APP_CRASH_OCCURRED_FIELD_NUMBER;
186         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
187                 atomTag,  /*uidInAttributionChain=*/false);
188 
189         DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
190                 "StatsdCtsForegroundActivity", "action", "action.crash");
191 
192         // Sorted list of events in order in which they occurred.
193         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
194 
195         assertThat(data).hasSize(1);
196         AppCrashOccurred atom = data.get(0).getAtom().getAppCrashOccurred();
197         // UID should belong to the run activity, not any system service.
198         assertThat(atom.getUid()).isGreaterThan(10000);
199         assertThat(atom.getEventType()).isEqualTo("crash");
200         assertThat(atom.getIsInstantApp().getNumber())
201                 .isEqualTo(AppCrashOccurred.InstantApp.FALSE_VALUE);
202         assertThat(atom.getForegroundState().getNumber())
203                 .isEqualTo(AppCrashOccurred.ForegroundState.FOREGROUND_VALUE);
204         assertThat(atom.getPackageName()).isEqualTo(DeviceUtils.STATSD_ATOM_TEST_PKG);
205         assertThat(atom.getErrorSource()).isEqualTo(ErrorSource.DATA_APP);
206         assertFalse(atom.getIsIncremental());
207         assertTrue((1 - atom.getLoadingProgress()) < 0.001);
208         assertEquals(-1, atom.getMillisSinceOldestPendingRead());
209     }
210 
211     public void testAppCrashOccurredNative() throws Exception {
212         if (DeviceUtils.hasFeature(getDevice(), FEATURE_TV)
213                 && DeviceUtils.isDebuggable(getDevice())) {
214             // Skip TVs that are debuggable because ActivityManager does not properly terminate
215             // the activity in the event of a native crash.
216             return;
217         }
218         final int atomTag = Atom.APP_CRASH_OCCURRED_FIELD_NUMBER;
219         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
220                 atomTag,  /*uidInAttributionChain=*/false);
221 
222         DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
223                 "StatsdCtsForegroundActivity", "action", "action.native_crash");
224 
225         // Sorted list of events in order in which they occurred.
226         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
227 
228         assertThat(data).hasSize(1);
229         AppCrashOccurred atom = data.get(0).getAtom().getAppCrashOccurred();
230         // UID should belong to the run activity, not any system service.
231         assertThat(atom.getUid()).isGreaterThan(10000);
232         assertThat(atom.getEventType()).isEqualTo("native_crash");
233         assertThat(atom.getIsInstantApp().getNumber())
234                 .isEqualTo(AppCrashOccurred.InstantApp.FALSE_VALUE);
235         assertThat(atom.getForegroundState().getNumber())
236                 .isEqualTo(AppCrashOccurred.ForegroundState.FOREGROUND_VALUE);
237         assertThat(atom.getPackageName()).isEqualTo(DeviceUtils.STATSD_ATOM_TEST_PKG);
238         assertThat(atom.getErrorSource()).isEqualTo(ErrorSource.DATA_APP);
239         // TODO(b/172866626): add tests for incremental packages that crashed during loading
240         assertFalse(atom.getIsIncremental());
241         assertTrue((1 - atom.getLoadingProgress()) < 0.001);
242         assertEquals(-1, atom.getMillisSinceOldestPendingRead());
243     }
244 
245 
246     public void testAudioState() throws Exception {
247         if (!DeviceUtils.hasFeature(getDevice(), FEATURE_AUDIO_OUTPUT)) return;
248 
249         final int atomTag = Atom.AUDIO_STATE_CHANGED_FIELD_NUMBER;
250         final String name = "testAudioState";
251 
252         Set<Integer> onState = new HashSet<>(
253                 Arrays.asList(AudioStateChanged.State.ON_VALUE));
254         Set<Integer> offState = new HashSet<>(
255                 Arrays.asList(AudioStateChanged.State.OFF_VALUE));
256 
257         // Add state sets to the list in order.
258         List<Set<Integer>> stateSet = Arrays.asList(onState, offState);
259 
260         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
261                 atomTag,  /*uidInAttributionChain=*/true);
262 
263         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", name);
264 
265         Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
266         // Sorted list of events in order in which they occurred.
267         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
268 
269         // Because the timestamp is truncated, we skip checking time differences between state
270         // changes.
271         AtomTestUtils.assertStatesOccurred(stateSet, data, 0,
272                 atom -> atom.getAudioStateChanged().getState().getNumber());
273 
274         // Check that timestamp is truncated
275         for (EventMetricData metric : data) {
276             long elapsedTimestampNs = metric.getElapsedTimestampNanos();
277             AtomTestUtils.assertTimestampIsTruncated(elapsedTimestampNs);
278         }
279     }
280 
281     public void testCameraState() throws Exception {
282         if (!DeviceUtils.hasFeature(getDevice(), FEATURE_CAMERA) && !DeviceUtils.hasFeature(
283                 getDevice(), FEATURE_CAMERA_FRONT)) {
284             return;
285         }
286 
287         final int atomTag = Atom.CAMERA_STATE_CHANGED_FIELD_NUMBER;
288         Set<Integer> cameraOn = new HashSet<>(Arrays.asList(CameraStateChanged.State.ON_VALUE));
289         Set<Integer> cameraOff = new HashSet<>(Arrays.asList(CameraStateChanged.State.OFF_VALUE));
290 
291         // Add state sets to the list in order.
292         List<Set<Integer>> stateSet = Arrays.asList(cameraOn, cameraOff);
293         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
294                 atomTag, /*useAttributionChain=*/ true);
295 
296         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testCameraState");
297 
298         // Sorted list of events in order in which they occurred.
299         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
300 
301         // Assert that the events happened in the expected order.
302         AtomTestUtils.assertStatesOccurred(stateSet, data, AtomTestUtils.WAIT_TIME_LONG,
303                 atom -> atom.getCameraStateChanged().getState().getNumber());
304     }
305 
testDeviceCalculatedPowerUse()306     public void testDeviceCalculatedPowerUse() throws Exception {
307         if (!DeviceUtils.hasFeature(getDevice(), FEATURE_LEANBACK_ONLY)) return;
308 
309         ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
310                 Atom.DEVICE_CALCULATED_POWER_USE_FIELD_NUMBER);
311         DeviceUtils.unplugDevice(getDevice());
312 
313         Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
314         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testSimpleCpu");
315         Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
316         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice());
317         Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
318 
319         Atom atom = ReportUtils.getGaugeMetricAtoms(getDevice()).get(0);
320         assertThat(atom.getDeviceCalculatedPowerUse().getComputedPowerNanoAmpSecs())
321                 .isGreaterThan(0L);
322         DeviceUtils.resetBatteryStatus(getDevice());
323     }
324 
325 
testDeviceCalculatedPowerBlameUid()326     public void testDeviceCalculatedPowerBlameUid() throws Exception {
327         if (!DeviceUtils.hasFeature(getDevice(), FEATURE_LEANBACK_ONLY)) return;
328         if (!DeviceUtils.hasBattery(getDevice())) {
329             return;
330         }
331         String kernelVersion = getDevice().executeShellCommand("uname -r");
332         if (kernelVersion.contains("3.18")) {
333             LogUtil.CLog.d("Skipping calculated power blame uid test.");
334             return;
335         }
336         ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
337                 Atom.DEVICE_CALCULATED_POWER_BLAME_UID_FIELD_NUMBER);
338         DeviceUtils.unplugDevice(getDevice());
339 
340         Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
341         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testSimpleCpu");
342         Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
343         AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice());
344         Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
345 
346         List<Atom> atomList = ReportUtils.getGaugeMetricAtoms(getDevice());
347         boolean uidFound = false;
348         int uid = DeviceUtils.getStatsdTestAppUid(getDevice());
349         long uidPower = 0;
350         for (Atom atom : atomList) {
351             DeviceCalculatedPowerBlameUid item = atom.getDeviceCalculatedPowerBlameUid();
352             if (item.getUid() == uid) {
353                 assertWithMessage(String.format("Found multiple power values for uid %d", uid))
354                         .that(uidFound).isFalse();
355                 uidFound = true;
356                 uidPower = item.getPowerNanoAmpSecs();
357             }
358         }
359         assertWithMessage(String.format("No power value for uid %d", uid)).that(uidFound).isTrue();
360         assertWithMessage(String.format("Non-positive power value for uid %d", uid))
361                 .that(uidPower).isGreaterThan(0L);
362         DeviceUtils.resetBatteryStatus(getDevice());
363     }
364 
testFlashlightState()365     public void testFlashlightState() throws Exception {
366         if (!DeviceUtils.hasFeature(getDevice(), FEATURE_CAMERA_FLASH)) return;
367 
368         final int atomTag = Atom.FLASHLIGHT_STATE_CHANGED_FIELD_NUMBER;
369         final String name = "testFlashlight";
370 
371         Set<Integer> flashlightOn = new HashSet<>(
372                 Arrays.asList(FlashlightStateChanged.State.ON_VALUE));
373         Set<Integer> flashlightOff = new HashSet<>(
374                 Arrays.asList(FlashlightStateChanged.State.OFF_VALUE));
375 
376         // Add state sets to the list in order.
377         List<Set<Integer>> stateSet = Arrays.asList(flashlightOn, flashlightOff);
378 
379         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
380                 atomTag, /*useUidAttributionChain=*/true);
381 
382         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", name);
383 
384         // Sorted list of events in order in which they occurred.
385         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
386 
387         // Assert that the events happened in the expected order.
388         AtomTestUtils.assertStatesOccurred(stateSet, data, AtomTestUtils.WAIT_TIME_SHORT,
389                 atom -> atom.getFlashlightStateChanged().getState().getNumber());
390     }
391 
testForegroundServiceState()392     public void testForegroundServiceState() throws Exception {
393         final int atomTag = Atom.FOREGROUND_SERVICE_STATE_CHANGED_FIELD_NUMBER;
394         final String name = "testForegroundService";
395 
396         Set<Integer> enterForeground = new HashSet<>(
397                 Arrays.asList(ForegroundServiceStateChanged.State.ENTER_VALUE));
398         Set<Integer> exitForeground = new HashSet<>(
399                 Arrays.asList(ForegroundServiceStateChanged.State.EXIT_VALUE));
400 
401         // Add state sets to the list in order.
402         List<Set<Integer>> stateSet = Arrays.asList(enterForeground, exitForeground);
403 
404         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
405                 atomTag, /*useUidAttributionChain=*/false);
406 
407         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", name);
408 
409         // Sorted list of events in order in which they occurred.
410         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
411 
412         // Assert that the events happened in the expected order.
413         AtomTestUtils.assertStatesOccurred(stateSet, data, AtomTestUtils.WAIT_TIME_SHORT,
414                 atom -> atom.getForegroundServiceStateChanged().getState().getNumber());
415     }
416 
417 
testForegroundServiceAccessAppOp()418     public void testForegroundServiceAccessAppOp() throws Exception {
419         final int atomTag = Atom.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED_FIELD_NUMBER;
420         final String name = "testForegroundServiceAccessAppOp";
421 
422         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
423                 atomTag, /*useUidAttributionChain=*/false);
424 
425         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", name);
426 
427         // Sorted list of events in order in which they occurred.
428         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
429 
430         assertWithMessage("Wrong atom size").that(data.size()).isEqualTo(3);
431         for (int i = 0; i < data.size(); i++) {
432             ForegroundServiceAppOpSessionEnded atom
433                     = data.get(i).getAtom().getForegroundServiceAppOpSessionEnded();
434             final int opName = atom.getAppOpName().getNumber();
435             final int acceptances = atom.getCountOpsAccepted();
436             final int rejections = atom.getCountOpsRejected();
437             final int count = acceptances + rejections;
438             int expectedCount = 0;
439             switch (opName) {
440                 case AppOpEnum.APP_OP_CAMERA_VALUE:
441                     expectedCount = 3;
442                     break;
443                 case AppOpEnum.APP_OP_FINE_LOCATION_VALUE:
444                     expectedCount = 1;
445                     break;
446                 case AppOpEnum.APP_OP_RECORD_AUDIO_VALUE:
447                     expectedCount = 2;
448                     break;
449                 case AppOpEnum.APP_OP_COARSE_LOCATION_VALUE:
450                     // fall-through
451                 default:
452                     fail("Unexpected opName " + opName);
453             }
454             assertWithMessage("Wrong count for " + opName).that(count).isEqualTo(expectedCount);
455         }
456     }
457 
testGpsScan()458     public void testGpsScan() throws Exception {
459         if (!DeviceUtils.hasFeature(getDevice(), FEATURE_LOCATION_GPS)) return;
460         // Whitelist this app against background location request throttling
461         String origWhitelist = getDevice().executeShellCommand(
462                 "settings get global location_background_throttle_package_whitelist").trim();
463         getDevice().executeShellCommand(String.format(
464                 "settings put global location_background_throttle_package_whitelist %s",
465                 DeviceUtils.STATSD_ATOM_TEST_PKG));
466 
467         try {
468             final int atom = Atom.GPS_SCAN_STATE_CHANGED_FIELD_NUMBER;
469             final int key = GpsScanStateChanged.STATE_FIELD_NUMBER;
470             final int stateOn = GpsScanStateChanged.State.ON_VALUE;
471             final int stateOff = GpsScanStateChanged.State.OFF_VALUE;
472             final int minTimeDiffMillis = 500;
473             final int maxTimeDiffMillis = 60_000;
474 
475             ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(),
476                     DeviceUtils.STATSD_ATOM_TEST_PKG,
477                     atom, /*useUidAttributionChain=*/true);
478 
479             DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests",
480                     "testGpsScan");
481 
482             List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
483             assertThat(data).hasSize(2);
484             GpsScanStateChanged a0 = data.get(0).getAtom().getGpsScanStateChanged();
485             GpsScanStateChanged a1 = data.get(1).getAtom().getGpsScanStateChanged();
486             AtomTestUtils.assertTimeDiffBetween(data.get(0), data.get(1), minTimeDiffMillis,
487                     maxTimeDiffMillis);
488             assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
489             assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
490         } finally {
491             if ("null".equals(origWhitelist) || "".equals(origWhitelist)) {
492                 getDevice().executeShellCommand(
493                         "settings delete global location_background_throttle_package_whitelist");
494             } else {
495                 getDevice().executeShellCommand(String.format(
496                         "settings put global location_background_throttle_package_whitelist %s",
497                         origWhitelist));
498             }
499         }
500     }
501 
testMediaCodecActivity()502     public void testMediaCodecActivity() throws Exception {
503         if (DeviceUtils.hasFeature(getDevice(), DeviceUtils.FEATURE_WATCH)) return;
504         final int atomTag = Atom.MEDIA_CODEC_STATE_CHANGED_FIELD_NUMBER;
505 
506         // 5 seconds. Starting video tends to be much slower than most other
507         // tests on slow devices. This is unfortunate, because it leaves a
508         // really big slop in assertStatesOccurred.  It would be better if
509         // assertStatesOccurred had a tighter range on large timeouts.
510         final int waitTime = 5000;
511 
512         // From {@link VideoPlayerActivity#DELAY_MILLIS}
513         final int videoDuration = 2000;
514 
515         Set<Integer> onState = new HashSet<>(
516                 Arrays.asList(MediaCodecStateChanged.State.ON_VALUE));
517         Set<Integer> offState = new HashSet<>(
518                 Arrays.asList(MediaCodecStateChanged.State.OFF_VALUE));
519 
520         // Add state sets to the list in order.
521         List<Set<Integer>> stateSet = Arrays.asList(onState, offState);
522 
523         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
524                 atomTag, /*useUidAttributionChain=*/true);
525 
526         DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
527                 "VideoPlayerActivity", "action", "action.play_video",
528                 waitTime);
529 
530         // Sorted list of events in order in which they occurred.
531         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
532 
533         // Assert that the events happened in the expected order.
534         AtomTestUtils.assertStatesOccurred(stateSet, data, videoDuration,
535                 atom -> atom.getMediaCodecStateChanged().getState().getNumber());
536     }
537 
testOverlayState()538     public void testOverlayState() throws Exception {
539         if (DeviceUtils.hasFeature(getDevice(), DeviceUtils.FEATURE_WATCH)) return;
540         final int atomTag = Atom.OVERLAY_STATE_CHANGED_FIELD_NUMBER;
541 
542         Set<Integer> entered = new HashSet<>(
543                 Arrays.asList(OverlayStateChanged.State.ENTERED_VALUE));
544         Set<Integer> exited = new HashSet<>(
545                 Arrays.asList(OverlayStateChanged.State.EXITED_VALUE));
546 
547         // Add state sets to the list in order.
548         List<Set<Integer>> stateSet = Arrays.asList(entered, exited);
549 
550         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
551                 atomTag, /*useUidAttributionChain=*/false);
552 
553         DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
554                 "StatsdCtsForegroundActivity", "action", "action.show_application_overlay",
555                 5_000);
556 
557         // Sorted list of events in order in which they occurred.
558         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
559 
560         // Assert that the events happened in the expected order.
561         // The overlay box should appear about 2sec after the app start
562         AtomTestUtils.assertStatesOccurred(stateSet, data, 0,
563                 atom -> atom.getOverlayStateChanged().getState().getNumber());
564     }
565 
testPictureInPictureState()566     public void testPictureInPictureState() throws Exception {
567         String supported = getDevice().executeShellCommand("am supports-multiwindow");
568         if (DeviceUtils.hasFeature(getDevice(), DeviceUtils.FEATURE_WATCH) ||
569                 !DeviceUtils.hasFeature(getDevice(), FEATURE_PICTURE_IN_PICTURE) ||
570                 !supported.contains("true")) {
571             LogUtil.CLog.d("Skipping picture in picture atom test.");
572             return;
573         }
574 
575         StatsdConfig.Builder config = ConfigUtils.createConfigBuilder(
576                 DeviceUtils.STATSD_ATOM_TEST_PKG);
577         FieldValueMatcher.Builder uidFvm = ConfigUtils.createUidFvm(/*uidInAttributionChain=*/false,
578                 DeviceUtils.STATSD_ATOM_TEST_PKG);
579 
580         // PictureInPictureStateChanged atom is used prior to rvc-qpr
581         ConfigUtils.addEventMetric(config, Atom.PICTURE_IN_PICTURE_STATE_CHANGED_FIELD_NUMBER,
582                 Collections.singletonList(uidFvm));
583         // Picture-in-picture logs' been migrated to UiEvent since rvc-qpr
584         FieldValueMatcher.Builder pkgMatcher = ConfigUtils.createFvm(
585                 UiEventReported.PACKAGE_NAME_FIELD_NUMBER)
586                 .setEqString(DeviceUtils.STATSD_ATOM_TEST_PKG);
587         ConfigUtils.addEventMetric(config, Atom.UI_EVENT_REPORTED_FIELD_NUMBER,
588                 Arrays.asList(pkgMatcher));
589         ConfigUtils.uploadConfig(getDevice(), config);
590 
591         LogUtil.CLog.d("Playing video in Picture-in-Picture mode");
592         DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
593                 "VideoPlayerActivity", "action", "action.play_video_picture_in_picture_mode");
594 
595         // Sorted list of events in order in which they occurred.
596         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
597 
598         // Filter out the PictureInPictureStateChanged and UiEventReported atom
599         List<EventMetricData> pictureInPictureStateChangedData = data.stream()
600                 .filter(e -> e.getAtom().hasPictureInPictureStateChanged())
601                 .collect(Collectors.toList());
602         List<EventMetricData> uiEventReportedData = data.stream()
603                 .filter(e -> e.getAtom().hasUiEventReported())
604                 .collect(Collectors.toList());
605 
606         assertThat(pictureInPictureStateChangedData).isEmpty();
607         assertThat(uiEventReportedData).isNotEmpty();
608 
609         // See PipUiEventEnum for definitions
610         final int enterPipEventId = 603;
611         // Assert that log for entering PiP happens exactly once, we do not use
612         // assertStateOccurred here since PiP may log something else when activity finishes.
613         List<EventMetricData> entered = uiEventReportedData.stream()
614                 .filter(e -> e.getAtom().getUiEventReported().getEventId() == enterPipEventId)
615                 .collect(Collectors.toList());
616         assertThat(entered).hasSize(1);
617     }
618 
619     //Note: this test does not have uid, but must run on the device
testScreenBrightness()620     public void testScreenBrightness() throws Exception {
621         int initialBrightness = getScreenBrightness();
622         boolean isInitialManual = isScreenBrightnessModeManual();
623         setScreenBrightnessMode(true);
624         setScreenBrightness(150);
625         Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
626 
627         final int atomTag = Atom.SCREEN_BRIGHTNESS_CHANGED_FIELD_NUMBER;
628 
629         Set<Integer> screenMin = new HashSet<>(Arrays.asList(47));
630         Set<Integer> screen100 = new HashSet<>(Arrays.asList(100));
631         Set<Integer> screen140 = new HashSet<>(Arrays.asList(140));
632         // Set<Integer> screenMax = new HashSet<>(Arrays.asList(255));
633 
634         // Add state sets to the list in order.
635         List<Set<Integer>> stateSet = Arrays.asList(screenMin, screen100, screen140);
636 
637         ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
638                 atomTag);
639         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testScreenBrightness");
640 
641         // Sorted list of events in order in which they occurred.
642         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
643 
644         // Restore initial screen brightness
645         setScreenBrightness(initialBrightness);
646         setScreenBrightnessMode(isInitialManual);
647 
648         AtomTestUtils.popUntilFind(data, screenMin,
649                 atom -> atom.getScreenBrightnessChanged().getLevel());
650         AtomTestUtils.popUntilFindFromEnd(data, screen140,
651                 atom -> atom.getScreenBrightnessChanged().getLevel());
652         // Assert that the events happened in the expected order.
653         AtomTestUtils.assertStatesOccurred(stateSet, data, AtomTestUtils.WAIT_TIME_SHORT,
654                 atom -> atom.getScreenBrightnessChanged().getLevel());
655     }
656 
testSyncState()657     public void testSyncState() throws Exception {
658         final int atomTag = Atom.SYNC_STATE_CHANGED_FIELD_NUMBER;
659         Set<Integer> syncOn = new HashSet<>(Arrays.asList(SyncStateChanged.State.ON_VALUE));
660         Set<Integer> syncOff = new HashSet<>(Arrays.asList(SyncStateChanged.State.OFF_VALUE));
661 
662         // Add state sets to the list in order.
663         List<Set<Integer>> stateSet = Arrays.asList(syncOn, syncOff, syncOn, syncOff);
664 
665         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
666                 atomTag, /*useUidAttributionChain=*/true);
667         DeviceUtils.allowImmediateSyncs(getDevice());
668         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testSyncState");
669 
670         // Sorted list of events in order in which they occurred.
671         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
672 
673         // Assert that the events happened in the expected order.
674         AtomTestUtils.assertStatesOccurred(stateSet, data,
675                 /* wait = */ 0 /* don't verify time differences between state changes */,
676                 atom -> atom.getSyncStateChanged().getState().getNumber());
677     }
678 
testVibratorState()679     public void testVibratorState() throws Exception {
680         if (!DeviceUtils.checkDeviceFor(getDevice(), "checkVibratorSupported")) return;
681 
682         final int atomTag = Atom.VIBRATOR_STATE_CHANGED_FIELD_NUMBER;
683         final String name = "testVibratorState";
684 
685         Set<Integer> onState = new HashSet<>(
686                 Arrays.asList(VibratorStateChanged.State.ON_VALUE));
687         Set<Integer> offState = new HashSet<>(
688                 Arrays.asList(VibratorStateChanged.State.OFF_VALUE));
689 
690         // Add state sets to the list in order.
691         List<Set<Integer>> stateSet = Arrays.asList(onState, offState);
692 
693         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
694                 atomTag, /*useUidAttributionChain=*/true);
695 
696         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", name);
697 
698         Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
699         // Sorted list of events in order in which they occurred.
700         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
701 
702         AtomTestUtils.assertStatesOccurred(stateSet, data, 300,
703                 atom -> atom.getVibratorStateChanged().getState().getNumber());
704     }
705 
testWakelockState()706     public void testWakelockState() throws Exception {
707         final int atomTag = Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER;
708         Set<Integer> wakelockOn = new HashSet<>(Arrays.asList(
709                 WakelockStateChanged.State.ACQUIRE_VALUE,
710                 WakelockStateChanged.State.CHANGE_ACQUIRE_VALUE));
711         Set<Integer> wakelockOff = new HashSet<>(Arrays.asList(
712                 WakelockStateChanged.State.RELEASE_VALUE,
713                 WakelockStateChanged.State.CHANGE_RELEASE_VALUE));
714 
715         final String EXPECTED_TAG = "StatsdPartialWakelock";
716         final WakeLockLevelEnum EXPECTED_LEVEL = WakeLockLevelEnum.PARTIAL_WAKE_LOCK;
717 
718         // Add state sets to the list in order.
719         List<Set<Integer>> stateSet = Arrays.asList(wakelockOn, wakelockOff);
720 
721         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
722                 atomTag, /*useUidAttributionChain=*/true);
723         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testWakelockState");
724 
725         // Sorted list of events in order in which they occurred.
726         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
727 
728         // Assert that the events happened in the expected order.
729         AtomTestUtils.assertStatesOccurred(stateSet, data, AtomTestUtils.WAIT_TIME_SHORT,
730                 atom -> atom.getWakelockStateChanged().getState().getNumber());
731 
732         for (EventMetricData event : data) {
733             String tag = event.getAtom().getWakelockStateChanged().getTag();
734             WakeLockLevelEnum type = event.getAtom().getWakelockStateChanged().getType();
735             assertThat(tag).isEqualTo(EXPECTED_TAG);
736             assertThat(type).isEqualTo(EXPECTED_LEVEL);
737         }
738     }
739 
testANROccurred()740     public void testANROccurred() throws Exception {
741         final int atomTag = Atom.ANR_OCCURRED_FIELD_NUMBER;
742         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
743                 atomTag, /*useUidAttributionChain=*/false);
744 
745         try (AutoCloseable a = DeviceUtils.withActivity(getDevice(),
746                 DeviceUtils.STATSD_ATOM_TEST_PKG, "ANRActivity", null, null)) {
747             Thread.sleep(AtomTestUtils.WAIT_TIME_LONG * 2);
748             getDevice().executeShellCommand(
749                     "am broadcast -a action_anr -p " + DeviceUtils.STATSD_ATOM_TEST_PKG);
750             Thread.sleep(20_000);
751         }
752 
753         // Sorted list of events in order in which they occurred.
754         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
755 
756         assertThat(data).hasSize(1);
757         assertThat(data.get(0).getAtom().hasAnrOccurred()).isTrue();
758         ANROccurred atom = data.get(0).getAtom().getAnrOccurred();
759         assertThat(atom.getIsInstantApp().getNumber())
760                 .isEqualTo(ANROccurred.InstantApp.FALSE_VALUE);
761         assertThat(atom.getForegroundState().getNumber())
762                 .isEqualTo(ANROccurred.ForegroundState.FOREGROUND_VALUE);
763         assertThat(atom.getErrorSource()).isEqualTo(ErrorSource.DATA_APP);
764         assertThat(atom.getPackageName()).isEqualTo(DeviceUtils.STATSD_ATOM_TEST_PKG);
765         assertFalse(atom.getIsIncremental());
766         assertTrue((1 - atom.getLoadingProgress()) < 0.001);
767         assertEquals(-1, atom.getMillisSinceOldestPendingRead());
768     }
769 
770     public void testWriteRawTestAtom() throws Exception {
771         final int atomTag = Atom.TEST_ATOM_REPORTED_FIELD_NUMBER;
772         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
773                 atomTag, /*useUidAttributionChain=*/true);
774 
775         DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testWriteRawTestAtom");
776 
777         Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
778         // Sorted list of events in order in which they occurred.
779         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
780         assertThat(data).hasSize(4);
781 
782         TestAtomReported atom = data.get(0).getAtom().getTestAtomReported();
783         List<AttributionNode> attrChain = atom.getAttributionNodeList();
784         assertThat(attrChain).hasSize(2);
785         assertThat(attrChain.get(0).getUid()).isEqualTo(1234);
786         assertThat(attrChain.get(0).getTag()).isEqualTo("tag1");
787         assertThat(attrChain.get(1).getUid()).isEqualTo(
788                 DeviceUtils.getStatsdTestAppUid(getDevice()));
789         assertThat(attrChain.get(1).getTag()).isEqualTo("tag2");
790 
791         assertThat(atom.getIntField()).isEqualTo(42);
792         assertThat(atom.getLongField()).isEqualTo(Long.MAX_VALUE);
793         assertThat(atom.getFloatField()).isEqualTo(3.14f);
794         assertThat(atom.getStringField()).isEqualTo("This is a basic test!");
795         assertThat(atom.getBooleanField()).isFalse();
796         assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.ON_VALUE);
797         assertThat(atom.getBytesField().getExperimentIdList())
798                 .containsExactly(1L, 2L, 3L).inOrder();
799 
800 
801         atom = data.get(1).getAtom().getTestAtomReported();
802         attrChain = atom.getAttributionNodeList();
803         assertThat(attrChain).hasSize(2);
804         assertThat(attrChain.get(0).getUid()).isEqualTo(9999);
805         assertThat(attrChain.get(0).getTag()).isEqualTo("tag9999");
806         assertThat(attrChain.get(1).getUid()).isEqualTo(
807                 DeviceUtils.getStatsdTestAppUid(getDevice()));
808         assertThat(attrChain.get(1).getTag()).isEmpty();
809 
810         assertThat(atom.getIntField()).isEqualTo(100);
811         assertThat(atom.getLongField()).isEqualTo(Long.MIN_VALUE);
812         assertThat(atom.getFloatField()).isEqualTo(-2.5f);
813         assertThat(atom.getStringField()).isEqualTo("Test null uid");
814         assertThat(atom.getBooleanField()).isTrue();
815         assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.UNKNOWN_VALUE);
816         assertThat(atom.getBytesField().getExperimentIdList())
817                 .containsExactly(1L, 2L, 3L).inOrder();
818 
819         atom = data.get(2).getAtom().getTestAtomReported();
820         attrChain = atom.getAttributionNodeList();
821         assertThat(attrChain).hasSize(1);
822         assertThat(attrChain.get(0).getUid()).isEqualTo(
823                 DeviceUtils.getStatsdTestAppUid(getDevice()));
824         assertThat(attrChain.get(0).getTag()).isEqualTo("tag1");
825 
826         assertThat(atom.getIntField()).isEqualTo(-256);
827         assertThat(atom.getLongField()).isEqualTo(-1234567890L);
828         assertThat(atom.getFloatField()).isEqualTo(42.01f);
829         assertThat(atom.getStringField()).isEqualTo("Test non chained");
830         assertThat(atom.getBooleanField()).isTrue();
831         assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.OFF_VALUE);
832         assertThat(atom.getBytesField().getExperimentIdList())
833                 .containsExactly(1L, 2L, 3L).inOrder();
834 
835         atom = data.get(3).getAtom().getTestAtomReported();
836         attrChain = atom.getAttributionNodeList();
837         assertThat(attrChain).hasSize(1);
838         assertThat(attrChain.get(0).getUid()).isEqualTo(
839                 DeviceUtils.getStatsdTestAppUid(getDevice()));
840         assertThat(attrChain.get(0).getTag()).isEmpty();
841 
842         assertThat(atom.getIntField()).isEqualTo(0);
843         assertThat(atom.getLongField()).isEqualTo(0L);
844         assertThat(atom.getFloatField()).isEqualTo(0f);
845         assertThat(atom.getStringField()).isEmpty();
846         assertThat(atom.getBooleanField()).isTrue();
847         assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.OFF_VALUE);
848         assertThat(atom.getBytesField().getExperimentIdList()).isEmpty();
849     }
850 
851     public void testAppForegroundBackground() throws Exception {
852         Set<Integer> onStates = new HashSet<>(Arrays.asList(
853                 AppUsageEventOccurred.EventType.MOVE_TO_FOREGROUND_VALUE));
854         Set<Integer> offStates = new HashSet<>(Arrays.asList(
855                 AppUsageEventOccurred.EventType.MOVE_TO_BACKGROUND_VALUE));
856 
857         List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
858         ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
859                 Atom.APP_USAGE_EVENT_OCCURRED_FIELD_NUMBER, /*useUidAttributionChain=*/false);
860 
861         // Overlay may need to sit there a while.
862         final int waitTime = 10_500;
863         DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
864                 "StatsdCtsForegroundActivity", "action", ACTION_SHOW_APPLICATION_OVERLAY, waitTime);
865 
866         List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
867         Function<Atom, Integer> appUsageStateFunction =
868                 atom -> atom.getAppUsageEventOccurred().getEventType().getNumber();
869         // clear out initial appusage states
870         AtomTestUtils.popUntilFind(data, onStates, appUsageStateFunction);
871         AtomTestUtils.assertStatesOccurred(stateSet, data, 0, appUsageStateFunction);
872     }
873 /*
874     public void testAppForceStopUsageEvent() throws Exception {
875         Set<Integer> onStates = new HashSet<>(Arrays.asList(
876                 AppUsageEventOccurred.EventType.MOVE_TO_FOREGROUND_VALUE));
877         Set<Integer> offStates = new HashSet<>(Arrays.asList(
878                 AppUsageEventOccurred.EventType.MOVE_TO_BACKGROUND_VALUE));
879 
880         List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
881         createAndUploadConfig(Atom.APP_USAGE_EVENT_OCCURRED_FIELD_NUMBER, false);
882         Thread.sleep(WAIT_TIME_FOR_CONFIG_UPDATE_MS);
883 
884         getDevice().executeShellCommand(String.format(
885                 "am start -n '%s' -e %s %s",
886                 "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity",
887                 "action", ACTION_LONG_SLEEP_WHILE_TOP));
888         final int waitTime = EXTRA_WAIT_TIME_MS + 5_000;
889         Thread.sleep(waitTime);
890 
891         getDevice().executeShellCommand(String.format(
892                 "am force-stop %s",
893                 "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity"));
894         Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS);
895 
896         List<EventMetricData> data = getEventMetricDataList();
897         Function<Atom, Integer> appUsageStateFunction =
898                 atom -> atom.getAppUsageEventOccurred().getEventType().getNumber();
899         popUntilFind(data, onStates, appUsageStateFunction); // clear out initial appusage states.
900         assertStatesOccurred(stateSet, data, 0, appUsageStateFunction);
901     }
902 */
903 
getScreenBrightness()904     private int getScreenBrightness() throws Exception {
905         return Integer.parseInt(
906                 getDevice().executeShellCommand("settings get system screen_brightness").trim());
907     }
908 
isScreenBrightnessModeManual()909     private boolean isScreenBrightnessModeManual() throws Exception {
910         String mode = getDevice().executeShellCommand("settings get system screen_brightness_mode");
911         return Integer.parseInt(mode.trim()) == 0;
912     }
913 
setScreenBrightnessMode(boolean manual)914     private void setScreenBrightnessMode(boolean manual) throws Exception {
915         getDevice().executeShellCommand(
916                 "settings put system screen_brightness_mode " + (manual ? 0 : 1));
917     }
918 
setScreenBrightness(int brightness)919     private void setScreenBrightness(int brightness) throws Exception {
920         getDevice().executeShellCommand("settings put system screen_brightness " + brightness);
921     }
922 }
923