| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package android.cts.statsd.atom; |
| |
| import static android.cts.statsd.atom.DeviceAtomTestCase.DEVICE_SIDE_TEST_APK; |
| import static android.cts.statsd.atom.DeviceAtomTestCase.DEVICE_SIDE_TEST_PACKAGE; |
| |
| import android.os.BatteryStatsProto; |
| import android.service.batterystats.BatteryStatsServiceDumpProto; |
| import android.view.DisplayStateEnum; |
| |
| import com.android.annotations.Nullable; |
| import com.android.internal.os.StatsdConfigProto.AtomMatcher; |
| import com.android.internal.os.StatsdConfigProto.EventMetric; |
| import com.android.internal.os.StatsdConfigProto.FieldFilter; |
| import com.android.internal.os.StatsdConfigProto.FieldMatcher; |
| import com.android.internal.os.StatsdConfigProto.FieldValueMatcher; |
| import com.android.internal.os.StatsdConfigProto.GaugeMetric; |
| import com.android.internal.os.StatsdConfigProto.Predicate; |
| import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher; |
| import com.android.internal.os.StatsdConfigProto.SimplePredicate; |
| import com.android.internal.os.StatsdConfigProto.StatsdConfig; |
| import com.android.internal.os.StatsdConfigProto.TimeUnit; |
| import com.android.os.AtomsProto.AppBreadcrumbReported; |
| import com.android.os.AtomsProto.Atom; |
| import com.android.os.AtomsProto.ScreenStateChanged; |
| import com.android.os.StatsLog.ConfigMetricsReport; |
| import com.android.os.StatsLog.ConfigMetricsReportList; |
| import com.android.os.StatsLog.EventMetricData; |
| import com.android.os.StatsLog.GaugeMetricData; |
| import com.android.os.StatsLog.StatsLogReport; |
| import com.android.tradefed.device.DeviceNotAvailableException; |
| import com.android.tradefed.log.LogUtil; |
| |
| import com.google.common.io.Files; |
| |
| import java.io.File; |
| import java.text.SimpleDateFormat; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Comparator; |
| import java.util.Date; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.function.Function; |
| |
| import perfetto.protos.PerfettoConfig.DataSourceConfig; |
| import perfetto.protos.PerfettoConfig.TraceConfig; |
| import perfetto.protos.PerfettoConfig.TraceConfig.BufferConfig; |
| import perfetto.protos.PerfettoConfig.TraceConfig.DataSource; |
| |
| /** |
| * Base class for testing Statsd atoms. |
| * Validates reporting of statsd logging based on different events |
| */ |
| public class AtomTestCase extends BaseTestCase { |
| |
| public static final String UPDATE_CONFIG_CMD = "cmd stats config update"; |
| public static final String DUMP_REPORT_CMD = "cmd stats dump-report"; |
| public static final String DUMP_BATTERYSTATS_CMD = "dumpsys batterystats"; |
| public static final String REMOVE_CONFIG_CMD = "cmd stats config remove"; |
| public static final String CONFIG_UID = "1000"; |
| /** ID of the config, which evaluates to -1572883457. */ |
| public static final long CONFIG_ID = "cts_config".hashCode(); |
| |
| protected static final int WAIT_TIME_SHORT = 500; |
| protected static final int WAIT_TIME_LONG = 2_000; |
| |
| protected static final long SCREEN_STATE_CHANGE_TIMEOUT = 4000; |
| protected static final long SCREEN_STATE_POLLING_INTERVAL = 500; |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| |
| if (statsdDisabled()) { |
| return; |
| } |
| |
| // Uninstall to clear the history in case it's still on the device. |
| removeConfig(CONFIG_ID); |
| getReportList(); // Clears data. |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| removeConfig(CONFIG_ID); |
| getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE); |
| super.tearDown(); |
| } |
| |
| /** |
| * Determines whether logcat indicates that incidentd fired since the given device date. |
| */ |
| protected boolean didIncidentdFireSince(String date) throws Exception { |
| final String INCIDENTD_TAG = "incidentd"; |
| final String INCIDENTD_STARTED_STRING = "reportIncident"; |
| // TODO: Do something more robust than this in case of delayed logging. |
| Thread.sleep(1000); |
| String log = getLogcatSince(date, String.format( |
| "-s %s -e %s", INCIDENTD_TAG, INCIDENTD_STARTED_STRING)); |
| return log.contains(INCIDENTD_STARTED_STRING); |
| } |
| |
| /** |
| * Determines whether logcat indicates that perfetto fired since the given device date. |
| */ |
| protected boolean didPerfettoStartSince(String date) throws Exception { |
| final String PERFETTO_TAG = "perfetto"; |
| final String PERFETTO_STARTED_STRING = "Enabled tracing"; |
| final String PERFETTO_STARTED_REGEX = ".*" + PERFETTO_STARTED_STRING + ".*"; |
| // TODO: Do something more robust than this in case of delayed logging. |
| Thread.sleep(1000); |
| String log = getLogcatSince(date, String.format( |
| "-s %s -e %s", PERFETTO_TAG, PERFETTO_STARTED_REGEX)); |
| return log.contains(PERFETTO_STARTED_STRING); |
| } |
| |
| protected boolean checkDeviceFor(String methodName) throws Exception { |
| try { |
| installPackage(DEVICE_SIDE_TEST_APK, true); |
| runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".Checkers", methodName); |
| // Test passes, meaning that the answer is true. |
| return true; |
| } catch (AssertionError e) { |
| // Method is designed to fail if the answer is false. |
| return false; |
| } |
| } |
| |
| protected static StatsdConfig.Builder createConfigBuilder() { |
| return StatsdConfig.newBuilder().setId(CONFIG_ID) |
| .addAllowedLogSource("AID_SYSTEM") |
| .addAllowedLogSource("AID_BLUETOOTH") |
| .addAllowedLogSource(DeviceAtomTestCase.DEVICE_SIDE_TEST_PACKAGE); |
| } |
| |
| protected void createAndUploadConfig(int atomTag) throws Exception { |
| StatsdConfig.Builder conf = createConfigBuilder(); |
| addAtomEvent(conf, atomTag); |
| uploadConfig(conf); |
| } |
| |
| protected void uploadConfig(StatsdConfig.Builder config) throws Exception { |
| uploadConfig(config.build()); |
| } |
| |
| protected void uploadConfig(StatsdConfig config) throws Exception { |
| LogUtil.CLog.d("Uploading the following config:\n" + config.toString()); |
| File configFile = File.createTempFile("statsdconfig", ".config"); |
| configFile.deleteOnExit(); |
| Files.write(config.toByteArray(), configFile); |
| String remotePath = "/data/local/tmp/" + configFile.getName(); |
| getDevice().pushFile(configFile, remotePath); |
| getDevice().executeShellCommand( |
| String.join(" ", "cat", remotePath, "|", UPDATE_CONFIG_CMD, |
| String.valueOf(CONFIG_ID))); |
| getDevice().executeShellCommand("rm " + remotePath); |
| } |
| |
| protected void removeConfig(long configId) throws Exception { |
| getDevice().executeShellCommand( |
| String.join(" ", REMOVE_CONFIG_CMD, String.valueOf(configId))); |
| } |
| |
| /** Gets the statsd report and sorts it. Note that this also deletes that report from statsd. */ |
| protected List<EventMetricData> getEventMetricDataList() throws Exception { |
| ConfigMetricsReportList reportList = getReportList(); |
| assertTrue("Expected one report", reportList.getReportsCount() == 1); |
| ConfigMetricsReport report = reportList.getReports(0); |
| |
| List<EventMetricData> data = new ArrayList<>(); |
| for (StatsLogReport metric : report.getMetricsList()) { |
| data.addAll(metric.getEventMetrics().getDataList()); |
| } |
| data.sort(Comparator.comparing(EventMetricData::getElapsedTimestampNanos)); |
| |
| LogUtil.CLog.d("Get EventMetricDataList as following:\n"); |
| for (EventMetricData d : data) { |
| LogUtil.CLog.d("Atom at " + d.getElapsedTimestampNanos() + ":\n" + d.getAtom().toString()); |
| } |
| return data; |
| } |
| |
| protected List<Atom> getGaugeMetricDataList() throws Exception { |
| ConfigMetricsReportList reportList = getReportList(); |
| assertTrue(reportList.getReportsCount() == 1); |
| // only config |
| ConfigMetricsReport report = reportList.getReports(0); |
| |
| List<Atom> data = new ArrayList<>(); |
| for (GaugeMetricData gaugeMetricData : |
| report.getMetrics(0).getGaugeMetrics().getDataList()) { |
| for (Atom atom : gaugeMetricData.getBucketInfo(0).getAtomList()) { |
| data.add(atom); |
| } |
| } |
| |
| LogUtil.CLog.d("Get GaugeMetricDataList as following:\n"); |
| for (Atom d : data) { |
| LogUtil.CLog.d("Atom:\n" + d.toString()); |
| } |
| return data; |
| } |
| |
| protected StatsLogReport getStatsLogReport() throws Exception { |
| ConfigMetricsReportList reportList = getReportList(); |
| assertTrue(reportList.getReportsCount() == 1); |
| ConfigMetricsReport report = reportList.getReports(0); |
| assertTrue(report.hasUidMap()); |
| assertEquals(1, report.getMetricsCount()); |
| return report.getMetrics(0); |
| } |
| |
| /** Gets the statsd report. Note that this also deletes that report from statsd. */ |
| protected ConfigMetricsReportList getReportList() throws Exception { |
| try { |
| ConfigMetricsReportList reportList = getDump(ConfigMetricsReportList.parser(), |
| String.join(" ", DUMP_REPORT_CMD, String.valueOf(CONFIG_ID), |
| "--proto")); |
| return reportList; |
| } catch (com.google.protobuf.InvalidProtocolBufferException e) { |
| LogUtil.CLog.e("Failed to fetch and parse the statsd output report. " |
| + "Perhaps there is not a valid statsd config for the requested " |
| + "uid=" + CONFIG_UID + ", id=" + CONFIG_ID + "."); |
| throw (e); |
| } |
| } |
| |
| protected BatteryStatsProto getBatteryStatsProto() throws Exception { |
| try { |
| BatteryStatsProto batteryStatsProto = getDump(BatteryStatsServiceDumpProto.parser(), |
| String.join(" ", DUMP_BATTERYSTATS_CMD, |
| "--proto")).getBatterystats(); |
| LogUtil.CLog.d("Got batterystats:\n " + batteryStatsProto.toString()); |
| return batteryStatsProto; |
| } catch (com.google.protobuf.InvalidProtocolBufferException e) { |
| LogUtil.CLog.e("Failed to dump batterystats proto"); |
| throw (e); |
| } |
| } |
| |
| /** Creates a FieldValueMatcher.Builder corresponding to the given field. */ |
| protected static FieldValueMatcher.Builder createFvm(int field) { |
| return FieldValueMatcher.newBuilder().setField(field); |
| } |
| |
| protected static TraceConfig createPerfettoTraceConfig() { |
| return TraceConfig.newBuilder() |
| .addBuffers(BufferConfig.newBuilder().setSizeKb(32)) |
| .addDataSources(DataSource.newBuilder() |
| .setConfig(DataSourceConfig.newBuilder() |
| .setName("linux.ftrace") |
| .setTargetBuffer(0) |
| .build() |
| ) |
| ) |
| .build(); |
| } |
| |
| protected void addAtomEvent(StatsdConfig.Builder conf, int atomTag) throws Exception { |
| addAtomEvent(conf, atomTag, new ArrayList<FieldValueMatcher.Builder>()); |
| } |
| |
| /** |
| * Adds an event to the config for an atom that matches the given key. |
| * |
| * @param conf configuration |
| * @param atomTag atom tag (from atoms.proto) |
| * @param fvm FieldValueMatcher.Builder for the relevant key |
| */ |
| protected void addAtomEvent(StatsdConfig.Builder conf, int atomTag, |
| FieldValueMatcher.Builder fvm) |
| throws Exception { |
| addAtomEvent(conf, atomTag, Arrays.asList(fvm)); |
| } |
| |
| /** |
| * Adds an event to the config for an atom that matches the given keys. |
| * |
| * @param conf configuration |
| * @param atomId atom tag (from atoms.proto) |
| * @param fvms list of FieldValueMatcher.Builders to attach to the atom. May be null. |
| */ |
| protected void addAtomEvent(StatsdConfig.Builder conf, int atomId, |
| List<FieldValueMatcher.Builder> fvms) throws Exception { |
| |
| final String atomName = "Atom" + System.nanoTime(); |
| final String eventName = "Event" + System.nanoTime(); |
| |
| SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder().setAtomId(atomId); |
| if (fvms != null) { |
| for (FieldValueMatcher.Builder fvm : fvms) { |
| sam.addFieldValueMatcher(fvm); |
| } |
| } |
| conf.addAtomMatcher(AtomMatcher.newBuilder() |
| .setId(atomName.hashCode()) |
| .setSimpleAtomMatcher(sam)); |
| conf.addEventMetric(EventMetric.newBuilder() |
| .setId(eventName.hashCode()) |
| .setWhat(atomName.hashCode())); |
| } |
| |
| /** |
| * Adds an atom to a gauge metric of a config |
| * |
| * @param conf configuration |
| * @param atomId atom id (from atoms.proto) |
| * @param dimension dimension is needed for most pulled atoms |
| */ |
| protected void addGaugeAtom(StatsdConfig.Builder conf, int atomId, |
| @Nullable FieldMatcher.Builder dimension) throws Exception { |
| final String atomName = "Atom" + System.nanoTime(); |
| final String gaugeName = "Gauge" + System.nanoTime(); |
| final String predicateName = "APP_BREADCRUMB"; |
| SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder().setAtomId(atomId); |
| conf.addAtomMatcher(AtomMatcher.newBuilder() |
| .setId(atomName.hashCode()) |
| .setSimpleAtomMatcher(sam)); |
| final String predicateTrueName = "APP_BREADCRUMB_1"; |
| final String predicateFalseName = "APP_BREADCRUMB_2"; |
| conf.addAtomMatcher(AtomMatcher.newBuilder() |
| .setId(predicateTrueName.hashCode()) |
| .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder() |
| .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER) |
| .addFieldValueMatcher(FieldValueMatcher.newBuilder() |
| .setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER) |
| .setEqInt(1) |
| ) |
| ) |
| ) |
| // Used to trigger predicate |
| .addAtomMatcher(AtomMatcher.newBuilder() |
| .setId(predicateFalseName.hashCode()) |
| .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder() |
| .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER) |
| .addFieldValueMatcher(FieldValueMatcher.newBuilder() |
| .setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER) |
| .setEqInt(2) |
| ) |
| ) |
| ); |
| conf.addPredicate(Predicate.newBuilder() |
| .setId(predicateName.hashCode()) |
| .setSimplePredicate(SimplePredicate.newBuilder() |
| .setStart(predicateTrueName.hashCode()) |
| .setStop(predicateFalseName.hashCode()) |
| .setCountNesting(false) |
| ) |
| ); |
| GaugeMetric.Builder gaugeMetric = GaugeMetric.newBuilder() |
| .setId(gaugeName.hashCode()) |
| .setWhat(atomName.hashCode()) |
| .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build()) |
| .setSamplingType(GaugeMetric.SamplingType.ALL_CONDITION_CHANGES) |
| .setBucket(TimeUnit.CTS) |
| .setCondition(predicateName.hashCode()); |
| if (dimension != null) { |
| gaugeMetric.setDimensionsInWhat(dimension.build()); |
| } |
| conf.addGaugeMetric(gaugeMetric.build()); |
| } |
| |
| /** |
| * Asserts that each set of states in stateSets occurs at least once in data. |
| * Asserts that the states in data occur in the same order as the sets in stateSets. |
| * |
| * @param stateSets A list of set of states, where each set represents an equivalent |
| * state of the device for the purpose of CTS. |
| * @param data list of EventMetricData from statsd, produced by |
| * getReportMetricListData() |
| * @param wait expected duration (in ms) between state changes; asserts that the |
| * actual wait |
| * time was wait/2 <= actual_wait <= 5*wait. Use 0 to ignore this |
| * assertion. |
| * @param getStateFromAtom expression that takes in an Atom and returns the state it contains |
| */ |
| public void assertStatesOccurred(List<Set<Integer>> stateSets, List<EventMetricData> data, |
| int wait, Function<Atom, Integer> getStateFromAtom) { |
| // Sometimes, there are more events than there are states. |
| // Eg: When the screen turns off, it may go into OFF and then DOZE immediately. |
| assertTrue("Too few states found (" + data.size() + ")", data.size() >= stateSets.size()); |
| int stateSetIndex = 0; // Tracks which state set we expect the data to be in. |
| for (int dataIndex = 0; dataIndex < data.size(); dataIndex++) { |
| Atom atom = data.get(dataIndex).getAtom(); |
| int state = getStateFromAtom.apply(atom); |
| // If state is in the current state set, we do not assert anything. |
| // If it is not, we expect to have transitioned to the next state set. |
| if (stateSets.get(stateSetIndex).contains(state)) { |
| // No need to assert anything. Just log it. |
| LogUtil.CLog.i("The following atom at dataIndex=" + dataIndex + " is " |
| + "in stateSetIndex " + stateSetIndex + ":\n" |
| + data.get(dataIndex).getAtom().toString()); |
| } else { |
| stateSetIndex += 1; |
| LogUtil.CLog.i("Assert that the following atom at dataIndex=" + dataIndex + " is" |
| + " in stateSetIndex " + stateSetIndex + ":\n" |
| + data.get(dataIndex).getAtom().toString()); |
| assertTrue("Missed first state", dataIndex != 0); // should not be on first data |
| assertTrue("Too many states (" + (stateSetIndex + 1) + ")", |
| stateSetIndex < stateSets.size()); |
| assertTrue("Is in wrong state (" + state + ")", |
| stateSets.get(stateSetIndex).contains(state)); |
| if (wait > 0) { |
| assertTimeDiffBetween(data.get(dataIndex - 1), data.get(dataIndex), |
| wait / 2, wait * 5); |
| } |
| } |
| } |
| assertTrue("Too few states (" + (stateSetIndex + 1) + ")", |
| stateSetIndex == stateSets.size() - 1); |
| } |
| |
| /** |
| * Removes all elements from data prior to the first occurrence of an element of state. After |
| * this method is called, the first element of data (if non-empty) is guaranteed to be an |
| * element in state. |
| * |
| * @param getStateFromAtom expression that takes in an Atom and returns the state it contains |
| */ |
| public void popUntilFind(List<EventMetricData> data, Set<Integer> state, |
| Function<Atom, Integer> getStateFromAtom) { |
| int firstStateIdx; |
| for (firstStateIdx = 0; firstStateIdx < data.size(); firstStateIdx++) { |
| Atom atom = data.get(firstStateIdx).getAtom(); |
| if (state.contains(getStateFromAtom.apply(atom))) { |
| break; |
| } |
| } |
| if (firstStateIdx == 0) { |
| // First first element already is in state, so there's nothing to do. |
| return; |
| } |
| data.subList(0, firstStateIdx).clear(); |
| } |
| |
| /** |
| * Removes all elements from data after to the last occurrence of an element of state. After |
| * this method is called, the last element of data (if non-empty) is guaranteed to be an |
| * element in state. |
| * |
| * @param getStateFromAtom expression that takes in an Atom and returns the state it contains |
| */ |
| public void popUntilFindFromEnd(List<EventMetricData> data, Set<Integer> state, |
| Function<Atom, Integer> getStateFromAtom) { |
| int lastStateIdx; |
| for (lastStateIdx = data.size() - 1; lastStateIdx >= 0; lastStateIdx--) { |
| Atom atom = data.get(lastStateIdx).getAtom(); |
| if (state.contains(getStateFromAtom.apply(atom))) { |
| break; |
| } |
| } |
| if (lastStateIdx == data.size()-1) { |
| // Last element already is in state, so there's nothing to do. |
| return; |
| } |
| data.subList(lastStateIdx+1, data.size()).clear(); |
| } |
| |
| /** Returns the UID of the host, which should always either be SHELL (2000) or ROOT (0). */ |
| protected int getHostUid() throws DeviceNotAvailableException { |
| String strUid = ""; |
| try { |
| strUid = getDevice().executeShellCommand("id -u"); |
| return Integer.parseInt(strUid.trim()); |
| } catch (NumberFormatException e) { |
| LogUtil.CLog.e("Failed to get host's uid via shell command. Found " + strUid); |
| // Fall back to alternative method... |
| if (getDevice().isAdbRoot()) { |
| return 0; // ROOT |
| } else { |
| return 2000; // SHELL |
| } |
| } |
| } |
| |
| protected void turnScreenOn() throws Exception { |
| getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP"); |
| getDevice().executeShellCommand("wm dismiss-keyguard"); |
| } |
| |
| protected void turnScreenOff() throws Exception { |
| getDevice().executeShellCommand("input keyevent KEYCODE_SLEEP"); |
| } |
| |
| protected void setChargingState(int state) throws Exception { |
| getDevice().executeShellCommand("cmd battery set status " + state); |
| } |
| |
| protected void unplugDevice() throws Exception { |
| getDevice().executeShellCommand("cmd battery unplug"); |
| } |
| |
| protected void plugInAc() throws Exception { |
| getDevice().executeShellCommand("cmd battery set ac 1"); |
| } |
| |
| protected void plugInUsb() throws Exception { |
| getDevice().executeShellCommand("cmd battery set usb 1"); |
| } |
| |
| protected void plugInWireless() throws Exception { |
| getDevice().executeShellCommand("cmd battery set wireless 1"); |
| } |
| |
| public void setAppBreadcrumbPredicate() throws Exception { |
| doAppBreadcrumbReportedStart(1); |
| } |
| |
| public void clearAppBreadcrumbPredicate() throws Exception { |
| doAppBreadcrumbReportedStart(2); |
| } |
| |
| public void doAppBreadcrumbReportedStart(int label) throws Exception { |
| doAppBreadcrumbReported(label, AppBreadcrumbReported.State.START.ordinal()); |
| } |
| |
| public void doAppBreadcrumbReportedStop(int label) throws Exception { |
| doAppBreadcrumbReported(label, AppBreadcrumbReported.State.STOP.ordinal()); |
| } |
| |
| public void doAppBreadcrumbReported(int label, int state) throws Exception { |
| getDevice().executeShellCommand(String.format( |
| "cmd stats log-app-breadcrumb %d %d", label, state)); |
| } |
| |
| protected void setBatteryLevel(int level) throws Exception { |
| getDevice().executeShellCommand("cmd battery set level " + level); |
| } |
| |
| protected void resetBatteryStatus() throws Exception { |
| getDevice().executeShellCommand("cmd battery reset"); |
| } |
| |
| protected int getScreenBrightness() throws Exception { |
| return Integer.parseInt( |
| getDevice().executeShellCommand("settings get system screen_brightness").trim()); |
| } |
| |
| protected void setScreenBrightness(int brightness) throws Exception { |
| getDevice().executeShellCommand("settings put system screen_brightness " + brightness); |
| } |
| |
| // Gets whether "Always on Display" setting is enabled. |
| // In rare cases, this is different from whether the device can enter SCREEN_STATE_DOZE. |
| protected String getAodState() throws Exception { |
| return getDevice().executeShellCommand("settings get secure doze_always_on"); |
| } |
| |
| protected void setAodState(String state) throws Exception { |
| getDevice().executeShellCommand("settings put secure doze_always_on " + state); |
| } |
| |
| protected boolean isScreenBrightnessModeManual() throws Exception { |
| String mode = getDevice().executeShellCommand("settings get system screen_brightness_mode"); |
| return Integer.parseInt(mode.trim()) == 0; |
| } |
| |
| protected void setScreenBrightnessMode(boolean manual) throws Exception { |
| getDevice().executeShellCommand( |
| "settings put system screen_brightness_mode " + (manual ? 0 : 1)); |
| } |
| |
| protected void enterDozeModeLight() throws Exception { |
| getDevice().executeShellCommand("dumpsys deviceidle force-idle light"); |
| } |
| |
| protected void enterDozeModeDeep() throws Exception { |
| getDevice().executeShellCommand("dumpsys deviceidle force-idle deep"); |
| } |
| |
| protected void leaveDozeMode() throws Exception { |
| getDevice().executeShellCommand("dumpsys deviceidle unforce"); |
| getDevice().executeShellCommand("dumpsys deviceidle disable"); |
| getDevice().executeShellCommand("dumpsys deviceidle enable"); |
| } |
| |
| protected void turnBatterySaverOn() throws Exception { |
| getDevice().executeShellCommand("cmd battery unplug"); |
| getDevice().executeShellCommand("settings put global low_power 1"); |
| } |
| |
| protected void turnBatterySaverOff() throws Exception { |
| getDevice().executeShellCommand("settings put global low_power 0"); |
| getDevice().executeShellCommand("cmd battery reset"); |
| } |
| |
| protected void rebootDevice() throws Exception { |
| getDevice().rebootUntilOnline(); |
| } |
| |
| /** |
| * Asserts that the two events are within the specified range of each other. |
| * |
| * @param d0 the event that should occur first |
| * @param d1 the event that should occur second |
| * @param minDiffMs d0 should precede d1 by at least this amount |
| * @param maxDiffMs d0 should precede d1 by at most this amount |
| */ |
| public static void assertTimeDiffBetween(EventMetricData d0, EventMetricData d1, |
| int minDiffMs, int maxDiffMs) { |
| long diffMs = (d1.getElapsedTimestampNanos() - d0.getElapsedTimestampNanos()) / 1_000_000; |
| assertTrue("Illegal time difference (" + diffMs + "ms)", minDiffMs <= diffMs); |
| assertTrue("Illegal time difference (" + diffMs + "ms)", diffMs <= maxDiffMs); |
| } |
| |
| protected String getCurrentLogcatDate() throws Exception { |
| // TODO: Do something more robust than this for getting logcat markers. |
| long timestampMs = getDevice().getDeviceDate(); |
| return new SimpleDateFormat("MM-dd HH:mm:ss.SSS") |
| .format(new Date(timestampMs)); |
| } |
| |
| protected String getLogcatSince(String date, String logcatParams) throws Exception { |
| return getDevice().executeShellCommand(String.format( |
| "logcat -v threadtime -t '%s' -d %s", date, logcatParams)); |
| } |
| |
| /** |
| * Pulled atoms should have a better way of constructing the config. |
| * Remove this config when that happens. |
| */ |
| protected StatsdConfig.Builder getPulledConfig() { |
| return StatsdConfig.newBuilder().setId(CONFIG_ID) |
| .addAllowedLogSource("AID_SYSTEM") |
| .addAllowedLogSource(DeviceAtomTestCase.DEVICE_SIDE_TEST_PACKAGE); |
| } |
| |
| /** |
| * Determines if the device has the given feature. |
| * Prints a warning if its value differs from requiredAnswer. |
| */ |
| protected boolean hasFeature(String featureName, boolean requiredAnswer) throws Exception { |
| final String features = getDevice().executeShellCommand("pm list features"); |
| boolean hasIt = features.contains(featureName); |
| if (hasIt != requiredAnswer) { |
| LogUtil.CLog.w("Device does " + (requiredAnswer ? "not " : "") + "have feature " |
| + featureName); |
| } |
| return hasIt == requiredAnswer; |
| } |
| |
| /** |
| * Determines if the device has |file|. |
| */ |
| protected boolean doesFileExist(String file) throws Exception { |
| return getDevice().doesFileExist(file); |
| } |
| |
| } |