• 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 #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