• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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