The change is guarded by STATS_SCHEMA_LEGACY flag in the autogenerator Android.bp. The flag can only be removed once rest of statsd switches over to the new socket schema. The new autogeneration uses StatsEvent for both Mainline module clients and the default platform client. For Mainline modules, the autogenerated code uses a runtime dessert version check to use new socket schema on R+ platforms and the legacy schema on the older platforms. The legacy schema is encapsulated in a separate autogenerated inner class called QLogger. generated DocumentsStatsLog.java: https://paste.googleplex.com/4665805503463424 generated StatsLogInternal.java: https://paste.googleplex.com/5955095055302656 generated android_util_StatsLogInternal.cpp: https://paste.googleplex.com/6737331711115264 Bug: 142811546 Test: m -j && m -j DocumentsUIGoogle Test: Flashes successfully and events are logged to statsd Change-Id: I4c804eaf4d5ae78001146c89ebe46dfb0a453853
335 lines
12 KiB
C++
335 lines
12 KiB
C++
/*
|
|
* Copyright (C) 2019, The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "utils.h"
|
|
|
|
namespace android {
|
|
namespace stats_log_api_gen {
|
|
|
|
/**
|
|
* Turn lower and camel case into upper case with underscores.
|
|
*/
|
|
string make_constant_name(const string& str) {
|
|
string result;
|
|
const int N = str.size();
|
|
bool underscore_next = false;
|
|
for (int i=0; i<N; i++) {
|
|
char c = str[i];
|
|
if (c >= 'A' && c <= 'Z') {
|
|
if (underscore_next) {
|
|
result += '_';
|
|
underscore_next = false;
|
|
}
|
|
} else if (c >= 'a' && c <= 'z') {
|
|
c = 'A' + c - 'a';
|
|
underscore_next = true;
|
|
} else if (c == '_') {
|
|
underscore_next = false;
|
|
}
|
|
result += c;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
const char* cpp_type_name(java_type_t type) {
|
|
switch (type) {
|
|
case JAVA_TYPE_BOOLEAN:
|
|
return "bool";
|
|
case JAVA_TYPE_INT:
|
|
case JAVA_TYPE_ENUM:
|
|
return "int32_t";
|
|
case JAVA_TYPE_LONG:
|
|
return "int64_t";
|
|
case JAVA_TYPE_FLOAT:
|
|
return "float";
|
|
case JAVA_TYPE_DOUBLE:
|
|
return "double";
|
|
case JAVA_TYPE_STRING:
|
|
return "char const*";
|
|
case JAVA_TYPE_BYTE_ARRAY:
|
|
return "const BytesField&";
|
|
default:
|
|
return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
const char* java_type_name(java_type_t type) {
|
|
switch (type) {
|
|
case JAVA_TYPE_BOOLEAN:
|
|
return "boolean";
|
|
case JAVA_TYPE_INT:
|
|
case JAVA_TYPE_ENUM:
|
|
return "int";
|
|
case JAVA_TYPE_LONG:
|
|
return "long";
|
|
case JAVA_TYPE_FLOAT:
|
|
return "float";
|
|
case JAVA_TYPE_DOUBLE:
|
|
return "double";
|
|
case JAVA_TYPE_STRING:
|
|
return "java.lang.String";
|
|
case JAVA_TYPE_BYTE_ARRAY:
|
|
return "byte[]";
|
|
default:
|
|
return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
bool atom_needed_for_module(const AtomDecl& atomDecl, const string& moduleName) {
|
|
if (moduleName == DEFAULT_MODULE_NAME) {
|
|
return true;
|
|
}
|
|
return atomDecl.hasModule && (moduleName == atomDecl.moduleName);
|
|
}
|
|
|
|
bool signature_needed_for_module(const set<string>& modules, const string& moduleName) {
|
|
if (moduleName == DEFAULT_MODULE_NAME) {
|
|
return true;
|
|
}
|
|
return modules.find(moduleName) != modules.end();
|
|
}
|
|
|
|
void build_non_chained_decl_map(const Atoms& atoms,
|
|
std::map<int, set<AtomDecl>::const_iterator>* decl_map) {
|
|
for (set<AtomDecl>::const_iterator atom = atoms.non_chained_decls.begin();
|
|
atom != atoms.non_chained_decls.end(); atom++) {
|
|
decl_map->insert(std::make_pair(atom->code, atom));
|
|
}
|
|
}
|
|
|
|
// Java
|
|
void write_java_atom_codes(FILE* out, const Atoms& atoms, const string& moduleName) {
|
|
fprintf(out, " // Constants for atom codes.\n");
|
|
|
|
std::map<int, set<AtomDecl>::const_iterator> atom_code_to_non_chained_decl_map;
|
|
build_non_chained_decl_map(atoms, &atom_code_to_non_chained_decl_map);
|
|
|
|
// Print constants for the atom codes.
|
|
for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
|
|
atom != atoms.decls.end(); atom++) {
|
|
// Skip if the atom is not needed for the module.
|
|
if (!atom_needed_for_module(*atom, moduleName)) {
|
|
continue;
|
|
}
|
|
string constant = make_constant_name(atom->name);
|
|
fprintf(out, "\n");
|
|
fprintf(out, " /**\n");
|
|
fprintf(out, " * %s %s<br>\n", atom->message.c_str(), atom->name.c_str());
|
|
write_java_usage(out, "write", constant, *atom);
|
|
auto non_chained_decl = atom_code_to_non_chained_decl_map.find(atom->code);
|
|
if (non_chained_decl != atom_code_to_non_chained_decl_map.end()) {
|
|
write_java_usage(out, "write_non_chained", constant, *non_chained_decl->second);
|
|
}
|
|
if (moduleName == DEFAULT_MODULE_NAME) {
|
|
fprintf(out, " * @hide\n");
|
|
}
|
|
fprintf(out, " */\n");
|
|
fprintf(out, " public static final int %s = %d;\n", constant.c_str(), atom->code);
|
|
}
|
|
fprintf(out, "\n");
|
|
}
|
|
|
|
void write_java_enum_values(FILE* out, const Atoms& atoms, const string& moduleName) {
|
|
fprintf(out, " // Constants for enum values.\n\n");
|
|
for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
|
|
atom != atoms.decls.end(); atom++) {
|
|
// Skip if the atom is not needed for the module.
|
|
if (!atom_needed_for_module(*atom, moduleName)) {
|
|
continue;
|
|
}
|
|
for (vector<AtomField>::const_iterator field = atom->fields.begin();
|
|
field != atom->fields.end(); field++) {
|
|
if (field->javaType == JAVA_TYPE_ENUM) {
|
|
fprintf(out, " // Values for %s.%s\n", atom->message.c_str(),
|
|
field->name.c_str());
|
|
for (map<int, string>::const_iterator value = field->enumValues.begin();
|
|
value != field->enumValues.end(); value++) {
|
|
if (moduleName == DEFAULT_MODULE_NAME) {
|
|
fprintf(out, " /** @hide */\n");
|
|
}
|
|
fprintf(out, " public static final int %s__%s__%s = %d;\n",
|
|
make_constant_name(atom->message).c_str(),
|
|
make_constant_name(field->name).c_str(),
|
|
make_constant_name(value->second).c_str(),
|
|
value->first);
|
|
}
|
|
fprintf(out, "\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void write_java_usage(FILE* out, const string& method_name, const string& atom_code_name,
|
|
const AtomDecl& atom) {
|
|
fprintf(out, " * Usage: StatsLog.%s(StatsLog.%s",
|
|
method_name.c_str(), atom_code_name.c_str());
|
|
for (vector<AtomField>::const_iterator field = atom.fields.begin();
|
|
field != atom.fields.end(); field++) {
|
|
if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
|
|
fprintf(out, ", android.os.WorkSource workSource");
|
|
} else if (field->javaType == JAVA_TYPE_KEY_VALUE_PAIR) {
|
|
fprintf(out, ", SparseArray<Object> value_map");
|
|
} else if (field->javaType == JAVA_TYPE_BYTE_ARRAY) {
|
|
fprintf(out, ", byte[] %s", field->name.c_str());
|
|
} else {
|
|
fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
|
|
}
|
|
}
|
|
fprintf(out, ");<br>\n");
|
|
}
|
|
|
|
int write_java_non_chained_methods(
|
|
FILE* out,
|
|
const map<vector<java_type_t>, set<string>>& signatures_to_modules,
|
|
const string& moduleName
|
|
) {
|
|
for (auto signature_to_modules_it = signatures_to_modules.begin();
|
|
signature_to_modules_it != signatures_to_modules.end(); signature_to_modules_it++) {
|
|
// Skip if this signature is not needed for the module.
|
|
if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
|
|
continue;
|
|
}
|
|
|
|
// Print method signature.
|
|
if (DEFAULT_MODULE_NAME == moduleName) {
|
|
fprintf(out, " /** @hide */\n");
|
|
}
|
|
fprintf(out, " public static void write_non_chained(int code");
|
|
vector<java_type_t> signature = signature_to_modules_it->first;
|
|
int argIndex = 1;
|
|
for (vector<java_type_t>::const_iterator arg = signature.begin();
|
|
arg != signature.end(); arg++) {
|
|
if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
|
|
// Non chained signatures should not have attribution chains.
|
|
return 1;
|
|
} else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
|
|
// Module logging does not yet support key value pair.
|
|
return 1;
|
|
} else {
|
|
fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
|
|
}
|
|
argIndex++;
|
|
}
|
|
fprintf(out, ") {\n");
|
|
|
|
fprintf(out, " write(code");
|
|
argIndex = 1;
|
|
for (vector<java_type_t>::const_iterator arg = signature.begin();
|
|
arg != signature.end(); arg++) {
|
|
// First two args are uid and tag of attribution chain.
|
|
if (argIndex == 1) {
|
|
fprintf(out, ", new int[] {arg%d}", argIndex);
|
|
} else if (argIndex == 2) {
|
|
fprintf(out, ", new java.lang.String[] {arg%d}", argIndex);
|
|
} else {
|
|
fprintf(out, ", arg%d", argIndex);
|
|
}
|
|
argIndex++;
|
|
}
|
|
fprintf(out, ");\n");
|
|
fprintf(out, " }\n");
|
|
fprintf(out, "\n");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int write_java_work_source_methods(
|
|
FILE* out,
|
|
const map<vector<java_type_t>, set<string>>& signatures_to_modules,
|
|
const string& moduleName
|
|
) {
|
|
fprintf(out, " // WorkSource methods.\n");
|
|
for (auto signature_to_modules_it = signatures_to_modules.begin();
|
|
signature_to_modules_it != signatures_to_modules.end(); signature_to_modules_it++) {
|
|
// Skip if this signature is not needed for the module.
|
|
if (!signature_needed_for_module(signature_to_modules_it->second, moduleName)) {
|
|
continue;
|
|
}
|
|
vector<java_type_t> signature = signature_to_modules_it->first;
|
|
// Determine if there is Attribution in this signature.
|
|
int attributionArg = -1;
|
|
int argIndexMax = 0;
|
|
for (vector<java_type_t>::const_iterator arg = signature.begin();
|
|
arg != signature.end(); arg++) {
|
|
argIndexMax++;
|
|
if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
|
|
if (attributionArg > -1) {
|
|
fprintf(stderr, "An atom contains multiple AttributionNode fields.\n");
|
|
fprintf(stderr, "This is not supported. Aborting WorkSource method writing.\n");
|
|
fprintf(out, "\n// Invalid for WorkSource: more than one attribution chain.\n");
|
|
return 1;
|
|
}
|
|
attributionArg = argIndexMax;
|
|
}
|
|
}
|
|
if (attributionArg < 0) {
|
|
continue;
|
|
}
|
|
|
|
fprintf(out, "\n");
|
|
// Method header (signature)
|
|
if (DEFAULT_MODULE_NAME == moduleName) {
|
|
fprintf(out, " /** @hide */\n");
|
|
}
|
|
fprintf(out, " public static void write(int code");
|
|
int argIndex = 1;
|
|
for (vector<java_type_t>::const_iterator arg = signature.begin();
|
|
arg != signature.end(); arg++) {
|
|
if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
|
|
fprintf(out, ", WorkSource ws");
|
|
} else {
|
|
fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
|
|
}
|
|
argIndex++;
|
|
}
|
|
fprintf(out, ") {\n");
|
|
|
|
// write_non_chained() component. TODO: Remove when flat uids are no longer needed.
|
|
fprintf(out, " for (int i = 0; i < ws.size(); ++i) {\n");
|
|
fprintf(out, " write_non_chained(code");
|
|
for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
|
|
if (argIndex == attributionArg) {
|
|
fprintf(out, ", ws.getUid(i), ws.getPackageName(i)");
|
|
} else {
|
|
fprintf(out, ", arg%d", argIndex);
|
|
}
|
|
}
|
|
fprintf(out, ");\n");
|
|
fprintf(out, " }\n"); // close for-loop
|
|
|
|
// write() component.
|
|
fprintf(out, " List<WorkSource.WorkChain> workChains = ws.getWorkChains();\n");
|
|
fprintf(out, " if (workChains != null) {\n");
|
|
fprintf(out, " for (WorkSource.WorkChain wc : workChains) {\n");
|
|
fprintf(out, " write(code");
|
|
for (int argIndex = 1; argIndex <= argIndexMax; argIndex++) {
|
|
if (argIndex == attributionArg) {
|
|
fprintf(out, ", wc.getUids(), wc.getTags()");
|
|
} else {
|
|
fprintf(out, ", arg%d", argIndex);
|
|
}
|
|
}
|
|
fprintf(out, ");\n");
|
|
fprintf(out, " }\n"); // close for-loop
|
|
fprintf(out, " }\n"); // close if
|
|
fprintf(out, " }\n"); // close method
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
} // namespace stats_log_api_gen
|
|
} // namespace android
|