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