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