blob: 33422c1cec430f01c5058b088c899fd3a465e774 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.cts.statsdatom.statsd;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import android.app.AppOpEnum;
import android.cts.statsdatom.lib.AtomTestUtils;
import android.cts.statsdatom.lib.ConfigUtils;
import android.cts.statsdatom.lib.DeviceUtils;
import android.cts.statsdatom.lib.ReportUtils;
import android.os.WakeLockLevelEnum;
import android.server.ErrorSource;
import com.android.compatibility.common.util.PropertyUtil;
import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
import com.android.os.AtomsProto.ANROccurred;
import com.android.os.AtomsProto.AppBreadcrumbReported;
import com.android.os.AtomsProto.AppCrashOccurred;
import com.android.os.AtomsProto.AppUsageEventOccurred;
import com.android.os.AtomsProto.Atom;
import com.android.os.AtomsProto.AttributionNode;
import com.android.os.AtomsProto.AudioStateChanged;
import com.android.os.AtomsProto.CameraStateChanged;
import com.android.os.AtomsProto.FlashlightStateChanged;
import com.android.os.AtomsProto.ForegroundServiceAppOpSessionEnded;
import com.android.os.AtomsProto.ForegroundServiceStateChanged;
import com.android.os.AtomsProto.GpsScanStateChanged;
import com.android.os.AtomsProto.LmkKillOccurred;
import com.android.os.AtomsProto.MediaCodecStateChanged;
import com.android.os.AtomsProto.OverlayStateChanged;
import com.android.os.AtomsProto.SyncStateChanged;
import com.android.os.AtomsProto.TestAtomReported;
import com.android.os.AtomsProto.UiEventReported;
import com.android.os.AtomsProto.VibratorStateChanged;
import com.android.os.AtomsProto.WakelockStateChanged;
import com.android.os.StatsLog.EventMetricData;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.log.LogUtil;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.testtype.IBuildReceiver;
import com.android.tradefed.util.Pair;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* Statsd atom tests that are done via app, for atoms that report a uid.
*/
public class UidAtomTests extends DeviceTestCase implements IBuildReceiver {
private static final String TAG = "Statsd.UidAtomTests";
private static final String TEST_PACKAGE_NAME = "com.android.server.cts.device.statsd";
private static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
private static final String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output";
private static final String FEATURE_CAMERA = "android.hardware.camera";
private static final String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash";
private static final String FEATURE_CAMERA_FRONT = "android.hardware.camera.front";
private static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
private static final String FEATURE_LOCATION_GPS = "android.hardware.location.gps";
private static final String FEATURE_PICTURE_IN_PICTURE = "android.software.picture_in_picture";
private static final String FEATURE_TV = "android.hardware.type.television";
private IBuildInfo mCtsBuild;
@Override
protected void setUp() throws Exception {
super.setUp();
assertThat(mCtsBuild).isNotNull();
ConfigUtils.removeConfig(getDevice());
ReportUtils.clearReports(getDevice());
DeviceUtils.installStatsdTestApp(getDevice(), mCtsBuild);
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
}
@Override
protected void tearDown() throws Exception {
ConfigUtils.removeConfig(getDevice());
ReportUtils.clearReports(getDevice());
DeviceUtils.uninstallStatsdTestApp(getDevice());
super.tearDown();
}
@Override
public void setBuild(IBuildInfo buildInfo) {
mCtsBuild = buildInfo;
}
/**
* Tests that statsd correctly maps isolated uids to host uids by verifying that atoms logged
* from an isolated process are seen as coming from their host process.
*/
public void testIsolatedToHostUidMapping() throws Exception {
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, /*uidInAttributionChain=*/false);
// Create an isolated service from which an AppBreadcrumbReported atom is logged.
DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests",
"testIsolatedProcessService");
// Verify correctness of data.
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data).hasSize(1);
AppBreadcrumbReported atom = data.get(0).getAtom().getAppBreadcrumbReported();
assertThat(atom.getUid()).isEqualTo(DeviceUtils.getStatsdTestAppUid(getDevice()));
assertThat(atom.getLabel()).isEqualTo(0);
assertThat(atom.getState()).isEqualTo(AppBreadcrumbReported.State.START);
}
private boolean shouldTestLmkdStats() throws Exception {
boolean hasKernel = DeviceUtils.isKernelGreaterEqual(getDevice(), Pair.create(4, 19));
boolean hasFirstApiLevel = PropertyUtil.getFirstApiLevel(getDevice()) > 30;
return (hasKernel && hasFirstApiLevel)
|| "true".equals(DeviceUtils.getProperty(getDevice(), "ro.lmk.log_stats"));
}
public void testLmkKillOccurred() throws Exception {
if (!shouldTestLmkdStats()) {
LogUtil.CLog.d("Skipping lmkd stats test.");
return;
}
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
Atom.LMK_KILL_OCCURRED_FIELD_NUMBER, /*uidInAttributionChain=*/false);
int appUid = DeviceUtils.getStatsdTestAppUid(getDevice());
// Start the victim process (service running in process :lmk_victim)
// We rely on a victim process (instead of expecting the allocating process to die)
// because 1. it is likely to be less flaky (higher oom score processes will be killed
// faster, making less likely for the OOM reaper to trigger and 2. we need two processes
// to be able to force evictions on 32-bit userspace devices with > 4 GB RAM.
DeviceUtils.executeServiceAction(getDevice(), "LmkVictimBackgroundService",
"action.allocate_memory");
// Start fg activity and allocate
try (AutoCloseable a = DeviceUtils.withActivity(
getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
"StatsdCtsForegroundActivity", "action", "action.lmk")) {
// Sorted list of events in order in which they occurred.
List<LmkKillOccurred> atoms = null;
for (int i = 0; i < 60; ++i) {
Thread.sleep(1_000);
atoms = ReportUtils.getEventMetricDataList(getDevice()).stream()
.map(EventMetricData::getAtom)
.filter(Atom::hasLmkKillOccurred)
.map(Atom::getLmkKillOccurred)
.filter(atom -> atom.getUid() == appUid)
.collect(Collectors.toList());
if (!atoms.isEmpty()) {
break;
}
}
assertThat(atoms).isNotEmpty();
// Even though both processes might have died, the non-fg one (victim)
// must have been first.
assertThat(atoms.get(0).getProcessName())
.isEqualTo(DeviceUtils.STATSD_ATOM_TEST_PKG + ":lmk_victim");
assertThat(atoms.get(0).getOomAdjScore()).isGreaterThan(0);
assertThat(atoms.get(0).getRssInBytes() + atoms.get(0).getSwapInBytes())
.isGreaterThan(0);
}
}
public void testAppCrashOccurred() throws Exception {
final int atomTag = Atom.APP_CRASH_OCCURRED_FIELD_NUMBER;
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
atomTag, /*uidInAttributionChain=*/false);
DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
"StatsdCtsForegroundActivity", "action", "action.crash");
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data).hasSize(1);
AppCrashOccurred atom = data.get(0).getAtom().getAppCrashOccurred();
// UID should belong to the run activity, not any system service.
assertThat(atom.getUid()).isGreaterThan(10000);
assertThat(atom.getEventType()).isEqualTo("crash");
assertThat(atom.getIsInstantApp().getNumber())
.isEqualTo(AppCrashOccurred.InstantApp.FALSE_VALUE);
assertThat(atom.getForegroundState().getNumber())
.isEqualTo(AppCrashOccurred.ForegroundState.FOREGROUND_VALUE);
assertThat(atom.getPackageName()).isEqualTo(DeviceUtils.STATSD_ATOM_TEST_PKG);
assertThat(atom.getErrorSource()).isEqualTo(ErrorSource.DATA_APP);
assertFalse(atom.getIsIncremental());
assertTrue((1 - atom.getLoadingProgress()) < 0.001);
assertEquals(-1, atom.getMillisSinceOldestPendingRead());
}
public void testAppCrashOccurredNative() throws Exception {
if (DeviceUtils.hasFeature(getDevice(), FEATURE_TV)
&& DeviceUtils.isDebuggable(getDevice())) {
// Skip TVs that are debuggable because ActivityManager does not properly terminate
// the activity in the event of a native crash.
return;
}
final int atomTag = Atom.APP_CRASH_OCCURRED_FIELD_NUMBER;
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
atomTag, /*uidInAttributionChain=*/false);
DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
"StatsdCtsForegroundActivity", "action", "action.native_crash",
/* waitTimeMs= */ 5000L);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data).hasSize(1);
AppCrashOccurred atom = data.get(0).getAtom().getAppCrashOccurred();
// UID should belong to the run activity, not any system service.
assertThat(atom.getUid()).isGreaterThan(10000);
assertThat(atom.getEventType()).isEqualTo("native_crash");
assertThat(atom.getIsInstantApp().getNumber())
.isEqualTo(AppCrashOccurred.InstantApp.FALSE_VALUE);
assertThat(atom.getForegroundState().getNumber())
.isEqualTo(AppCrashOccurred.ForegroundState.FOREGROUND_VALUE);
assertThat(atom.getPackageName()).isEqualTo(DeviceUtils.STATSD_ATOM_TEST_PKG);
assertThat(atom.getErrorSource()).isEqualTo(ErrorSource.DATA_APP);
// TODO(b/172866626): add tests for incremental packages that crashed during loading
assertFalse(atom.getIsIncremental());
assertTrue((1 - atom.getLoadingProgress()) < 0.001);
assertEquals(-1, atom.getMillisSinceOldestPendingRead());
}
public void testAudioState() throws Exception {
if (!DeviceUtils.hasFeature(getDevice(), FEATURE_AUDIO_OUTPUT)) return;
final int atomTag = Atom.AUDIO_STATE_CHANGED_FIELD_NUMBER;
final String name = "testAudioState";
Set<Integer> onState = new HashSet<>(
Arrays.asList(AudioStateChanged.State.ON_VALUE));
Set<Integer> offState = new HashSet<>(
Arrays.asList(AudioStateChanged.State.OFF_VALUE));
// Add state sets to the list in order.
List<Set<Integer>> stateSet = Arrays.asList(onState, offState);
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
atomTag, /*uidInAttributionChain=*/true);
DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", name);
Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
List<Integer> atomStates = data.stream().map(
eventMetricData -> eventMetricData.getAtom().getAudioStateChanged()
.getState().getNumber())
.collect(Collectors.toList());
// Because the timestamp is truncated, we skip checking time differences between state
// changes.
assertThat(data.size()).isEqualTo(2);
assertThat(new ArrayList<>(Arrays.asList(AudioStateChanged.State.ON_VALUE,
AudioStateChanged.State.OFF_VALUE))).containsExactlyElementsIn(atomStates);
// Check that timestamp is truncated
for (EventMetricData metric : data) {
long elapsedTimestampNs = metric.getElapsedTimestampNanos();
AtomTestUtils.assertTimestampIsTruncated(elapsedTimestampNs);
}
}
public void testCameraState() throws Exception {
if (!DeviceUtils.hasFeature(getDevice(), FEATURE_CAMERA) && !DeviceUtils.hasFeature(
getDevice(), FEATURE_CAMERA_FRONT)) {
return;
}
final int atomTag = Atom.CAMERA_STATE_CHANGED_FIELD_NUMBER;
Set<Integer> cameraOn = new HashSet<>(Arrays.asList(CameraStateChanged.State.ON_VALUE));
Set<Integer> cameraOff = new HashSet<>(Arrays.asList(CameraStateChanged.State.OFF_VALUE));
// Add state sets to the list in order.
List<Set<Integer>> stateSet = Arrays.asList(cameraOn, cameraOff);
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
atomTag, /*useAttributionChain=*/ true);
DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testCameraState");
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
// Assert that the events happened in the expected order.
AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, AtomTestUtils.WAIT_TIME_LONG,
atom -> atom.getCameraStateChanged().getState().getNumber());
}
public void testDeviceCalculatedPowerUse() throws Exception {
if (DeviceUtils.hasFeature(getDevice(), FEATURE_TV)) {
// Skip TVs because they do not have batteries.
return;
}
if (!DeviceUtils.hasFeature(getDevice(), FEATURE_LEANBACK_ONLY)) return;
ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
Atom.DEVICE_CALCULATED_POWER_USE_FIELD_NUMBER);
DeviceUtils.unplugDevice(getDevice());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testSimpleCpu");
Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice());
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
Atom atom = ReportUtils.getGaugeMetricAtoms(getDevice()).get(0);
assertThat(atom.getDeviceCalculatedPowerUse().getComputedPowerNanoAmpSecs())
.isGreaterThan(0L);
DeviceUtils.resetBatteryStatus(getDevice());
}
public void testFlashlightState() throws Exception {
if (!DeviceUtils.hasFeature(getDevice(), FEATURE_CAMERA_FLASH)) return;
final int atomTag = Atom.FLASHLIGHT_STATE_CHANGED_FIELD_NUMBER;
final String name = "testFlashlight";
Set<Integer> flashlightOn = new HashSet<>(
Arrays.asList(FlashlightStateChanged.State.ON_VALUE));
Set<Integer> flashlightOff = new HashSet<>(
Arrays.asList(FlashlightStateChanged.State.OFF_VALUE));
// Add state sets to the list in order.
List<Set<Integer>> stateSet = Arrays.asList(flashlightOn, flashlightOff);
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
atomTag, /*useUidAttributionChain=*/true);
DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", name);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
// Assert that the events happened in the expected order.
AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, AtomTestUtils.WAIT_TIME_SHORT,
atom -> atom.getFlashlightStateChanged().getState().getNumber());
}
public void testForegroundServiceState() throws Exception {
final int atomTag = Atom.FOREGROUND_SERVICE_STATE_CHANGED_FIELD_NUMBER;
final String name = "testForegroundService";
Set<Integer> enterForeground = new HashSet<>(
Arrays.asList(ForegroundServiceStateChanged.State.ENTER_VALUE));
Set<Integer> exitForeground = new HashSet<>(
Arrays.asList(ForegroundServiceStateChanged.State.EXIT_VALUE));
// Add state sets to the list in order.
List<Set<Integer>> stateSet = Arrays.asList(enterForeground, exitForeground);
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
atomTag, /*useUidAttributionChain=*/false);
DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", name);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
// Assert that the events happened in the expected order.
AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, AtomTestUtils.WAIT_TIME_SHORT,
atom -> atom.getForegroundServiceStateChanged().getState().getNumber());
}
public void testForegroundServiceAccessAppOp() throws Exception {
final int atomTag = Atom.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED_FIELD_NUMBER;
final String name = "testForegroundServiceAccessAppOp";
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
atomTag, /*useUidAttributionChain=*/false);
DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", name);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertWithMessage("Wrong atom size").that(data.size()).isEqualTo(3);
for (int i = 0; i < data.size(); i++) {
ForegroundServiceAppOpSessionEnded atom
= data.get(i).getAtom().getForegroundServiceAppOpSessionEnded();
final int opName = atom.getAppOpName().getNumber();
final int acceptances = atom.getCountOpsAccepted();
final int rejections = atom.getCountOpsRejected();
final int count = acceptances + rejections;
int expectedCount = 0;
switch (opName) {
case AppOpEnum.APP_OP_CAMERA_VALUE:
expectedCount = 3;
break;
case AppOpEnum.APP_OP_FINE_LOCATION_VALUE:
expectedCount = 1;
break;
case AppOpEnum.APP_OP_RECORD_AUDIO_VALUE:
expectedCount = 2;
break;
case AppOpEnum.APP_OP_COARSE_LOCATION_VALUE:
// fall-through
default:
fail("Unexpected opName " + opName);
}
assertWithMessage("Wrong count for " + opName).that(count).isEqualTo(expectedCount);
}
}
public void testGpsScan() throws Exception {
if (!DeviceUtils.hasFeature(getDevice(), FEATURE_LOCATION_GPS)) return;
// Whitelist this app against background location request throttling
String origWhitelist = getDevice().executeShellCommand(
"settings get global location_background_throttle_package_whitelist").trim();
getDevice().executeShellCommand(String.format(
"settings put global location_background_throttle_package_whitelist %s",
DeviceUtils.STATSD_ATOM_TEST_PKG));
try {
final int atom = Atom.GPS_SCAN_STATE_CHANGED_FIELD_NUMBER;
final int key = GpsScanStateChanged.STATE_FIELD_NUMBER;
final int stateOn = GpsScanStateChanged.State.ON_VALUE;
final int stateOff = GpsScanStateChanged.State.OFF_VALUE;
final int minTimeDiffMillis = 500;
final int maxTimeDiffMillis = 60_000;
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(),
DeviceUtils.STATSD_ATOM_TEST_PKG,
atom, /*useUidAttributionChain=*/true);
DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests",
"testGpsScan");
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data).hasSize(2);
GpsScanStateChanged a0 = data.get(0).getAtom().getGpsScanStateChanged();
GpsScanStateChanged a1 = data.get(1).getAtom().getGpsScanStateChanged();
AtomTestUtils.assertTimeDiffBetween(data.get(0), data.get(1), minTimeDiffMillis,
maxTimeDiffMillis);
assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
} finally {
if ("null".equals(origWhitelist) || "".equals(origWhitelist)) {
getDevice().executeShellCommand(
"settings delete global location_background_throttle_package_whitelist");
} else {
getDevice().executeShellCommand(String.format(
"settings put global location_background_throttle_package_whitelist %s",
origWhitelist));
}
}
}
public void testMediaCodecActivity() throws Exception {
if (DeviceUtils.hasFeature(getDevice(), DeviceUtils.FEATURE_WATCH)) return;
final int atomTag = Atom.MEDIA_CODEC_STATE_CHANGED_FIELD_NUMBER;
// 5 seconds. Starting video tends to be much slower than most other
// tests on slow devices. This is unfortunate, because it leaves a
// really big slop in assertStatesOccurred. It would be better if
// assertStatesOccurred had a tighter range on large timeouts.
final int waitTime = 5000;
// From {@link VideoPlayerActivity#DELAY_MILLIS}
final int videoDuration = 2000;
Set<Integer> onState = new HashSet<>(
Arrays.asList(MediaCodecStateChanged.State.ON_VALUE));
Set<Integer> offState = new HashSet<>(
Arrays.asList(MediaCodecStateChanged.State.OFF_VALUE));
// Add state sets to the list in order.
List<Set<Integer>> stateSet = Arrays.asList(onState, offState);
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
atomTag, /*useUidAttributionChain=*/true);
DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
"VideoPlayerActivity", "action", "action.play_video",
waitTime);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
// Assert that the events happened in the expected order.
AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, videoDuration,
atom -> atom.getMediaCodecStateChanged().getState().getNumber());
}
public void testOverlayState() throws Exception {
if (DeviceUtils.hasFeature(getDevice(), DeviceUtils.FEATURE_WATCH)) return;
final int atomTag = Atom.OVERLAY_STATE_CHANGED_FIELD_NUMBER;
Set<Integer> entered = new HashSet<>(
Arrays.asList(OverlayStateChanged.State.ENTERED_VALUE));
Set<Integer> exited = new HashSet<>(
Arrays.asList(OverlayStateChanged.State.EXITED_VALUE));
// Add state sets to the list in order.
List<Set<Integer>> stateSet = Arrays.asList(entered, exited);
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
atomTag, /*useUidAttributionChain=*/false);
DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
"StatsdCtsForegroundActivity", "action", "action.show_application_overlay",
5_000);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
// Assert that the events happened in the expected order.
// The overlay box should appear about 2sec after the app start
AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, 0,
atom -> atom.getOverlayStateChanged().getState().getNumber());
}
public void testPictureInPictureState() throws Exception {
String supported = getDevice().executeShellCommand("am supports-multiwindow");
if (DeviceUtils.hasFeature(getDevice(), DeviceUtils.FEATURE_WATCH) ||
!DeviceUtils.hasFeature(getDevice(), FEATURE_PICTURE_IN_PICTURE) ||
!supported.contains("true")) {
LogUtil.CLog.d("Skipping picture in picture atom test.");
return;
}
StatsdConfig.Builder config = ConfigUtils.createConfigBuilder(
DeviceUtils.STATSD_ATOM_TEST_PKG);
FieldValueMatcher.Builder uidFvm = ConfigUtils.createUidFvm(/*uidInAttributionChain=*/false,
DeviceUtils.STATSD_ATOM_TEST_PKG);
// PictureInPictureStateChanged atom is used prior to rvc-qpr
ConfigUtils.addEventMetric(config, Atom.PICTURE_IN_PICTURE_STATE_CHANGED_FIELD_NUMBER,
Collections.singletonList(uidFvm));
// Picture-in-picture logs' been migrated to UiEvent since rvc-qpr
FieldValueMatcher.Builder pkgMatcher = ConfigUtils.createFvm(
UiEventReported.PACKAGE_NAME_FIELD_NUMBER)
.setEqString(DeviceUtils.STATSD_ATOM_TEST_PKG);
ConfigUtils.addEventMetric(config, Atom.UI_EVENT_REPORTED_FIELD_NUMBER,
Arrays.asList(pkgMatcher));
ConfigUtils.uploadConfig(getDevice(), config);
LogUtil.CLog.d("Playing video in Picture-in-Picture mode");
DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
"VideoPlayerActivity", "action", "action.play_video_picture_in_picture_mode");
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
// Filter out the PictureInPictureStateChanged and UiEventReported atom
List<EventMetricData> pictureInPictureStateChangedData = data.stream()
.filter(e -> e.getAtom().hasPictureInPictureStateChanged())
.collect(Collectors.toList());
List<EventMetricData> uiEventReportedData = data.stream()
.filter(e -> e.getAtom().hasUiEventReported())
.collect(Collectors.toList());
assertThat(pictureInPictureStateChangedData).isEmpty();
assertThat(uiEventReportedData).isNotEmpty();
// See PipUiEventEnum for definitions
final int enterPipEventId = 603;
// Assert that log for entering PiP happens exactly once, we do not use
// assertStateOccurred here since PiP may log something else when activity finishes.
List<EventMetricData> entered = uiEventReportedData.stream()
.filter(e -> e.getAtom().getUiEventReported().getEventId() == enterPipEventId)
.collect(Collectors.toList());
assertThat(entered).hasSize(1);
}
//Note: this test does not have uid, but must run on the device
public void testScreenBrightness() throws Exception {
int initialBrightness = getScreenBrightness();
boolean isInitialManual = isScreenBrightnessModeManual();
setScreenBrightnessMode(true);
setScreenBrightness(150);
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
final int atomTag = Atom.SCREEN_BRIGHTNESS_CHANGED_FIELD_NUMBER;
Set<Integer> screenMin = new HashSet<>(Arrays.asList(47));
Set<Integer> screen100 = new HashSet<>(Arrays.asList(100));
// Add state sets to the list in order.
List<Set<Integer>> stateSet = Arrays.asList(screenMin, screen100);
ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
atomTag);
DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testScreenBrightness");
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
// Restore initial screen brightness
setScreenBrightness(initialBrightness);
setScreenBrightnessMode(isInitialManual);
AtomTestUtils.popUntilFind(data, screenMin,
atom -> atom.getScreenBrightnessChanged().getLevel());
AtomTestUtils.popUntilFindFromEnd(data, screen100,
atom -> atom.getScreenBrightnessChanged().getLevel());
// Assert that the events happened in the expected order.
AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, AtomTestUtils.WAIT_TIME_SHORT,
atom -> atom.getScreenBrightnessChanged().getLevel());
}
public void testSyncState() throws Exception {
final int atomTag = Atom.SYNC_STATE_CHANGED_FIELD_NUMBER;
Set<Integer> syncOn = new HashSet<>(Arrays.asList(SyncStateChanged.State.ON_VALUE));
Set<Integer> syncOff = new HashSet<>(Arrays.asList(SyncStateChanged.State.OFF_VALUE));
// Add state sets to the list in order.
List<Set<Integer>> stateSet = Arrays.asList(syncOn, syncOff, syncOn, syncOff);
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
atomTag, /*useUidAttributionChain=*/true);
DeviceUtils.allowImmediateSyncs(getDevice());
DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testSyncState");
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
// Assert that the events happened in the expected order.
AtomTestUtils.assertStatesOccurredInOrder(stateSet, data,
/* wait = */ 0 /* don't verify time differences between state changes */,
atom -> atom.getSyncStateChanged().getState().getNumber());
}
public void testVibratorState() throws Exception {
if (!DeviceUtils.checkDeviceFor(getDevice(), "checkVibratorSupported")) return;
final int atomTag = Atom.VIBRATOR_STATE_CHANGED_FIELD_NUMBER;
final String name = "testVibratorState";
Set<Integer> onState = new HashSet<>(
Arrays.asList(VibratorStateChanged.State.ON_VALUE));
Set<Integer> offState = new HashSet<>(
Arrays.asList(VibratorStateChanged.State.OFF_VALUE));
// Add state sets to the list in order.
List<Set<Integer>> stateSet = Arrays.asList(onState, offState);
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
atomTag, /*useUidAttributionChain=*/true);
DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", name);
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, 300,
atom -> atom.getVibratorStateChanged().getState().getNumber());
}
public void testWakelockState() throws Exception {
final int atomTag = Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER;
Set<Integer> wakelockOn = new HashSet<>(Arrays.asList(
WakelockStateChanged.State.ACQUIRE_VALUE,
WakelockStateChanged.State.CHANGE_ACQUIRE_VALUE));
Set<Integer> wakelockOff = new HashSet<>(Arrays.asList(
WakelockStateChanged.State.RELEASE_VALUE,
WakelockStateChanged.State.CHANGE_RELEASE_VALUE));
final String EXPECTED_TAG = "StatsdPartialWakelock";
final WakeLockLevelEnum EXPECTED_LEVEL = WakeLockLevelEnum.PARTIAL_WAKE_LOCK;
// Add state sets to the list in order.
List<Set<Integer>> stateSet = Arrays.asList(wakelockOn, wakelockOff);
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
atomTag, /*useUidAttributionChain=*/true);
DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testWakelockState");
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
// Assert that the events happened in the expected order.
AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, AtomTestUtils.WAIT_TIME_SHORT,
atom -> atom.getWakelockStateChanged().getState().getNumber());
for (EventMetricData event : data) {
String tag = event.getAtom().getWakelockStateChanged().getTag();
WakeLockLevelEnum type = event.getAtom().getWakelockStateChanged().getType();
assertThat(tag).isEqualTo(EXPECTED_TAG);
assertThat(type).isEqualTo(EXPECTED_LEVEL);
}
}
public void testANROccurred() throws Exception {
final int atomTag = Atom.ANR_OCCURRED_FIELD_NUMBER;
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
atomTag, /*useUidAttributionChain=*/false);
try (AutoCloseable a = DeviceUtils.withActivity(getDevice(),
DeviceUtils.STATSD_ATOM_TEST_PKG, "ANRActivity", null, null)) {
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG * 2);
getDevice().executeShellCommand(
"am broadcast -a action_anr -p " + DeviceUtils.STATSD_ATOM_TEST_PKG);
Thread.sleep(20_000);
}
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data).hasSize(1);
assertThat(data.get(0).getAtom().hasAnrOccurred()).isTrue();
ANROccurred atom = data.get(0).getAtom().getAnrOccurred();
assertThat(atom.getIsInstantApp().getNumber())
.isEqualTo(ANROccurred.InstantApp.FALSE_VALUE);
assertThat(atom.getForegroundState().getNumber())
.isEqualTo(ANROccurred.ForegroundState.FOREGROUND_VALUE);
assertThat(atom.getErrorSource()).isEqualTo(ErrorSource.DATA_APP);
assertThat(atom.getPackageName()).isEqualTo(DeviceUtils.STATSD_ATOM_TEST_PKG);
assertFalse(atom.getIsIncremental());
assertTrue((1 - atom.getLoadingProgress()) < 0.001);
assertEquals(-1, atom.getMillisSinceOldestPendingRead());
}
public void testWriteRawTestAtom() throws Exception {
final int atomTag = Atom.TEST_ATOM_REPORTED_FIELD_NUMBER;
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
atomTag, /*useUidAttributionChain=*/true);
DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testWriteRawTestAtom");
Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
assertThat(data).hasSize(4);
TestAtomReported atom = data.get(0).getAtom().getTestAtomReported();
List<AttributionNode> attrChain = atom.getAttributionNodeList();
assertThat(attrChain).hasSize(2);
assertThat(attrChain.get(0).getUid()).isEqualTo(1234);
assertThat(attrChain.get(0).getTag()).isEqualTo("tag1");
assertThat(attrChain.get(1).getUid()).isEqualTo(
DeviceUtils.getStatsdTestAppUid(getDevice()));
assertThat(attrChain.get(1).getTag()).isEqualTo("tag2");
assertThat(atom.getIntField()).isEqualTo(42);
assertThat(atom.getLongField()).isEqualTo(Long.MAX_VALUE);
assertThat(atom.getFloatField()).isEqualTo(3.14f);
assertThat(atom.getStringField()).isEqualTo("This is a basic test!");
assertThat(atom.getBooleanField()).isFalse();
assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.ON_VALUE);
assertThat(atom.getBytesField().getExperimentIdList())
.containsExactly(1L, 2L, 3L).inOrder();
assertThat(atom.getRepeatedIntFieldList()).containsExactly(3, 6).inOrder();
assertThat(atom.getRepeatedLongFieldList()).containsExactly(1000L, 1002L).inOrder();
assertThat(atom.getRepeatedFloatFieldList()).containsExactly(0.3f, 0.09f).inOrder();
assertThat(atom.getRepeatedStringFieldList()).containsExactly("str1", "str2").inOrder();
assertThat(atom.getRepeatedBooleanFieldList()).containsExactly(true, false).inOrder();
assertThat(atom.getRepeatedEnumFieldList())
.containsExactly(TestAtomReported.State.OFF, TestAtomReported.State.ON)
.inOrder();
atom = data.get(1).getAtom().getTestAtomReported();
attrChain = atom.getAttributionNodeList();
assertThat(attrChain).hasSize(2);
assertThat(attrChain.get(0).getUid()).isEqualTo(9999);
assertThat(attrChain.get(0).getTag()).isEqualTo("tag9999");
assertThat(attrChain.get(1).getUid()).isEqualTo(
DeviceUtils.getStatsdTestAppUid(getDevice()));
assertThat(attrChain.get(1).getTag()).isEmpty();
assertThat(atom.getIntField()).isEqualTo(100);
assertThat(atom.getLongField()).isEqualTo(Long.MIN_VALUE);
assertThat(atom.getFloatField()).isEqualTo(-2.5f);
assertThat(atom.getStringField()).isEqualTo("Test null uid");
assertThat(atom.getBooleanField()).isTrue();
assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.UNKNOWN_VALUE);
assertThat(atom.getBytesField().getExperimentIdList())
.containsExactly(1L, 2L, 3L).inOrder();
assertThat(atom.getRepeatedIntFieldList()).containsExactly(3, 6).inOrder();
assertThat(atom.getRepeatedLongFieldList()).containsExactly(1000L, 1002L).inOrder();
assertThat(atom.getRepeatedFloatFieldList()).containsExactly(0.3f, 0.09f).inOrder();
assertThat(atom.getRepeatedStringFieldList()).containsExactly("str1", "str2").inOrder();
assertThat(atom.getRepeatedBooleanFieldList()).containsExactly(true, false).inOrder();
assertThat(atom.getRepeatedEnumFieldList())
.containsExactly(TestAtomReported.State.OFF, TestAtomReported.State.ON)
.inOrder();
atom = data.get(2).getAtom().getTestAtomReported();
attrChain = atom.getAttributionNodeList();
assertThat(attrChain).hasSize(1);
assertThat(attrChain.get(0).getUid()).isEqualTo(
DeviceUtils.getStatsdTestAppUid(getDevice()));
assertThat(attrChain.get(0).getTag()).isEqualTo("tag1");
assertThat(atom.getIntField()).isEqualTo(-256);
assertThat(atom.getLongField()).isEqualTo(-1234567890L);
assertThat(atom.getFloatField()).isEqualTo(42.01f);
assertThat(atom.getStringField()).isEqualTo("Test non chained");
assertThat(atom.getBooleanField()).isTrue();
assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.OFF_VALUE);
assertThat(atom.getBytesField().getExperimentIdList())
.containsExactly(1L, 2L, 3L).inOrder();
assertThat(atom.getRepeatedIntFieldList()).isEmpty();
assertThat(atom.getRepeatedLongFieldList()).isEmpty();
assertThat(atom.getRepeatedFloatFieldList()).isEmpty();
assertThat(atom.getRepeatedStringFieldList()).isEmpty();
assertThat(atom.getRepeatedBooleanFieldList()).isEmpty();
assertThat(atom.getRepeatedEnumFieldList()).isEmpty();
atom = data.get(3).getAtom().getTestAtomReported();
attrChain = atom.getAttributionNodeList();
assertThat(attrChain).hasSize(1);
assertThat(attrChain.get(0).getUid()).isEqualTo(
DeviceUtils.getStatsdTestAppUid(getDevice()));
assertThat(attrChain.get(0).getTag()).isEmpty();
assertThat(atom.getIntField()).isEqualTo(0);
assertThat(atom.getLongField()).isEqualTo(0L);
assertThat(atom.getFloatField()).isEqualTo(0f);
assertThat(atom.getStringField()).isEmpty();
assertThat(atom.getBooleanField()).isTrue();
assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.OFF_VALUE);
assertThat(atom.getBytesField().getExperimentIdList()).isEmpty();
assertThat(atom.getRepeatedIntFieldList()).isEmpty();
assertThat(atom.getRepeatedLongFieldList()).isEmpty();
assertThat(atom.getRepeatedFloatFieldList()).isEmpty();
assertThat(atom.getRepeatedStringFieldList()).isEmpty();
assertThat(atom.getRepeatedBooleanFieldList()).isEmpty();
assertThat(atom.getRepeatedEnumFieldList()).isEmpty();
}
public void testAppForegroundBackground() throws Exception {
Set<Integer> onStates = new HashSet<>(Arrays.asList(
AppUsageEventOccurred.EventType.MOVE_TO_FOREGROUND_VALUE));
Set<Integer> offStates = new HashSet<>(Arrays.asList(
AppUsageEventOccurred.EventType.MOVE_TO_BACKGROUND_VALUE));
List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
Atom.APP_USAGE_EVENT_OCCURRED_FIELD_NUMBER, /*useUidAttributionChain=*/false);
// Overlay may need to sit there a while.
final int waitTime = 10_500;
DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
"StatsdCtsForegroundActivity", "action", ACTION_SHOW_APPLICATION_OVERLAY, waitTime);
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
Function<Atom, Integer> appUsageStateFunction =
atom -> atom.getAppUsageEventOccurred().getEventType().getNumber();
// clear out initial appusage states
AtomTestUtils.popUntilFind(data, onStates, appUsageStateFunction);
AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, 0, appUsageStateFunction);
}
/*
public void testAppForceStopUsageEvent() throws Exception {
Set<Integer> onStates = new HashSet<>(Arrays.asList(
AppUsageEventOccurred.EventType.MOVE_TO_FOREGROUND_VALUE));
Set<Integer> offStates = new HashSet<>(Arrays.asList(
AppUsageEventOccurred.EventType.MOVE_TO_BACKGROUND_VALUE));
List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
createAndUploadConfig(Atom.APP_USAGE_EVENT_OCCURRED_FIELD_NUMBER, false);
Thread.sleep(WAIT_TIME_FOR_CONFIG_UPDATE_MS);
getDevice().executeShellCommand(String.format(
"am start -n '%s' -e %s %s",
"com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity",
"action", ACTION_LONG_SLEEP_WHILE_TOP));
final int waitTime = EXTRA_WAIT_TIME_MS + 5_000;
Thread.sleep(waitTime);
getDevice().executeShellCommand(String.format(
"am force-stop %s",
"com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity"));
Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS);
List<EventMetricData> data = getEventMetricDataList();
Function<Atom, Integer> appUsageStateFunction =
atom -> atom.getAppUsageEventOccurred().getEventType().getNumber();
popUntilFind(data, onStates, appUsageStateFunction); // clear out initial appusage states.
assertStatesOccurred(stateSet, data, 0, appUsageStateFunction);
}
*/
private int getScreenBrightness() throws Exception {
return Integer.parseInt(
getDevice().executeShellCommand("settings get system screen_brightness").trim());
}
private boolean isScreenBrightnessModeManual() throws Exception {
String mode = getDevice().executeShellCommand("settings get system screen_brightness_mode");
return Integer.parseInt(mode.trim()) == 0;
}
private void setScreenBrightnessMode(boolean manual) throws Exception {
getDevice().executeShellCommand(
"settings put system screen_brightness_mode " + (manual ? 0 : 1));
}
private void setScreenBrightness(int brightness) throws Exception {
getDevice().executeShellCommand("settings put system screen_brightness " + brightness);
}
}