blob: 11353ce4b6e0784a9c820e181a5c14ab053d04cd [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.cts.statsdatom.statsd;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import android.app.ProcessStateEnum; // From enums.proto for atoms.proto's UidProcessStateChanged.
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 com.android.os.AtomsProto.Atom;
import com.android.os.StatsLog.EventMetricData;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.testtype.IBuildReceiver;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Statsd atom tests that are done via app, for atoms that report a uid.
*/
public class ProcStateAtomTests extends DeviceTestCase implements IBuildReceiver {
private static final String TAG = "Statsd.ProcStateAtomTests";
private static final String DEVICE_SIDE_FG_ACTIVITY_COMPONENT
= "com.android.server.cts.device.statsdatom/.StatsdCtsForegroundActivity";
private static final String DEVICE_SIDE_FG_SERVICE_COMPONENT
= "com.android.server.cts.device.statsdatom/.StatsdCtsForegroundService";
// Constants from the device-side tests (not directly accessible here).
private static final String ACTION_END_IMMEDIATELY = "action.end_immediately";
private static final String ACTION_BACKGROUND_SLEEP = "action.background_sleep";
private static final String ACTION_SLEEP_WHILE_TOP = "action.sleep_top";
private static final String ACTION_LONG_SLEEP_WHILE_TOP = "action.long_sleep_top";
private static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
// Sleep times (ms) that actions invoke device-side.
private static final int SLEEP_OF_ACTION_SLEEP_WHILE_TOP = 2_000;
private static final int SLEEP_OF_ACTION_LONG_SLEEP_WHILE_TOP = 60_000;
private static final int SLEEP_OF_ACTION_BACKGROUND_SLEEP = 2_000;
private static final int SLEEP_OF_FOREGROUND_SERVICE = 2_000;
private static final int WAIT_TIME_FOR_CONFIG_UPDATE_MS = 200;
// ActivityManager can take a while to register screen state changes, mandating an extra delay.
private static final int WAIT_TIME_FOR_SCREEN_MS = 1_000;
private static final int EXTRA_WAIT_TIME_MS = 5_000; // as buffer when proc state changing.
private static final int STATSD_REPORT_WAIT_TIME_MS = 500; // make sure statsd finishes log.
private static final String FEATURE_WATCH = "android.hardware.type.watch";
// The tests here are using the BatteryStats definition of 'background'.
private static final Set<Integer> BG_STATES = new HashSet<>(
Arrays.asList(
ProcessStateEnum.PROCESS_STATE_IMPORTANT_BACKGROUND_VALUE,
ProcessStateEnum.PROCESS_STATE_TRANSIENT_BACKGROUND_VALUE,
ProcessStateEnum.PROCESS_STATE_BACKUP_VALUE,
ProcessStateEnum.PROCESS_STATE_SERVICE_VALUE,
ProcessStateEnum.PROCESS_STATE_RECEIVER_VALUE,
ProcessStateEnum.PROCESS_STATE_HEAVY_WEIGHT_VALUE
));
// Using the BatteryStats definition of 'cached', which is why HOME (etc) are considered cached.
private static final Set<Integer> CACHED_STATES = new HashSet<>(
Arrays.asList(
ProcessStateEnum.PROCESS_STATE_HOME_VALUE,
ProcessStateEnum.PROCESS_STATE_LAST_ACTIVITY_VALUE,
ProcessStateEnum.PROCESS_STATE_CACHED_ACTIVITY_VALUE,
ProcessStateEnum.PROCESS_STATE_CACHED_ACTIVITY_CLIENT_VALUE,
ProcessStateEnum.PROCESS_STATE_CACHED_RECENT_VALUE,
ProcessStateEnum.PROCESS_STATE_CACHED_EMPTY_VALUE
));
private static final Set<Integer> MISC_STATES = new HashSet<>(
Arrays.asList(
ProcessStateEnum.PROCESS_STATE_PERSISTENT_VALUE, // TODO: untested
ProcessStateEnum.PROCESS_STATE_PERSISTENT_UI_VALUE, // TODO: untested
ProcessStateEnum.PROCESS_STATE_TOP_VALUE,
ProcessStateEnum.PROCESS_STATE_BOUND_TOP_VALUE, // TODO: untested
ProcessStateEnum.PROCESS_STATE_BOUND_FOREGROUND_SERVICE_VALUE, // TODO: untested
ProcessStateEnum.PROCESS_STATE_FOREGROUND_SERVICE_VALUE,
ProcessStateEnum.PROCESS_STATE_IMPORTANT_FOREGROUND_VALUE,
ProcessStateEnum.PROCESS_STATE_TOP_SLEEPING_VALUE,
ProcessStateEnum.PROCESS_STATE_UNKNOWN_VALUE,
ProcessStateEnum.PROCESS_STATE_NONEXISTENT_VALUE
));
private static final Set<Integer> ALL_STATES = Stream.of(MISC_STATES, CACHED_STATES, BG_STATES)
.flatMap(s -> s.stream()).collect(Collectors.toSet());
private static final Function<Atom, Integer> PROC_STATE_FUNCTION =
atom -> atom.getUidProcessStateChanged().getState().getNumber();
private static final int PROC_STATE_ATOM_TAG = Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER;
private IBuildInfo mCtsBuild;
@Override
protected void setUp() throws Exception {
super.setUp();
assertThat(mCtsBuild).isNotNull();
ConfigUtils.removeConfig(getDevice());
ReportUtils.clearReports(getDevice());
DeviceUtils.installStatsdTestApp(getDevice(), mCtsBuild);
Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
}
@Override
protected void tearDown() throws Exception {
ConfigUtils.removeConfig(getDevice());
ReportUtils.clearReports(getDevice());
DeviceUtils.uninstallStatsdTestApp(getDevice());
super.tearDown();
}
@Override
public void setBuild(IBuildInfo buildInfo) {
mCtsBuild = buildInfo;
}
public void testForegroundService() throws Exception {
Set<Integer> onStates = new HashSet<>(Arrays.asList(
ProcessStateEnum.PROCESS_STATE_FOREGROUND_SERVICE_VALUE));
Set<Integer> offStates = complement(onStates);
List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
PROC_STATE_ATOM_TAG, /*useUidAttributionChain=*/false);
executeForegroundService(getDevice());
final int waitTime = SLEEP_OF_FOREGROUND_SERVICE;
Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS + EXTRA_WAIT_TIME_MS);
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
AtomTestUtils.popUntilFind(data, onStates,
PROC_STATE_FUNCTION); // clear out initial proc states.
AtomTestUtils.assertStatesOccurred(stateSet, data, waitTime, PROC_STATE_FUNCTION);
}
public void testForeground() throws Exception {
Set<Integer> onStates = new HashSet<>(Arrays.asList(
ProcessStateEnum.PROCESS_STATE_IMPORTANT_FOREGROUND_VALUE));
// There are no offStates, since the app remains in foreground until killed.
List<Set<Integer>> stateSet = Arrays.asList(onStates); // state sets, in order
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
PROC_STATE_ATOM_TAG, /*useUidAttributionChain=*/false);
executeForegroundActivity(getDevice(), ACTION_SHOW_APPLICATION_OVERLAY);
final int waitTime = EXTRA_WAIT_TIME_MS + 5_000; // Overlay may need to sit there a while.
Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS);
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
AtomTestUtils.popUntilFind(data, onStates,
PROC_STATE_FUNCTION); // clear out initial proc states.
AtomTestUtils.assertStatesOccurred(stateSet, data, 0, PROC_STATE_FUNCTION);
}
public void testBackground() throws Exception {
Set<Integer> onStates = BG_STATES;
Set<Integer> offStates = complement(onStates);
List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
PROC_STATE_ATOM_TAG, /*useUidAttributionChain=*/false);
DeviceUtils.executeBackgroundService(getDevice(), ACTION_BACKGROUND_SLEEP);
final int waitTime = SLEEP_OF_ACTION_BACKGROUND_SLEEP;
Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS + EXTRA_WAIT_TIME_MS);
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
AtomTestUtils.popUntilFind(data, onStates,
PROC_STATE_FUNCTION); // clear out initial proc states.
AtomTestUtils.assertStatesOccurred(stateSet, data, waitTime, PROC_STATE_FUNCTION);
}
public void testTop() throws Exception {
Set<Integer> onStates = new HashSet<>(Arrays.asList(
ProcessStateEnum.PROCESS_STATE_TOP_VALUE));
Set<Integer> offStates = complement(onStates);
List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
PROC_STATE_ATOM_TAG, /*useUidAttributionChain=*/false);
executeForegroundActivity(getDevice(), ACTION_SLEEP_WHILE_TOP);
final int waitTime = SLEEP_OF_ACTION_SLEEP_WHILE_TOP;
Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS + EXTRA_WAIT_TIME_MS);
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
AtomTestUtils.popUntilFind(data, onStates,
PROC_STATE_FUNCTION); // clear out initial proc states.
AtomTestUtils.assertStatesOccurred(stateSet, data, waitTime, PROC_STATE_FUNCTION);
}
public void testTopSleeping() throws Exception {
if (DeviceUtils.hasFeature(getDevice(), FEATURE_WATCH)) return;
Set<Integer> onStates = new HashSet<>(Arrays.asList(
ProcessStateEnum.PROCESS_STATE_TOP_SLEEPING_VALUE));
Set<Integer> offStates = complement(onStates);
List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
PROC_STATE_ATOM_TAG, /*useUidAttributionChain=*/false);
DeviceUtils.turnScreenOn(getDevice());
Thread.sleep(WAIT_TIME_FOR_SCREEN_MS);
executeForegroundActivity(getDevice(), ACTION_SLEEP_WHILE_TOP);
Thread.sleep(WAIT_TIME_FOR_SCREEN_MS);
DeviceUtils.turnScreenOff(getDevice());
final int waitTime = SLEEP_OF_ACTION_SLEEP_WHILE_TOP + EXTRA_WAIT_TIME_MS;
Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS);
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
AtomTestUtils.popUntilFind(data,
new HashSet<>(Arrays.asList(ProcessStateEnum.PROCESS_STATE_TOP_VALUE)),
PROC_STATE_FUNCTION); // clear out anything prior to it entering TOP.
AtomTestUtils.popUntilFind(data, onStates, PROC_STATE_FUNCTION); // clear out TOP itself.
// reset screen back on
DeviceUtils.turnScreenOn(getDevice());
// Don't check the wait time, since it's up to the system how long top sleeping persists.
AtomTestUtils.assertStatesOccurred(stateSet, data, 0, PROC_STATE_FUNCTION);
}
public void testCached() throws Exception {
Set<Integer> onStates = CACHED_STATES;
Set<Integer> offStates = complement(onStates);
List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
ConfigUtils.uploadConfigForPushedAtomWithUid(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
PROC_STATE_ATOM_TAG, /*useUidAttributionChain=*/false);
// The schedule is as follows
// #1. The system may do anything it wants, such as moving the app into a cache state.
// #2. We move the app into the background.
// #3. The background process ends, so the app definitely moves to a cache state
// (this is the ultimate goal of the test).
// #4. We start a foreground activity, moving the app out of cache.
// Start extremely short-lived activity, so app goes into cache state (#1 - #3 above).
DeviceUtils.executeBackgroundService(getDevice(), ACTION_END_IMMEDIATELY);
final int cacheTime = 2_000; // process should be in cached state for up to this long
Thread.sleep(cacheTime);
// Now forcibly bring the app out of cache (#4 above).
executeForegroundActivity(getDevice(), ACTION_SHOW_APPLICATION_OVERLAY);
// Now check the data *before* the app enters cache again (to avoid another cache event).
List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
// First, clear out any incidental cached states of step #1, prior to step #2.
AtomTestUtils.popUntilFind(data, BG_STATES, PROC_STATE_FUNCTION);
// Now clear out the bg state from step #2 (since we are interested in the cache after it).
AtomTestUtils.popUntilFind(data, onStates, PROC_STATE_FUNCTION);
// The result is that data should start at step #3, definitively in a cached state.
AtomTestUtils.assertStatesOccurred(stateSet, data, 1_000, PROC_STATE_FUNCTION);
}
public void testValidityOfStates() throws Exception {
assertWithMessage("UNKNOWN_TO_PROTO should not be a valid state")
.that(ALL_STATES).doesNotContain(
ProcessStateEnum.PROCESS_STATE_UNKNOWN_TO_PROTO_VALUE);
}
/** Returns the a set containing elements of a that are not elements of b. */
private Set<Integer> difference(Set<Integer> a, Set<Integer> b) {
Set<Integer> result = new HashSet<Integer>(a);
result.removeAll(b);
return result;
}
/** Returns the set of all states that are not in set. */
private Set<Integer> complement(Set<Integer> set) {
return difference(ALL_STATES, set);
}
/**
* Runs an activity (in the foreground) to perform the given action.
*
* @param actionValue the action code constants indicating the desired action to perform.
*/
private static void executeForegroundActivity(ITestDevice device, String actionValue)
throws Exception {
device.executeShellCommand(String.format(
"am start -n '%s' -e %s %s",
DEVICE_SIDE_FG_ACTIVITY_COMPONENT,
"action", actionValue));
}
/**
* Runs a simple foreground service.
*/
private static void executeForegroundService(ITestDevice device) throws Exception {
executeForegroundActivity(device, ACTION_END_IMMEDIATELY);
device.executeShellCommand(String.format(
"am startservice -n '%s'", DEVICE_SIDE_FG_SERVICE_COMPONENT));
}
}