1 /*
2 * Copyright (C) 2019, 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 "utils.h"
18
19 namespace android {
20 namespace stats_log_api_gen {
21
22 /**
23 * Inlining this method because "android-base/strings.h" is not available on
24 * google3.
25 */
Split(const string & s,const string & delimiters)26 static vector<string> Split(const string& s, const string& delimiters) {
27 GOOGLE_CHECK_NE(delimiters.size(), 0U);
28
29 vector<string> result;
30
31 size_t base = 0;
32 size_t found;
33 while (true) {
34 found = s.find_first_of(delimiters, base);
35 result.push_back(s.substr(base, found - base));
36 if (found == s.npos) break;
37 base = found + 1;
38 }
39
40 return result;
41 }
42
build_non_chained_decl_map(const Atoms & atoms,std::map<int,AtomDeclSet::const_iterator> * decl_map)43 void build_non_chained_decl_map(const Atoms& atoms,
44 std::map<int, AtomDeclSet::const_iterator>* decl_map) {
45 for (AtomDeclSet::const_iterator atomIt = atoms.non_chained_decls.begin();
46 atomIt != atoms.non_chained_decls.end(); atomIt++) {
47 decl_map->insert(std::make_pair((*atomIt)->code, atomIt));
48 }
49 }
50
get_annotation_id_constants()51 const map<AnnotationId, string>& get_annotation_id_constants() {
52 static const map<AnnotationId, string>* ANNOTATION_ID_CONSTANTS =
53 new map<AnnotationId, string>{
54 {ANNOTATION_ID_IS_UID, "ANNOTATION_ID_IS_UID"},
55 {ANNOTATION_ID_TRUNCATE_TIMESTAMP, "ANNOTATION_ID_TRUNCATE_TIMESTAMP"},
56 {ANNOTATION_ID_PRIMARY_FIELD, "ANNOTATION_ID_PRIMARY_FIELD"},
57 {ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, "ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID"},
58 {ANNOTATION_ID_EXCLUSIVE_STATE, "ANNOTATION_ID_EXCLUSIVE_STATE"},
59 {ANNOTATION_ID_TRIGGER_STATE_RESET, "ANNOTATION_ID_TRIGGER_STATE_RESET"},
60 {ANNOTATION_ID_STATE_NESTED, "ANNOTATION_ID_STATE_NESTED"}};
61
62 return *ANNOTATION_ID_CONSTANTS;
63 }
64
65 /**
66 * Turn lower and camel case into upper case with underscores.
67 */
make_constant_name(const string & str)68 string make_constant_name(const string& str) {
69 string result;
70 const int N = str.size();
71 bool underscore_next = false;
72 for (int i = 0; i < N; i++) {
73 char c = str[i];
74 if (c >= 'A' && c <= 'Z') {
75 if (underscore_next) {
76 result += '_';
77 underscore_next = false;
78 }
79 } else if (c >= 'a' && c <= 'z') {
80 c = 'A' + c - 'a';
81 underscore_next = true;
82 } else if (c == '_') {
83 underscore_next = false;
84 }
85 result += c;
86 }
87 return result;
88 }
89
cpp_type_name(java_type_t type)90 const char* cpp_type_name(java_type_t type) {
91 switch (type) {
92 case JAVA_TYPE_BOOLEAN:
93 return "bool";
94 case JAVA_TYPE_INT:
95 case JAVA_TYPE_ENUM:
96 return "int32_t";
97 case JAVA_TYPE_LONG:
98 return "int64_t";
99 case JAVA_TYPE_FLOAT:
100 return "float";
101 case JAVA_TYPE_DOUBLE:
102 return "double";
103 case JAVA_TYPE_STRING:
104 return "char const*";
105 case JAVA_TYPE_BYTE_ARRAY:
106 return "const BytesField&";
107 default:
108 return "UNKNOWN";
109 }
110 }
111
java_type_name(java_type_t type)112 const char* java_type_name(java_type_t type) {
113 switch (type) {
114 case JAVA_TYPE_BOOLEAN:
115 return "boolean";
116 case JAVA_TYPE_INT:
117 case JAVA_TYPE_ENUM:
118 return "int";
119 case JAVA_TYPE_LONG:
120 return "long";
121 case JAVA_TYPE_FLOAT:
122 return "float";
123 case JAVA_TYPE_DOUBLE:
124 return "double";
125 case JAVA_TYPE_STRING:
126 return "java.lang.String";
127 case JAVA_TYPE_BYTE_ARRAY:
128 return "byte[]";
129 default:
130 return "UNKNOWN";
131 }
132 }
133
134 // Native
135 // Writes namespaces for the cpp and header files, returning the number of
136 // namespaces written.
write_namespace(FILE * out,const string & cppNamespaces)137 void write_namespace(FILE* out, const string& cppNamespaces) {
138 vector<string> cppNamespaceVec = Split(cppNamespaces, ",");
139 for (const string& cppNamespace : cppNamespaceVec) {
140 fprintf(out, "namespace %s {\n", cppNamespace.c_str());
141 }
142 }
143
144 // Writes namespace closing brackets for cpp and header files.
write_closing_namespace(FILE * out,const string & cppNamespaces)145 void write_closing_namespace(FILE* out, const string& cppNamespaces) {
146 vector<string> cppNamespaceVec = Split(cppNamespaces, ",");
147 for (auto it = cppNamespaceVec.rbegin(); it != cppNamespaceVec.rend(); ++it) {
148 fprintf(out, "} // namespace %s\n", it->c_str());
149 }
150 }
151
write_cpp_usage(FILE * out,const string & method_name,const string & atom_code_name,const shared_ptr<AtomDecl> atom,const AtomDecl & attributionDecl)152 static void write_cpp_usage(FILE* out, const string& method_name, const string& atom_code_name,
153 const shared_ptr<AtomDecl> atom, const AtomDecl& attributionDecl) {
154 fprintf(out, " * Usage: %s(StatsLog.%s", method_name.c_str(), atom_code_name.c_str());
155
156 for (vector<AtomField>::const_iterator field = atom->fields.begin();
157 field != atom->fields.end(); field++) {
158 if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
159 for (const auto& chainField : attributionDecl.fields) {
160 if (chainField.javaType == JAVA_TYPE_STRING) {
161 fprintf(out, ", const std::vector<%s>& %s", cpp_type_name(chainField.javaType),
162 chainField.name.c_str());
163 } else {
164 fprintf(out, ", const %s* %s, size_t %s_length",
165 cpp_type_name(chainField.javaType), chainField.name.c_str(),
166 chainField.name.c_str());
167 }
168 }
169 } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
170 fprintf(out,
171 ", const std::map<int, int32_t>& %s_int"
172 ", const std::map<int, int64_t>& %s_long"
173 ", const std::map<int, char const*>& %s_str"
174 ", const std::map<int, float>& %s_float",
175 field->name.c_str(), field->name.c_str(), field->name.c_str(),
176 field->name.c_str());
177 } else {
178 fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
179 }
180 }
181 fprintf(out, ");\n");
182 }
183
write_native_atom_constants(FILE * out,const Atoms & atoms,const AtomDecl & attributionDecl)184 void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl) {
185 fprintf(out, "/**\n");
186 fprintf(out, " * Constants for atom codes.\n");
187 fprintf(out, " */\n");
188 fprintf(out, "enum {\n");
189
190 std::map<int, AtomDeclSet::const_iterator> atom_code_to_non_chained_decl_map;
191 build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
192
193 size_t i = 0;
194 // Print atom constants
195 for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
196 atomIt++) {
197 string constant = make_constant_name((*atomIt)->name);
198 fprintf(out, "\n");
199 fprintf(out, " /**\n");
200 fprintf(out, " * %s %s\n", (*atomIt)->message.c_str(), (*atomIt)->name.c_str());
201 write_cpp_usage(out, "stats_write", constant, *atomIt, attributionDecl);
202
203 auto non_chained_decl = atom_code_to_non_chained_decl_map.find((*atomIt)->code);
204 if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
205 write_cpp_usage(out, "stats_write_non_chained", constant, *non_chained_decl->second,
206 attributionDecl);
207 }
208 fprintf(out, " */\n");
209 char const* const comma = (i == atoms.decls.size() - 1) ? "" : ",";
210 fprintf(out, " %s = %d%s\n", constant.c_str(), (*atomIt)->code, comma);
211 i++;
212 }
213 fprintf(out, "\n");
214 fprintf(out, "};\n");
215 fprintf(out, "\n");
216 }
217
write_native_method_signature(FILE * out,const string & signaturePrefix,const vector<java_type_t> & signature,const AtomDecl & attributionDecl,const string & closer)218 void write_native_method_signature(FILE* out, const string& signaturePrefix,
219 const vector<java_type_t>& signature,
220 const AtomDecl& attributionDecl, const string& closer) {
221 fprintf(out, "%sint32_t code", signaturePrefix.c_str());
222 int argIndex = 1;
223 for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
224 arg++) {
225 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
226 for (const auto& chainField : attributionDecl.fields) {
227 if (chainField.javaType == JAVA_TYPE_STRING) {
228 fprintf(out, ", const std::vector<%s>& %s", cpp_type_name(chainField.javaType),
229 chainField.name.c_str());
230 } else {
231 fprintf(out, ", const %s* %s, size_t %s_length",
232 cpp_type_name(chainField.javaType), chainField.name.c_str(),
233 chainField.name.c_str());
234 }
235 }
236 } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
237 fprintf(out,
238 ", const std::map<int, int32_t>& arg%d_1, "
239 "const std::map<int, int64_t>& arg%d_2, "
240 "const std::map<int, char const*>& arg%d_3, "
241 "const std::map<int, float>& arg%d_4",
242 argIndex, argIndex, argIndex, argIndex);
243 } else {
244 fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
245 }
246 argIndex++;
247 }
248 fprintf(out, ")%s\n", closer.c_str());
249 }
250
write_native_method_call(FILE * out,const string & methodName,const vector<java_type_t> & signature,const AtomDecl & attributionDecl,int argIndex)251 void write_native_method_call(FILE* out, const string& methodName,
252 const vector<java_type_t>& signature, const AtomDecl& attributionDecl,
253 int argIndex) {
254 fprintf(out, "%s(code", methodName.c_str());
255 for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
256 arg++) {
257 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
258 for (const auto& chainField : attributionDecl.fields) {
259 if (chainField.javaType == JAVA_TYPE_STRING) {
260 fprintf(out, ", %s", chainField.name.c_str());
261 } else {
262 fprintf(out, ", %s, %s_length", chainField.name.c_str(),
263 chainField.name.c_str());
264 }
265 }
266 } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
267 fprintf(out, ", arg%d_1, arg%d_2, arg%d_3, arg%d_4", argIndex, argIndex, argIndex,
268 argIndex);
269 } else {
270 fprintf(out, ", arg%d", argIndex);
271 }
272 argIndex++;
273 }
274 fprintf(out, ");\n");
275 }
276
277 // Java
write_java_atom_codes(FILE * out,const Atoms & atoms)278 void write_java_atom_codes(FILE* out, const Atoms& atoms) {
279 fprintf(out, " // Constants for atom codes.\n");
280
281 std::map<int, AtomDeclSet::const_iterator> atom_code_to_non_chained_decl_map;
282 build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
283
284 // Print constants for the atom codes.
285 for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
286 atomIt++) {
287 string constant = make_constant_name((*atomIt)->name);
288 fprintf(out, "\n");
289 fprintf(out, " /**\n");
290 fprintf(out, " * %s %s<br>\n", (*atomIt)->message.c_str(), (*atomIt)->name.c_str());
291 write_java_usage(out, "write", constant, **atomIt);
292 auto non_chained_decl = atom_code_to_non_chained_decl_map.find((*atomIt)->code);
293 if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
294 write_java_usage(out, "write_non_chained", constant, **(non_chained_decl->second));
295 }
296 fprintf(out, " */\n");
297 fprintf(out, " public static final int %s = %d;\n", constant.c_str(), (*atomIt)->code);
298 }
299 fprintf(out, "\n");
300 }
301
write_java_enum_values(FILE * out,const Atoms & atoms)302 void write_java_enum_values(FILE* out, const Atoms& atoms) {
303 fprintf(out, " // Constants for enum values.\n\n");
304 for (AtomDeclSet::const_iterator atomIt = atoms.decls.begin(); atomIt != atoms.decls.end();
305 atomIt++) {
306 for (vector<AtomField>::const_iterator field = (*atomIt)->fields.begin();
307 field != (*atomIt)->fields.end(); field++) {
308 if (field->javaType == JAVA_TYPE_ENUM) {
309 fprintf(out, " // Values for %s.%s\n", (*atomIt)->message.c_str(),
310 field->name.c_str());
311 for (map<int, string>::const_iterator value = field->enumValues.begin();
312 value != field->enumValues.end(); value++) {
313 fprintf(out, " public static final int %s__%s__%s = %d;\n",
314 make_constant_name((*atomIt)->message).c_str(),
315 make_constant_name(field->name).c_str(),
316 make_constant_name(value->second).c_str(), value->first);
317 }
318 fprintf(out, "\n");
319 }
320 }
321 }
322 }
323
write_java_usage(FILE * out,const string & method_name,const string & atom_code_name,const AtomDecl & atom)324 void write_java_usage(FILE* out, const string& method_name, const string& atom_code_name,
325 const AtomDecl& atom) {
326 fprintf(out, " * Usage: StatsLog.%s(StatsLog.%s", method_name.c_str(),
327 atom_code_name.c_str());
328 for (vector<AtomField>::const_iterator field = atom.fields.begin(); field != atom.fields.end();
329 field++) {
330 if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
331 fprintf(out, ", android.os.WorkSource workSource");
332 } else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
333 fprintf(out, ", android.util.SparseArray<Object> value_map");
334 } else if (field->javaType == JAVA_TYPE_BYTE_ARRAY) {
335 fprintf(out, ", byte[] %s", field->name.c_str());
336 } else {
337 fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
338 }
339 }
340 fprintf(out, ");<br>\n");
341 }
342
write_java_non_chained_methods(FILE * out,const SignatureInfoMap & signatureInfoMap)343 int write_java_non_chained_methods(FILE* out, const SignatureInfoMap& signatureInfoMap) {
344 for (auto signatureInfoMapIt = signatureInfoMap.begin();
345 signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
346 // Print method signature.
347 fprintf(out, " public static void write_non_chained(int code");
348 vector<java_type_t> signature = signatureInfoMapIt->first;
349 int argIndex = 1;
350 for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
351 arg++) {
352 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
353 fprintf(stderr, "Non chained signatures should not have attribution chains.\n");
354 return 1;
355 } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
356 fprintf(stderr, "Module logging does not yet support key value pair.\n");
357 return 1;
358 } else {
359 fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
360 }
361 argIndex++;
362 }
363 fprintf(out, ") {\n");
364
365 fprintf(out, " write(code");
366 argIndex = 1;
367 for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
368 arg++) {
369 // First two args are uid and tag of attribution chain.
370 if (argIndex == 1) {
371 fprintf(out, ", new int[] {arg%d}", argIndex);
372 } else if (argIndex == 2) {
373 fprintf(out, ", new java.lang.String[] {arg%d}", argIndex);
374 } else {
375 fprintf(out, ", arg%d", argIndex);
376 }
377 argIndex++;
378 }
379 fprintf(out, ");\n");
380 fprintf(out, " }\n");
381 fprintf(out, "\n");
382 }
383 return 0;
384 }
385
write_java_work_source_methods(FILE * out,const SignatureInfoMap & signatureInfoMap)386 int write_java_work_source_methods(FILE* out, const SignatureInfoMap& signatureInfoMap) {
387 fprintf(out, " // WorkSource methods.\n");
388 for (auto signatureInfoMapIt = signatureInfoMap.begin();
389 signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
390 vector<java_type_t> signature = signatureInfoMapIt->first;
391 // Determine if there is Attribution in this signature.
392 int attributionArg = -1;
393 int argIndexMax = 0;
394 for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
395 arg++) {
396 argIndexMax++;
397 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
398 if (attributionArg > -1) {
399 fprintf(stderr, "An atom contains multiple AttributionNode fields.\n");
400 fprintf(stderr, "This is not supported. Aborting WorkSource method writing.\n");
401 fprintf(out,
402 "\n// Invalid for WorkSource: more than one attribution "
403 "chain.\n");
404 return 1;
405 }
406 attributionArg = argIndexMax;
407 }
408 }
409 if (attributionArg < 0) {
410 continue;
411 }
412
413 fprintf(out, "\n");
414 // Method header (signature)
415 fprintf(out, " public static void write(int code");
416 int argIndex = 1;
417 for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
418 arg++) {
419 if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
420 fprintf(out, ", android.os.WorkSource ws");
421 } else {
422 fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
423 }
424 argIndex++;
425 }
426 fprintf(out, ") {\n");
427
428 // write_non_chained() component. TODO: Remove when flat uids are no longer
429 // needed.
430 fprintf(out, " for (int i = 0; i < ws.size(); ++i) {\n");
431 fprintf(out, " write_non_chained(code");
432 for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
433 if (argIndex == attributionArg) {
434 fprintf(out, ", ws.getUid(i), ws.getPackageName(i)");
435 } else {
436 fprintf(out, ", arg%d", argIndex);
437 }
438 }
439 fprintf(out, ");\n");
440 fprintf(out, " }\n"); // close for-loop
441
442 // write() component.
443 fprintf(out,
444 " java.util.List<android.os.WorkSource.WorkChain> workChains = "
445 "ws.getWorkChains();\n");
446 fprintf(out, " if (workChains != null) {\n");
447 fprintf(out,
448 " for (android.os.WorkSource.WorkChain wc : workChains) "
449 "{\n");
450 fprintf(out, " write(code");
451 for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
452 if (argIndex == attributionArg) {
453 fprintf(out, ", wc.getUids(), wc.getTags()");
454 } else {
455 fprintf(out, ", arg%d", argIndex);
456 }
457 }
458 fprintf(out, ");\n");
459 fprintf(out, " }\n"); // close for-loop
460 fprintf(out, " }\n"); // close if
461 fprintf(out, " }\n"); // close method
462 }
463 return 0;
464 }
465
466 } // namespace stats_log_api_gen
467 } // namespace android
468