| /* |
| * 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.android.os.AtomsProto.IntegrityCheckResultReported.Response.ALLOWED; |
| |
| 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.net.wifi.WifiModeEnum; |
| import android.os.WakeLockLevelEnum; |
| import android.server.ErrorSource; |
| import android.telephony.NetworkTypeEnum; |
| |
| import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; |
| 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; |
| import com.android.os.AtomsProto.ANROccurred; |
| import com.android.os.AtomsProto.AppBreadcrumbReported; |
| import com.android.os.AtomsProto.AppCrashOccurred; |
| import com.android.os.AtomsProto.AppOps; |
| import com.android.os.AtomsProto.AppStartOccurred; |
| import com.android.os.AtomsProto.AppUsageEventOccurred; |
| import com.android.os.AtomsProto.Atom; |
| import com.android.os.AtomsProto.AttributedAppOps; |
| 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.BlobCommitted; |
| import com.android.os.AtomsProto.BlobLeased; |
| import com.android.os.AtomsProto.BlobOpened; |
| import com.android.os.AtomsProto.CameraStateChanged; |
| import com.android.os.AtomsProto.DangerousPermissionState; |
| import com.android.os.AtomsProto.DangerousPermissionStateSampled; |
| import com.android.os.AtomsProto.DeviceCalculatedPowerBlameUid; |
| 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.IntegrityCheckResultReported; |
| import com.android.os.AtomsProto.IonHeapSize; |
| import com.android.os.AtomsProto.LmkKillOccurred; |
| import com.android.os.AtomsProto.LooperStats; |
| import com.android.os.AtomsProto.MediaCodecStateChanged; |
| import com.android.os.AtomsProto.NotificationReported; |
| import com.android.os.AtomsProto.OverlayStateChanged; |
| import com.android.os.AtomsProto.PackageNotificationChannelGroupPreferences; |
| import com.android.os.AtomsProto.PackageNotificationChannelPreferences; |
| import com.android.os.AtomsProto.PackageNotificationPreferences; |
| 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.SettingSnapshot; |
| 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.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.server.notification.SmallHash; |
| import com.android.tradefed.log.LogUtil; |
| import com.android.tradefed.testtype.DeviceTestCase; |
| |
| import com.google.common.collect.Range; |
| import com.google.protobuf.Descriptors; |
| |
| import java.io.File; |
| 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. |
| * |
| * TODO: Once all tests have migrated to use the new helper test library, this class should not |
| * extend DeviceAtomTestCase. Instead, this class should extend DeviceTestCase and implement |
| * IBuildReceiver. At the same time, the setBuild function should be overridden. |
| */ |
| public class UidAtomTests extends DeviceAtomTestCase { |
| |
| private static final String TAG = "Statsd.UidAtomTests"; |
| |
| private static final String TEST_PACKAGE_NAME = "com.android.server.cts.device.statsd"; |
| |
| private static final boolean DAVEY_ENABLED = false; |
| |
| private static final int NUM_APP_OPS = AttributedAppOps.getDefaultInstance().getOp(). |
| getDescriptorForType().getValues().size() - 1; |
| |
| private static final String TEST_INSTALL_APK = "CtsStatsdAtomEmptyApp.apk"; |
| private static final String TEST_INSTALL_APK_BASE = "CtsStatsdAtomEmptySplitApp.apk"; |
| private static final String TEST_INSTALL_APK_SPLIT = "CtsStatsdAtomEmptySplitApp_pl.apk"; |
| private static final String TEST_INSTALL_PACKAGE = |
| "com.android.cts.device.statsdatom.emptyapp"; |
| private static final String TEST_REMOTE_DIR = "/data/local/tmp/statsdatom"; |
| private static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay"; |
| private static final String ACTION_LONG_SLEEP_WHILE_TOP = "action.long_sleep_top"; |
| |
| private static final int WAIT_TIME_FOR_CONFIG_UPDATE_MS = 200; |
| private static final int EXTRA_WAIT_TIME_MS = 5_000; // as buffer when app starting/stopping. |
| private static final int STATSD_REPORT_WAIT_TIME_MS = 500; // make sure statsd finishes log. |
| |
| private static final String FEATURE_AUDIO_OUTPUT = "android.hardware.audio.output"; |
| private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive"; |
| 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_PC = "android.hardware.type.pc"; |
| private static final String FEATURE_PICTURE_IN_PICTURE = "android.software.picture_in_picture"; |
| private static final String FEATURE_INCREMENTAL_DELIVERY = |
| "android.software.incremental_delivery"; |
| private static final String FEATURE_WIFI = "android.hardware.wifi"; |
| |
| @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(); |
| } |
| |
| /** |
| * 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); |
| } |
| |
| public void testLmkKillOccurred() throws Exception { |
| if (!"true".equals(DeviceUtils.getProperty(getDevice(), "ro.lmk.log_stats"))) { |
| return; |
| } |
| |
| final int atomTag = Atom.LMK_KILL_OCCURRED_FIELD_NUMBER; |
| final String actionLmk = "action.lmk"; |
| ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| atomTag, /*uidInAttributionChain=*/false); |
| |
| DeviceUtils.executeBackgroundService(getDevice(), actionLmk); |
| Thread.sleep(15_000); |
| |
| // Sorted list of events in order in which they occurred. |
| List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice()); |
| int appUid = DeviceUtils.getStatsdTestAppUid(getDevice()); |
| |
| assertThat(data).hasSize(1); |
| assertThat(data.get(0).getAtom().hasLmkKillOccurred()).isTrue(); |
| LmkKillOccurred atom = data.get(0).getAtom().getLmkKillOccurred(); |
| assertThat(atom.getUid()).isEqualTo(appUid); |
| assertThat(atom.getProcessName()).isEqualTo(DeviceUtils.STATSD_ATOM_TEST_PKG); |
| assertThat(atom.getOomAdjScore()).isAtLeast(500); |
| } |
| |
| 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(); |
| 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); |
| } |
| |
| public void testAppStartOccurred() throws Exception { |
| final int atomTag = Atom.APP_START_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.sleep_top", 3_500); |
| |
| // Sorted list of events in order in which they occurred. |
| List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice()); |
| |
| assertThat(data).hasSize(1); |
| AppStartOccurred atom = data.get(0).getAtom().getAppStartOccurred(); |
| assertThat(atom.getPkgName()).isEqualTo(DeviceUtils.STATSD_ATOM_TEST_PKG); |
| assertThat(atom.getActivityName()) |
| .isEqualTo("com.android.server.cts.device.statsdatom.StatsdCtsForegroundActivity"); |
| assertThat(atom.getIsInstantApp()).isFalse(); |
| assertThat(atom.getActivityStartMillis()).isGreaterThan(0L); |
| assertThat(atom.getTransitionDelayMillis()).isGreaterThan(0); |
| } |
| |
| 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()); |
| |
| // Because the timestamp is truncated, we skip checking time differences between state |
| // changes. |
| AtomTestUtils.assertStatesOccurred(stateSet, data, 0, |
| atom -> atom.getAudioStateChanged().getState().getNumber()); |
| |
| // 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.assertStatesOccurred(stateSet, data, AtomTestUtils.WAIT_TIME_LONG, |
| atom -> atom.getCameraStateChanged().getState().getNumber()); |
| } |
| |
| public void testDeviceCalculatedPowerUse() throws Exception { |
| 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 testDeviceCalculatedPowerBlameUid() throws Exception { |
| if (!DeviceUtils.hasFeature(getDevice(), FEATURE_LEANBACK_ONLY)) return; |
| if (!DeviceUtils.hasBattery(getDevice())) { |
| return; |
| } |
| String kernelVersion = getDevice().executeShellCommand("uname -r"); |
| if (kernelVersion.contains("3.18")) { |
| LogUtil.CLog.d("Skipping calculated power blame uid test."); |
| return; |
| } |
| ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| Atom.DEVICE_CALCULATED_POWER_BLAME_UID_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); |
| |
| List<Atom> atomList = ReportUtils.getGaugeMetricAtoms(getDevice()); |
| boolean uidFound = false; |
| int uid = DeviceUtils.getStatsdTestAppUid(getDevice()); |
| 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); |
| DeviceUtils.resetBatteryStatus(getDevice()); |
| } |
| |
| public void testDavey() throws Exception { |
| if (!DAVEY_ENABLED) return; |
| long MAX_DURATION = 2000; |
| long MIN_DURATION = 750; |
| final int atomTag = Atom.DAVEY_OCCURRED_FIELD_NUMBER; |
| |
| ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| atomTag, /*useUidAttributionChain=*/false); |
| |
| DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| "DaveyActivity", /*actionKey=*/null, /*actionValue=*/null); |
| |
| List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice()); |
| 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 (!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.assertStatesOccurred(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.assertStatesOccurred(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 testGnssStats() throws Exception { |
| // Get GnssMetrics as a simple gauge metric. |
| ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| Atom.GNSS_STATS_FIELD_NUMBER); |
| |
| 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 { |
| DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testGpsStatus"); |
| |
| Thread.sleep(AtomTestUtils.WAIT_TIME_LONG); |
| // Trigger a pull and wait for new pull before killing the process. |
| AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice()); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_LONG); |
| |
| // Assert about GnssMetrics for the test app. |
| List<Atom> atoms = ReportUtils.getGaugeMetricAtoms(getDevice()); |
| |
| boolean found = false; |
| for (Atom atom : atoms) { |
| AtomsProto.GnssStats state = atom.getGnssStats(); |
| found = true; |
| if ((state.getSvStatusReports() > 0 || state.getL5SvStatusReports() > 0) |
| && state.getLocationReports() == 0) { |
| // Device is detected to be indoors and not able to acquire location. |
| // flaky test device |
| break; |
| } |
| assertThat(state.getLocationReports()).isGreaterThan((long) 0); |
| assertThat(state.getLocationFailureReports()).isAtLeast((long) 0); |
| assertThat(state.getTimeToFirstFixReports()).isGreaterThan((long) 0); |
| assertThat(state.getTimeToFirstFixMillis()).isGreaterThan((long) 0); |
| assertThat(state.getPositionAccuracyReports()).isGreaterThan((long) 0); |
| assertThat(state.getPositionAccuracyMeters()).isGreaterThan((long) 0); |
| assertThat(state.getTopFourAverageCn0Reports()).isGreaterThan((long) 0); |
| assertThat(state.getTopFourAverageCn0DbMhz()).isGreaterThan((long) 0); |
| assertThat(state.getL5TopFourAverageCn0Reports()).isAtLeast((long) 0); |
| assertThat(state.getL5TopFourAverageCn0DbMhz()).isAtLeast((long) 0); |
| assertThat(state.getSvStatusReports()).isAtLeast((long) 0); |
| assertThat(state.getSvStatusReportsUsedInFix()).isAtLeast((long) 0); |
| assertThat(state.getL5SvStatusReports()).isAtLeast((long) 0); |
| assertThat(state.getL5SvStatusReportsUsedInFix()).isAtLeast((long) 0); |
| } |
| assertWithMessage(String.format("Did not find a matching atom")) |
| .that(found).isTrue(); |
| } 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.assertStatesOccurred(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.assertStatesOccurred(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); |
| } |
| |
| public void testScheduledJobState() throws Exception { |
| String expectedName = "com.android.server.cts.device.statsdatom/.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); |
| |
| ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| atomTag, /*useUidAttributionChain=*/true); |
| allowImmediateSyncs(); |
| DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testScheduledJob"); |
| |
| // Sorted list of events in order in which they occurred. |
| List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice()); |
| |
| AtomTestUtils.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 { |
| int initialBrightness = getScreenBrightness(); |
| boolean isInitialManual = isScreenBrightnessModeManual(); |
| setScreenBrightnessMode(true); |
| setScreenBrightness(200); |
| 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)); |
| 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); |
| |
| 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, screen200, |
| atom -> atom.getScreenBrightnessChanged().getLevel()); |
| // Assert that the events happened in the expected order. |
| AtomTestUtils.assertStatesOccurred(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); |
| allowImmediateSyncs(); |
| 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.assertStatesOccurred(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.assertStatesOccurred(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.assertStatesOccurred(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 testWakeupAlarm() throws Exception { |
| // For automotive, all wakeup alarm becomes normal alarm. So this |
| // test does not work. |
| if (DeviceUtils.hasFeature(getDevice(), FEATURE_AUTOMOTIVE)) return; |
| final int atomTag = Atom.WAKEUP_ALARM_OCCURRED_FIELD_NUMBER; |
| |
| ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| atomTag, /*useUidAttributionChain=*/true); |
| DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testWakeupAlarm"); |
| |
| List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice()); |
| 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.statsdatom.testWakeupAlarm"); |
| assertThat(wao.getPackageName()).isEqualTo(DeviceUtils.STATSD_ATOM_TEST_PKG); |
| } |
| } |
| |
| public void testWifiLockHighPerf() throws Exception { |
| if (!DeviceUtils.hasFeature(getDevice(), FEATURE_WIFI)) return; |
| if (DeviceUtils.hasFeature(getDevice(), FEATURE_PC)) 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); |
| |
| ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| atomTag, /*useUidAttributionChain=*/true); |
| DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testWifiLockHighPerf"); |
| |
| // 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.assertStatesOccurred(stateSet, data, AtomTestUtils.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 (!DeviceUtils.hasFeature(getDevice(), FEATURE_WIFI)) return; |
| if (DeviceUtils.hasFeature(getDevice(), FEATURE_PC)) 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); |
| |
| ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| atomTag, /*useUidAttributionChain=*/true); |
| DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testWifiLockLowLatency"); |
| |
| // 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.assertStatesOccurred(stateSet, data, AtomTestUtils.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 (!DeviceUtils.hasFeature(getDevice(), FEATURE_WIFI)) return; |
| if (DeviceUtils.hasFeature(getDevice(), FEATURE_PC)) 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); |
| |
| ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| atomTag, /*useUidAttributionChain=*/true); |
| DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testWifiMulticastLock"); |
| |
| // 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.assertStatesOccurred(stateSet, data, AtomTestUtils.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 (!DeviceUtils.hasFeature(getDevice(), FEATURE_WIFI)) return; |
| |
| final int atom = Atom.WIFI_SCAN_STATE_CHANGED_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; |
| |
| ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| atom, /*useAttributionChain=*/ true); |
| DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testWifiScan"); |
| Thread.sleep(WAIT_TIME_SHORT); |
| |
| List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice()); |
| assertThat(data.size()).isIn(Range.closed(2, 4)); |
| AtomTestUtils.assertTimeDiffBetween(data.get(0), data.get(1), minTimeDiffMillis, |
| maxTimeDiffMillis); |
| 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 testLooperStats() throws Exception { |
| try { |
| DeviceUtils.unplugDevice(getDevice()); |
| setUpLooperStats(); |
| |
| ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| Atom.LOOPER_STATS_FIELD_NUMBER); |
| |
| DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| "StatsdCtsForegroundActivity", "action", "action.show_notification", 3_000); |
| |
| AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice()); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| |
| List<Atom> atomList = ReportUtils.getGaugeMetricAtoms(getDevice()); |
| |
| boolean found = false; |
| int uid = DeviceUtils.getStatsdTestAppUid(getDevice()); |
| 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(); |
| DeviceUtils.plugInAc(getDevice()); |
| } |
| } |
| |
| public void testProcessMemoryState() throws Exception { |
| // Get ProcessMemoryState as a simple gauge metric. |
| ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| Atom.PROCESS_MEMORY_STATE_FIELD_NUMBER); |
| |
| // Start test app. |
| try (AutoCloseable a = DeviceUtils.withActivity(getDevice(), |
| DeviceUtils.STATSD_ATOM_TEST_PKG, "StatsdCtsForegroundActivity", "action", |
| "action.show_notification")) { |
| Thread.sleep(AtomTestUtils.WAIT_TIME_LONG); |
| // Trigger a pull and wait for new pull before killing the process. |
| AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice()); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_LONG); |
| } |
| |
| // Assert about ProcessMemoryState for the test app. |
| List<Atom> atoms = getGaugeMetricDataList(); |
| int uid = DeviceUtils.getStatsdTestAppUid(getDevice()); |
| boolean found = false; |
| for (Atom atom : atoms) { |
| ProcessMemoryState state = atom.getProcessMemoryState(); |
| if (state.getUid() != uid) { |
| continue; |
| } |
| found = true; |
| assertThat(state.getProcessName()).isEqualTo(DeviceUtils.STATSD_ATOM_TEST_PKG); |
| 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 { |
| // Get ProcessMemoryHighWaterMark as a simple gauge metric. |
| ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| Atom.PROCESS_MEMORY_HIGH_WATER_MARK_FIELD_NUMBER); |
| |
| // Start test app and trigger a pull while it is running. |
| try (AutoCloseable a = DeviceUtils.withActivity(getDevice(), |
| DeviceUtils.STATSD_ATOM_TEST_PKG, "StatsdCtsForegroundActivity", "action", |
| "action.show_notification")) { |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| // Trigger a pull and wait for new pull before killing the process. |
| AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice()); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_LONG); |
| } |
| |
| // Assert about ProcessMemoryHighWaterMark for the test app, statsd and system server. |
| List<Atom> atoms = ReportUtils.getGaugeMetricAtoms(getDevice()); |
| int uid = DeviceUtils.getStatsdTestAppUid(getDevice()); |
| 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(DeviceUtils.STATSD_ATOM_TEST_PKG); |
| 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 { |
| // Get ProcessMemorySnapshot as a simple gauge metric. |
| ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| Atom.PROCESS_MEMORY_SNAPSHOT_FIELD_NUMBER); |
| |
| // Start test app and trigger a pull while it is running. |
| try (AutoCloseable a = DeviceUtils.withActivity(getDevice(), |
| DeviceUtils.STATSD_ATOM_TEST_PKG, "StatsdCtsForegroundActivity", "action", |
| "action.show_notification")) { |
| Thread.sleep(AtomTestUtils.WAIT_TIME_LONG); |
| AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice()); |
| } |
| |
| // Assert about ProcessMemorySnapshot for the test app, statsd and system server. |
| List<Atom> atoms = ReportUtils.getGaugeMetricAtoms(getDevice()); |
| int uid = DeviceUtils.getStatsdTestAppUid(getDevice()); |
| 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(DeviceUtils.STATSD_ATOM_TEST_PKG); |
| } else if (snapshot.getProcessName().contains("/statsd")) { |
| foundStatsd = true; |
| } else if (snapshot.getProcessName().equals("system")) { |
| foundSystemServer = true; |
| } |
| |
| assertThat(snapshot.getPid()).isGreaterThan(0); |
| assertThat(snapshot.getAnonRssAndSwapInKilobytes()).isAtLeast(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(); |
| } |
| |
| public void testIonHeapSize_optional() throws Exception { |
| if (isIonHeapSizeMandatory()) { |
| return; |
| } |
| |
| List<Atom> atoms = pullIonHeapSizeAsGaugeMetric(); |
| if (atoms.isEmpty()) { |
| // No support. |
| return; |
| } |
| assertIonHeapSize(atoms); |
| } |
| |
| public void testIonHeapSize_mandatory() throws Exception { |
| if (!isIonHeapSizeMandatory()) { |
| return; |
| } |
| |
| List<Atom> atoms = pullIonHeapSizeAsGaugeMetric(); |
| assertIonHeapSize(atoms); |
| } |
| |
| /** Returns whether IonHeapSize atom is supported. */ |
| private boolean isIonHeapSizeMandatory() throws Exception { |
| // Support is guaranteed by libmeminfo VTS. |
| return PropertyUtil.getFirstApiLevel(getDevice()) >= 30; |
| } |
| |
| /** Returns IonHeapSize atoms pulled as a simple gauge metric while test app is running. */ |
| private List<Atom> pullIonHeapSizeAsGaugeMetric() throws Exception { |
| // Get IonHeapSize as a simple gauge metric. |
| ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| Atom.ION_HEAP_SIZE_FIELD_NUMBER); |
| |
| // Start test app and trigger a pull while it is running. |
| try (AutoCloseable a = DeviceUtils.withActivity(getDevice(), |
| DeviceUtils.STATSD_ATOM_TEST_PKG, "StatsdCtsForegroundActivity", "action", |
| "action.show_notification")) { |
| AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice()); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_LONG); |
| } |
| |
| return ReportUtils.getGaugeMetricAtoms(getDevice()); |
| } |
| |
| private static void assertIonHeapSize(List<Atom> atoms) { |
| assertThat(atoms).hasSize(1); |
| IonHeapSize ionHeapSize = atoms.get(0).getIonHeapSize(); |
| assertThat(ionHeapSize.getTotalSizeKb()).isAtLeast(0); |
| } |
| |
| /** |
| * 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 { |
| // Make device side test package a role holder |
| String callScreenAppRole = "android.app.role.CALL_SCREENING"; |
| getDevice().executeShellCommand( |
| "cmd role add-role-holder " + callScreenAppRole + " " |
| + DeviceUtils.STATSD_ATOM_TEST_PKG); |
| |
| // Set up what to collect |
| ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| Atom.ROLE_HOLDER_FIELD_NUMBER); |
| |
| boolean verifiedKnowRoleState = false; |
| |
| // Pull a report |
| AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice()); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| |
| int testAppId = getAppId(DeviceUtils.getStatsdTestAppUid(getDevice())); |
| |
| for (Atom atom : ReportUtils.getGaugeMetricAtoms(getDevice())) { |
| AtomsProto.RoleHolder roleHolder = atom.getRoleHolder(); |
| |
| assertThat(roleHolder.getPackageName()).isNotNull(); |
| assertThat(roleHolder.getUid()).isAtLeast(0); |
| assertThat(roleHolder.getRole()).isNotNull(); |
| |
| if (roleHolder.getPackageName().equals(DeviceUtils.STATSD_ATOM_TEST_PKG)) { |
| assertThat(getAppId(roleHolder.getUid())).isEqualTo(testAppId); |
| assertThat(roleHolder.getPackageName()).isEqualTo(DeviceUtils.STATSD_ATOM_TEST_PKG); |
| assertThat(roleHolder.getRole()).isEqualTo(callScreenAppRole); |
| |
| verifiedKnowRoleState = true; |
| } |
| } |
| |
| assertThat(verifiedKnowRoleState).isTrue(); |
| } |
| |
| public void testDangerousPermissionState() throws Exception { |
| 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 |
| ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| Atom.DANGEROUS_PERMISSION_STATE_FIELD_NUMBER); |
| |
| boolean verifiedKnowPermissionState = false; |
| |
| // Pull a report |
| AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice()); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| |
| int testAppId = getAppId(DeviceUtils.getStatsdTestAppUid(getDevice())); |
| |
| for (Atom atom : ReportUtils.getGaugeMetricAtoms(getDevice())) { |
| 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().contains( |
| "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 testDangerousPermissionStateSampled() throws Exception { |
| // get full atom for reference |
| ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| Atom.DANGEROUS_PERMISSION_STATE_FIELD_NUMBER); |
| |
| AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice()); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| |
| List<DangerousPermissionState> fullDangerousPermissionState = new ArrayList<>(); |
| for (Atom atom : ReportUtils.getGaugeMetricAtoms(getDevice())) { |
| fullDangerousPermissionState.add(atom.getDangerousPermissionState()); |
| } |
| |
| ConfigUtils.removeConfig(getDevice()); |
| ReportUtils.clearReports(getDevice()); // Clears data. |
| List<Atom> gaugeMetricDataList = null; |
| |
| // retries in case sampling returns full list or empty list - which should be extremely rare |
| for (int attempt = 0; attempt < 10; attempt++) { |
| // Set up what to collect |
| ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| Atom.DANGEROUS_PERMISSION_STATE_SAMPLED_FIELD_NUMBER); |
| |
| // Pull a report |
| AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice()); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| |
| gaugeMetricDataList = ReportUtils.getGaugeMetricAtoms(getDevice()); |
| if (gaugeMetricDataList.size() > 0 |
| && gaugeMetricDataList.size() < fullDangerousPermissionState.size()) { |
| break; |
| } |
| ConfigUtils.removeConfig(getDevice()); |
| ReportUtils.clearReports(getDevice()); // Clears data. |
| } |
| assertThat(gaugeMetricDataList.size()).isGreaterThan(0); |
| assertThat(gaugeMetricDataList.size()).isLessThan(fullDangerousPermissionState.size()); |
| |
| long lastUid = -1; |
| int fullIndex = 0; |
| |
| for (Atom atom : getGaugeMetricDataList()) { |
| DangerousPermissionStateSampled permissionState = |
| atom.getDangerousPermissionStateSampled(); |
| |
| DangerousPermissionState referenceState = fullDangerousPermissionState.get(fullIndex); |
| |
| if (referenceState.getUid() != permissionState.getUid()) { |
| // atoms are sampled on uid basis if uid is present, all related permissions must |
| // be logged. |
| assertThat(permissionState.getUid()).isNotEqualTo(lastUid); |
| continue; |
| } |
| |
| lastUid = permissionState.getUid(); |
| |
| assertThat(permissionState.getPermissionFlags()).isEqualTo( |
| referenceState.getPermissionFlags()); |
| assertThat(permissionState.getIsGranted()).isEqualTo(referenceState.getIsGranted()); |
| assertThat(permissionState.getPermissionName()).isEqualTo( |
| referenceState.getPermissionName()); |
| |
| fullIndex++; |
| } |
| } |
| |
| public void testAppOps() throws Exception { |
| // Set up what to collect |
| ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| Atom.APP_OPS_FIELD_NUMBER); |
| |
| DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testAppOps"); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| |
| // Pull a report |
| AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice()); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| |
| ArrayList<Integer> expectedOps = new ArrayList<>(); |
| for (int i = 0; i < NUM_APP_OPS; i++) { |
| expectedOps.add(i); |
| } |
| |
| for (Descriptors.EnumValueDescriptor valueDescriptor : |
| AttributedAppOps.getDefaultInstance().getOp().getDescriptorForType().getValues()) { |
| if (valueDescriptor.getOptions().hasDeprecated()) { |
| // Deprecated app op, remove from list of expected ones. |
| expectedOps.remove(expectedOps.indexOf(valueDescriptor.getNumber())); |
| } |
| } |
| for (Atom atom : ReportUtils.getGaugeMetricAtoms(getDevice())) { |
| |
| AppOps appOps = atom.getAppOps(); |
| if (appOps.getPackageName().equals(DeviceUtils.STATSD_ATOM_TEST_PKG)) { |
| if (appOps.getOpId().getNumber() == -1) { |
| continue; |
| } |
| long totalNoted = appOps.getTrustedForegroundGrantedCount() |
| + appOps.getTrustedBackgroundGrantedCount() |
| + appOps.getTrustedForegroundRejectedCount() |
| + appOps.getTrustedBackgroundRejectedCount(); |
| assertWithMessage("Operation in APP_OPS_ENUM_MAP: " + appOps.getOpId().getNumber()) |
| .that(totalNoted - 1).isEqualTo(appOps.getOpId().getNumber()); |
| assertWithMessage("Unexpected Op reported").that(expectedOps).contains( |
| appOps.getOpId().getNumber()); |
| expectedOps.remove(expectedOps.indexOf(appOps.getOpId().getNumber())); |
| } |
| } |
| assertWithMessage("Logging app op ids are missing in report.").that(expectedOps).isEmpty(); |
| } |
| |
| 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); |
| } |
| |
| 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(); |
| |
| |
| 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(); |
| |
| 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(); |
| |
| 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(); |
| } |
| |
| public void testNotificationPackagePreferenceExtraction() throws Exception { |
| ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| Atom.PACKAGE_NOTIFICATION_PREFERENCES_FIELD_NUMBER); |
| |
| DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| "StatsdCtsForegroundActivity", "action", "action.show_notification"); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice()); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| |
| List<PackageNotificationPreferences> allPreferences = new ArrayList<>(); |
| for (Atom atom : getGaugeMetricDataList()) { |
| if (atom.hasPackageNotificationPreferences()) { |
| allPreferences.add(atom.getPackageNotificationPreferences()); |
| } |
| } |
| assertThat(allPreferences.size()).isGreaterThan(0); |
| |
| boolean foundTestPackagePreferences = false; |
| int uid = DeviceUtils.getStatsdTestAppUid(getDevice()); |
| for (PackageNotificationPreferences pref : allPreferences) { |
| assertThat(pref.getUid()).isGreaterThan(0); |
| assertTrue(pref.hasImportance()); |
| assertTrue(pref.hasVisibility()); |
| assertTrue(pref.hasUserLockedFields()); |
| if (pref.getUid() == uid) { |
| assertThat(pref.getImportance()).isEqualTo(-1000); //UNSPECIFIED_IMPORTANCE |
| assertThat(pref.getVisibility()).isEqualTo(-1000); //UNSPECIFIED_VISIBILITY |
| foundTestPackagePreferences = true; |
| } |
| } |
| assertTrue(foundTestPackagePreferences); |
| } |
| |
| public void testNotificationChannelPreferencesExtraction() throws Exception { |
| ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| Atom.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES_FIELD_NUMBER); |
| |
| DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| "StatsdCtsForegroundActivity", "action", "action.show_notification"); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice()); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| |
| List<PackageNotificationChannelPreferences> allChannelPreferences = new ArrayList<>(); |
| for (Atom atom : getGaugeMetricDataList()) { |
| if (atom.hasPackageNotificationChannelPreferences()) { |
| allChannelPreferences.add(atom.getPackageNotificationChannelPreferences()); |
| } |
| } |
| assertThat(allChannelPreferences.size()).isGreaterThan(0); |
| |
| boolean foundTestPackagePreferences = false; |
| int uid = DeviceUtils.getStatsdTestAppUid(getDevice()); |
| for (PackageNotificationChannelPreferences pref : allChannelPreferences) { |
| assertThat(pref.getUid()).isGreaterThan(0); |
| assertTrue(pref.hasChannelId()); |
| assertTrue(pref.hasChannelName()); |
| assertTrue(pref.hasDescription()); |
| assertTrue(pref.hasImportance()); |
| assertTrue(pref.hasUserLockedFields()); |
| assertTrue(pref.hasIsDeleted()); |
| if (uid == pref.getUid() && pref.getChannelId().equals("StatsdCtsChannel")) { |
| assertThat(pref.getChannelName()).isEqualTo("Statsd Cts"); |
| assertThat(pref.getDescription()).isEqualTo("Statsd Cts Channel"); |
| assertThat(pref.getImportance()).isEqualTo(3); // IMPORTANCE_DEFAULT |
| foundTestPackagePreferences = true; |
| } |
| } |
| assertTrue(foundTestPackagePreferences); |
| } |
| |
| public void testNotificationChannelGroupPreferencesExtraction() throws Exception { |
| ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| Atom.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES_FIELD_NUMBER); |
| |
| DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| "StatsdCtsForegroundActivity", "action", "action.create_channel_group"); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice()); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| |
| List<PackageNotificationChannelGroupPreferences> allGroupPreferences = new ArrayList<>(); |
| for (Atom atom : getGaugeMetricDataList()) { |
| if (atom.hasPackageNotificationChannelGroupPreferences()) { |
| allGroupPreferences.add(atom.getPackageNotificationChannelGroupPreferences()); |
| } |
| } |
| assertThat(allGroupPreferences.size()).isGreaterThan(0); |
| |
| boolean foundTestPackagePreferences = false; |
| int uid = DeviceUtils.getStatsdTestAppUid(getDevice()); |
| for (PackageNotificationChannelGroupPreferences pref : allGroupPreferences) { |
| assertThat(pref.getUid()).isGreaterThan(0); |
| assertTrue(pref.hasGroupId()); |
| assertTrue(pref.hasGroupName()); |
| assertTrue(pref.hasDescription()); |
| assertTrue(pref.hasIsBlocked()); |
| assertTrue(pref.hasUserLockedFields()); |
| if (uid == pref.getUid() && pref.getGroupId().equals("StatsdCtsGroup")) { |
| assertThat(pref.getGroupName()).isEqualTo("Statsd Cts Group"); |
| assertThat(pref.getDescription()).isEqualTo("StatsdCtsGroup Description"); |
| assertThat(pref.getIsBlocked()).isFalse(); |
| foundTestPackagePreferences = true; |
| } |
| } |
| assertTrue(foundTestPackagePreferences); |
| } |
| |
| public void testNotificationReported() throws Exception { |
| StatsdConfig.Builder config = ConfigUtils.createConfigBuilder( |
| DeviceUtils.STATSD_ATOM_TEST_PKG); |
| FieldValueMatcher.Builder fvm = ConfigUtils.createFvm( |
| NotificationReported.PACKAGE_NAME_FIELD_NUMBER).setEqString( |
| DeviceUtils.STATSD_ATOM_TEST_PKG); |
| ConfigUtils.addEventMetric(config, Atom.NOTIFICATION_REPORTED_FIELD_NUMBER, |
| Collections.singletonList(fvm)); |
| ConfigUtils.uploadConfig(getDevice(), config); |
| |
| DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| "StatsdCtsForegroundActivity", "action", "action.show_notification"); |
| 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(1); |
| assertThat(data.get(0).getAtom().hasNotificationReported()).isTrue(); |
| AtomsProto.NotificationReported n = data.get(0).getAtom().getNotificationReported(); |
| assertThat(n.getPackageName()).isEqualTo(DeviceUtils.STATSD_ATOM_TEST_PKG); |
| assertThat(n.getUid()).isEqualTo(DeviceUtils.getStatsdTestAppUid(getDevice())); |
| assertThat(n.getNotificationIdHash()).isEqualTo(1); // smallHash(0x7f080001) |
| assertThat(n.getChannelIdHash()).isEqualTo(SmallHash.hash("StatsdCtsChannel")); |
| assertThat(n.getGroupIdHash()).isEqualTo(0); |
| assertFalse(n.getIsGroupSummary()); |
| assertThat(n.getCategory()).isEmpty(); |
| assertThat(n.getStyle()).isEqualTo(0); |
| assertThat(n.getNumPeople()).isEqualTo(0); |
| } |
| |
| public void testSettingsStatsReported() throws Exception { |
| // Base64 encoded proto com.android.service.nano.StringListParamProto, |
| // which contains two strings "font_scale" and "screen_auto_brightness_adj". |
| final String encoded = "ChpzY3JlZW5fYXV0b19icmlnaHRuZXNzX2FkagoKZm9udF9zY2FsZQ"; |
| final String font_scale = "font_scale"; |
| SettingSnapshot snapshot = null; |
| final float originalFontScale = Float.parseFloat( |
| getDevice().executeShellCommand("settings get system font_scale")); |
| |
| // Set whitelist through device config. |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| getDevice().executeShellCommand( |
| "device_config put settings_stats SystemFeature__float_whitelist " + encoded); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| // Set font_scale value |
| getDevice().executeShellCommand("settings put system font_scale 1.5"); |
| |
| // Get SettingSnapshot as a simple gauge metric. |
| ConfigUtils.uploadConfigForPulledAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| Atom.SETTING_SNAPSHOT_FIELD_NUMBER); |
| |
| // Start test app and trigger a pull while it is running. |
| try (AutoCloseable a = DeviceUtils.withActivity(getDevice(), |
| DeviceUtils.STATSD_ATOM_TEST_PKG, "StatsdCtsForegroundActivity", "action", |
| "action.show_notification")) { |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| // Trigger a pull and wait for new pull before killing the process. |
| AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice()); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_LONG); |
| } |
| |
| // Test the size of atoms. It should contain at least "font_scale" and |
| // "screen_auto_brightness_adj" two setting values. |
| List<Atom> atoms = ReportUtils.getGaugeMetricAtoms(getDevice()); |
| assertThat(atoms.size()).isAtLeast(2); |
| for (Atom atom : atoms) { |
| SettingSnapshot settingSnapshot = atom.getSettingSnapshot(); |
| if (font_scale.equals(settingSnapshot.getName())) { |
| snapshot = settingSnapshot; |
| break; |
| } |
| } |
| |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| // Test the data of atom. |
| assertNotNull(snapshot); |
| // Get font_scale value and test value type. |
| final float newFontScale = Float.parseFloat( |
| getDevice().executeShellCommand("settings get system font_scale")); |
| assertThat(snapshot.getType()).isEqualTo( |
| SettingSnapshot.SettingsValueType.ASSIGNED_FLOAT_TYPE); |
| assertThat(snapshot.getBoolValue()).isEqualTo(false); |
| assertThat(snapshot.getIntValue()).isEqualTo(0); |
| assertThat(snapshot.getFloatValue()).isEqualTo(newFontScale); |
| assertThat(snapshot.getStrValue()).isEqualTo(""); |
| assertThat(snapshot.getUserId()).isEqualTo(0); |
| |
| // Restore the font value. |
| getDevice().executeShellCommand("settings put system font_scale " + originalFontScale); |
| } |
| |
| public void testIntegrityCheckAtomReportedDuringInstall() throws Exception { |
| ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| Atom.INTEGRITY_CHECK_RESULT_REPORTED_FIELD_NUMBER); |
| |
| DeviceUtils.uninstallStatsdTestApp(getDevice()); |
| DeviceUtils.installStatsdTestApp(getDevice(), mCtsBuild); |
| |
| List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice()); |
| |
| assertThat(data.size()).isEqualTo(1); |
| assertThat(data.get(0).getAtom().hasIntegrityCheckResultReported()).isTrue(); |
| IntegrityCheckResultReported result = data.get(0) |
| .getAtom().getIntegrityCheckResultReported(); |
| assertThat(result.getPackageName()).isEqualTo(DeviceUtils.STATSD_ATOM_TEST_PKG); |
| // we do not assert on certificates since it seem to differ by device. |
| assertThat(result.getInstallerPackageName()).isEqualTo("adb"); |
| long testPackageVersion = 10; |
| assertThat(result.getVersionCode()).isEqualTo(testPackageVersion); |
| assertThat(result.getResponse()).isEqualTo(ALLOWED); |
| assertThat(result.getCausedByAppCertRule()).isFalse(); |
| assertThat(result.getCausedByInstallerRule()).isFalse(); |
| } |
| /* |
| public void testMobileBytesTransfer() throws Throwable { |
| final int appUid = getUid(); |
| |
| // Verify MobileBytesTransfer, passing a ThrowingPredicate that verifies contents of |
| // corresponding atom type to prevent code duplication. The passed predicate returns |
| // true if the atom of appUid is found, false otherwise, and throws an exception if |
| // contents are not expected. |
| doTestMobileBytesTransferThat(Atom.MOBILE_BYTES_TRANSFER_FIELD_NUMBER, (atom) -> { |
| final AtomsProto.MobileBytesTransfer data = ((Atom) atom).getMobileBytesTransfer(); |
| if (data.getUid() == appUid) { |
| assertDataUsageAtomDataExpected(data.getRxBytes(), data.getTxBytes(), |
| data.getRxPackets(), data.getTxPackets()); |
| return true; // found |
| } |
| return false; |
| }); |
| } |
| */ |
| /* |
| public void testMobileBytesTransferByFgBg() throws Throwable { |
| final int appUid = getUid(); |
| |
| doTestMobileBytesTransferThat(Atom.MOBILE_BYTES_TRANSFER_BY_FG_BG_FIELD_NUMBER, (atom) -> { |
| final AtomsProto.MobileBytesTransferByFgBg data = |
| ((Atom) atom).getMobileBytesTransferByFgBg(); |
| if (data.getUid() == appUid && data.getIsForeground()) { |
| assertDataUsageAtomDataExpected(data.getRxBytes(), data.getTxBytes(), |
| data.getRxPackets(), data.getTxPackets()); |
| return true; // found |
| } |
| return false; |
| }); |
| } |
| */ |
| private void assertSubscriptionInfo(AtomsProto.DataUsageBytesTransfer data) { |
| assertThat(data.getSimMcc()).matches("^\\d{3}$"); |
| assertThat(data.getSimMnc()).matches("^\\d{2,3}$"); |
| assertThat(data.getCarrierId()).isNotEqualTo(-1); // TelephonyManager#UNKNOWN_CARRIER_ID |
| } |
| |
| private void doTestDataUsageBytesTransferEnabled(boolean enable) throws Throwable { |
| // Set value to enable/disable combine subtype. |
| setNetworkStatsCombinedSubTypeEnabled(enable); |
| |
| doTestMobileBytesTransferThat(Atom.DATA_USAGE_BYTES_TRANSFER_FIELD_NUMBER, (atom) -> { |
| final AtomsProto.DataUsageBytesTransfer data = |
| ((Atom) atom).getDataUsageBytesTransfer(); |
| final boolean ratTypeEqualsToUnknown = |
| (data.getRatType() == NetworkTypeEnum.NETWORK_TYPE_UNKNOWN_VALUE); |
| final boolean ratTypeGreaterThanUnknown = |
| (data.getRatType() > NetworkTypeEnum.NETWORK_TYPE_UNKNOWN_VALUE); |
| |
| if ((data.getState() == 1 /*NetworkStats.SET_FOREGROUND*/) |
| && ((enable && ratTypeEqualsToUnknown) |
| || (!enable && ratTypeGreaterThanUnknown))) { |
| assertDataUsageAtomDataExpected(data.getRxBytes(), data.getTxBytes(), |
| data.getRxPackets(), data.getTxPackets()); |
| // Assert that subscription info is valid. |
| assertSubscriptionInfo(data); |
| |
| return true; // found |
| } |
| return false; |
| }); |
| } |
| /* |
| public void testDataUsageBytesTransfer() throws Throwable { |
| final boolean oldSubtypeCombined = getNetworkStatsCombinedSubTypeEnabled(); |
| |
| doTestDataUsageBytesTransferEnabled(true); |
| |
| // Remove config from memory and disk to clear the history. |
| removeConfig(CONFIG_ID); |
| getReportList(); // Clears data. |
| |
| doTestDataUsageBytesTransferEnabled(false); |
| |
| // Restore to original default value. |
| setNetworkStatsCombinedSubTypeEnabled(oldSubtypeCombined); |
| } |
| // TODO(b/157651730): Determine how to test tag and metered state within atom. |
| public void testBytesTransferByTagAndMetered() throws Throwable { |
| final int appUid = getUid(); |
| final int atomId = Atom.BYTES_TRANSFER_BY_TAG_AND_METERED_FIELD_NUMBER; |
| |
| doTestMobileBytesTransferThat(atomId, (atom) -> { |
| final AtomsProto.BytesTransferByTagAndMetered data = |
| ((Atom) atom).getBytesTransferByTagAndMetered(); |
| if (data.getUid() == appUid && data.getTag() == 0) { // app traffic generated on tag 0 |
| assertDataUsageAtomDataExpected(data.getRxBytes(), data.getTxBytes(), |
| data.getRxPackets(), data.getTxPackets()); |
| return true; // found |
| } |
| return false; |
| }); |
| } |
| */ |
| public void testPushedBlobStoreStats() throws Exception { |
| StatsdConfig.Builder conf = createConfigBuilder(); |
| addAtomEvent(conf, Atom.BLOB_COMMITTED_FIELD_NUMBER, false); |
| addAtomEvent(conf, Atom.BLOB_LEASED_FIELD_NUMBER, false); |
| addAtomEvent(conf, Atom.BLOB_OPENED_FIELD_NUMBER, false); |
| uploadConfig(conf); |
| |
| Thread.sleep(WAIT_TIME_SHORT); |
| |
| runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testBlobStore"); |
| |
| List<EventMetricData> data = getEventMetricDataList(); |
| assertThat(data).hasSize(3); |
| |
| BlobCommitted blobCommitted = data.get(0).getAtom().getBlobCommitted(); |
| final long blobId = blobCommitted.getBlobId(); |
| final long blobSize = blobCommitted.getSize(); |
| assertThat(blobCommitted.getUid()).isEqualTo(getUid()); |
| assertThat(blobId).isNotEqualTo(0); |
| assertThat(blobSize).isNotEqualTo(0); |
| assertThat(blobCommitted.getResult()).isEqualTo(BlobCommitted.Result.SUCCESS); |
| |
| BlobLeased blobLeased = data.get(1).getAtom().getBlobLeased(); |
| assertThat(blobLeased.getUid()).isEqualTo(getUid()); |
| assertThat(blobLeased.getBlobId()).isEqualTo(blobId); |
| assertThat(blobLeased.getSize()).isEqualTo(blobSize); |
| assertThat(blobLeased.getResult()).isEqualTo(BlobLeased.Result.SUCCESS); |
| |
| BlobOpened blobOpened = data.get(2).getAtom().getBlobOpened(); |
| assertThat(blobOpened.getUid()).isEqualTo(getUid()); |
| assertThat(blobOpened.getBlobId()).isEqualTo(blobId); |
| assertThat(blobOpened.getSize()).isEqualTo(blobSize); |
| assertThat(blobOpened.getResult()).isEqualTo(BlobOpened.Result.SUCCESS); |
| } |
| |
| // Constants that match the constants for AtomTests#testBlobStore |
| private static final long BLOB_COMMIT_CALLBACK_TIMEOUT_SEC = 5; |
| private static final long BLOB_EXPIRY_DURATION_MS = 24 * 60 * 60 * 1000; |
| private static final long BLOB_FILE_SIZE_BYTES = 23 * 1024L; |
| private static final long BLOB_LEASE_EXPIRY_DURATION_MS = 60 * 60 * 1000; |
| |
| public void testPulledBlobStoreStats() throws Exception { |
| StatsdConfig.Builder config = createConfigBuilder(); |
| addGaugeAtomWithDimensions(config, |
| Atom.BLOB_INFO_FIELD_NUMBER, |
| null); |
| uploadConfig(config); |
| |
| final long testStartTimeMs = System.currentTimeMillis(); |
| Thread.sleep(WAIT_TIME_SHORT); |
| runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testBlobStore"); |
| Thread.sleep(WAIT_TIME_LONG); |
| setAppBreadcrumbPredicate(); |
| Thread.sleep(WAIT_TIME_SHORT); |
| |
| // Add commit callback time to test end time to account for async execution |
| final long testEndTimeMs = |
| System.currentTimeMillis() + BLOB_COMMIT_CALLBACK_TIMEOUT_SEC * 1000; |
| |
| // Find the BlobInfo for the blob created in the test run |
| AtomsProto.BlobInfo blobInfo = null; |
| for (Atom atom : getGaugeMetricDataList()) { |
| if (atom.hasBlobInfo()) { |
| final AtomsProto.BlobInfo temp = atom.getBlobInfo(); |
| if (temp.getCommitters().getCommitter(0).getUid() == getUid()) { |
| blobInfo = temp; |
| break; |
| } |
| } |
| } |
| assertThat(blobInfo).isNotNull(); |
| |
| assertThat(blobInfo.getSize()).isEqualTo(BLOB_FILE_SIZE_BYTES); |
| |
| // Check that expiry time is reasonable |
| assertThat(blobInfo.getExpiryTimestampMillis()).isGreaterThan( |
| testStartTimeMs + BLOB_EXPIRY_DURATION_MS); |
| assertThat(blobInfo.getExpiryTimestampMillis()).isLessThan( |
| testEndTimeMs + BLOB_EXPIRY_DURATION_MS); |
| |
| // Check that commit time is reasonable |
| final long commitTimeMs = blobInfo.getCommitters().getCommitter( |
| 0).getCommitTimestampMillis(); |
| assertThat(commitTimeMs).isGreaterThan(testStartTimeMs); |
| assertThat(commitTimeMs).isLessThan(testEndTimeMs); |
| |
| // Check that WHITELIST and PRIVATE access mode flags are set |
| assertThat(blobInfo.getCommitters().getCommitter(0).getAccessMode()).isEqualTo(0b1001); |
| assertThat(blobInfo.getCommitters().getCommitter(0).getNumWhitelistedPackage()).isEqualTo( |
| 1); |
| |
| assertThat(blobInfo.getLeasees().getLeaseeCount()).isGreaterThan(0); |
| assertThat(blobInfo.getLeasees().getLeasee(0).getUid()).isEqualTo(getUid()); |
| |
| // Check that lease expiry time is reasonable |
| final long leaseExpiryMs = blobInfo.getLeasees().getLeasee( |
| 0).getLeaseExpiryTimestampMillis(); |
| assertThat(leaseExpiryMs).isGreaterThan(testStartTimeMs + BLOB_LEASE_EXPIRY_DURATION_MS); |
| assertThat(leaseExpiryMs).isLessThan(testEndTimeMs + BLOB_LEASE_EXPIRY_DURATION_MS); |
| } |
| |
| private void assertDataUsageAtomDataExpected(long rxb, long txb, long rxp, long txp) { |
| assertThat(rxb).isGreaterThan(0L); |
| assertThat(txb).isGreaterThan(0L); |
| assertThat(rxp).isGreaterThan(0L); |
| assertThat(txp).isGreaterThan(0L); |
| } |
| |
| private void doTestMobileBytesTransferThat(int atomTag, ThrowingPredicate p) |
| throws Throwable { |
| if (!hasFeature(FEATURE_TELEPHONY, true)) return; |
| |
| // Get MobileBytesTransfer as a simple gauge metric. |
| final StatsdConfig.Builder config = getPulledConfig(); |
| addGaugeAtomWithDimensions(config, atomTag, null); |
| uploadConfig(config); |
| Thread.sleep(WAIT_TIME_SHORT); |
| |
| // Generate some traffic on mobile network. |
| runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testGenerateMobileTraffic"); |
| Thread.sleep(WAIT_TIME_SHORT); |
| |
| // Force polling NetworkStatsService to get most updated network stats from lower layer. |
| runActivity("StatsdCtsForegroundActivity", "action", "action.poll_network_stats"); |
| Thread.sleep(WAIT_TIME_SHORT); |
| |
| // Pull a report |
| setAppBreadcrumbPredicate(); |
| Thread.sleep(WAIT_TIME_SHORT); |
| |
| final List<Atom> atoms = getGaugeMetricDataList(/*checkTimestampTruncated=*/true); |
| assertThat(atoms.size()).isAtLeast(1); |
| |
| boolean foundAppStats = false; |
| for (final Atom atom : atoms) { |
| if (p.accept(atom)) { |
| foundAppStats = true; |
| } |
| } |
| assertWithMessage("uid " + getUid() + " is not found in " + atoms.size() + " atoms") |
| .that(foundAppStats).isTrue(); |
| } |
| |
| @FunctionalInterface |
| private interface ThrowingPredicate<S, T extends Throwable> { |
| boolean accept(S s) throws T; |
| } |
| |
| public void testPackageInstallerV2MetricsReported() throws Throwable { |
| if (!DeviceUtils.hasFeature(getDevice(), FEATURE_INCREMENTAL_DELIVERY)) return; |
| final AtomsProto.PackageInstallerV2Reported report = installPackageUsingV2AndGetReport( |
| new String[]{TEST_INSTALL_APK}); |
| assertTrue(report.getIsIncremental()); |
| // tests are ran using SHELL_UID and installation will be treated as adb install |
| assertEquals("", report.getPackageName()); |
| assertEquals(1, report.getReturnCode()); |
| assertTrue(report.getDurationMillis() > 0); |
| assertEquals(getTestFileSize(TEST_INSTALL_APK), report.getApksSizeBytes()); |
| |
| getDevice().uninstallPackage(TEST_INSTALL_PACKAGE); |
| } |
| |
| public void testPackageInstallerV2MetricsReportedForSplits() throws Throwable { |
| if (!DeviceUtils.hasFeature(getDevice(), FEATURE_INCREMENTAL_DELIVERY)) return; |
| |
| final AtomsProto.PackageInstallerV2Reported report = installPackageUsingV2AndGetReport( |
| new String[]{TEST_INSTALL_APK_BASE, TEST_INSTALL_APK_SPLIT}); |
| assertTrue(report.getIsIncremental()); |
| // tests are ran using SHELL_UID and installation will be treated as adb install |
| assertEquals("", report.getPackageName()); |
| assertEquals(1, report.getReturnCode()); |
| assertTrue(report.getDurationMillis() > 0); |
| assertEquals( |
| getTestFileSize(TEST_INSTALL_APK_BASE) + getTestFileSize(TEST_INSTALL_APK_SPLIT), |
| report.getApksSizeBytes()); |
| |
| getDevice().uninstallPackage(TEST_INSTALL_PACKAGE); |
| } |
| |
| 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.assertStatesOccurred(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 void setUpLooperStats() throws Exception { |
| getDevice().executeShellCommand("cmd looper_stats enable"); |
| getDevice().executeShellCommand("cmd looper_stats sampling_interval 1"); |
| getDevice().executeShellCommand("cmd looper_stats reset"); |
| } |
| |
| private void cleanUpLooperStats() throws Exception { |
| getDevice().executeShellCommand("cmd looper_stats disable"); |
| } |
| |
| private AtomsProto.PackageInstallerV2Reported installPackageUsingV2AndGetReport( |
| String[] apkNames) throws Exception { |
| ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, |
| Atom.PACKAGE_INSTALLER_V2_REPORTED_FIELD_NUMBER); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| installPackageUsingIncremental(apkNames, TEST_REMOTE_DIR); |
| assertTrue(getDevice().isPackageInstalled(TEST_INSTALL_PACKAGE)); |
| Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT); |
| |
| List<AtomsProto.PackageInstallerV2Reported> reports = new ArrayList<>(); |
| for (EventMetricData data : ReportUtils.getEventMetricDataList(getDevice())) { |
| if (data.getAtom().hasPackageInstallerV2Reported()) { |
| reports.add(data.getAtom().getPackageInstallerV2Reported()); |
| } |
| } |
| assertEquals(1, reports.size()); |
| return reports.get(0); |
| } |
| |
| private void installPackageUsingIncremental(String[] apkNames, String remoteDirPath) |
| throws Exception { |
| getDevice().executeShellCommand("mkdir " + remoteDirPath); |
| String[] remoteApkPaths = new String[apkNames.length]; |
| for (int i = 0; i < remoteApkPaths.length; i++) { |
| remoteApkPaths[i] = pushApkToRemote(apkNames[i], remoteDirPath); |
| } |
| getDevice().executeShellCommand( |
| "pm install-incremental -t -g " + String.join(" ", remoteApkPaths)); |
| } |
| |
| private String pushApkToRemote(String apkName, String remoteDirPath) |
| throws Exception { |
| CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild()); |
| final File apk = buildHelper.getTestFile(apkName); |
| final String remoteApkPath = remoteDirPath + "/" + apk.getName(); |
| assertTrue(getDevice().pushFile(apk, remoteApkPath)); |
| assertNotNull(apk); |
| return remoteApkPath; |
| } |
| |
| private long getTestFileSize(String fileName) throws Exception { |
| CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild()); |
| final File file = buildHelper.getTestFile(fileName); |
| return file.length(); |
| } |
| |
| /** Make the test app standby-active so it can run syncs and jobs immediately. */ |
| private void allowImmediateSyncs() throws Exception { |
| getDevice().executeShellCommand("am set-standby-bucket " |
| + DeviceUtils.STATSD_ATOM_TEST_PKG + " active"); |
| } |
| |
| 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); |
| } |
| |
| } |