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