1 /*
2 * Copyright (C) 2017, 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 #include "Collation.h"
18
19 #include <google/protobuf/descriptor.h>
20 #include <stdarg.h>
21 #include <stdio.h>
22
23 #include <map>
24 #include <string_view>
25
26 #include "frameworks/proto_logging/stats/atom_field_options.pb.h"
27 #include "frameworks/proto_logging/stats/atoms.pb.h"
28 #include "frameworks/proto_logging/stats/attribution_node.pb.h"
29 #include "utils.h"
30
31 namespace android {
32 namespace stats_log_api_gen {
33
34 using google::protobuf::EnumDescriptor;
35 using google::protobuf::FieldDescriptor;
36 using google::protobuf::FileDescriptor;
37 using google::protobuf::SourceLocation;
38 using std::make_shared;
39 using std::map;
40
41 const bool dbg = false;
42
43 const int PLATFORM_PULLED_ATOMS_START = 10000;
44 const int PLATFORM_PULLED_ATOMS_END = 99999;
45 const int VENDOR_PULLED_ATOMS_START = 150000;
46 const int VENDOR_PULLED_ATOMS_END = 199999;
47
48 //
49 // AtomDecl class
50 //
51
AtomDecl()52 AtomDecl::AtomDecl() : code(0), name(), atomType(ATOM_TYPE_PUSHED) {
53 }
54
AtomDecl(const AtomDecl & that)55 AtomDecl::AtomDecl(const AtomDecl& that)
56 : code(that.code),
57 name(that.name),
58 message(that.message),
59 fields(that.fields),
60 atomType(that.atomType),
61 fieldNumberToAnnotations(that.fieldNumberToAnnotations),
62 primaryFields(that.primaryFields),
63 exclusiveField(that.exclusiveField),
64 defaultState(that.defaultState),
65 triggerStateReset(that.triggerStateReset),
66 nested(that.nested) {
67 }
68
AtomDecl(int c,const string & n,const string & m,AtomType a)69 AtomDecl::AtomDecl(int c, const string& n, const string& m, AtomType a)
70 : code(c), name(n), message(m), atomType(a) {
71 }
72
~AtomDecl()73 AtomDecl::~AtomDecl() {
74 }
75
76 /**
77 * Print an error message for a FieldDescriptor, including the file name and
78 * line number.
79 */
80 // NOLINTNEXTLINE(cert-dcl50-cpp)
print_error(const FieldDescriptor & field,const char * format,...)81 static void print_error(const FieldDescriptor& field, const char* format, ...) {
82 const Descriptor* message = field.containing_type();
83 const FileDescriptor* file = message->file();
84
85 SourceLocation loc;
86 if (field.GetSourceLocation(&loc)) {
87 // TODO(b/162454173): this will work if we can figure out how to pass
88 // --include_source_info to protoc
89 fprintf(stderr, "%s:%d: ", std::string(file->name()).c_str(), loc.start_line);
90 } else {
91 fprintf(stderr, "%s: ", std::string(file->name()).c_str());
92 }
93 va_list args;
94 va_start(args, format);
95 vfprintf(stderr, format, args);
96 va_end(args);
97 }
98
99 /**
100 * Convert a protobuf type into a java type.
101 */
java_type(const FieldDescriptor & field,const bool isUintAllowed)102 static java_type_t java_type(const FieldDescriptor& field, const bool isUintAllowed) {
103 const int protoType = field.type();
104 const bool isRepeatedField = field.is_repeated();
105
106 switch (protoType) {
107 case FieldDescriptor::TYPE_FLOAT:
108 return isRepeatedField ? JAVA_TYPE_FLOAT_ARRAY : JAVA_TYPE_FLOAT;
109 case FieldDescriptor::TYPE_INT64:
110 return isRepeatedField ? JAVA_TYPE_LONG_ARRAY : JAVA_TYPE_LONG;
111 case FieldDescriptor::TYPE_INT32:
112 return isRepeatedField ? JAVA_TYPE_INT_ARRAY : JAVA_TYPE_INT;
113 case FieldDescriptor::TYPE_BOOL:
114 return isRepeatedField ? JAVA_TYPE_BOOLEAN_ARRAY : JAVA_TYPE_BOOLEAN;
115 case FieldDescriptor::TYPE_STRING:
116 return isRepeatedField ? JAVA_TYPE_STRING_ARRAY : JAVA_TYPE_STRING;
117 case FieldDescriptor::TYPE_ENUM:
118 return isRepeatedField ? JAVA_TYPE_ENUM_ARRAY : JAVA_TYPE_ENUM;
119 case FieldDescriptor::TYPE_GROUP:
120 return JAVA_TYPE_UNKNOWN_OR_INVALID;
121 case FieldDescriptor::TYPE_MESSAGE:
122 if (field.message_type()->full_name() == "android.os.statsd.AttributionNode") {
123 return JAVA_TYPE_ATTRIBUTION_CHAIN;
124 } else if ((field.options().GetExtension(os::statsd::log_mode) ==
125 os::statsd::LogMode::MODE_BYTES) &&
126 !isRepeatedField) {
127 return JAVA_TYPE_BYTE_ARRAY;
128 } else {
129 return isRepeatedField ? JAVA_TYPE_UNKNOWN_OR_INVALID : JAVA_TYPE_OBJECT;
130 }
131 case FieldDescriptor::TYPE_BYTES:
132 return isRepeatedField ? JAVA_TYPE_UNKNOWN_OR_INVALID : JAVA_TYPE_BYTE_ARRAY;
133 case FieldDescriptor::TYPE_UINT64:
134 return isRepeatedField || !isUintAllowed ? JAVA_TYPE_UNKNOWN_OR_INVALID
135 : JAVA_TYPE_LONG;
136 case FieldDescriptor::TYPE_UINT32:
137 return isRepeatedField || !isUintAllowed ? JAVA_TYPE_UNKNOWN_OR_INVALID : JAVA_TYPE_INT;
138 default:
139 return JAVA_TYPE_UNKNOWN_OR_INVALID;
140 }
141 }
142
143 /**
144 * Gather the enums info.
145 */
collate_enums(const EnumDescriptor & enumDescriptor,AtomField & atomField)146 void collate_enums(const EnumDescriptor& enumDescriptor, AtomField& atomField) {
147 for (int i = 0; i < enumDescriptor.value_count(); i++) {
148 atomField.enumValues[enumDescriptor.value(i)->number()] = enumDescriptor.value(i)->name();
149 }
150 }
151
addAnnotationToAtomDecl(AtomDecl & atomDecl,const int fieldNumber,const AnnotationId annotationId,const AnnotationType annotationType,const AnnotationValue annotationValue)152 static void addAnnotationToAtomDecl(AtomDecl& atomDecl, const int fieldNumber,
153 const AnnotationId annotationId,
154 const AnnotationType annotationType,
155 const AnnotationValue annotationValue) {
156 if (dbg) {
157 printf(" Adding annotation to %s: [%d] = {id: %d, type: %d}\n", atomDecl.name.c_str(),
158 fieldNumber, annotationId, annotationType);
159 }
160 atomDecl.fieldNumberToAnnotations[fieldNumber].insert(
161 make_shared<Annotation>(annotationId, atomDecl.code, annotationType, annotationValue));
162 }
163
collate_field_restricted_annotations(AtomDecl & atomDecl,const FieldDescriptor & field,const int fieldNumber)164 static int collate_field_restricted_annotations(AtomDecl& atomDecl, const FieldDescriptor& field,
165 const int fieldNumber) {
166 int errorCount = 0;
167
168 if (field.options().HasExtension(os::statsd::field_restriction_option)) {
169 if (!atomDecl.restricted) {
170 print_error(field,
171 "field_restriction_option annotations must be from an atom with "
172 "a restriction_category annotation: '%s'\n",
173 atomDecl.message.c_str());
174 errorCount++;
175 }
176
177 const os::statsd::FieldRestrictionOption& fieldRestrictionOption =
178 field.options().GetExtension(os::statsd::field_restriction_option);
179
180 if (fieldRestrictionOption.peripheral_device_info()) {
181 addAnnotationToAtomDecl(atomDecl, fieldNumber,
182 ANNOTATION_ID_FIELD_RESTRICTION_PERIPHERAL_DEVICE_INFO,
183 ANNOTATION_TYPE_BOOL, AnnotationValue(true));
184 }
185
186 if (fieldRestrictionOption.app_usage()) {
187 addAnnotationToAtomDecl(atomDecl, fieldNumber,
188 ANNOTATION_ID_FIELD_RESTRICTION_APP_USAGE, ANNOTATION_TYPE_BOOL,
189 AnnotationValue(true));
190 }
191
192 if (fieldRestrictionOption.app_activity()) {
193 addAnnotationToAtomDecl(atomDecl, fieldNumber,
194 ANNOTATION_ID_FIELD_RESTRICTION_APP_ACTIVITY,
195 ANNOTATION_TYPE_BOOL, AnnotationValue(true));
196 }
197
198 if (fieldRestrictionOption.health_connect()) {
199 addAnnotationToAtomDecl(atomDecl, fieldNumber,
200 ANNOTATION_ID_FIELD_RESTRICTION_HEALTH_CONNECT,
201 ANNOTATION_TYPE_BOOL, AnnotationValue(true));
202 }
203
204 if (fieldRestrictionOption.accessibility()) {
205 addAnnotationToAtomDecl(atomDecl, fieldNumber,
206 ANNOTATION_ID_FIELD_RESTRICTION_ACCESSIBILITY,
207 ANNOTATION_TYPE_BOOL, AnnotationValue(true));
208 }
209
210 if (fieldRestrictionOption.system_search()) {
211 addAnnotationToAtomDecl(atomDecl, fieldNumber,
212 ANNOTATION_ID_FIELD_RESTRICTION_SYSTEM_SEARCH,
213 ANNOTATION_TYPE_BOOL, AnnotationValue(true));
214 }
215
216 if (fieldRestrictionOption.user_engagement()) {
217 addAnnotationToAtomDecl(atomDecl, fieldNumber,
218 ANNOTATION_ID_FIELD_RESTRICTION_USER_ENGAGEMENT,
219 ANNOTATION_TYPE_BOOL, AnnotationValue(true));
220 }
221
222 if (fieldRestrictionOption.ambient_sensing()) {
223 addAnnotationToAtomDecl(atomDecl, fieldNumber,
224 ANNOTATION_ID_FIELD_RESTRICTION_AMBIENT_SENSING,
225 ANNOTATION_TYPE_BOOL, AnnotationValue(true));
226 }
227
228 if (fieldRestrictionOption.demographic_classification()) {
229 addAnnotationToAtomDecl(atomDecl, fieldNumber,
230 ANNOTATION_ID_FIELD_RESTRICTION_DEMOGRAPHIC_CLASSIFICATION,
231 ANNOTATION_TYPE_BOOL, AnnotationValue(true));
232 }
233 }
234
235 if (field.options().HasExtension(os::statsd::restriction_category)) {
236 print_error(field, "restriction_category must be an atom-level annotation: '%s'\n",
237 atomDecl.message.c_str());
238 errorCount++;
239 }
240
241 return errorCount;
242 }
243
collate_histogram_bin_option(AtomDecl & atomDecl,const FieldDescriptor & field,const java_type_t & javaType)244 static int collate_histogram_bin_option(AtomDecl& atomDecl, const FieldDescriptor& field,
245 const java_type_t& javaType) {
246 if (!field.options().HasExtension(os::statsd::histogram_bin_option)) {
247 return 0;
248 }
249
250 int errorCount = 0;
251 if (javaType != JAVA_TYPE_INT_ARRAY) {
252 print_error(field,
253 "histogram annotations can only be applied to repeated int32 fields: '%s'\n",
254 atomDecl.message.c_str());
255 errorCount++;
256 }
257
258 const os::statsd::HistogramBinOption& histogramBinOption =
259 field.options().GetExtension(os::statsd::histogram_bin_option);
260 if (histogramBinOption.has_generated_bins()) {
261 const os::statsd::HistogramBinOption::GeneratedBins& generatedBins =
262 histogramBinOption.generated_bins();
263 if (!generatedBins.has_min() || !generatedBins.has_max() || !generatedBins.has_count() ||
264 !generatedBins.has_strategy() ||
265 generatedBins.strategy() ==
266 os::statsd::HistogramBinOption::GeneratedBins::STRATEGY_UNKNOWN) {
267 print_error(field,
268 "For generated bins, all of min, max, count, and strategy need to be "
269 "specified: '%s',\n",
270 atomDecl.message.c_str());
271 errorCount++;
272 }
273 } else if (histogramBinOption.has_explicit_bins()) {
274 const os::statsd::HistogramBinOption::ExplicitBins& explicitBins =
275 histogramBinOption.explicit_bins();
276 if (explicitBins.bin().empty()) {
277 print_error(field, "For explicit bins, at least 1 bin needs to be specified: '%s',\n",
278 atomDecl.message.c_str());
279 errorCount++;
280 }
281 } else {
282 print_error(field, "binning_strategy needs to be specified: '%s',\n",
283 atomDecl.message.c_str());
284 errorCount++;
285 }
286
287 atomDecl.fieldNameToHistBinOption[std::string(field.name())] = histogramBinOption;
288 return errorCount;
289 }
290
collate_field_annotations(AtomDecl & atomDecl,const FieldDescriptor & field,const int fieldNumber,const java_type_t & javaType)291 static int collate_field_annotations(AtomDecl& atomDecl, const FieldDescriptor& field,
292 const int fieldNumber, const java_type_t& javaType) {
293 int errorCount = 0;
294
295 if (field.options().HasExtension(os::statsd::state_field_option)) {
296 if (is_repeated_field(javaType)) {
297 print_error(field,
298 "State field annotations are not allowed for repeated fields: '%s'\n",
299 atomDecl.message.c_str());
300 errorCount++;
301 return errorCount;
302 }
303
304 const os::statsd::StateAtomFieldOption& stateFieldOption =
305 field.options().GetExtension(os::statsd::state_field_option);
306 const bool primaryField = stateFieldOption.primary_field();
307 const bool exclusiveState = stateFieldOption.exclusive_state();
308 const bool primaryFieldFirstUid = stateFieldOption.primary_field_first_uid();
309
310 // Check the field is only one of primaryField, exclusiveState, or primaryFieldFirstUid.
311 if (primaryField + primaryFieldFirstUid + exclusiveState > 1) {
312 print_error(field,
313 "Field can be max 1 of primary_field, exclusive_state, "
314 "or primary_field_first_uid: '%s'\n",
315 atomDecl.message.c_str());
316 errorCount++;
317 }
318
319 if (primaryField) {
320 if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || javaType == JAVA_TYPE_OBJECT ||
321 javaType == JAVA_TYPE_BYTE_ARRAY) {
322 print_error(field, "Invalid primary state field: '%s'\n", atomDecl.message.c_str());
323 errorCount++;
324 } else {
325 atomDecl.primaryFields.push_back(fieldNumber);
326 addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_PRIMARY_FIELD,
327 ANNOTATION_TYPE_BOOL, AnnotationValue(true));
328 }
329 }
330
331 if (primaryFieldFirstUid) {
332 if (javaType != JAVA_TYPE_ATTRIBUTION_CHAIN) {
333 print_error(field,
334 "PRIMARY_FIELD_FIRST_UID annotation is only for AttributionChains: "
335 "'%s'\n",
336 atomDecl.message.c_str());
337 errorCount++;
338 } else {
339 atomDecl.primaryFields.push_back(FIRST_UID_IN_CHAIN_ID);
340 addAnnotationToAtomDecl(atomDecl, fieldNumber,
341 ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, ANNOTATION_TYPE_BOOL,
342 AnnotationValue(true));
343 }
344 }
345
346 if (exclusiveState) {
347 if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN || javaType == JAVA_TYPE_OBJECT ||
348 javaType == JAVA_TYPE_BYTE_ARRAY) {
349 print_error(field, "Invalid exclusive state field: '%s'\n",
350 atomDecl.message.c_str());
351 errorCount++;
352 }
353
354 if (atomDecl.exclusiveField != 0) {
355 print_error(field,
356 "Cannot have more than one exclusive state field in an "
357 "atom: '%s'\n",
358 atomDecl.message.c_str());
359 errorCount++;
360 } else {
361 atomDecl.exclusiveField = fieldNumber;
362 addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_EXCLUSIVE_STATE,
363 ANNOTATION_TYPE_BOOL, AnnotationValue(true));
364 }
365
366 if (stateFieldOption.has_default_state_value()) {
367 const int defaultState = stateFieldOption.default_state_value();
368 atomDecl.defaultState = defaultState;
369
370 addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_DEFAULT_STATE,
371 ANNOTATION_TYPE_INT, AnnotationValue(defaultState));
372 }
373
374 if (stateFieldOption.has_trigger_state_reset_value()) {
375 const int triggerStateReset = stateFieldOption.trigger_state_reset_value();
376
377 atomDecl.triggerStateReset = triggerStateReset;
378 addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_TRIGGER_STATE_RESET,
379 ANNOTATION_TYPE_INT, AnnotationValue(triggerStateReset));
380 }
381
382 if (stateFieldOption.has_nested()) {
383 const bool nested = stateFieldOption.nested();
384 atomDecl.nested = nested;
385
386 addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_STATE_NESTED,
387 ANNOTATION_TYPE_BOOL, AnnotationValue(nested));
388 }
389 }
390 }
391
392 errorCount += collate_field_restricted_annotations(atomDecl, field, fieldNumber);
393
394 if (field.options().GetExtension(os::statsd::is_uid) == true) {
395 if (javaType != JAVA_TYPE_INT && javaType != JAVA_TYPE_INT_ARRAY) {
396 print_error(field,
397 "is_uid annotation can only be applied to int32 fields and repeated int32 "
398 "fields: '%s'\n",
399 atomDecl.message.c_str());
400 errorCount++;
401 }
402
403 addAnnotationToAtomDecl(atomDecl, fieldNumber, ANNOTATION_ID_IS_UID, ANNOTATION_TYPE_BOOL,
404 AnnotationValue(true));
405 }
406
407 errorCount += collate_histogram_bin_option(atomDecl, field, javaType);
408
409 return errorCount;
410 }
411
412 /**
413 * Gather the info about an atom proto.
414 */
collate_atom(const Descriptor & atom,AtomDecl & atomDecl,vector<java_type_t> & signature)415 int collate_atom(const Descriptor& atom, AtomDecl& atomDecl, vector<java_type_t>& signature) {
416 int errorCount = 0;
417
418 // Build a sorted list of the fields. Descriptor has them in source file
419 // order.
420 map<int, const FieldDescriptor*> fields;
421 for (int j = 0; j < atom.field_count(); j++) {
422 const FieldDescriptor* field = atom.field(j);
423 fields[field->number()] = field;
424 }
425
426 // Check that the parameters start at 1 and go up sequentially.
427 int expectedNumber = 1;
428 for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
429 it++) {
430 const int number = it->first;
431 const FieldDescriptor& field = *it->second;
432 if (number != expectedNumber) {
433 print_error(field,
434 "Fields must be numbered consecutively starting at 1:"
435 " '%s' is %d but should be %d\n",
436 std::string(field.name()).c_str(), number, expectedNumber);
437 errorCount++;
438 expectedNumber = number;
439 continue;
440 }
441 expectedNumber++;
442 }
443
444 // Check if atom is in uint type allowlist.
445 std::string_view atomName = atom.name();
446 bool isUintAllowed = !(find(begin(UINT_ATOM_ALLOWLIST), end(UINT_ATOM_ALLOWLIST), atomName) ==
447 end(UINT_ATOM_ALLOWLIST));
448
449 // Check that only allowed types are present. Remove any invalid ones.
450 for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
451 it++) {
452 const FieldDescriptor& field = *it->second;
453 const bool isBinaryField = field.options().GetExtension(os::statsd::log_mode) ==
454 os::statsd::LogMode::MODE_BYTES;
455
456 const java_type_t javaType = java_type(field, isUintAllowed);
457
458 if (javaType == JAVA_TYPE_UNKNOWN_OR_INVALID) {
459 if (field.is_repeated()) {
460 print_error(field, "Repeated field type %s is not allowed for field: %s\n",
461 std::string(field.type_name()).c_str(),
462 std::string(field.name()).c_str());
463 } else {
464 print_error(field, "Field type %s is not allowed for field: %s\n",
465 std::string(field.type_name()).c_str(),
466 std::string(field.name()).c_str());
467 }
468 errorCount++;
469 continue;
470 } else if (javaType == JAVA_TYPE_OBJECT) {
471 // Allow attribution chain, but only at position 1.
472 print_error(field, "Message type not allowed for field without mode_bytes: %s\n",
473 std::string(field.name()).c_str());
474 errorCount++;
475 continue;
476 } else if (javaType == JAVA_TYPE_BYTE_ARRAY && !isBinaryField) {
477 print_error(field, "Raw bytes type not allowed for field: %s\n",
478 std::string(field.name()).c_str());
479 errorCount++;
480 continue;
481 }
482
483 if (isBinaryField && javaType != JAVA_TYPE_BYTE_ARRAY) {
484 print_error(field, "Cannot mark field %s as bytes.\n",
485 std::string(field.name()).c_str());
486 errorCount++;
487 continue;
488 }
489
490 if (atomDecl.restricted && !is_primitive_field(javaType)) {
491 print_error(field, "Restricted atom '%s' cannot have nonprimitive field: '%s'\n",
492 atomDecl.message.c_str(), std::string(field.name()).c_str());
493 errorCount++;
494 continue;
495 }
496 }
497
498 // Check that if there's an attribution chain, it's at position 1.
499 for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
500 it++) {
501 const int number = it->first;
502 if (number != 1) {
503 const FieldDescriptor& field = *it->second;
504 const java_type_t javaType = java_type(field, isUintAllowed);
505 if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
506 print_error(field,
507 "AttributionChain fields must have field id 1, in message: '%s'\n",
508 std::string(atom.name()).c_str());
509 errorCount++;
510 }
511 }
512 }
513
514 // Build the type signature and the atom data.
515 for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
516 it++) {
517 const FieldDescriptor& field = *it->second;
518 const java_type_t javaType = java_type(field, isUintAllowed);
519 const bool isBinaryField = field.options().GetExtension(os::statsd::log_mode) ==
520 os::statsd::LogMode::MODE_BYTES;
521
522 AtomField atField(std::string(field.name()), javaType);
523
524 if (javaType == JAVA_TYPE_ENUM || javaType == JAVA_TYPE_ENUM_ARRAY) {
525 atField.enumTypeName = field.enum_type()->name();
526 // All enums are treated as ints when it comes to function signatures.
527 collate_enums(*field.enum_type(), atField);
528 }
529
530 // Generate signature for atom.
531 if (javaType == JAVA_TYPE_ENUM) {
532 // All enums are treated as ints when it comes to function signatures.
533 signature.push_back(JAVA_TYPE_INT);
534 } else if (javaType == JAVA_TYPE_ENUM_ARRAY) {
535 signature.push_back(JAVA_TYPE_INT_ARRAY);
536 } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) {
537 signature.push_back(JAVA_TYPE_BYTE_ARRAY);
538 } else {
539 signature.push_back(javaType);
540 }
541
542 atomDecl.fields.push_back(atField);
543
544 errorCount += collate_field_annotations(atomDecl, field, it->first, javaType);
545 }
546
547 return errorCount;
548 }
549
550 // This function flattens the fields of the AttributionNode proto in an Atom
551 // proto and generates the corresponding atom decl and signature.
get_non_chained_node(const Descriptor & atom,AtomDecl & atomDecl,vector<java_type_t> & signature)552 bool get_non_chained_node(const Descriptor& atom, AtomDecl& atomDecl,
553 vector<java_type_t>& signature) {
554 // Build a sorted list of the fields. Descriptor has them in source file
555 // order.
556 map<int, const FieldDescriptor*> fields;
557 for (int j = 0; j < atom.field_count(); j++) {
558 const FieldDescriptor& field = *atom.field(j);
559 fields[field.number()] = &field;
560 }
561
562 AtomDecl attributionDecl;
563 vector<java_type_t> attributionSignature;
564 collate_atom(*android::os::statsd::AttributionNode::descriptor(), attributionDecl,
565 attributionSignature);
566
567 // Build the type signature and the atom data.
568 bool has_attribution_node = false;
569 for (map<int, const FieldDescriptor*>::const_iterator it = fields.begin(); it != fields.end();
570 it++) {
571 const FieldDescriptor& field = *it->second;
572 const java_type_t javaType = java_type(field, true);
573 if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
574 atomDecl.fields.insert(atomDecl.fields.end(), attributionDecl.fields.begin(),
575 attributionDecl.fields.end());
576 signature.insert(signature.end(), attributionSignature.begin(),
577 attributionSignature.end());
578 has_attribution_node = true;
579
580 } else {
581 AtomField atField(std::string(field.name()), javaType);
582 if (javaType == JAVA_TYPE_ENUM) {
583 // All enums are treated as ints when it comes to function signatures.
584 signature.push_back(JAVA_TYPE_INT);
585 collate_enums(*field.enum_type(), atField);
586 } else {
587 signature.push_back(javaType);
588 }
589 atomDecl.fields.push_back(atField);
590 }
591 }
592 return has_attribution_node;
593 }
594
populateFieldNumberToAtomDeclSet(const shared_ptr<AtomDecl> & atomDecl,FieldNumberToAtomDeclSet & fieldNumberToAtomDeclSet)595 static void populateFieldNumberToAtomDeclSet(const shared_ptr<AtomDecl>& atomDecl,
596 FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet) {
597 for (FieldNumberToAnnotations::const_iterator it = atomDecl->fieldNumberToAnnotations.begin();
598 it != atomDecl->fieldNumberToAnnotations.end(); it++) {
599 const int fieldNumber = it->first;
600 fieldNumberToAtomDeclSet[fieldNumber].insert(atomDecl);
601 }
602 }
603
getAtomType(const FieldDescriptor & atomField)604 static AtomType getAtomType(const FieldDescriptor& atomField) {
605 const int atomId = atomField.number();
606 if ((atomId >= PLATFORM_PULLED_ATOMS_START && atomId <= PLATFORM_PULLED_ATOMS_END) ||
607 (atomId >= VENDOR_PULLED_ATOMS_START && atomId <= VENDOR_PULLED_ATOMS_END)) {
608 return ATOM_TYPE_PULLED;
609 } else {
610 return ATOM_TYPE_PUSHED;
611 }
612 }
613
collate_from_field_descriptor(const FieldDescriptor & atomField,const string & moduleName,Atoms & atoms)614 static int collate_from_field_descriptor(const FieldDescriptor& atomField, const string& moduleName,
615 Atoms& atoms) {
616 int errorCount = 0;
617
618 if (moduleName != DEFAULT_MODULE_NAME) {
619 const int moduleCount = atomField.options().ExtensionSize(os::statsd::module);
620 bool moduleFound = false;
621 for (int j = 0; j < moduleCount; ++j) {
622 const string atomModuleName = atomField.options().GetExtension(os::statsd::module, j);
623 if (atomModuleName == moduleName) {
624 moduleFound = true;
625 break;
626 }
627 }
628
629 // This atom is not in the module we're interested in; skip it.
630 if (!moduleFound) {
631 if (dbg) {
632 printf(" Skipping %s (%d)\n", std::string(atomField.name()).c_str(),
633 atomField.number());
634 }
635 return errorCount;
636 }
637 }
638
639 if (dbg) {
640 printf(" %s (%d)\n", std::string(atomField.name()).c_str(), atomField.number());
641 }
642
643 // StatsEvent only has one oneof, which contains only messages. Don't allow
644 // other types.
645 if (atomField.type() != FieldDescriptor::TYPE_MESSAGE) {
646 print_error(atomField,
647 "Bad type for atom. StatsEvent can only have message type "
648 "fields: %s\n",
649 std::string(atomField.name()).c_str());
650 errorCount++;
651 return errorCount;
652 }
653
654 const AtomType atomType = getAtomType(atomField);
655
656 const Descriptor& atom = *atomField.message_type();
657 const shared_ptr<AtomDecl> atomDecl = make_shared<AtomDecl>(
658 atomField.number(), std::string(atomField.name()), std::string(atom.name()), atomType);
659
660 if (atomField.options().GetExtension(os::statsd::truncate_timestamp)) {
661 addAnnotationToAtomDecl(*atomDecl, ATOM_ID_FIELD_NUMBER, ANNOTATION_ID_TRUNCATE_TIMESTAMP,
662 ANNOTATION_TYPE_BOOL, AnnotationValue(true));
663 if (dbg) {
664 printf("%s can have timestamp truncated\n", std::string(atomField.name()).c_str());
665 }
666 }
667
668 if (atomField.options().HasExtension(os::statsd::restriction_category)) {
669 if (atomType == ATOM_TYPE_PULLED) {
670 print_error(atomField, "Restricted atoms cannot be pulled: '%s'\n",
671 std::string(atomField.name()).c_str());
672 errorCount++;
673 return errorCount;
674 }
675 const int restrictionCategory =
676 atomField.options().GetExtension(os::statsd::restriction_category);
677 atomDecl->restricted = true;
678 addAnnotationToAtomDecl(*atomDecl, ATOM_ID_FIELD_NUMBER, ANNOTATION_ID_RESTRICTION_CATEGORY,
679 ANNOTATION_TYPE_INT, AnnotationValue(restrictionCategory));
680 }
681
682 vector<java_type_t> signature;
683 errorCount += collate_atom(atom, *atomDecl, signature);
684 if (!atomDecl->primaryFields.empty() && atomDecl->exclusiveField == 0) {
685 print_error(atomField, "Cannot have a primary field without an exclusive field: %s\n",
686 std::string(atomField.name()).c_str());
687 errorCount++;
688 return errorCount;
689 }
690
691 FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet =
692 atomType == ATOM_TYPE_PUSHED ? atoms.signatureInfoMap[signature]
693 : atoms.pulledAtomsSignatureInfoMap[signature];
694 populateFieldNumberToAtomDeclSet(atomDecl, fieldNumberToAtomDeclSet);
695
696 atoms.decls.insert(atomDecl);
697
698 const shared_ptr<AtomDecl> nonChainedAtomDecl = make_shared<AtomDecl>(
699 atomField.number(), std::string(atomField.name()), std::string(atom.name()), atomType);
700 vector<java_type_t> nonChainedSignature;
701 if (get_non_chained_node(atom, *nonChainedAtomDecl, nonChainedSignature)) {
702 FieldNumberToAtomDeclSet& nonChainedFieldNumberToAtomDeclSet =
703 atoms.nonChainedSignatureInfoMap[nonChainedSignature];
704 populateFieldNumberToAtomDeclSet(nonChainedAtomDecl, nonChainedFieldNumberToAtomDeclSet);
705
706 atoms.non_chained_decls.insert(nonChainedAtomDecl);
707 }
708
709 if (atomField.options().HasExtension(os::statsd::field_restriction_option)) {
710 print_error(atomField, "field_restriction_option must be a field-level annotation: '%s'\n",
711 std::string(atomField.name()).c_str());
712 errorCount++;
713 }
714
715 return errorCount;
716 }
717
718 /**
719 * Gather the info about the atoms.
720 */
collate_atoms(const Descriptor & descriptor,const string & moduleName,Atoms & atoms)721 int collate_atoms(const Descriptor& descriptor, const string& moduleName, Atoms& atoms) {
722 int errorCount = 0;
723
724 // Regular field atoms in Atom
725 for (int i = 0; i < descriptor.field_count(); i++) {
726 const FieldDescriptor* atomField = descriptor.field(i);
727 errorCount += collate_from_field_descriptor(*atomField, moduleName, atoms);
728 }
729
730 // Extension field atoms in Atom.
731 vector<const FieldDescriptor*> extensions;
732 descriptor.file()->pool()->FindAllExtensions(&descriptor, &extensions);
733 for (const FieldDescriptor* atomField : extensions) {
734 errorCount += collate_from_field_descriptor(*atomField, moduleName, atoms);
735 }
736
737 if (dbg) {
738 // Signatures for pushed atoms.
739 printf("signatures = [\n");
740 for (SignatureInfoMap::const_iterator it = atoms.signatureInfoMap.begin();
741 it != atoms.signatureInfoMap.end(); it++) {
742 printf(" ");
743 for (vector<java_type_t>::const_iterator jt = it->first.begin(); jt != it->first.end();
744 jt++) {
745 printf(" %d", static_cast<int>(*jt));
746 }
747 printf("\n");
748 }
749
750 // Signatures for pull atoms.
751 for (SignatureInfoMap::const_iterator it = atoms.pulledAtomsSignatureInfoMap.begin();
752 it != atoms.pulledAtomsSignatureInfoMap.end(); it++) {
753 printf(" ");
754 for (vector<java_type_t>::const_iterator jt = it->first.begin(); jt != it->first.end();
755 jt++) {
756 printf(" %d", static_cast<int>(*jt));
757 }
758 printf("\n");
759 }
760 printf("]\n");
761 }
762
763 return errorCount;
764 }
765
766 } // namespace stats_log_api_gen
767 } // namespace android
768