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 #include "frameworks/base/cmds/statsd/src/atoms.pb.h"
19 
20 #include <stdio.h>
21 #include <map>
22 
23 namespace android {
24 namespace stats_log_api_gen {
25 
26 using google::protobuf::EnumDescriptor;
27 using google::protobuf::FieldDescriptor;
28 using google::protobuf::FileDescriptor;
29 using google::protobuf::SourceLocation;
30 using std::map;
31 
32 
33 //
34 // AtomDecl class
35 //
36 
AtomDecl()37 AtomDecl::AtomDecl()
38     :code(0),
39      name()
40 {
41 }
42 
AtomDecl(const AtomDecl & that)43 AtomDecl::AtomDecl(const AtomDecl& that)
44     : code(that.code),
45       name(that.name),
46       message(that.message),
47       fields(that.fields),
48       primaryFields(that.primaryFields),
49       exclusiveField(that.exclusiveField),
50       uidField(that.uidField),
51       whitelisted(that.whitelisted),
52       binaryFields(that.binaryFields),
53       hasModule(that.hasModule),
54       moduleName(that.moduleName) {}
55 
AtomDecl(int c,const string & n,const string & m)56 AtomDecl::AtomDecl(int c, const string& n, const string& m)
57     :code(c),
58      name(n),
59      message(m)
60 {
61 }
62 
~AtomDecl()63 AtomDecl::~AtomDecl()
64 {
65 }
66 
67 
68 /**
69  * Print an error message for a FieldDescriptor, including the file name and line number.
70  */
71 static void
print_error(const FieldDescriptor * field,const char * format,...)72 print_error(const FieldDescriptor* field, const char* format, ...)
73 {
74     const Descriptor* message = field->containing_type();
75     const FileDescriptor* file = message->file();
76 
77     SourceLocation loc;
78     if (field->GetSourceLocation(&loc)) {
79         // TODO: this will work if we can figure out how to pass --include_source_info to protoc
80         fprintf(stderr, "%s:%d: ", file->name().c_str(), loc.start_line);
81     } else {
82         fprintf(stderr, "%s: ", file->name().c_str());
83     }
84     va_list args;
85     va_start(args, format);
86     vfprintf(stderr, format, args);
87     va_end (args);
88 }
89 
90 /**
91  * Convert a protobuf type into a java type.
92  */
93 static java_type_t
java_type(const FieldDescriptor * field)94 java_type(const FieldDescriptor* field)
95 {
96     int protoType = field->type();
97     switch (protoType) {
98         case FieldDescriptor::TYPE_DOUBLE:
99             return JAVA_TYPE_DOUBLE;
100         case FieldDescriptor::TYPE_FLOAT:
101             return JAVA_TYPE_FLOAT;
102         case FieldDescriptor::TYPE_INT64:
103             return JAVA_TYPE_LONG;
104         case FieldDescriptor::TYPE_UINT64:
105             return JAVA_TYPE_LONG;
106         case FieldDescriptor::TYPE_INT32:
107             return JAVA_TYPE_INT;
108         case FieldDescriptor::TYPE_FIXED64:
109             return JAVA_TYPE_LONG;
110         case FieldDescriptor::TYPE_FIXED32:
111             return JAVA_TYPE_INT;
112         case FieldDescriptor::TYPE_BOOL:
113             return JAVA_TYPE_BOOLEAN;
114         case FieldDescriptor::TYPE_STRING:
115             return JAVA_TYPE_STRING;
116         case FieldDescriptor::TYPE_GROUP:
117             return JAVA_TYPE_UNKNOWN;
118         case FieldDescriptor::TYPE_MESSAGE:
119             // TODO: not the final package name
120             if (field->message_type()->full_name() ==
121                 "android.os.statsd.AttributionNode") {
122               return JAVA_TYPE_ATTRIBUTION_CHAIN;
123             } else if (field->message_type()->full_name() ==
124                        "android.os.statsd.KeyValuePair") {
125               return JAVA_TYPE_KEY_VALUE_PAIR;
126             } else if (field->options().GetExtension(os::statsd::log_mode) ==
127                        os::statsd::LogMode::MODE_BYTES) {
128                 return JAVA_TYPE_BYTE_ARRAY;
129             } else {
130                 return JAVA_TYPE_OBJECT;
131             }
132         case FieldDescriptor::TYPE_BYTES:
133             return JAVA_TYPE_BYTE_ARRAY;
134         case FieldDescriptor::TYPE_UINT32:
135             return JAVA_TYPE_INT;
136         case FieldDescriptor::TYPE_ENUM:
137             return JAVA_TYPE_ENUM;
138         case FieldDescriptor::TYPE_SFIXED32:
139             return JAVA_TYPE_INT;
140         case FieldDescriptor::TYPE_SFIXED64:
141             return JAVA_TYPE_LONG;
142         case FieldDescriptor::TYPE_SINT32:
143             return JAVA_TYPE_INT;
144         case FieldDescriptor::TYPE_SINT64:
145             return JAVA_TYPE_LONG;
146         default:
147             return JAVA_TYPE_UNKNOWN;
148     }
149 }
150 
151 /**
152  * Gather the enums info.
153  */
collate_enums(const EnumDescriptor & enumDescriptor,AtomField * atomField)154 void collate_enums(const EnumDescriptor &enumDescriptor, AtomField *atomField) {
155     for (int i = 0; i < enumDescriptor.value_count(); i++) {
156         atomField->enumValues[enumDescriptor.value(i)->number()] =
157             enumDescriptor.value(i)->name().c_str();
158     }
159 }
160 
161 /**
162  * Gather the info about an atom proto.
163  */
collate_atom(const Descriptor * atom,AtomDecl * atomDecl,vector<java_type_t> * signature)164 int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
165                  vector<java_type_t> *signature) {
166 
167   int errorCount = 0;
168 
169   // Build a sorted list of the fields. Descriptor has them in source file
170   // order.
171   map<int, const FieldDescriptor *> fields;
172   for (int j = 0; j < atom->field_count(); j++) {
173     const FieldDescriptor *field = atom->field(j);
174     fields[field->number()] = field;
175   }
176 
177   // Check that the parameters start at 1 and go up sequentially.
178   int expectedNumber = 1;
179   for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
180        it != fields.end(); it++) {
181     const int number = it->first;
182     const FieldDescriptor *field = it->second;
183     if (number != expectedNumber) {
184       print_error(field,
185                   "Fields must be numbered consecutively starting at 1:"
186                   " '%s' is %d but should be %d\n",
187                   field->name().c_str(), number, expectedNumber);
188       errorCount++;
189       expectedNumber = number;
190       continue;
191     }
192     expectedNumber++;
193   }
194 
195   // Check that only allowed types are present. Remove any invalid ones.
196   for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
197        it != fields.end(); it++) {
198     const FieldDescriptor *field = it->second;
199     bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
200                          os::statsd::LogMode::MODE_BYTES;
201 
202     java_type_t javaType = java_type(field);
203 
204     if (javaType == JAVA_TYPE_UNKNOWN) {
205       print_error(field, "Unkown type for field: %s\n", field->name().c_str());
206       errorCount++;
207       continue;
208     } else if (javaType == JAVA_TYPE_OBJECT &&
209                atomDecl->code < PULL_ATOM_START_ID) {
210         // Allow attribution chain, but only at position 1.
211         print_error(field,
212                     "Message type not allowed for field in pushed atoms: %s\n",
213                     field->name().c_str());
214         errorCount++;
215         continue;
216     } else if (javaType == JAVA_TYPE_BYTE_ARRAY && !isBinaryField) {
217         print_error(field, "Raw bytes type not allowed for field: %s\n",
218                     field->name().c_str());
219         errorCount++;
220         continue;
221     }
222 
223     if (isBinaryField && javaType != JAVA_TYPE_BYTE_ARRAY) {
224         print_error(field, "Cannot mark field %s as bytes.\n",
225                     field->name().c_str());
226         errorCount++;
227         continue;
228     }
229 
230     // Doubles are not supported yet.
231     if (javaType == JAVA_TYPE_DOUBLE) {
232         print_error(field, "Doubles are not supported in atoms. Please change field %s to float\n",
233                     field->name().c_str());
234         errorCount++;
235         continue;
236     }
237   }
238 
239   // Check that if there's an attribution chain, it's at position 1.
240   for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
241        it != fields.end(); it++) {
242     int number = it->first;
243     if (number != 1) {
244       const FieldDescriptor *field = it->second;
245       java_type_t javaType = java_type(field);
246       if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
247         print_error(
248             field,
249             "AttributionChain fields must have field id 1, in message: '%s'\n",
250             atom->name().c_str());
251         errorCount++;
252       }
253     }
254   }
255 
256   // Build the type signature and the atom data.
257   for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
258        it != fields.end(); it++) {
259     const FieldDescriptor *field = it->second;
260     java_type_t javaType = java_type(field);
261     bool isBinaryField = field->options().GetExtension(os::statsd::log_mode) ==
262                          os::statsd::LogMode::MODE_BYTES;
263 
264     AtomField atField(field->name(), javaType);
265     // Generate signature for pushed atoms
266     if (atomDecl->code < PULL_ATOM_START_ID) {
267       if (javaType == JAVA_TYPE_ENUM) {
268         // All enums are treated as ints when it comes to function signatures.
269         signature->push_back(JAVA_TYPE_INT);
270         collate_enums(*field->enum_type(), &atField);
271       } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) {
272           signature->push_back(JAVA_TYPE_BYTE_ARRAY);
273       } else {
274           signature->push_back(javaType);
275       }
276     }
277     if (javaType == JAVA_TYPE_ENUM) {
278       // All enums are treated as ints when it comes to function signatures.
279       collate_enums(*field->enum_type(), &atField);
280     }
281     atomDecl->fields.push_back(atField);
282 
283     if (field->options().GetExtension(os::statsd::state_field_option).option() ==
284         os::statsd::StateField::PRIMARY) {
285         if (javaType == JAVA_TYPE_UNKNOWN ||
286             javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
287             javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
288             errorCount++;
289         }
290         atomDecl->primaryFields.push_back(it->first);
291     }
292 
293     if (field->options().GetExtension(os::statsd::state_field_option).option() ==
294         os::statsd::StateField::EXCLUSIVE) {
295         if (javaType == JAVA_TYPE_UNKNOWN ||
296             javaType == JAVA_TYPE_ATTRIBUTION_CHAIN ||
297             javaType == JAVA_TYPE_OBJECT || javaType == JAVA_TYPE_BYTE_ARRAY) {
298             errorCount++;
299         }
300 
301         if (atomDecl->exclusiveField == 0) {
302             atomDecl->exclusiveField = it->first;
303         } else {
304             errorCount++;
305         }
306     }
307 
308     if (field->options().GetExtension(os::statsd::is_uid) == true) {
309         if (javaType != JAVA_TYPE_INT) {
310             errorCount++;
311         }
312 
313         if (atomDecl->uidField == 0) {
314             atomDecl->uidField = it->first;
315         } else {
316             errorCount++;
317         }
318     }
319     // Binary field validity is already checked above.
320     if (isBinaryField) {
321         atomDecl->binaryFields.push_back(it->first);
322     }
323   }
324 
325   return errorCount;
326 }
327 
328 // This function flattens the fields of the AttributionNode proto in an Atom proto and generates
329 // the corresponding atom decl and signature.
get_non_chained_node(const Descriptor * atom,AtomDecl * atomDecl,vector<java_type_t> * signature)330 bool get_non_chained_node(const Descriptor *atom, AtomDecl *atomDecl,
331                           vector<java_type_t> *signature) {
332     // Build a sorted list of the fields. Descriptor has them in source file
333     // order.
334     map<int, const FieldDescriptor *> fields;
335     for (int j = 0; j < atom->field_count(); j++) {
336         const FieldDescriptor *field = atom->field(j);
337         fields[field->number()] = field;
338     }
339 
340     AtomDecl attributionDecl;
341     vector<java_type_t> attributionSignature;
342     collate_atom(android::os::statsd::AttributionNode::descriptor(),
343                  &attributionDecl, &attributionSignature);
344 
345     // Build the type signature and the atom data.
346     bool has_attribution_node = false;
347     for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
348         it != fields.end(); it++) {
349         const FieldDescriptor *field = it->second;
350         java_type_t javaType = java_type(field);
351         if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
352             atomDecl->fields.insert(
353                 atomDecl->fields.end(),
354                 attributionDecl.fields.begin(), attributionDecl.fields.end());
355             signature->insert(
356                 signature->end(),
357                 attributionSignature.begin(), attributionSignature.end());
358             has_attribution_node = true;
359 
360         } else {
361             AtomField atField(field->name(), javaType);
362             if (javaType == JAVA_TYPE_ENUM) {
363                 // All enums are treated as ints when it comes to function signatures.
364                 signature->push_back(JAVA_TYPE_INT);
365                 collate_enums(*field->enum_type(), &atField);
366             } else {
367                 signature->push_back(javaType);
368             }
369             atomDecl->fields.push_back(atField);
370         }
371     }
372     return has_attribution_node;
373 }
374 
375 /**
376  * Gather the info about the atoms.
377  */
collate_atoms(const Descriptor * descriptor,Atoms * atoms)378 int collate_atoms(const Descriptor *descriptor, Atoms *atoms) {
379   int errorCount = 0;
380   const bool dbg = false;
381 
382   int maxPushedAtomId = 2;
383   for (int i = 0; i < descriptor->field_count(); i++) {
384     const FieldDescriptor *atomField = descriptor->field(i);
385 
386     if (dbg) {
387       printf("   %s (%d)\n", atomField->name().c_str(), atomField->number());
388     }
389 
390     // StatsEvent only has one oneof, which contains only messages. Don't allow
391     // other types.
392     if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) {
393       print_error(atomField,
394                   "Bad type for atom. StatsEvent can only have message type "
395                   "fields: %s\n",
396                   atomField->name().c_str());
397       errorCount++;
398       continue;
399     }
400 
401     const Descriptor *atom = atomField->message_type();
402     AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name());
403 
404     if (atomField->options().GetExtension(os::statsd::allow_from_any_uid) == true) {
405         atomDecl.whitelisted = true;
406     }
407 
408     if (atomField->options().HasExtension(os::statsd::log_from_module)) {
409         atomDecl.hasModule = true;
410         atomDecl.moduleName = atomField->options().GetExtension(os::statsd::log_from_module);
411     }
412 
413     vector<java_type_t> signature;
414     errorCount += collate_atom(atom, &atomDecl, &signature);
415     if (atomDecl.primaryFields.size() != 0 && atomDecl.exclusiveField == 0) {
416         errorCount++;
417     }
418 
419     // Add the signature if does not already exist.
420     auto signature_to_modules_it = atoms->signatures_to_modules.find(signature);
421     if (signature_to_modules_it == atoms->signatures_to_modules.end()) {
422         set<string> modules;
423         if (atomDecl.hasModule) {
424             modules.insert(atomDecl.moduleName);
425         }
426         atoms->signatures_to_modules[signature] = modules;
427     } else {
428         if (atomDecl.hasModule) {
429             signature_to_modules_it->second.insert(atomDecl.moduleName);
430         }
431     }
432     atoms->decls.insert(atomDecl);
433 
434     AtomDecl nonChainedAtomDecl(atomField->number(), atomField->name(), atom->name());
435     vector<java_type_t> nonChainedSignature;
436     if (get_non_chained_node(atom, &nonChainedAtomDecl, &nonChainedSignature)) {
437         auto it = atoms->non_chained_signatures_to_modules.find(nonChainedSignature);
438         if (it == atoms->non_chained_signatures_to_modules.end()) {
439             set<string> modules_non_chained;
440             if (atomDecl.hasModule) {
441                 modules_non_chained.insert(atomDecl.moduleName);
442             }
443             atoms->non_chained_signatures_to_modules[nonChainedSignature] = modules_non_chained;
444         } else {
445             if (atomDecl.hasModule) {
446                 it->second.insert(atomDecl.moduleName);
447             }
448         }
449         atoms->non_chained_decls.insert(nonChainedAtomDecl);
450     }
451 
452     if (atomDecl.code < PULL_ATOM_START_ID && atomDecl.code > maxPushedAtomId) {
453         maxPushedAtomId = atomDecl.code;
454     }
455   }
456 
457   atoms->maxPushedAtomId = maxPushedAtomId;
458 
459   if (dbg) {
460     printf("signatures = [\n");
461     for (map<vector<java_type_t>, set<string>>::const_iterator it =
462              atoms->signatures_to_modules.begin();
463          it != atoms->signatures_to_modules.end(); it++) {
464       printf("   ");
465       for (vector<java_type_t>::const_iterator jt = it->first.begin();
466            jt != it->first.end(); jt++) {
467         printf(" %d", (int)*jt);
468       }
469       printf("\n");
470     }
471     printf("]\n");
472   }
473 
474   return errorCount;
475 }
476 
477 }  // namespace stats_log_api_gen
478 }  // namespace android
479