blob: 23ffc03e686b1c39fb36c0b0e975431161e4f9f1 [file] [log] [blame]
/*
* Copyright (C) 2020 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.lib;
import com.android.os.AtomsProto.AppBreadcrumbReported;
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.MessageMatcher;
import com.android.internal.os.StatsdConfigProto.Position;
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.Atom;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
import com.google.common.io.Files;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nullable;
public final class ConfigUtils {
public static final long CONFIG_ID = "cts_config".hashCode(); // evaluates to -1572883457
public static final String CONFIG_ID_STRING = String.valueOf(CONFIG_ID);
// Attribution chains are the first field in atoms.
private static final int ATTRIBUTION_CHAIN_FIELD_NUMBER = 1;
// Uids are the first field in attribution nodes.
private static final int ATTRIBUTION_NODE_UID_FIELD_NUMBER = 1;
// Uids as standalone fields are the first field in atoms.
private static final int UID_FIELD_NUMBER = 1;
// adb shell commands
private static final String UPDATE_CONFIG_CMD = "cmd stats config update";
private static final String REMOVE_CONFIG_CMD = "cmd stats config remove";
/**
* Create a new config with common fields filled out, such as allowed log sources and
* default pull packages.
*
* @param pkgName test app package from which pushed atoms will be sent
*/
public static StatsdConfig.Builder createConfigBuilder(String pkgName) {
return StatsdConfig.newBuilder()
.setId(CONFIG_ID)
.addAllowedLogSource("AID_SYSTEM")
.addAllowedLogSource("AID_BLUETOOTH")
// TODO(b/134091167): Fix bluetooth source name issue in Auto platform.
.addAllowedLogSource("com.android.bluetooth.services")
.addAllowedLogSource("com.google.android.bluetooth.services")
.addAllowedLogSource("AID_LMKD")
.addAllowedLogSource("AID_MEDIA")
.addAllowedLogSource("AID_RADIO")
.addAllowedLogSource("AID_ROOT")
.addAllowedLogSource("AID_STATSD")
.addAllowedLogSource("com.android.systemui")
.addAllowedLogSource(pkgName)
.addDefaultPullPackages("AID_RADIO")
.addDefaultPullPackages("AID_SYSTEM")
.addWhitelistedAtomIds(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER);
}
/**
* Adds an event metric for the specified atom. The atom should contain a uid either within
* an attribution chain or as a standalone field. Only those atoms which contain the uid of
* the test app will be included in statsd's report.
*
* @param config
* @param atomId index of atom within atoms.proto
* @param uidInAttributionChain if true, the uid is part of the attribution chain; if false,
* uid is a standalone field
* @param pkgName test app package from which atom will be logged
*/
public static void addEventMetricForUidAtom(StatsdConfig.Builder config, int atomId,
boolean uidInAttributionChain, String pkgName) {
FieldValueMatcher.Builder fvm = createUidFvm(uidInAttributionChain, pkgName);
addEventMetric(config, atomId, Arrays.asList(fvm));
}
/**
* Adds an event metric for the specified atom. All such atoms received by statsd will be
* included in the report. If only atoms meeting certain constraints should be added to the
* report, use #addEventMetric(int atomId, List<FieldValueMatcher.Builder> fvms instead.
*
* @param config
* @param atomId index of atom within atoms.proto
*/
public static void addEventMetric(StatsdConfig.Builder config, int atomId) {
addEventMetric(config, atomId, /*fvms=*/null);
}
/**
* Adds an event metric to the config for the specified atom. The atom's fields must meet
* the constraints specified in fvms for the atom to be included in statsd's report.
*
* @param config
* @param atomId index of atom within atoms.proto
* @param fvms list of constraints that atoms are filtered on
*/
public static void addEventMetric(StatsdConfig.Builder config, int atomId,
@Nullable List<FieldValueMatcher.Builder> fvms) {
final String matcherName = "Atom matcher" + 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);
}
}
config.addAtomMatcher(AtomMatcher.newBuilder()
.setId(matcherName.hashCode())
.setSimpleAtomMatcher(sam));
config.addEventMetric(EventMetric.newBuilder()
.setId(eventName.hashCode())
.setWhat(matcherName.hashCode()));
}
/**
* Adds a gauge metric for a pulled atom with a uid field to the config. The atom will be
* pulled when an AppBreadcrumbReported atom is logged to statsd, and only those pulled atoms
* containing the uid of the test app will be included in statsd's report.
*
* @param config
* @param atomId index of atom within atoms.proto
* @param uidInAttributionChain if true, the uid is part of the attribution chain; if false, uid
* is a standalone field
* @param pkgName test app package from which atom will be logged
*/
public static void addGaugeMetricForUidAtom(StatsdConfig.Builder config, int atomId,
boolean uidInAttributionChain, String pkgName) {
addGaugeMetricInternal(config, atomId, /*filterByUid=*/true, uidInAttributionChain, pkgName,
/*dimensionsInWhat=*/null);
}
/**
* Equivalent to addGaugeMetricForUidAtom except that the output in the report is sliced by the
* specified dimensions.
*
* @param dimensionsInWhat dimensions to slice the output by
*/
public static void addGaugeMetricForUidAtomWithDimensions(StatsdConfig.Builder config,
int atomId, boolean uidInAttributionChain, String pkgName,
FieldMatcher.Builder dimensionsInWhat) {
addGaugeMetricInternal(config, atomId, /*filterByUid=*/true, uidInAttributionChain, pkgName,
dimensionsInWhat);
}
/**
* Adds a gauge metric for a pulled atom to the config. The atom will be pulled when an
* AppBreadcrumbReported atom is logged to statsd.
*
* @param config
* @param atomId index of the atom within atoms.proto
* @param dimensionsInWhat dimensions to slice the output by
*/
public static void addGaugeMetric(StatsdConfig.Builder config, int atomId) {
addGaugeMetricInternal(config, atomId, /*filterByUid=*/false,
/*uidInAttributionChain=*/false, /*pkgName=*/null, /*dimensionsInWhat=*/null);
}
/**
* Equivalent to addGaugeMetric except that output in the report is sliced by the specified
* dimensions.
*
* @param dimensionsInWhat dimensions to slice the output by
*/
public static void addGaugeMetricWithDimensions(StatsdConfig.Builder config, int atomId,
FieldMatcher.Builder dimensionsInWhat) {
addGaugeMetricInternal(config, atomId, /*filterByUid=*/false,
/*uidInAttributionChain=*/false, /*pkgName=*/null, dimensionsInWhat);
}
private static void addGaugeMetricInternal(StatsdConfig.Builder config, int atomId,
boolean filterByUid, boolean uidInAttributionChain, @Nullable String pkgName,
@Nullable FieldMatcher.Builder dimensionsInWhat) {
final String gaugeName = "Gauge metric " + System.nanoTime();
final String whatName = "What atom matcher " + System.nanoTime();
final String triggerName = "Trigger atom matcher " + System.nanoTime();
// Add atom matcher for "what"
SimpleAtomMatcher.Builder whatMatcher = SimpleAtomMatcher.newBuilder().setAtomId(atomId);
if (filterByUid && pkgName != null) {
whatMatcher.addFieldValueMatcher(createUidFvm(uidInAttributionChain, pkgName));
}
config.addAtomMatcher(AtomMatcher.newBuilder()
.setId(whatName.hashCode())
.setSimpleAtomMatcher(whatMatcher));
// Add atom matcher for trigger event
SimpleAtomMatcher.Builder triggerMatcher = SimpleAtomMatcher.newBuilder()
.setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER);
config.addAtomMatcher(AtomMatcher.newBuilder()
.setId(triggerName.hashCode())
.setSimpleAtomMatcher(triggerMatcher));
// Add gauge metric
GaugeMetric.Builder gaugeMetric = GaugeMetric.newBuilder()
.setId(gaugeName.hashCode())
.setWhat(whatName.hashCode())
.setTriggerEvent(triggerName.hashCode())
.setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build())
.setBucket(TimeUnit.CTS)
.setSamplingType(GaugeMetric.SamplingType.FIRST_N_SAMPLES)
.setMaxNumGaugeAtomsPerBucket(10_000);
if (dimensionsInWhat != null) {
gaugeMetric.setDimensionsInWhat(dimensionsInWhat.build());
}
config.addGaugeMetric(gaugeMetric.build());
}
/**
* Creates a FieldValueMatcher.Builder object that matches atoms whose uid field is equal to
* the uid of pkgName.
*
* @param uidInAttributionChain if true, the uid is part of the attribution chain; if false, uid
* is a standalone field
* @param pkgName test app package from which atom will be logged
*/
public static FieldValueMatcher.Builder createUidFvm(boolean uidInAttributionChain,
String pkgName) {
if (uidInAttributionChain) {
FieldValueMatcher.Builder nodeFvm = createFvm(ATTRIBUTION_NODE_UID_FIELD_NUMBER)
.setEqString(pkgName);
return createFvm(ATTRIBUTION_CHAIN_FIELD_NUMBER)
.setPosition(Position.ANY)
.setMatchesTuple(MessageMatcher.newBuilder().addFieldValueMatcher(nodeFvm));
} else {
return createFvm(UID_FIELD_NUMBER).setEqString(pkgName);
}
}
/**
* Creates a FieldValueMatcher.Builder for a particular field. Note that the value still needs
* to be set.
*
* @param fieldNumber index of field within the atom
*/
public static FieldValueMatcher.Builder createFvm(int fieldNumber) {
return FieldValueMatcher.newBuilder().setField(fieldNumber);
}
/**
* Upload a config to statsd.
*/
public static void uploadConfig(ITestDevice device, StatsdConfig.Builder configBuilder)
throws Exception {
StatsdConfig config = configBuilder.build();
CLog.d("Uploading the following config to statsd:\n" + config.toString());
File configFile = File.createTempFile("statsdconfig", ".config");
configFile.deleteOnExit();
Files.write(config.toByteArray(), configFile);
// Push config to temporary location
String remotePath = "/data/local/tmp/" + configFile.getName();
device.pushFile(configFile, remotePath);
// Send config to statsd
device.executeShellCommand(String.join(" ", "cat", remotePath, "|", UPDATE_CONFIG_CMD,
CONFIG_ID_STRING));
// Remove config from temporary location
device.executeShellCommand("rm " + remotePath);
// Sleep for a bit so that statsd receives config before more work is done within the test.
Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
}
/**
* Removes any pre-existing CTS configs from statsd.
*/
public static void removeConfig(ITestDevice device) throws Exception {
device.executeShellCommand(String.join(" ", REMOVE_CONFIG_CMD, CONFIG_ID_STRING));
}
public static void uploadConfigForPushedAtomWithUid(ITestDevice device, String pkgName,
int atomId,
boolean useUidAttributionChain) throws Exception {
StatsdConfig.Builder config = createConfigBuilder(pkgName);
addEventMetricForUidAtom(config, atomId, useUidAttributionChain, pkgName);
uploadConfig(device, config);
}
public static void uploadConfigForPulledAtomWithUid(ITestDevice device, String pkgName,
int atomId,
boolean useUidAttributionChain) throws Exception {
StatsdConfig.Builder config = createConfigBuilder(pkgName);
addGaugeMetricForUidAtom(config, atomId, useUidAttributionChain, pkgName);
uploadConfig(device, config);
}
public static void uploadConfigForPushedAtom(ITestDevice device, String pkgName, int atomId)
throws Exception {
StatsdConfig.Builder config = createConfigBuilder(pkgName);
addEventMetric(config, atomId);
uploadConfig(device, config);
}
public static void uploadConfigForPushedAtoms(ITestDevice device, String pkgName, int[] atomIds)
throws Exception {
StatsdConfig.Builder config = createConfigBuilder(pkgName);
for (int atomId : atomIds) {
addEventMetric(config, atomId);
}
uploadConfig(device, config);
}
public static void uploadConfigForPulledAtom(ITestDevice device, String pkgName, int atomId)
throws Exception {
StatsdConfig.Builder config = createConfigBuilder(pkgName);
addGaugeMetric(config, atomId);
uploadConfig(device, config);
}
private ConfigUtils() {
}
}