• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.cts.statsdatom.lib;
18 
19 import com.android.os.AtomsProto.AppBreadcrumbReported;
20 import com.android.internal.os.StatsdConfigProto.AtomMatcher;
21 import com.android.internal.os.StatsdConfigProto.EventMetric;
22 import com.android.internal.os.StatsdConfigProto.FieldFilter;
23 import com.android.internal.os.StatsdConfigProto.FieldMatcher;
24 import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
25 import com.android.internal.os.StatsdConfigProto.GaugeMetric;
26 import com.android.internal.os.StatsdConfigProto.MessageMatcher;
27 import com.android.internal.os.StatsdConfigProto.Position;
28 import com.android.internal.os.StatsdConfigProto.Predicate;
29 import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
30 import com.android.internal.os.StatsdConfigProto.SimplePredicate;
31 import com.android.internal.os.StatsdConfigProto.StatsdConfig;
32 import com.android.internal.os.StatsdConfigProto.TimeUnit;
33 import com.android.os.AtomsProto.Atom;
34 import com.android.tradefed.device.ITestDevice;
35 import com.android.tradefed.log.LogUtil.CLog;
36 
37 import com.google.common.io.Files;
38 
39 import java.io.File;
40 import java.util.Arrays;
41 import java.util.List;
42 
43 import javax.annotation.Nullable;
44 
45 public final class ConfigUtils {
46     public static final long CONFIG_ID = "cts_config".hashCode(); // evaluates to -1572883457
47     public static final String CONFIG_ID_STRING = String.valueOf(CONFIG_ID);
48 
49     // Attribution chains are the first field in atoms.
50     private static final int ATTRIBUTION_CHAIN_FIELD_NUMBER = 1;
51     // Uids are the first field in attribution nodes.
52     private static final int ATTRIBUTION_NODE_UID_FIELD_NUMBER = 1;
53     // Uids as standalone fields are the first field in atoms.
54     private static final int UID_FIELD_NUMBER = 1;
55 
56     // adb shell commands
57     private static final String UPDATE_CONFIG_CMD = "cmd stats config update";
58     private static final String REMOVE_CONFIG_CMD = "cmd stats config remove";
59 
60     /**
61      * Create a new config with common fields filled out, such as allowed log sources and
62      * default pull packages.
63      *
64      * @param pkgName test app package from which pushed atoms will be sent
65      */
createConfigBuilder(String pkgName)66     public static StatsdConfig.Builder createConfigBuilder(String pkgName) {
67         return StatsdConfig.newBuilder()
68                 .setId(CONFIG_ID)
69                 .addAllowedLogSource("AID_SYSTEM")
70                 .addAllowedLogSource("AID_BLUETOOTH")
71                 .addAllowedLogSource("com.android.bluetooth")
72                 .addAllowedLogSource("AID_LMKD")
73                 .addAllowedLogSource("AID_MEDIA")
74                 .addAllowedLogSource("AID_RADIO")
75                 .addAllowedLogSource("AID_ROOT")
76                 .addAllowedLogSource("AID_STATSD")
77                 .addAllowedLogSource("com.android.systemui")
78                 .addAllowedLogSource(pkgName)
79                 .addDefaultPullPackages("AID_RADIO")
80                 .addDefaultPullPackages("AID_SYSTEM")
81                 .addWhitelistedAtomIds(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER);
82     }
83 
84     /**
85      * Adds an event metric for the specified atom. The atom should contain a uid either within
86      * an attribution chain or as a standalone field. Only those atoms which contain the uid of
87      * the test app will be included in statsd's report.
88      *
89      * @param config
90      * @param atomId index of atom within atoms.proto
91      * @param uidInAttributionChain if true, the uid is part of the attribution chain; if false,
92      *    uid is a standalone field
93      * @param pkgName test app package from which atom will be logged
94      */
addEventMetricForUidAtom(StatsdConfig.Builder config, int atomId, boolean uidInAttributionChain, String pkgName)95     public static void addEventMetricForUidAtom(StatsdConfig.Builder config, int atomId,
96             boolean uidInAttributionChain, String pkgName) {
97         FieldValueMatcher.Builder fvm = createUidFvm(uidInAttributionChain, pkgName);
98         addEventMetric(config, atomId, Arrays.asList(fvm));
99     }
100 
101     /**
102      * Adds an event metric for the specified atom. All such atoms received by statsd will be
103      * included in the report. If only atoms meeting certain constraints should be added to the
104      * report, use #addEventMetric(int atomId, List<FieldValueMatcher.Builder> fvms instead.
105      *
106      * @param config
107      * @param atomId index of atom within atoms.proto
108      */
addEventMetric(StatsdConfig.Builder config, int atomId)109     public static void addEventMetric(StatsdConfig.Builder config, int atomId) {
110         addEventMetric(config, atomId, /*fvms=*/null);
111     }
112 
113     /**
114      * Adds an event metric to the config for the specified atom. The atom's fields must meet
115      * the constraints specified in fvms for the atom to be included in statsd's report.
116      *
117      * @param config
118      * @param atomId index of atom within atoms.proto
119      * @param fvms list of constraints that atoms are filtered on
120      */
addEventMetric(StatsdConfig.Builder config, int atomId, @Nullable List<FieldValueMatcher.Builder> fvms)121     public static void addEventMetric(StatsdConfig.Builder config, int atomId,
122             @Nullable List<FieldValueMatcher.Builder> fvms) {
123         final String matcherName = "Atom matcher" + System.nanoTime();
124         final String eventName = "Event " + System.nanoTime();
125 
126         SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder().setAtomId(atomId);
127         if (fvms != null) {
128             for (FieldValueMatcher.Builder fvm : fvms) {
129                 sam.addFieldValueMatcher(fvm);
130             }
131         }
132 
133         config.addAtomMatcher(AtomMatcher.newBuilder()
134                 .setId(matcherName.hashCode())
135                 .setSimpleAtomMatcher(sam));
136         config.addEventMetric(EventMetric.newBuilder()
137                 .setId(eventName.hashCode())
138                 .setWhat(matcherName.hashCode()));
139     }
140 
141     /**
142      * Adds a gauge metric for a pulled atom with a uid field to the config. The atom will be
143      * pulled when an AppBreadcrumbReported atom is logged to statsd, and only those pulled atoms
144      * containing the uid of the test app will be included in statsd's report.
145      *
146      * @param config
147      * @param atomId index of atom within atoms.proto
148      * @param uidInAttributionChain if true, the uid is part of the attribution chain; if false, uid
149      *    is a standalone field
150      * @param pkgName test app package from which atom will be logged
151      */
addGaugeMetricForUidAtom(StatsdConfig.Builder config, int atomId, boolean uidInAttributionChain, String pkgName)152     public static void addGaugeMetricForUidAtom(StatsdConfig.Builder config, int atomId,
153             boolean uidInAttributionChain, String pkgName) {
154         addGaugeMetricInternal(config, atomId, /*filterByUid=*/true, uidInAttributionChain, pkgName,
155                 /*dimensionsInWhat=*/null);
156     }
157 
158     /**
159      * Equivalent to addGaugeMetricForUidAtom except that the output in the report is sliced by the
160      * specified dimensions.
161      *
162      * @param dimensionsInWhat dimensions to slice the output by
163      */
addGaugeMetricForUidAtomWithDimensions(StatsdConfig.Builder config, int atomId, boolean uidInAttributionChain, String pkgName, FieldMatcher.Builder dimensionsInWhat)164     public static void addGaugeMetricForUidAtomWithDimensions(StatsdConfig.Builder config,
165             int atomId, boolean uidInAttributionChain, String pkgName,
166             FieldMatcher.Builder dimensionsInWhat) {
167         addGaugeMetricInternal(config, atomId, /*filterByUid=*/true, uidInAttributionChain, pkgName,
168                 dimensionsInWhat);
169     }
170 
171     /**
172      * Adds a gauge metric for a pulled atom to the config. The atom will be pulled when an
173      * AppBreadcrumbReported atom is logged to statsd.
174      *
175      * @param config
176      * @param atomId index of the atom within atoms.proto
177      * @param dimensionsInWhat dimensions to slice the output by
178      */
addGaugeMetric(StatsdConfig.Builder config, int atomId)179     public static void addGaugeMetric(StatsdConfig.Builder config, int atomId) {
180         addGaugeMetricInternal(config, atomId, /*filterByUid=*/false,
181                 /*uidInAttributionChain=*/false, /*pkgName=*/null, /*dimensionsInWhat=*/null);
182     }
183 
184     /**
185      * Equivalent to addGaugeMetric except that output in the report is sliced by the specified
186      * dimensions.
187      *
188      * @param dimensionsInWhat dimensions to slice the output by
189      */
addGaugeMetricWithDimensions(StatsdConfig.Builder config, int atomId, FieldMatcher.Builder dimensionsInWhat)190     public static void addGaugeMetricWithDimensions(StatsdConfig.Builder config, int atomId,
191             FieldMatcher.Builder dimensionsInWhat) {
192         addGaugeMetricInternal(config, atomId, /*filterByUid=*/false,
193                 /*uidInAttributionChain=*/false, /*pkgName=*/null, dimensionsInWhat);
194     }
195 
addGaugeMetricInternal(StatsdConfig.Builder config, int atomId, boolean filterByUid, boolean uidInAttributionChain, @Nullable String pkgName, @Nullable FieldMatcher.Builder dimensionsInWhat)196     private static void addGaugeMetricInternal(StatsdConfig.Builder config, int atomId,
197             boolean filterByUid, boolean uidInAttributionChain, @Nullable String pkgName,
198             @Nullable FieldMatcher.Builder dimensionsInWhat) {
199         final String gaugeName = "Gauge metric " + System.nanoTime();
200         final String whatName = "What atom matcher " + System.nanoTime();
201         final String triggerName = "Trigger atom matcher " + System.nanoTime();
202 
203         // Add atom matcher for "what"
204         SimpleAtomMatcher.Builder whatMatcher = SimpleAtomMatcher.newBuilder().setAtomId(atomId);
205         if (filterByUid && pkgName != null) {
206             whatMatcher.addFieldValueMatcher(createUidFvm(uidInAttributionChain, pkgName));
207         }
208         config.addAtomMatcher(AtomMatcher.newBuilder()
209                 .setId(whatName.hashCode())
210                 .setSimpleAtomMatcher(whatMatcher));
211 
212         // Add atom matcher for trigger event
213         SimpleAtomMatcher.Builder triggerMatcher = SimpleAtomMatcher.newBuilder()
214                 .setAtomId(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER);
215         config.addAtomMatcher(AtomMatcher.newBuilder()
216                 .setId(triggerName.hashCode())
217                 .setSimpleAtomMatcher(triggerMatcher));
218 
219         // Add gauge metric
220         GaugeMetric.Builder gaugeMetric = GaugeMetric.newBuilder()
221                 .setId(gaugeName.hashCode())
222                 .setWhat(whatName.hashCode())
223                 .setTriggerEvent(triggerName.hashCode())
224                 .setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build())
225                 .setBucket(TimeUnit.CTS)
226                 .setSamplingType(GaugeMetric.SamplingType.FIRST_N_SAMPLES)
227                 .setMaxNumGaugeAtomsPerBucket(10_000);
228         if (dimensionsInWhat != null) {
229             gaugeMetric.setDimensionsInWhat(dimensionsInWhat.build());
230         }
231         config.addGaugeMetric(gaugeMetric.build());
232     }
233 
234     /**
235      * Creates a FieldValueMatcher.Builder object that matches atoms whose uid field is equal to
236      * the uid of pkgName.
237      *
238      * @param uidInAttributionChain if true, the uid is part of the attribution chain; if false, uid
239      * is a standalone field
240      * @param pkgName test app package from which atom will be logged
241      */
createUidFvm(boolean uidInAttributionChain, String pkgName)242     public static FieldValueMatcher.Builder createUidFvm(boolean uidInAttributionChain,
243             String pkgName) {
244         if (uidInAttributionChain) {
245             FieldValueMatcher.Builder nodeFvm = createFvm(ATTRIBUTION_NODE_UID_FIELD_NUMBER)
246                     .setEqString(pkgName);
247             return createFvm(ATTRIBUTION_CHAIN_FIELD_NUMBER)
248                     .setPosition(Position.ANY)
249                     .setMatchesTuple(MessageMatcher.newBuilder().addFieldValueMatcher(nodeFvm));
250         } else {
251             return createFvm(UID_FIELD_NUMBER).setEqString(pkgName);
252         }
253     }
254 
255     /**
256      * Creates a FieldValueMatcher.Builder for a particular field. Note that the value still needs
257      * to be set.
258      *
259      * @param fieldNumber index of field within the atom
260      */
createFvm(int fieldNumber)261     public static FieldValueMatcher.Builder createFvm(int fieldNumber) {
262         return FieldValueMatcher.newBuilder().setField(fieldNumber);
263     }
264 
265     /**
266      * Upload a config to statsd.
267      */
uploadConfig(ITestDevice device, StatsdConfig.Builder configBuilder)268     public static void uploadConfig(ITestDevice device, StatsdConfig.Builder configBuilder)
269             throws Exception {
270         StatsdConfig config = configBuilder.build();
271         CLog.d("Uploading the following config to statsd:\n" + config.toString());
272 
273         File configFile = File.createTempFile("statsdconfig", ".config");
274         configFile.deleteOnExit();
275         Files.write(config.toByteArray(), configFile);
276 
277         // Push config to temporary location
278         String remotePath = "/data/local/tmp/" + configFile.getName();
279         device.pushFile(configFile, remotePath);
280 
281         // Send config to statsd
282         device.executeShellCommand(String.join(" ", "cat", remotePath, "|", UPDATE_CONFIG_CMD,
283                 CONFIG_ID_STRING));
284 
285         // Remove config from temporary location
286         device.executeShellCommand("rm " + remotePath);
287 
288         // Sleep for a bit so that statsd receives config before more work is done within the test.
289         Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
290     }
291 
292     /**
293      * Removes any pre-existing CTS configs from statsd.
294      */
removeConfig(ITestDevice device)295     public static void removeConfig(ITestDevice device) throws Exception {
296         device.executeShellCommand(String.join(" ", REMOVE_CONFIG_CMD, CONFIG_ID_STRING));
297     }
298 
uploadConfigForPushedAtomWithUid(ITestDevice device, String pkgName, int atomId, boolean useUidAttributionChain)299     public static void uploadConfigForPushedAtomWithUid(ITestDevice device, String pkgName,
300             int atomId,
301             boolean useUidAttributionChain) throws Exception {
302         StatsdConfig.Builder config = createConfigBuilder(pkgName);
303         addEventMetricForUidAtom(config, atomId, useUidAttributionChain, pkgName);
304         uploadConfig(device, config);
305     }
306 
uploadConfigForPulledAtomWithUid(ITestDevice device, String pkgName, int atomId, boolean useUidAttributionChain)307     public static void uploadConfigForPulledAtomWithUid(ITestDevice device, String pkgName,
308             int atomId,
309             boolean useUidAttributionChain) throws Exception {
310         StatsdConfig.Builder config = createConfigBuilder(pkgName);
311         addGaugeMetricForUidAtom(config, atomId, useUidAttributionChain, pkgName);
312         uploadConfig(device, config);
313     }
314 
uploadConfigForPushedAtom(ITestDevice device, String pkgName, int atomId)315     public static void uploadConfigForPushedAtom(ITestDevice device, String pkgName, int atomId)
316             throws Exception {
317         StatsdConfig.Builder config = createConfigBuilder(pkgName);
318         addEventMetric(config, atomId);
319         uploadConfig(device, config);
320     }
321 
uploadConfigForPushedAtoms(ITestDevice device, String pkgName, int[] atomIds)322     public static void uploadConfigForPushedAtoms(ITestDevice device, String pkgName, int[] atomIds)
323             throws Exception {
324         StatsdConfig.Builder config = createConfigBuilder(pkgName);
325         for (int atomId : atomIds) {
326             addEventMetric(config, atomId);
327         }
328         uploadConfig(device, config);
329     }
330 
uploadConfigForPulledAtom(ITestDevice device, String pkgName, int atomId)331     public static void uploadConfigForPulledAtom(ITestDevice device, String pkgName, int atomId)
332             throws Exception {
333         StatsdConfig.Builder config = createConfigBuilder(pkgName);
334         addGaugeMetric(config, atomId);
335         uploadConfig(device, config);
336     }
337 
ConfigUtils()338     private ConfigUtils() {
339     }
340 }
341