blob: e12f77c9348b40bda94d784e2facafbbc8b77d28 [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.statsd.atom;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import android.net.wifi.WifiModeEnum;
import android.os.WakeLockLevelEnum;
import android.server.ErrorSource;
import com.android.internal.os.StatsdConfigProto.FieldMatcher;
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
import com.android.os.AtomsProto;
import com.android.os.AtomsProto.ANROccurred;
import com.android.os.AtomsProto.AppCrashOccurred;
import com.android.os.AtomsProto.AppOps;
import com.android.os.AtomsProto.AppStartOccurred;
import com.android.os.AtomsProto.Atom;
import com.android.os.AtomsProto.AttributionNode;
import com.android.os.AtomsProto.AudioStateChanged;
import com.android.os.AtomsProto.BinderCalls;
import com.android.os.AtomsProto.BleScanResultReceived;
import com.android.os.AtomsProto.BleScanStateChanged;
import com.android.os.AtomsProto.CameraStateChanged;
import com.android.os.AtomsProto.DangerousPermissionState;
import com.android.os.AtomsProto.DeviceCalculatedPowerBlameUid;
import com.android.os.AtomsProto.FlashlightStateChanged;
import com.android.os.AtomsProto.ForegroundServiceStateChanged;
import com.android.os.AtomsProto.GpsScanStateChanged;
import com.android.os.AtomsProto.HiddenApiUsed;
import com.android.os.AtomsProto.LooperStats;
import com.android.os.AtomsProto.LmkKillOccurred;
import com.android.os.AtomsProto.MediaCodecStateChanged;
import com.android.os.AtomsProto.OverlayStateChanged;
import com.android.os.AtomsProto.PictureInPictureStateChanged;
import com.android.os.AtomsProto.ProcessMemoryHighWaterMark;
import com.android.os.AtomsProto.ProcessMemorySnapshot;
import com.android.os.AtomsProto.ProcessMemoryState;
import com.android.os.AtomsProto.ScheduledJobStateChanged;
import com.android.os.AtomsProto.SyncStateChanged;
import com.android.os.AtomsProto.TestAtomReported;
import com.android.os.AtomsProto.VibratorStateChanged;
import com.android.os.AtomsProto.WakelockStateChanged;
import com.android.os.AtomsProto.WakeupAlarmOccurred;
import com.android.os.AtomsProto.WifiLockStateChanged;
import com.android.os.AtomsProto.WifiMulticastLockStateChanged;
import com.android.os.AtomsProto.WifiScanStateChanged;
import com.android.os.StatsLog.EventMetricData;
import com.android.tradefed.log.LogUtil;
import com.google.common.collect.Range;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Statsd atom tests that are done via app, for atoms that report a uid.
*/
public class UidAtomTests extends DeviceAtomTestCase {
private static final String TAG = "Statsd.UidAtomTests";
private static final boolean DAVEY_ENABLED = false;
@Override
protected void setUp() throws Exception {
super.setUp();
}
@Override
protected void tearDown() throws Exception {
resetBatteryStatus();
super.tearDown();
}
public void testLmkKillOccurred() throws Exception {
if (statsdDisabled() || !"true".equals(getProperty("ro.lmk.log_stats"))) {
return;
}
StatsdConfig.Builder conf = createConfigBuilder()
.addAllowedLogSource("AID_LMKD");
final int atomTag = Atom.LMK_KILL_OCCURRED_FIELD_NUMBER;
addAtomEvent(conf, atomTag, false);
uploadConfig(conf);
Thread.sleep(WAIT_TIME_SHORT);
executeBackgroundService(ACTION_LMK);
Thread.sleep(5_000);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
assertThat(data).hasSize(1);
assertThat(data.get(0).getAtom().hasLmkKillOccurred()).isTrue();
LmkKillOccurred atom = data.get(0).getAtom().getLmkKillOccurred();
assertThat(atom.getUid()).isEqualTo(getUid());
assertThat(atom.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
assertThat(atom.getOomAdjScore()).isAtLeast(500);
}
public void testAppCrashOccurred() throws Exception {
if (statsdDisabled()) {
return;
}
final int atomTag = Atom.APP_CRASH_OCCURRED_FIELD_NUMBER;
createAndUploadConfig(atomTag, false);
Thread.sleep(WAIT_TIME_SHORT);
runActivity("StatsdCtsForegroundActivity", "action", "action.crash");
Thread.sleep(WAIT_TIME_SHORT);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
AppCrashOccurred atom = data.get(0).getAtom().getAppCrashOccurred();
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("com.android.server.cts.device.statsd");
}
public void testAppStartOccurred() throws Exception {
if (statsdDisabled()) {
return;
}
final int atomTag = Atom.APP_START_OCCURRED_FIELD_NUMBER;
createAndUploadConfig(atomTag, false);
Thread.sleep(WAIT_TIME_SHORT);
runActivity("StatsdCtsForegroundActivity", "action", "action.sleep_top");
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
AppStartOccurred atom = data.get(0).getAtom().getAppStartOccurred();
assertThat(atom.getPkgName()).isEqualTo("com.android.server.cts.device.statsd");
assertThat(atom.getActivityName())
.isEqualTo("com.android.server.cts.device.statsd.StatsdCtsForegroundActivity");
assertThat(atom.getIsInstantApp()).isFalse();
assertThat(atom.getActivityStartMillis()).isGreaterThan(0L);
assertThat(atom.getTransitionDelayMillis()).isGreaterThan(0);
}
public void testAudioState() throws Exception {
if (statsdDisabled()) {
return;
}
if (!hasFeature(FEATURE_AUDIO_OUTPUT, true)) 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);
createAndUploadConfig(atomTag, true); // True: uses attribution.
Thread.sleep(WAIT_TIME_SHORT);
runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", name);
Thread.sleep(WAIT_TIME_SHORT);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
// AudioStateChanged timestamp is fuzzed to 5min buckets
assertStatesOccurred(stateSet, data, 0,
atom -> atom.getAudioStateChanged().getState().getNumber());
}
public void testBleScan() throws Exception {
if (statsdDisabled()) {
return;
}
if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
final int atom = Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER;
final int field = BleScanStateChanged.STATE_FIELD_NUMBER;
final int stateOn = BleScanStateChanged.State.ON_VALUE;
final int stateOff = BleScanStateChanged.State.OFF_VALUE;
final int minTimeDiffMillis = 1_500;
final int maxTimeDiffMillis = 3_000;
List<EventMetricData> data = doDeviceMethodOnOff("testBleScanUnoptimized", atom, field,
stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, true);
BleScanStateChanged a0 = data.get(0).getAtom().getBleScanStateChanged();
BleScanStateChanged a1 = data.get(1).getAtom().getBleScanStateChanged();
assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
}
public void testBleUnoptimizedScan() throws Exception {
if (statsdDisabled()) {
return;
}
if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
final int atom = Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER;
final int field = BleScanStateChanged.STATE_FIELD_NUMBER;
final int stateOn = BleScanStateChanged.State.ON_VALUE;
final int stateOff = BleScanStateChanged.State.OFF_VALUE;
final int minTimeDiffMillis = 1_500;
final int maxTimeDiffMillis = 3_000;
List<EventMetricData> data = doDeviceMethodOnOff("testBleScanUnoptimized", atom, field,
stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, true);
BleScanStateChanged a0 = data.get(0).getAtom().getBleScanStateChanged();
assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
assertThat(a0.getIsFiltered()).isFalse();
assertThat(a0.getIsFirstMatch()).isFalse();
assertThat(a0.getIsOpportunistic()).isFalse();
BleScanStateChanged a1 = data.get(1).getAtom().getBleScanStateChanged();
assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
assertThat(a1.getIsFiltered()).isFalse();
assertThat(a1.getIsFirstMatch()).isFalse();
assertThat(a1.getIsOpportunistic()).isFalse();
// Now repeat the test for opportunistic scanning and make sure it is reported correctly.
data = doDeviceMethodOnOff("testBleScanOpportunistic", atom, field,
stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, true);
a0 = data.get(0).getAtom().getBleScanStateChanged();
assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
assertThat(a0.getIsFiltered()).isFalse();
assertThat(a0.getIsFirstMatch()).isFalse();
assertThat(a0.getIsOpportunistic()).isTrue(); // This scan is opportunistic.
a1 = data.get(1).getAtom().getBleScanStateChanged();
assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
assertThat(a1.getIsFiltered()).isFalse();
assertThat(a1.getIsFirstMatch()).isFalse();
assertThat(a1.getIsOpportunistic()).isTrue();
}
public void testBleScanResult() throws Exception {
if (statsdDisabled()) {
return;
}
if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
final int atom = Atom.BLE_SCAN_RESULT_RECEIVED_FIELD_NUMBER;
final int field = BleScanResultReceived.NUM_RESULTS_FIELD_NUMBER;
StatsdConfig.Builder conf = createConfigBuilder();
addAtomEvent(conf, atom, createFvm(field).setGteInt(0));
List<EventMetricData> data = doDeviceMethod("testBleScanResult", conf);
assertThat(data.size()).isAtLeast(1);
BleScanResultReceived a0 = data.get(0).getAtom().getBleScanResultReceived();
assertThat(a0.getNumResults()).isAtLeast(1);
}
public void testHiddenApiUsed() throws Exception {
if (statsdDisabled()) {
return;
}
String oldRate = getDevice().executeShellCommand(
"device_config get app_compat hidden_api_access_statslog_sampling_rate").trim();
getDevice().executeShellCommand(
"device_config put app_compat hidden_api_access_statslog_sampling_rate 65536");
try {
final int atomTag = Atom.HIDDEN_API_USED_FIELD_NUMBER;
createAndUploadConfig(atomTag, false);
runActivity("HiddenApiUsedActivity", null, null);
List<EventMetricData> data = getEventMetricDataList();
assertThat(data).hasSize(1);
HiddenApiUsed atom = data.get(0).getAtom().getHiddenApiUsed();
int uid = getUid();
assertThat(atom.getUid()).isEqualTo(uid);
assertThat(atom.getAccessDenied()).isFalse();
assertThat(atom.getSignature())
.isEqualTo("Landroid/app/Activity;->mWindow:Landroid/view/Window;");
} finally {
if (!oldRate.equals("null")) {
getDevice().executeShellCommand(
"device_config put app_compat hidden_api_access_statslog_sampling_rate "
+ oldRate);
} else {
getDevice().executeShellCommand(
"device_config delete hidden_api_access_statslog_sampling_rate");
}
}
}
public void testCameraState() throws Exception {
if (statsdDisabled()) {
return;
}
if (!hasFeature(FEATURE_CAMERA, true) && !hasFeature(FEATURE_CAMERA_FRONT, true)) 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);
createAndUploadConfig(atomTag, true); // True: uses attribution.
runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testCameraState");
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
// Assert that the events happened in the expected order.
assertStatesOccurred(stateSet, data, WAIT_TIME_LONG,
atom -> atom.getCameraStateChanged().getState().getNumber());
}
public void testCpuTimePerUid() throws Exception {
if (statsdDisabled()) {
return;
}
if (!hasFeature(FEATURE_WATCH, false)) return;
StatsdConfig.Builder config = getPulledConfig();
addGaugeAtomWithDimensions(config, Atom.CPU_TIME_PER_UID_FIELD_NUMBER, null);
uploadConfig(config);
runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testSimpleCpu");
Thread.sleep(WAIT_TIME_SHORT);
setAppBreadcrumbPredicate();
Thread.sleep(WAIT_TIME_LONG);
List<Atom> atomList = getGaugeMetricDataList();
// TODO: We don't have atom matching on gauge yet. Let's refactor this after that feature is
// implemented.
boolean found = false;
int uid = getUid();
for (Atom atom : atomList) {
if (atom.getCpuTimePerUid().getUid() == uid) {
found = true;
assertThat(atom.getCpuTimePerUid().getUserTimeMicros()).isGreaterThan(0L);
assertThat(atom.getCpuTimePerUid().getSysTimeMicros()).isGreaterThan(0L);
}
}
assertWithMessage(String.format("did not find uid %d", uid)).that(found).isTrue();
}
public void testDeviceCalculatedPowerUse() throws Exception {
if (statsdDisabled()) {
return;
}
if (!hasFeature(FEATURE_LEANBACK_ONLY, false)) return;
StatsdConfig.Builder config = getPulledConfig();
addGaugeAtomWithDimensions(config, Atom.DEVICE_CALCULATED_POWER_USE_FIELD_NUMBER, null);
uploadConfig(config);
unplugDevice();
Thread.sleep(WAIT_TIME_LONG);
runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testSimpleCpu");
Thread.sleep(WAIT_TIME_SHORT);
setAppBreadcrumbPredicate();
Thread.sleep(WAIT_TIME_LONG);
Atom atom = getGaugeMetricDataList().get(0);
assertThat(atom.getDeviceCalculatedPowerUse().getComputedPowerNanoAmpSecs())
.isGreaterThan(0L);
}
public void testDeviceCalculatedPowerBlameUid() throws Exception {
if (statsdDisabled()) {
return;
}
if (!hasFeature(FEATURE_LEANBACK_ONLY, false)) return;
StatsdConfig.Builder config = getPulledConfig();
addGaugeAtomWithDimensions(config,
Atom.DEVICE_CALCULATED_POWER_BLAME_UID_FIELD_NUMBER, null);
uploadConfig(config);
unplugDevice();
Thread.sleep(WAIT_TIME_LONG);
runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testSimpleCpu");
Thread.sleep(WAIT_TIME_SHORT);
setAppBreadcrumbPredicate();
Thread.sleep(WAIT_TIME_LONG);
List<Atom> atomList = getGaugeMetricDataList();
boolean uidFound = false;
int uid = getUid();
long uidPower = 0;
for (Atom atom : atomList) {
DeviceCalculatedPowerBlameUid item = atom.getDeviceCalculatedPowerBlameUid();
if (item.getUid() == uid) {
assertWithMessage(String.format("Found multiple power values for uid %d", uid))
.that(uidFound).isFalse();
uidFound = true;
uidPower = item.getPowerNanoAmpSecs();
}
}
assertWithMessage(String.format("No power value for uid %d", uid)).that(uidFound).isTrue();
assertWithMessage(String.format("Non-positive power value for uid %d", uid))
.that(uidPower).isGreaterThan(0L);
}
public void testDavey() throws Exception {
if (statsdDisabled()) {
return;
}
if (!DAVEY_ENABLED ) return;
long MAX_DURATION = 2000;
long MIN_DURATION = 750;
final int atomTag = Atom.DAVEY_OCCURRED_FIELD_NUMBER;
createAndUploadConfig(atomTag, false); // UID is logged without attribution node
runActivity("DaveyActivity", null, null);
List<EventMetricData> data = getEventMetricDataList();
assertThat(data).hasSize(1);
long duration = data.get(0).getAtom().getDaveyOccurred().getJankDurationMillis();
assertWithMessage("Incorrect jank duration")
.that(duration).isIn(Range.closed(MIN_DURATION, MAX_DURATION));
}
public void testFlashlightState() throws Exception {
if (statsdDisabled()) {
return;
}
if (!hasFeature(FEATURE_CAMERA_FLASH, true)) 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);
createAndUploadConfig(atomTag, true); // True: uses attribution.
Thread.sleep(WAIT_TIME_SHORT);
runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", name);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
// Assert that the events happened in the expected order.
assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
atom -> atom.getFlashlightStateChanged().getState().getNumber());
}
public void testForegroundServiceState() throws Exception {
if (statsdDisabled()) {
return;
}
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);
createAndUploadConfig(atomTag, false);
Thread.sleep(WAIT_TIME_SHORT);
runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", name);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
// Assert that the events happened in the expected order.
assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
atom -> atom.getForegroundServiceStateChanged().getState().getNumber());
}
public void testGpsScan() throws Exception {
if (statsdDisabled()) {
return;
}
if (!hasFeature(FEATURE_LOCATION_GPS, true)) 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",
DEVICE_SIDE_TEST_PACKAGE));
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;
List<EventMetricData> data = doDeviceMethodOnOff("testGpsScan", atom, key,
stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, true);
GpsScanStateChanged a0 = data.get(0).getAtom().getGpsScanStateChanged();
GpsScanStateChanged a1 = data.get(1).getAtom().getGpsScanStateChanged();
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 testGpsStatus() throws Exception {
if (statsdDisabled()) {
return;
}
if (!hasFeature(FEATURE_LOCATION_GPS, true)) 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",
DEVICE_SIDE_TEST_PACKAGE));
try {
final int atom = Atom.GPS_LOCATION_STATUS_REPORTED_FIELD_NUMBER;
createAndUploadConfig(atom);
Thread.sleep(WAIT_TIME_SHORT);
runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testGpsStatus");
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
/*
We will sleep for a minimum of 5 seconds and if time to first fix is at max we would
wait for at most 90 seconds. Meaning we should see a minimum of 1 status message and a
maximum of 90 status messages.
*/
assertThat(data.size()).isAtLeast(1);
assertThat(data.size()).isAtMost(90);
} 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 testGpsTimeToFirstFix() throws Exception {
if (statsdDisabled()) {
return;
}
if (!hasFeature(FEATURE_LOCATION_GPS, true)) 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",
DEVICE_SIDE_TEST_PACKAGE));
try {
final int atom = Atom.GPS_TIME_TO_FIRST_FIX_REPORTED_FIELD_NUMBER;
createAndUploadConfig(atom);
Thread.sleep(WAIT_TIME_SHORT);
runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testGpsStatus");
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
assertThat(data.size()).isEqualTo(1);
assertThat(data.get(0).getAtom().getGpsTimeToFirstFixReported()
.getTimeToFirstFixMillis()).isGreaterThan(0);
assertThat(data.get(0).getAtom().getGpsTimeToFirstFixReported()
.getTimeToFirstFixMillis()).isAtMost(90_000);
} 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 (statsdDisabled()) {
return;
}
if (!hasFeature(FEATURE_WATCH, false)) 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;
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);
createAndUploadConfig(atomTag, true); // True: uses attribution.
Thread.sleep(WAIT_TIME_SHORT);
runActivity("VideoPlayerActivity", "action", "action.play_video",
waitTime);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
// Assert that the events happened in the expected order.
assertStatesOccurred(stateSet, data, waitTime,
atom -> atom.getMediaCodecStateChanged().getState().getNumber());
}
public void testOverlayState() throws Exception {
if (statsdDisabled()) {
return;
}
if (!hasFeature(FEATURE_WATCH, false)) 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);
createAndUploadConfig(atomTag, false);
runActivity("StatsdCtsForegroundActivity", "action", "action.show_application_overlay",
5_000);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
// Assert that the events happened in the expected order.
// The overlay box should appear about 2sec after the app start
assertStatesOccurred(stateSet, data, 0,
atom -> atom.getOverlayStateChanged().getState().getNumber());
}
public void testPictureInPictureState() throws Exception {
if (statsdDisabled()) {
return;
}
String supported = getDevice().executeShellCommand("am supports-multiwindow");
if (!hasFeature(FEATURE_WATCH, false) ||
!hasFeature(FEATURE_PICTURE_IN_PICTURE, true) ||
!supported.contains("true")) {
LogUtil.CLog.d("Skipping picture in picture atom test.");
return;
}
final int atomTag = Atom.PICTURE_IN_PICTURE_STATE_CHANGED_FIELD_NUMBER;
Set<Integer> entered = new HashSet<>(
Arrays.asList(PictureInPictureStateChanged.State.ENTERED_VALUE));
// Add state sets to the list in order.
List<Set<Integer>> stateSet = Arrays.asList(entered);
createAndUploadConfig(atomTag, false);
LogUtil.CLog.d("Playing video in Picture-in-Picture mode");
runActivity("VideoPlayerActivity", "action", "action.play_video_picture_in_picture_mode");
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
// Assert that the events happened in the expected order.
assertStatesOccurred(stateSet, data, WAIT_TIME_LONG,
atom -> atom.getPictureInPictureStateChanged().getState().getNumber());
}
public void testScheduledJobState() throws Exception {
if (statsdDisabled()) {
return;
}
String expectedName = "com.android.server.cts.device.statsd/.StatsdJobService";
final int atomTag = Atom.SCHEDULED_JOB_STATE_CHANGED_FIELD_NUMBER;
Set<Integer> jobSchedule = new HashSet<>(
Arrays.asList(ScheduledJobStateChanged.State.SCHEDULED_VALUE));
Set<Integer> jobOn = new HashSet<>(
Arrays.asList(ScheduledJobStateChanged.State.STARTED_VALUE));
Set<Integer> jobOff = new HashSet<>(
Arrays.asList(ScheduledJobStateChanged.State.FINISHED_VALUE));
// Add state sets to the list in order.
List<Set<Integer>> stateSet = Arrays.asList(jobSchedule, jobOn, jobOff);
createAndUploadConfig(atomTag, true); // True: uses attribution.
allowImmediateSyncs();
runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testScheduledJob");
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
assertStatesOccurred(stateSet, data, 0,
atom -> atom.getScheduledJobStateChanged().getState().getNumber());
for (EventMetricData e : data) {
assertThat(e.getAtom().getScheduledJobStateChanged().getJobName())
.isEqualTo(expectedName);
}
}
//Note: this test does not have uid, but must run on the device
public void testScreenBrightness() throws Exception {
if (statsdDisabled()) {
return;
}
int initialBrightness = getScreenBrightness();
boolean isInitialManual = isScreenBrightnessModeManual();
setScreenBrightnessMode(true);
setScreenBrightness(200);
Thread.sleep(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));
Set<Integer> screen200 = new HashSet<>(Arrays.asList(198));
// Set<Integer> screenMax = new HashSet<>(Arrays.asList(255));
// Add state sets to the list in order.
List<Set<Integer>> stateSet = Arrays.asList(screenMin, screen100, screen200);
createAndUploadConfig(atomTag);
Thread.sleep(WAIT_TIME_SHORT);
runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testScreenBrightness");
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
// Restore initial screen brightness
setScreenBrightness(initialBrightness);
setScreenBrightnessMode(isInitialManual);
popUntilFind(data, screenMin, atom->atom.getScreenBrightnessChanged().getLevel());
popUntilFindFromEnd(data, screen200, atom->atom.getScreenBrightnessChanged().getLevel());
// Assert that the events happened in the expected order.
assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
atom -> atom.getScreenBrightnessChanged().getLevel());
}
public void testSyncState() throws Exception {
if (statsdDisabled()) {
return;
}
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);
createAndUploadConfig(atomTag, true);
allowImmediateSyncs();
runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testSyncState");
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
// Assert that the events happened in the expected order.
assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
atom -> atom.getSyncStateChanged().getState().getNumber());
}
public void testVibratorState() throws Exception {
if (statsdDisabled()) {
return;
}
if (!checkDeviceFor("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);
createAndUploadConfig(atomTag, true); // True: uses attribution.
Thread.sleep(WAIT_TIME_SHORT);
runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", name);
Thread.sleep(WAIT_TIME_LONG);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
assertStatesOccurred(stateSet, data, 300,
atom -> atom.getVibratorStateChanged().getState().getNumber());
}
public void testWakelockState() throws Exception {
if (statsdDisabled()) {
return;
}
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);
createAndUploadConfig(atomTag, true); // True: uses attribution.
runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testWakelockState");
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
// Assert that the events happened in the expected order.
assertStatesOccurred(stateSet, data, 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 testWakeupAlarm() throws Exception {
if (statsdDisabled()) {
return;
}
// For automotive, all wakeup alarm becomes normal alarm. So this
// test does not work.
if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
final int atomTag = Atom.WAKEUP_ALARM_OCCURRED_FIELD_NUMBER;
StatsdConfig.Builder config = createConfigBuilder();
addAtomEvent(config, atomTag, true); // True: uses attribution.
List<EventMetricData> data = doDeviceMethod("testWakeupAlarm", config);
assertThat(data.size()).isAtLeast(1);
for (int i = 0; i < data.size(); i++) {
WakeupAlarmOccurred wao = data.get(i).getAtom().getWakeupAlarmOccurred();
assertThat(wao.getTag()).isEqualTo("*walarm*:android.cts.statsd.testWakeupAlarm");
assertThat(wao.getPackageName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
}
}
public void testWifiLockHighPerf() throws Exception {
if (statsdDisabled()) {
return;
}
if (!hasFeature(FEATURE_WIFI, true)) return;
if (!hasFeature(FEATURE_PC, false)) return;
final int atomTag = Atom.WIFI_LOCK_STATE_CHANGED_FIELD_NUMBER;
Set<Integer> lockOn = new HashSet<>(Arrays.asList(WifiLockStateChanged.State.ON_VALUE));
Set<Integer> lockOff = new HashSet<>(Arrays.asList(WifiLockStateChanged.State.OFF_VALUE));
// Add state sets to the list in order.
List<Set<Integer>> stateSet = Arrays.asList(lockOn, lockOff);
createAndUploadConfig(atomTag, true); // True: uses attribution.
runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testWifiLockHighPerf");
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
// Assert that the events happened in the expected order.
assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
atom -> atom.getWifiLockStateChanged().getState().getNumber());
for (EventMetricData event : data) {
assertThat(event.getAtom().getWifiLockStateChanged().getMode())
.isEqualTo(WifiModeEnum.WIFI_MODE_FULL_HIGH_PERF);
}
}
public void testWifiLockLowLatency() throws Exception {
if (statsdDisabled()) {
return;
}
if (!hasFeature(FEATURE_WIFI, true)) return;
if (!hasFeature(FEATURE_PC, false)) return;
final int atomTag = Atom.WIFI_LOCK_STATE_CHANGED_FIELD_NUMBER;
Set<Integer> lockOn = new HashSet<>(Arrays.asList(WifiLockStateChanged.State.ON_VALUE));
Set<Integer> lockOff = new HashSet<>(Arrays.asList(WifiLockStateChanged.State.OFF_VALUE));
// Add state sets to the list in order.
List<Set<Integer>> stateSet = Arrays.asList(lockOn, lockOff);
createAndUploadConfig(atomTag, true); // True: uses attribution.
runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testWifiLockLowLatency");
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
// Assert that the events happened in the expected order.
assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
atom -> atom.getWifiLockStateChanged().getState().getNumber());
for (EventMetricData event : data) {
assertThat(event.getAtom().getWifiLockStateChanged().getMode())
.isEqualTo(WifiModeEnum.WIFI_MODE_FULL_LOW_LATENCY);
}
}
public void testWifiMulticastLock() throws Exception {
if (statsdDisabled()) {
return;
}
if (!hasFeature(FEATURE_WIFI, true)) return;
if (!hasFeature(FEATURE_PC, false)) return;
final int atomTag = Atom.WIFI_MULTICAST_LOCK_STATE_CHANGED_FIELD_NUMBER;
Set<Integer> lockOn = new HashSet<>(
Arrays.asList(WifiMulticastLockStateChanged.State.ON_VALUE));
Set<Integer> lockOff = new HashSet<>(
Arrays.asList(WifiMulticastLockStateChanged.State.OFF_VALUE));
final String EXPECTED_TAG = "StatsdCTSMulticastLock";
// Add state sets to the list in order.
List<Set<Integer>> stateSet = Arrays.asList(lockOn, lockOff);
createAndUploadConfig(atomTag, true);
runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testWifiMulticastLock");
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
// Assert that the events happened in the expected order.
assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
atom -> atom.getWifiMulticastLockStateChanged().getState().getNumber());
for (EventMetricData event: data) {
String tag = event.getAtom().getWifiMulticastLockStateChanged().getTag();
assertThat(tag).isEqualTo(EXPECTED_TAG);
}
}
public void testWifiScan() throws Exception {
if (statsdDisabled()) {
return;
}
if (!hasFeature(FEATURE_WIFI, true)) return;
final int atom = Atom.WIFI_SCAN_STATE_CHANGED_FIELD_NUMBER;
final int key = WifiScanStateChanged.STATE_FIELD_NUMBER;
final int stateOn = WifiScanStateChanged.State.ON_VALUE;
final int stateOff = WifiScanStateChanged.State.OFF_VALUE;
final int minTimeDiffMillis = 250;
final int maxTimeDiffMillis = 60_000;
final boolean demandExactlyTwo = false; // Two scans are performed, so up to 4 atoms logged.
List<EventMetricData> data = doDeviceMethodOnOff("testWifiScan", atom, key,
stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, demandExactlyTwo);
assertThat(data.size()).isIn(Range.closed(2, 4));
WifiScanStateChanged a0 = data.get(0).getAtom().getWifiScanStateChanged();
WifiScanStateChanged a1 = data.get(1).getAtom().getWifiScanStateChanged();
assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
}
public void testBinderStats() throws Exception {
if (statsdDisabled()) {
return;
}
try {
unplugDevice();
Thread.sleep(WAIT_TIME_SHORT);
enableBinderStats();
binderStatsNoSampling();
resetBinderStats();
StatsdConfig.Builder config = getPulledConfig();
addGaugeAtomWithDimensions(config, Atom.BINDER_CALLS_FIELD_NUMBER, null);
uploadConfig(config);
Thread.sleep(WAIT_TIME_SHORT);
runActivity("StatsdCtsForegroundActivity", "action", "action.show_notification",3_000);
setAppBreadcrumbPredicate();
Thread.sleep(WAIT_TIME_SHORT);
boolean found = false;
int uid = getUid();
List<Atom> atomList = getGaugeMetricDataList();
for (Atom atom : atomList) {
BinderCalls calls = atom.getBinderCalls();
boolean classMatches = calls.getServiceClassName().contains(
"com.android.server.notification.NotificationManagerService");
boolean methodMatches = calls.getServiceMethodName()
.equals("createNotificationChannels");
if (calls.getUid() == uid && classMatches && methodMatches) {
found = true;
assertThat(calls.getRecordedCallCount()).isGreaterThan(0L);
assertThat(calls.getCallCount()).isGreaterThan(0L);
assertThat(calls.getRecordedTotalLatencyMicros())
.isIn(Range.open(0L, 1000000L));
assertThat(calls.getRecordedTotalCpuMicros()).isIn(Range.open(0L, 1000000L));
}
}
assertWithMessage(String.format("Did not find a matching atom for uid %d", uid))
.that(found).isTrue();
} finally {
disableBinderStats();
plugInAc();
}
}
public void testLooperStats() throws Exception {
if (statsdDisabled()) {
return;
}
try {
unplugDevice();
setUpLooperStats();
StatsdConfig.Builder config = getPulledConfig();
addGaugeAtomWithDimensions(config, Atom.LOOPER_STATS_FIELD_NUMBER, null);
uploadConfig(config);
Thread.sleep(WAIT_TIME_SHORT);
runActivity("StatsdCtsForegroundActivity", "action", "action.show_notification", 3_000);
setAppBreadcrumbPredicate();
Thread.sleep(WAIT_TIME_SHORT);
List<Atom> atomList = getGaugeMetricDataList();
boolean found = false;
int uid = getUid();
for (Atom atom : atomList) {
LooperStats stats = atom.getLooperStats();
String notificationServiceFullName =
"com.android.server.notification.NotificationManagerService";
boolean handlerMatches =
stats.getHandlerClassName().equals(
notificationServiceFullName + "$WorkerHandler");
boolean messageMatches =
stats.getMessageName().equals(
notificationServiceFullName + "$EnqueueNotificationRunnable");
if (atom.getLooperStats().getUid() == uid && handlerMatches && messageMatches) {
found = true;
assertThat(stats.getMessageCount()).isGreaterThan(0L);
assertThat(stats.getRecordedMessageCount()).isGreaterThan(0L);
assertThat(stats.getRecordedTotalLatencyMicros())
.isIn(Range.open(0L, 1000000L));
assertThat(stats.getRecordedTotalCpuMicros()).isIn(Range.open(0L, 1000000L));
assertThat(stats.getRecordedMaxLatencyMicros()).isIn(Range.open(0L, 1000000L));
assertThat(stats.getRecordedMaxCpuMicros()).isIn(Range.open(0L, 1000000L));
assertThat(stats.getRecordedDelayMessageCount()).isGreaterThan(0L);
assertThat(stats.getRecordedTotalDelayMillis())
.isIn(Range.closedOpen(0L, 5000L));
assertThat(stats.getRecordedMaxDelayMillis()).isIn(Range.closedOpen(0L, 5000L));
}
}
assertWithMessage(String.format("Did not find a matching atom for uid %d", uid))
.that(found).isTrue();
} finally {
cleanUpLooperStats();
plugInAc();
}
}
public void testProcessMemoryState() throws Exception {
if (statsdDisabled()) {
return;
}
// Get ProcessMemoryState as a simple gauge metric.
StatsdConfig.Builder config = getPulledConfig();
addGaugeAtomWithDimensions(config, Atom.PROCESS_MEMORY_STATE_FIELD_NUMBER, null);
uploadConfig(config);
Thread.sleep(WAIT_TIME_SHORT);
// Start test app.
try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
"action.show_notification")) {
Thread.sleep(WAIT_TIME_SHORT);
// Trigger a pull and wait for new pull before killing the process.
setAppBreadcrumbPredicate();
Thread.sleep(WAIT_TIME_LONG);
}
// Assert about ProcessMemoryState for the test app.
List<Atom> atoms = getGaugeMetricDataList();
int uid = getUid();
boolean found = false;
for (Atom atom : atoms) {
ProcessMemoryState state = atom.getProcessMemoryState();
if (state.getUid() != uid) {
continue;
}
found = true;
assertThat(state.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
assertThat(state.getOomAdjScore()).isAtLeast(0);
assertThat(state.getPageFault()).isAtLeast(0L);
assertThat(state.getPageMajorFault()).isAtLeast(0L);
assertThat(state.getRssInBytes()).isGreaterThan(0L);
assertThat(state.getCacheInBytes()).isAtLeast(0L);
assertThat(state.getSwapInBytes()).isAtLeast(0L);
}
assertWithMessage(String.format("Did not find a matching atom for uid %d", uid))
.that(found).isTrue();
}
public void testProcessMemoryHighWaterMark() throws Exception {
if (statsdDisabled()) {
return;
}
// Get ProcessMemoryHighWaterMark as a simple gauge metric.
StatsdConfig.Builder config = getPulledConfig();
addGaugeAtomWithDimensions(config, Atom.PROCESS_MEMORY_HIGH_WATER_MARK_FIELD_NUMBER, null);
uploadConfig(config);
Thread.sleep(WAIT_TIME_SHORT);
// Start test app and trigger a pull while it is running.
try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
"action.show_notification")) {
Thread.sleep(WAIT_TIME_SHORT);
// Trigger a pull and wait for new pull before killing the process.
setAppBreadcrumbPredicate();
Thread.sleep(WAIT_TIME_LONG);
}
// Assert about ProcessMemoryHighWaterMark for the test app, statsd and system server.
List<Atom> atoms = getGaugeMetricDataList();
int uid = getUid();
boolean foundTestApp = false;
boolean foundStatsd = false;
boolean foundSystemServer = false;
for (Atom atom : atoms) {
ProcessMemoryHighWaterMark state = atom.getProcessMemoryHighWaterMark();
if (state.getUid() == uid) {
foundTestApp = true;
assertThat(state.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
assertThat(state.getRssHighWaterMarkInBytes()).isGreaterThan(0L);
} else if (state.getProcessName().contains("/statsd")) {
foundStatsd = true;
assertThat(state.getRssHighWaterMarkInBytes()).isGreaterThan(0L);
} else if (state.getProcessName().equals("system")) {
foundSystemServer = true;
assertThat(state.getRssHighWaterMarkInBytes()).isGreaterThan(0L);
}
}
assertWithMessage(String.format("Did not find a matching atom for test app uid=%d",uid))
.that(foundTestApp).isTrue();
assertWithMessage("Did not find a matching atom for statsd").that(foundStatsd).isTrue();
assertWithMessage("Did not find a matching atom for system server")
.that(foundSystemServer).isTrue();
}
public void testProcessMemorySnapshot() throws Exception {
if (statsdDisabled()) {
return;
}
// Get ProcessMemorySnapshot as a simple gauge metric.
StatsdConfig.Builder config = getPulledConfig();
addGaugeAtomWithDimensions(config, Atom.PROCESS_MEMORY_SNAPSHOT_FIELD_NUMBER, null);
uploadConfig(config);
Thread.sleep(WAIT_TIME_SHORT);
// Start test app and trigger a pull while it is running.
try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
"action.show_notification")) {
setAppBreadcrumbPredicate();
Thread.sleep(WAIT_TIME_LONG);
}
// Assert about ProcessMemorySnapshot for the test app, statsd and system server.
List<Atom> atoms = getGaugeMetricDataList();
int uid = getUid();
boolean foundTestApp = false;
boolean foundStatsd = false;
boolean foundSystemServer = false;
for (Atom atom : atoms) {
ProcessMemorySnapshot snapshot = atom.getProcessMemorySnapshot();
if (snapshot.getUid() == uid) {
foundTestApp = true;
assertThat(snapshot.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
} else if (snapshot.getProcessName().contains("/statsd")) {
foundStatsd = true;
} else if (snapshot.getProcessName().equals("system")) {
foundSystemServer = true;
}
assertThat(snapshot.getPid()).isGreaterThan(0);
assertThat(snapshot.getAnonRssAndSwapInKilobytes()).isGreaterThan(0);
assertThat(snapshot.getAnonRssAndSwapInKilobytes()).isEqualTo(
snapshot.getAnonRssInKilobytes() + snapshot.getSwapInKilobytes());
assertThat(snapshot.getRssInKilobytes()).isAtLeast(0);
assertThat(snapshot.getAnonRssInKilobytes()).isAtLeast(0);
assertThat(snapshot.getSwapInKilobytes()).isAtLeast(0);
}
assertWithMessage(String.format("Did not find a matching atom for test app uid=%d",uid))
.that(foundTestApp).isTrue();
assertWithMessage("Did not find a matching atom for statsd").that(foundStatsd).isTrue();
assertWithMessage("Did not find a matching atom for system server")
.that(foundSystemServer).isTrue();
}
/**
* The the app id from a uid.
*
* @param uid The uid of the app
*
* @return The app id of the app
*
* @see android.os.UserHandle#getAppId
*/
private static int getAppId(int uid) {
return uid % 100000;
}
public void testRoleHolder() throws Exception {
if (statsdDisabled()) {
return;
}
// Make device side test package a role holder
String callScreenAppRole = "android.app.role.CALL_SCREENING";
getDevice().executeShellCommand(
"cmd role add-role-holder " + callScreenAppRole + " " + DEVICE_SIDE_TEST_PACKAGE);
// Set up what to collect
StatsdConfig.Builder config = getPulledConfig();
addGaugeAtomWithDimensions(config, Atom.ROLE_HOLDER_FIELD_NUMBER, null);
uploadConfig(config);
Thread.sleep(WAIT_TIME_SHORT);
boolean verifiedKnowRoleState = false;
// Pull a report
setAppBreadcrumbPredicate();
Thread.sleep(WAIT_TIME_SHORT);
int testAppId = getAppId(getUid());
for (Atom atom : getGaugeMetricDataList()) {
AtomsProto.RoleHolder roleHolder = atom.getRoleHolder();
assertThat(roleHolder.getPackageName()).isNotNull();
assertThat(roleHolder.getUid()).isAtLeast(0);
assertThat(roleHolder.getRole()).isNotNull();
if (roleHolder.getPackageName().equals(DEVICE_SIDE_TEST_PACKAGE)) {
assertThat(getAppId(roleHolder.getUid())).isEqualTo(testAppId);
assertThat(roleHolder.getPackageName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
assertThat(roleHolder.getRole()).isEqualTo(callScreenAppRole);
verifiedKnowRoleState = true;
}
}
assertThat(verifiedKnowRoleState).isTrue();
}
public void testDangerousPermissionState() throws Exception {
if (statsdDisabled()) {
return;
}
final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED = 1 << 8;
final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED = 1 << 9;
// Set up what to collect
StatsdConfig.Builder config = getPulledConfig();
addGaugeAtomWithDimensions(config, Atom.DANGEROUS_PERMISSION_STATE_FIELD_NUMBER, null);
uploadConfig(config);
Thread.sleep(WAIT_TIME_SHORT);
boolean verifiedKnowPermissionState = false;
// Pull a report
setAppBreadcrumbPredicate();
Thread.sleep(WAIT_TIME_SHORT);
int testAppId = getAppId(getUid());
for (Atom atom : getGaugeMetricDataList()) {
DangerousPermissionState permissionState = atom.getDangerousPermissionState();
assertThat(permissionState.getPermissionName()).isNotNull();
assertThat(permissionState.getUid()).isAtLeast(0);
assertThat(permissionState.getPackageName()).isNotNull();
if (getAppId(permissionState.getUid()) == testAppId) {
if (permissionState.getPermissionName().equals(
"android.permission.ACCESS_FINE_LOCATION")) {
assertThat(permissionState.getIsGranted()).isTrue();
assertThat(permissionState.getPermissionFlags() & (~(
FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED
| FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED)))
.isEqualTo(0);
verifiedKnowPermissionState = true;
}
}
}
assertThat(verifiedKnowPermissionState).isTrue();
}
public void testAppOps() throws Exception {
if (statsdDisabled()) {
return;
}
// Set up what to collect
StatsdConfig.Builder config = getPulledConfig();
addGaugeAtomWithDimensions(config, Atom.APP_OPS_FIELD_NUMBER, null);
uploadConfig(config);
Thread.sleep(WAIT_TIME_SHORT);
// Pull a report
setAppBreadcrumbPredicate();
Thread.sleep(WAIT_TIME_SHORT);
int accessInstancesRecorded = 0;
for (Atom atom : getGaugeMetricDataList()) {
AppOps appOps = atom.getAppOps();
accessInstancesRecorded += appOps.getTrustedForegroundGrantedCount();
}
assertThat(accessInstancesRecorded).isAtLeast(1);
}
public void testANROccurred() throws Exception {
if (statsdDisabled()) {
return;
}
final int atomTag = Atom.ANR_OCCURRED_FIELD_NUMBER;
createAndUploadConfig(atomTag, false);
Thread.sleep(WAIT_TIME_SHORT);
try (AutoCloseable a = withActivity("ANRActivity", null, null)) {
Thread.sleep(WAIT_TIME_SHORT);
getDevice().executeShellCommand(
"am broadcast -a action_anr -p " + DEVICE_SIDE_TEST_PACKAGE);
Thread.sleep(20_000);
}
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
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(DEVICE_SIDE_TEST_PACKAGE);
}
public void testWriteRawTestAtom() throws Exception {
if (statsdDisabled()) {
return;
}
final int atomTag = Atom.TEST_ATOM_REPORTED_FIELD_NUMBER;
createAndUploadConfig(atomTag, true);
Thread.sleep(WAIT_TIME_SHORT);
runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testWriteRawTestAtom");
Thread.sleep(WAIT_TIME_SHORT);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
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(getUid());
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();
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(getUid());
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();
atom = data.get(2).getAtom().getTestAtomReported();
attrChain = atom.getAttributionNodeList();
assertThat(attrChain).hasSize(1);
assertThat(attrChain.get(0).getUid()).isEqualTo(getUid());
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();
atom = data.get(3).getAtom().getTestAtomReported();
attrChain = atom.getAttributionNodeList();
assertThat(attrChain).hasSize(1);
assertThat(attrChain.get(0).getUid()).isEqualTo(getUid());
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();
}
}