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