summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/aapt2/Android.bp5
-rw-r--r--tools/aapt2/LoadedApk.cpp8
-rw-r--r--tools/aapt2/Resource.h13
-rw-r--r--tools/aapt2/ResourceParser.cpp2
-rw-r--r--tools/aapt2/ResourceUtils.cpp2
-rw-r--r--tools/aapt2/cmd/Compile.cpp20
-rw-r--r--tools/aapt2/cmd/Compile.h4
-rw-r--r--tools/aapt2/cmd/Compile_test.cpp87
-rw-r--r--tools/aapt2/cmd/Link.cpp63
-rw-r--r--tools/aapt2/cmd/Optimize.cpp6
-rw-r--r--tools/aapt2/cmd/Optimize.h4
-rw-r--r--tools/aapt2/compile/PngChunkFilter.cpp6
-rw-r--r--tools/aapt2/configuration/ConfigurationParser_test.cpp2
-rw-r--r--tools/aapt2/dump/DumpManifest.cpp244
-rw-r--r--tools/aapt2/format/binary/TableFlattener.cpp14
-rw-r--r--tools/aapt2/format/proto/ProtoDeserialize.cpp13
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize.cpp11
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize.h9
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize_test.cpp171
-rw-r--r--tools/aapt2/java/JavaClassGenerator.cpp7
-rw-r--r--tools/aapt2/java/JavaClassGenerator_test.cpp35
-rw-r--r--tools/aapt2/jni/aapt2_jni.cpp2
-rw-r--r--tools/aapt2/link/ManifestFixer.cpp11
-rw-r--r--tools/aapt2/link/TableMerger.cpp20
-rw-r--r--tools/aapt2/link/TableMerger.h4
-rw-r--r--tools/aapt2/link/XmlCompatVersioner.cpp4
-rw-r--r--tools/aapt2/optimize/ResourceFilter.cpp6
-rw-r--r--tools/aapt2/optimize/ResourceFilter.h6
-rw-r--r--tools/aapt2/optimize/ResourceFilter_test.cpp46
-rw-r--r--tools/aapt2/test/Common.cpp4
-rw-r--r--tools/aapt2/trace/TraceBuffer.h2
-rw-r--r--tools/aapt2/util/Maybe_test.cpp34
-rw-r--r--tools/aapt2/xml/XmlActionExecutor.cpp8
-rw-r--r--tools/aapt2/xml/XmlActionExecutor.h4
-rw-r--r--tools/aapt2/xml/XmlActionExecutor_test.cpp4
-rwxr-xr-xtools/aosp/aosp_sha.sh2
-rw-r--r--tools/codegen/src/com/android/codegen/ClassInfo.kt2
-rw-r--r--tools/codegen/src/com/android/codegen/Debug.kt39
-rw-r--r--tools/codegen/src/com/android/codegen/FileInfo.kt2
-rw-r--r--tools/codegen/src/com/android/codegen/Generators.kt12
-rw-r--r--tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt10
-rw-r--r--tools/codegen/src/com/android/codegen/SharedConstants.kt2
-rw-r--r--tools/codegen/src/com/android/codegen/Utils.kt4
-rwxr-xr-xtools/fonts/fontchain_linter.py23
-rw-r--r--tools/powerstats/Android.bp10
-rw-r--r--tools/powerstats/PowerStatsServiceProtoParser.java140
-rw-r--r--tools/powerstats/PowerStatsServiceProtoParser_manifest.txt1
-rw-r--r--tools/processors/intdef_mappings/Android.bp33
-rw-r--r--tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt190
-rw-r--r--tools/processors/intdef_mappings/test/android/processor/IntDefProcessorTest.kt162
-rw-r--r--tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt14
-rw-r--r--tools/protologtool/Android.bp2
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/LogParser.kt9
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogGroupReader.kt2
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt8
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt2
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt20
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt4
-rw-r--r--tools/stats_log_api_gen/Android.bp20
-rw-r--r--tools/stats_log_api_gen/Collation.cpp61
-rw-r--r--tools/stats_log_api_gen/Collation.h16
-rw-r--r--tools/stats_log_api_gen/java_writer.cpp328
-rw-r--r--tools/stats_log_api_gen/java_writer.h7
-rw-r--r--tools/stats_log_api_gen/java_writer_q.cpp4
-rw-r--r--tools/stats_log_api_gen/java_writer_q.h7
-rw-r--r--tools/stats_log_api_gen/main.cpp23
-rw-r--r--tools/stats_log_api_gen/native_writer.cpp204
-rw-r--r--tools/stats_log_api_gen/native_writer.h7
-rw-r--r--tools/stats_log_api_gen/test.proto41
-rw-r--r--tools/stats_log_api_gen/test_collation.cpp71
-rw-r--r--tools/stats_log_api_gen/utils.cpp53
-rw-r--r--tools/stats_log_api_gen/utils.h22
-rw-r--r--tools/stringslint/stringslint.py12
-rwxr-xr-xtools/stringslint/stringslint_sha.sh2
-rw-r--r--tools/validatekeymaps/Android.bp11
-rw-r--r--tools/validatekeymaps/Main.cpp26
-rw-r--r--tools/xmlpersistence/Android.bp11
-rw-r--r--tools/xmlpersistence/OWNERS1
-rw-r--r--tools/xmlpersistence/manifest.txt1
-rw-r--r--tools/xmlpersistence/src/main/kotlin/Generator.kt576
-rw-r--r--tools/xmlpersistence/src/main/kotlin/Main.kt45
-rw-r--r--tools/xmlpersistence/src/main/kotlin/Parser.kt248
-rw-r--r--tools/xmlpersistence/src/main/kotlin/StringCaseExtensions.kt44
83 files changed, 2816 insertions, 599 deletions
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index ade0dc4d9c42..46ae2ecd9406 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -202,6 +202,9 @@ cc_binary_host {
use_version_lib: true,
static_libs: ["libaapt2"],
defaults: ["aapt2_defaults"],
+ dist: {
+ targets: ["aapt2_artifacts"],
+ },
}
// ==========================================================
@@ -220,6 +223,6 @@ genrule {
"cp $(in) $(genDir)/protos && " +
"$(location :soong_zip) -o $(out) -C $(genDir)/protos -D $(genDir)/protos",
dist: {
- targets: ["sdk_repo"],
+ targets: ["sdk_repo", "aapt2_artifacts"],
},
}
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index 45719ef474cd..e930b47f2901 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -267,8 +267,14 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table
return false;
}
} else if (format_ == ApkFormat::kProto && path == kProtoResourceTablePath) {
+ SerializeTableOptions proto_serialize_options;
+ proto_serialize_options.collapse_key_stringpool =
+ options.collapse_key_stringpool;
+ proto_serialize_options.name_collapse_exemptions =
+ options.name_collapse_exemptions;
pb::ResourceTable pb_table;
- SerializeTableToPb(*split_table, &pb_table, context->GetDiagnostics());
+ SerializeTableToPb(*split_table, &pb_table, context->GetDiagnostics(),
+ proto_serialize_options);
if (!io::CopyProtoToArchive(context,
&pb_table,
path,
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index 4e051a37f3ed..8fe0eb3b51bf 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -274,6 +274,19 @@ inline std::string to_string(const ResourceId& id) {
return id.to_string();
}
+// Helper to compare resource IDs, moving dynamic IDs after framework IDs.
+inline bool cmp_ids_dynamic_after_framework(const ResourceId& a, const ResourceId& b) {
+ // If one of a and b is from the framework package (package ID 0x01), and the
+ // other is a dynamic ID (package ID 0x00), then put the dynamic ID after the
+ // framework ID. This ensures that when AssetManager resolves the dynamic IDs,
+ // they will be in sorted order as expected by AssetManager.
+ if ((a.package_id() == kFrameworkPackageId && b.package_id() == 0x00) ||
+ (a.package_id() == 0x00 && b.package_id() == kFrameworkPackageId)) {
+ return b < a;
+ }
+ return a < b;
+}
+
//
// ResourceType implementation.
//
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 931a14b1f650..3d9be59dd960 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -342,7 +342,7 @@ bool ResourceParser::FlattenXmlSubtree(
}
}
- // Sanity check to make sure we processed all the nodes.
+ // Validity check to make sure we processed all the nodes.
CHECK(node_stack.size() == 1u);
CHECK(node_stack.back() == &root);
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index 469128b1e50b..7dfc983b54ba 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -740,7 +740,7 @@ std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const Config
if (type == ResourceType::kId) {
if (res_value.dataType != android::Res_value::TYPE_REFERENCE &&
res_value.dataType != android::Res_value::TYPE_DYNAMIC_REFERENCE) {
- // plain "id" resources are actually encoded as dummy values (aapt1 uses an empty string,
+ // plain "id" resources are actually encoded as unused values (aapt1 uses an empty string,
// while aapt2 uses a false boolean).
return util::make_unique<Id>();
}
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 32686538c10d..ff54fccda767 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -75,8 +75,10 @@ struct ResourcePathData {
};
// Resource file paths are expected to look like: [--/res/]type[-config]/name
-static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path, const char dir_sep,
- std::string* out_error) {
+static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
+ const char dir_sep,
+ std::string* out_error,
+ const CompileOptions& options) {
std::vector<std::string> parts = util::Split(path, dir_sep);
if (parts.size() < 2) {
if (out_error) *out_error = "bad resource path";
@@ -121,7 +123,11 @@ static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
}
}
- return ResourcePathData{Source(path), dir_str.to_string(), name.to_string(),
+ const Source res_path = options.source_path
+ ? StringPiece(options.source_path.value())
+ : StringPiece(path);
+
+ return ResourcePathData{res_path, dir_str.to_string(), name.to_string(),
extension.to_string(), config_str.to_string(), config};
}
@@ -667,7 +673,8 @@ int Compile(IAaptContext* context, io::IFileCollection* inputs, IArchiveWriter*
// Extract resource type information from the full path
std::string err_str;
ResourcePathData path_data;
- if (auto maybe_path_data = ExtractResourcePathData(path, inputs->GetDirSeparator(), &err_str)) {
+ if (auto maybe_path_data = ExtractResourcePathData(
+ path, inputs->GetDirSeparator(), &err_str, options)) {
path_data = maybe_path_data.value();
} else {
context->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << err_str);
@@ -747,6 +754,11 @@ int CompileCommand::Action(const std::vector<std::string>& args) {
context.GetDiagnostics()->Error(DiagMessage()
<< "only one of --dir and --zip can be specified");
return 1;
+ } else if ((options_.res_dir || options_.res_zip) &&
+ options_.source_path && args.size() > 1) {
+ context.GetDiagnostics()->Error(DiagMessage(kPath)
+ << "Cannot use an overriding source path with multiple files.");
+ return 1;
} else if (options_.res_dir) {
if (!args.empty()) {
context.GetDiagnostics()->Error(DiagMessage() << "files given but --dir specified");
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index 1752a1adac24..1bc1f6651f85 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -28,6 +28,7 @@ namespace aapt {
struct CompileOptions {
std::string output_path;
+ Maybe<std::string> source_path;
Maybe<std::string> res_dir;
Maybe<std::string> res_zip;
Maybe<std::string> generate_text_symbols_path;
@@ -69,6 +70,9 @@ class CompileCommand : public Command {
AddOptionalSwitch("-v", "Enables verbose logging", &options_.verbose);
AddOptionalFlag("--trace-folder", "Generate systrace json trace fragment to specified folder.",
&trace_folder_);
+ AddOptionalFlag("--source-path",
+ "Sets the compiled resource file source file path to the given string.",
+ &options_.source_path);
}
int Action(const std::vector<std::string>& args) override;
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index fb786a31360e..0aab94d3299f 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -24,6 +24,7 @@
#include "io/ZipArchive.h"
#include "java/AnnotationProcessor.h"
#include "test/Test.h"
+#include "format/proto/ProtoDeserialize.h"
namespace aapt {
@@ -253,4 +254,90 @@ TEST_F(CompilerTest, DoNotTranslateTest) {
AssertTranslations(this, "donottranslate_foo", expected_not_translatable);
}
+TEST_F(CompilerTest, RelativePathTest) {
+ StdErrDiagnostics diag;
+ const std::string res_path = BuildPath(
+ {android::base::Dirname(android::base::GetExecutablePath()),
+ "integration-tests", "CompileTest", "res"});
+
+ const std::string path_values_colors = GetTestPath("values/colors.xml");
+ WriteFile(path_values_colors, "<resources>"
+ "<color name=\"color_one\">#008577</color>"
+ "</resources>");
+
+ const std::string path_layout_layout_one = GetTestPath("layout/layout_one.xml");
+ WriteFile(path_layout_layout_one, "<LinearLayout "
+ "xmlns:android=\"http://schemas.android.com/apk/res/android\">"
+ "<TextBox android:id=\"@+id/text_one\" android:background=\"@color/color_one\"/>"
+ "</LinearLayout>");
+
+ const std::string compiled_files_dir = BuildPath(
+ {android::base::Dirname(android::base::GetExecutablePath()),
+ "integration-tests", "CompileTest", "compiled"});
+ CHECK(file::mkdirs(compiled_files_dir.data()));
+
+ const std::string path_values_colors_out =
+ BuildPath({compiled_files_dir,"values_colors.arsc.flat"});
+ const std::string path_layout_layout_one_out =
+ BuildPath({compiled_files_dir, "layout_layout_one.flat"});
+ ::android::base::utf8::unlink(path_values_colors_out.c_str());
+ ::android::base::utf8::unlink(path_layout_layout_one_out.c_str());
+ const std::string apk_path = BuildPath(
+ {android::base::Dirname(android::base::GetExecutablePath()),
+ "integration-tests", "CompileTest", "out.apk"});
+
+ const std::string source_set_res = BuildPath({"main", "res"});
+ const std::string relative_path_values_colors =
+ BuildPath({source_set_res, "values", "colors.xml"});
+ const std::string relative_path_layout_layout_one =
+ BuildPath({source_set_res, "layout", "layout_one.xml"});
+
+ CompileCommand(&diag).Execute({
+ path_values_colors,
+ "-o",
+ compiled_files_dir,
+ "--source-path",
+ relative_path_values_colors},
+ &std::cerr);
+
+ CompileCommand(&diag).Execute({
+ path_layout_layout_one,
+ "-o",
+ compiled_files_dir,
+ "--source-path",
+ relative_path_layout_layout_one},
+ &std::cerr);
+
+ std::ifstream ifs_values(path_values_colors_out);
+ std::string content_values((std::istreambuf_iterator<char>(ifs_values)),
+ (std::istreambuf_iterator<char>()));
+ ASSERT_NE(content_values.find(relative_path_values_colors), -1);
+ ASSERT_EQ(content_values.find(path_values_colors), -1);
+
+ Link({"-o", apk_path, "--manifest", GetDefaultManifest(), "--proto-format"},
+ compiled_files_dir, &diag);
+
+ std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(apk_path, &diag);
+ ResourceTable* resource_table = apk.get()->GetResourceTable();
+ const std::vector<std::unique_ptr<StringPool::Entry>>& pool_strings =
+ resource_table->string_pool.strings();
+
+ ASSERT_EQ(pool_strings.size(), 2);
+ ASSERT_EQ(pool_strings[0]->value, "res/layout/layout_one.xml");
+ ASSERT_EQ(pool_strings[1]->value, "res/layout-v1/layout_one.xml");
+
+ // Check resources.pb contains relative sources.
+ io::IFile* proto_file =
+ apk.get()->GetFileCollection()->FindFile("resources.pb");
+ std::unique_ptr<io::InputStream> proto_stream = proto_file->OpenInputStream();
+ io::ProtoInputStreamReader proto_reader(proto_stream.get());
+ pb::ResourceTable pb_table;
+ proto_reader.ReadMessage(&pb_table);
+
+ const std::string pool_strings_proto = pb_table.source_pool().data();
+
+ ASSERT_NE(pool_strings_proto.find(relative_path_values_colors), -1);
+ ASSERT_NE(pool_strings_proto.find(relative_path_layout_layout_one), -1);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index fd12d02434fa..118a76ff73a8 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -78,6 +78,8 @@ using ::android::base::StringPrintf;
namespace aapt {
+constexpr uint8_t kAndroidPackageId = 0x01;
+
class LinkContext : public IAaptContext {
public:
explicit LinkContext(IDiagnostics* diagnostics)
@@ -1401,7 +1403,7 @@ class Linker {
return MergeExportedSymbols(compiled_file.source, compiled_file.exported_symbols);
}
- // Takes a path to load as a ZIP file and merges the files within into the master ResourceTable.
+ // Takes a path to load as a ZIP file and merges the files within into the main ResourceTable.
// If override is true, conflicting resources are allowed to override each other, in order of last
// seen.
// An io::IFileCollection is created from the ZIP file and added to the set of
@@ -1432,7 +1434,7 @@ class Linker {
return !error;
}
- // Takes a path to load and merge into the master ResourceTable. If override is true,
+ // Takes a path to load and merge into the main ResourceTable. If override is true,
// conflicting resources are allowed to override each other, in order of last seen.
// If the file path ends with .flata, .jar, .jack, or .zip the file is treated
// as ZIP archive and the files within are merged individually.
@@ -1449,7 +1451,7 @@ class Linker {
return MergeFile(file, override);
}
- // Takes an AAPT Container file (.apc/.flat) to load and merge into the master ResourceTable.
+ // Takes an AAPT Container file (.apc/.flat) to load and merge into the main ResourceTable.
// If override is true, conflicting resources are allowed to override each other, in order of last
// seen.
// All other file types are ignored. This is because these files could be coming from a zip,
@@ -1579,37 +1581,35 @@ class Linker {
}
void AliasAdaptiveIcon(xml::XmlResource* manifest, ResourceTable* table) {
- xml::Element* application = manifest->root->FindChild("", "application");
+ const xml::Element* application = manifest->root->FindChild("", "application");
if (!application) {
return;
}
- xml::Attribute* icon = application->FindAttribute(xml::kSchemaAndroid, "icon");
- xml::Attribute* round_icon = application->FindAttribute(xml::kSchemaAndroid, "roundIcon");
+ const xml::Attribute* icon = application->FindAttribute(xml::kSchemaAndroid, "icon");
+ const xml::Attribute* round_icon = application->FindAttribute(xml::kSchemaAndroid, "roundIcon");
if (!icon || !round_icon) {
return;
}
// Find the icon resource defined within the application.
- auto icon_reference = ValueCast<Reference>(icon->compiled_value.get());
+ const auto icon_reference = ValueCast<Reference>(icon->compiled_value.get());
if (!icon_reference || !icon_reference->name) {
return;
}
- auto package = table->FindPackageById(icon_reference->id.value().package_id());
- if (!package) {
- return;
- }
- auto type = package->FindType(icon_reference->name.value().type);
- if (!type) {
- return;
+
+ auto icon_name = ResourceNameRef(icon_reference->name.value());
+ if (icon_name.package.empty()) {
+ icon_name.package = context_->GetCompilationPackage();
}
- auto icon_entry = type->FindEntry(icon_reference->name.value().entry);
- if (!icon_entry) {
+
+ const auto icon_entry_result = table->FindResource(icon_name);
+ if (!icon_entry_result) {
return;
}
int icon_max_sdk = 0;
- for (auto& config_value : icon_entry->values) {
+ for (auto& config_value : icon_entry_result.value().entry->values) {
icon_max_sdk = (icon_max_sdk < config_value->config.sdkVersion)
? config_value->config.sdkVersion : icon_max_sdk;
}
@@ -1619,25 +1619,23 @@ class Linker {
}
// Find the roundIcon resource defined within the application.
- auto round_icon_reference = ValueCast<Reference>(round_icon->compiled_value.get());
+ const auto round_icon_reference = ValueCast<Reference>(round_icon->compiled_value.get());
if (!round_icon_reference || !round_icon_reference->name) {
return;
}
- package = table->FindPackageById(round_icon_reference->id.value().package_id());
- if (!package) {
- return;
- }
- type = package->FindType(round_icon_reference->name.value().type);
- if (!type) {
- return;
+
+ auto round_icon_name = ResourceNameRef(round_icon_reference->name.value());
+ if (round_icon_name.package.empty()) {
+ round_icon_name.package = context_->GetCompilationPackage();
}
- auto round_icon_entry = type->FindEntry(round_icon_reference->name.value().entry);
- if (!round_icon_entry) {
+
+ const auto round_icon_entry_result = table->FindResource(round_icon_name);
+ if (!round_icon_entry_result) {
return;
}
int round_icon_max_sdk = 0;
- for (auto& config_value : round_icon_entry->values) {
+ for (auto& config_value : round_icon_entry_result.value().entry->values) {
round_icon_max_sdk = (round_icon_max_sdk < config_value->config.sdkVersion)
? config_value->config.sdkVersion : round_icon_max_sdk;
}
@@ -1648,7 +1646,7 @@ class Linker {
}
// Add an equivalent v26 entry to the roundIcon for each v26 variant of the regular icon.
- for (auto& config_value : icon_entry->values) {
+ for (auto& config_value : icon_entry_result.value().entry->values) {
if (config_value->config.sdkVersion < SDK_O) {
continue;
}
@@ -1659,7 +1657,7 @@ class Linker {
<< "\" for round icon compatibility");
auto value = icon_reference->Clone(&table->string_pool);
- auto round_config_value = round_icon_entry->FindOrCreateValue(
+ auto round_config_value = round_icon_entry_result.value().entry->FindOrCreateValue(
config_value->config, config_value->product);
round_config_value->value.reset(value);
}
@@ -1809,7 +1807,7 @@ class Linker {
// Override the package ID when it is "android".
if (context_->GetCompilationPackage() == "android") {
- context_->SetPackageId(0x01);
+ context_->SetPackageId(kAndroidPackageId);
// Verify we're building a regular app.
if (context_->GetPackageType() != PackageType::kApp) {
@@ -1866,7 +1864,8 @@ class Linker {
if (context_->GetPackageType() != PackageType::kStaticLib) {
PrivateAttributeMover mover;
- if (!mover.Consume(context_, &final_table_)) {
+ if (context_->GetPackageId() == kAndroidPackageId &&
+ !mover.Consume(context_, &final_table_)) {
context_->GetDiagnostics()->Error(DiagMessage() << "failed moving private attributes");
return 1;
}
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index e36668e5a043..5b18a3789d76 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -132,8 +132,8 @@ class Optimizer {
if (context_->IsVerbose()) {
context_->GetDiagnostics()->Note(DiagMessage() << "Optimizing APK...");
}
- if (!options_.resources_blacklist.empty()) {
- ResourceFilter filter(options_.resources_blacklist);
+ if (!options_.resources_exclude_list.empty()) {
+ ResourceFilter filter(options_.resources_exclude_list);
if (!filter.Consume(context_, apk->GetResourceTable())) {
context_->GetDiagnostics()->Error(DiagMessage() << "failed filtering resources");
return 1;
@@ -328,7 +328,7 @@ bool ParseConfig(const std::string& content, IAaptContext* context, OptimizeOpti
}
for (StringPiece directive : util::Tokenize(directives, ',')) {
if (directive == "remove") {
- options->resources_blacklist.insert(resource_name.ToResourceName());
+ options->resources_exclude_list.insert(resource_name.ToResourceName());
} else if (directive == "no_collapse" || directive == "no_obfuscate") {
options->table_flattener_options.name_collapse_exemptions.insert(
resource_name.ToResourceName());
diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h
index 5070ccc8afbf..3afc46b04af6 100644
--- a/tools/aapt2/cmd/Optimize.h
+++ b/tools/aapt2/cmd/Optimize.h
@@ -36,8 +36,8 @@ struct OptimizeOptions {
// Details of the app extracted from the AndroidManifest.xml
AppInfo app_info;
- // Blacklist of unused resources that should be removed from the apk.
- std::unordered_set<ResourceName> resources_blacklist;
+ // Exclude list of unused resources that should be removed from the apk.
+ std::unordered_set<ResourceName> resources_exclude_list;
// Split APK options.
TableSplitterOptions table_splitter_options;
diff --git a/tools/aapt2/compile/PngChunkFilter.cpp b/tools/aapt2/compile/PngChunkFilter.cpp
index bc2e6990433c..4db2392b4eab 100644
--- a/tools/aapt2/compile/PngChunkFilter.cpp
+++ b/tools/aapt2/compile/PngChunkFilter.cpp
@@ -35,7 +35,7 @@ constexpr uint32_t u32(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
((uint32_t)d);
}
-// Whitelist of PNG chunk types that we want to keep in the resulting PNG.
+// Allow list of PNG chunk types that we want to keep in the resulting PNG.
enum PngChunkTypes {
kPngChunkIHDR = u32(73, 72, 68, 82),
kPngChunkIDAT = u32(73, 68, 65, 84),
@@ -56,7 +56,7 @@ static uint32_t Peek32LE(const char* data) {
return word;
}
-static bool IsPngChunkWhitelisted(uint32_t type) {
+static bool IsPngChunkAllowed(uint32_t type) {
switch (type) {
case kPngChunkIHDR:
case kPngChunkIDAT:
@@ -128,7 +128,7 @@ bool PngChunkFilter::Next(const void** buffer, size_t* len) {
// Do we strip this chunk?
const uint32_t chunk_type = Peek32LE(data_.data() + window_end_ + sizeof(uint32_t));
- if (IsPngChunkWhitelisted(chunk_type)) {
+ if (IsPngChunkAllowed(chunk_type)) {
// Advance the window to include this chunk.
window_end_ += kMinChunkHeaderSize + chunk_len;
diff --git a/tools/aapt2/configuration/ConfigurationParser_test.cpp b/tools/aapt2/configuration/ConfigurationParser_test.cpp
index 2ef8b999a192..e5b3107877cb 100644
--- a/tools/aapt2/configuration/ConfigurationParser_test.cpp
+++ b/tools/aapt2/configuration/ConfigurationParser_test.cpp
@@ -187,7 +187,7 @@ TEST_F(ConfigurationParserTest, ForPath_NoFile) {
TEST_F(ConfigurationParserTest, ExtractConfiguration) {
Maybe<PostProcessingConfiguration> maybe_config =
- ExtractConfiguration(kValidConfig, "dummy.xml", &diag_);
+ ExtractConfiguration(kValidConfig, "fake.xml", &diag_);
PostProcessingConfiguration config = maybe_config.value();
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 4a6bfd031284..8862405189c0 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -79,8 +79,8 @@ enum {
ISGAME_ATTR = 0x10103f4,
VERSION_ATTR = 0x01010519,
CERT_DIGEST_ATTR = 0x01010548,
- REQUIRED_FEATURE_ATTR = 0x01010557,
- REQUIRED_NOT_FEATURE_ATTR = 0x01010558,
+ REQUIRED_FEATURE_ATTR = 0x01010554,
+ REQUIRED_NOT_FEATURE_ATTR = 0x01010555,
IS_STATIC_ATTR = 0x0101055a,
REQUIRED_SYSTEM_PROPERTY_NAME_ATTR = 0x01010565,
REQUIRED_SYSTEM_PROPERTY_VALUE_ATTR = 0x01010566,
@@ -188,7 +188,7 @@ class ManifestExtractor {
/** Retrieves the resource assigned to the specified resource id if one exists. */
Value* FindValueById(const ResourceTable* table, const ResourceId& res_id,
- const ConfigDescription& config = DummyConfig()) {
+ const ConfigDescription& config = DefaultConfig()) {
if (table) {
for (auto& package : table->packages) {
if (package->id && package->id.value() == res_id.package_id()) {
@@ -210,7 +210,7 @@ class ManifestExtractor {
}
/** Attempts to resolve the reference to a non-reference value. */
- Value* ResolveReference(Reference* ref, const ConfigDescription& config = DummyConfig()) {
+ Value* ResolveReference(Reference* ref, const ConfigDescription& config = DefaultConfig()) {
const int kMaxIterations = 40;
int i = 0;
while (ref && ref->id && i++ < kMaxIterations) {
@@ -231,10 +231,10 @@ class ManifestExtractor {
* this will attempt to resolve the reference to an integer value.
**/
int32_t* GetAttributeInteger(xml::Attribute* attr,
- const ConfigDescription& config = DummyConfig()) {
+ const ConfigDescription& config = DefaultConfig()) {
if (attr != nullptr) {
if (attr->compiled_value) {
- // Resolve references using the dummy configuration
+ // Resolve references using the configuration
Value* value = attr->compiled_value.get();
if (ValueCast<Reference>(value)) {
value = ResolveReference(ValueCast<Reference>(value), config);
@@ -257,7 +257,7 @@ class ManifestExtractor {
* exist or cannot be resolved to an integer value.
**/
int32_t GetAttributeIntegerDefault(xml::Attribute* attr, int32_t def,
- const ConfigDescription& config = DummyConfig()) {
+ const ConfigDescription& config = DefaultConfig()) {
auto value = GetAttributeInteger(attr, config);
if (value) {
return *value;
@@ -270,10 +270,10 @@ class ManifestExtractor {
* this will attempt to resolve the reference to a string value.
**/
const std::string* GetAttributeString(xml::Attribute* attr,
- const ConfigDescription& config = DummyConfig()) {
+ const ConfigDescription& config = DefaultConfig()) {
if (attr != nullptr) {
if (attr->compiled_value) {
- // Resolve references using the dummy configuration
+ // Resolve references using the configuration
Value* value = attr->compiled_value.get();
if (ValueCast<Reference>(value)) {
value = ResolveReference(ValueCast<Reference>(value), config);
@@ -305,7 +305,7 @@ class ManifestExtractor {
* exist or cannot be resolved to an string value.
**/
std::string GetAttributeStringDefault(xml::Attribute* attr, std::string def,
- const ConfigDescription& config = DummyConfig()) {
+ const ConfigDescription& config = DefaultConfig()) {
auto value = GetAttributeString(attr, config);
if (value) {
return *value;
@@ -322,7 +322,7 @@ class ManifestExtractor {
friend Element;
/** Creates a default configuration used to retrieve resources. */
- static ConfigDescription DummyConfig() {
+ static ConfigDescription DefaultConfig() {
ConfigDescription config;
config.orientation = android::ResTable_config::ORIENTATION_PORT;
config.density = android::ResTable_config::DENSITY_MEDIUM;
@@ -1063,17 +1063,23 @@ class UsesPermission : public ManifestExtractor::Element {
public:
UsesPermission() = default;
std::string name;
- std::string requiredFeature;
- std::string requiredNotFeature;
+ std::vector<std::string> requiredFeatures;
+ std::vector<std::string> requiredNotFeatures;
int32_t required = true;
int32_t maxSdkVersion = -1;
void Extract(xml::Element* element) override {
name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
- requiredFeature = GetAttributeStringDefault(
- FindAttribute(element, REQUIRED_FEATURE_ATTR), "");
- requiredNotFeature = GetAttributeStringDefault(
- FindAttribute(element, REQUIRED_NOT_FEATURE_ATTR), "");
+ std::string feature =
+ GetAttributeStringDefault(FindAttribute(element, REQUIRED_FEATURE_ATTR), "");
+ if (!feature.empty()) {
+ requiredFeatures.push_back(feature);
+ }
+ feature = GetAttributeStringDefault(FindAttribute(element, REQUIRED_NOT_FEATURE_ATTR), "");
+ if (!feature.empty()) {
+ requiredNotFeatures.push_back(feature);
+ }
+
required = GetAttributeIntegerDefault(FindAttribute(element, REQUIRED_ATTR), 1);
maxSdkVersion = GetAttributeIntegerDefault(
FindAttribute(element, MAX_SDK_VERSION_ATTR), -1);
@@ -1090,13 +1096,13 @@ class UsesPermission : public ManifestExtractor::Element {
if (maxSdkVersion >= 0) {
printer->Print(StringPrintf(" maxSdkVersion='%d'", maxSdkVersion));
}
- if (!requiredFeature.empty()) {
- printer->Print(StringPrintf(" requiredFeature='%s'", requiredFeature.data()));
+ printer->Print("\n");
+ for (const std::string& requiredFeature : requiredFeatures) {
+ printer->Print(StringPrintf(" required-feature='%s'\n", requiredFeature.data()));
}
- if (!requiredNotFeature.empty()) {
- printer->Print(StringPrintf(" requiredNotFeature='%s'", requiredNotFeature.data()));
+ for (const std::string& requiredNotFeature : requiredNotFeatures) {
+ printer->Print(StringPrintf(" required-not-feature='%s'\n", requiredNotFeature.data()));
}
- printer->Print("\n");
if (required == 0) {
printer->Print(StringPrintf("optional-permission: name='%s'", name.data()));
if (maxSdkVersion >= 0) {
@@ -1116,6 +1122,38 @@ class UsesPermission : public ManifestExtractor::Element {
}
};
+/** Represents <required-feature> elements. **/
+class RequiredFeature : public ManifestExtractor::Element {
+ public:
+ RequiredFeature() = default;
+ std::string name;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ auto parent_stack = extractor()->parent_stack();
+ if (!name.empty() && ElementCast<UsesPermission>(parent_stack[0])) {
+ UsesPermission* uses_permission = ElementCast<UsesPermission>(parent_stack[0]);
+ uses_permission->requiredFeatures.push_back(name);
+ }
+ }
+};
+
+/** Represents <required-not-feature> elements. **/
+class RequiredNotFeature : public ManifestExtractor::Element {
+ public:
+ RequiredNotFeature() = default;
+ std::string name;
+
+ void Extract(xml::Element* element) override {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ auto parent_stack = extractor()->parent_stack();
+ if (!name.empty() && ElementCast<UsesPermission>(parent_stack[0])) {
+ UsesPermission* uses_permission = ElementCast<UsesPermission>(parent_stack[0]);
+ uses_permission->requiredNotFeatures.push_back(name);
+ }
+ }
+};
+
/** Represents <uses-permission-sdk-23> elements. **/
class UsesPermissionSdk23 : public ManifestExtractor::Element {
public:
@@ -1405,6 +1443,29 @@ class UsesStaticLibrary : public ManifestExtractor::Element {
}
};
+/** Represents <uses-native-library> elements. **/
+class UsesNativeLibrary : public ManifestExtractor::Element {
+ public:
+ UsesNativeLibrary() = default;
+ std::string name;
+ int required;
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ required = GetAttributeIntegerDefault(FindAttribute(element, REQUIRED_ATTR), 1);
+ }
+ }
+
+ void Print(text::Printer* printer) override {
+ if (!name.empty()) {
+ printer->Print(StringPrintf("uses-native-library%s:'%s'\n",
+ (required == 0) ? "-not-required" : "", name.data()));
+ }
+ }
+};
+
/**
* Represents <meta-data> elements. These tags are only printed when a flag is passed in to
* explicitly enable meta data printing.
@@ -1822,7 +1883,8 @@ bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) {
for (xml::Element* child : element->GetChildElements()) {
if (child->name == "uses-permission" || child->name == "uses-permission-sdk-23"
|| child->name == "permission") {
- auto permission_element = ManifestExtractor::Element::Inflate(this, child);
+ // Inflate the element and its descendants
+ auto permission_element = Visit(child);
manifest->AddChild(permission_element);
}
}
@@ -1848,7 +1910,7 @@ bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) {
// Collect all the unique locales of the apk
if (locales_.find(locale_str) == locales_.end()) {
- ConfigDescription config = ManifestExtractor::DummyConfig();
+ ConfigDescription config = ManifestExtractor::DefaultConfig();
config.setBcp47Locale(locale_str.data());
locales_.insert(std::make_pair(locale_str, config));
}
@@ -1857,7 +1919,7 @@ bool ManifestExtractor::Dump(text::Printer* printer, IDiagnostics* diag) {
uint16_t density = (value->config.density == 0) ? (uint16_t) 160
: value->config.density;
if (densities_.find(density) == densities_.end()) {
- ConfigDescription config = ManifestExtractor::DummyConfig();
+ ConfigDescription config = ManifestExtractor::DefaultConfig();
config.density = density;
densities_.insert(std::make_pair(density, config));
}
@@ -2214,37 +2276,40 @@ T* ElementCast(ManifestExtractor::Element* element) {
}
const std::unordered_map<std::string, bool> kTagCheck = {
- {"action", std::is_base_of<Action, T>::value},
- {"activity", std::is_base_of<Activity, T>::value},
- {"application", std::is_base_of<Application, T>::value},
- {"category", std::is_base_of<Category, T>::value},
- {"compatible-screens", std::is_base_of<CompatibleScreens, T>::value},
- {"feature-group", std::is_base_of<FeatureGroup, T>::value},
- {"input-type", std::is_base_of<InputType, T>::value},
- {"intent-filter", std::is_base_of<IntentFilter, T>::value},
- {"meta-data", std::is_base_of<MetaData, T>::value},
- {"manifest", std::is_base_of<Manifest, T>::value},
- {"original-package", std::is_base_of<OriginalPackage, T>::value},
- {"overlay", std::is_base_of<Overlay, T>::value},
- {"package-verifier", std::is_base_of<PackageVerifier, T>::value},
- {"permission", std::is_base_of<Permission, T>::value},
- {"provider", std::is_base_of<Provider, T>::value},
- {"receiver", std::is_base_of<Receiver, T>::value},
- {"screen", std::is_base_of<Screen, T>::value},
- {"service", std::is_base_of<Service, T>::value},
- {"supports-gl-texture", std::is_base_of<SupportsGlTexture, T>::value},
- {"supports-input", std::is_base_of<SupportsInput, T>::value},
- {"supports-screens", std::is_base_of<SupportsScreen, T>::value},
- {"uses-configuration", std::is_base_of<UsesConfiguarion, T>::value},
- {"uses-feature", std::is_base_of<UsesFeature, T>::value},
- {"uses-permission", std::is_base_of<UsesPermission, T>::value},
- {"uses-permission-sdk-23", std::is_base_of<UsesPermissionSdk23, T>::value},
- {"uses-library", std::is_base_of<UsesLibrary, T>::value},
- {"uses-package", std::is_base_of<UsesPackage, T>::value},
- {"static-library", std::is_base_of<StaticLibrary, T>::value},
- {"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value},
- {"additional-certificate", std::is_base_of<AdditionalCertificate, T>::value},
- {"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
+ {"action", std::is_base_of<Action, T>::value},
+ {"activity", std::is_base_of<Activity, T>::value},
+ {"additional-certificate", std::is_base_of<AdditionalCertificate, T>::value},
+ {"application", std::is_base_of<Application, T>::value},
+ {"category", std::is_base_of<Category, T>::value},
+ {"compatible-screens", std::is_base_of<CompatibleScreens, T>::value},
+ {"feature-group", std::is_base_of<FeatureGroup, T>::value},
+ {"input-type", std::is_base_of<InputType, T>::value},
+ {"intent-filter", std::is_base_of<IntentFilter, T>::value},
+ {"meta-data", std::is_base_of<MetaData, T>::value},
+ {"manifest", std::is_base_of<Manifest, T>::value},
+ {"original-package", std::is_base_of<OriginalPackage, T>::value},
+ {"overlay", std::is_base_of<Overlay, T>::value},
+ {"package-verifier", std::is_base_of<PackageVerifier, T>::value},
+ {"permission", std::is_base_of<Permission, T>::value},
+ {"provider", std::is_base_of<Provider, T>::value},
+ {"receiver", std::is_base_of<Receiver, T>::value},
+ {"required-feature", std::is_base_of<RequiredFeature, T>::value},
+ {"required-not-feature", std::is_base_of<RequiredNotFeature, T>::value},
+ {"screen", std::is_base_of<Screen, T>::value},
+ {"service", std::is_base_of<Service, T>::value},
+ {"static-library", std::is_base_of<StaticLibrary, T>::value},
+ {"supports-gl-texture", std::is_base_of<SupportsGlTexture, T>::value},
+ {"supports-input", std::is_base_of<SupportsInput, T>::value},
+ {"supports-screens", std::is_base_of<SupportsScreen, T>::value},
+ {"uses-configuration", std::is_base_of<UsesConfiguarion, T>::value},
+ {"uses-feature", std::is_base_of<UsesFeature, T>::value},
+ {"uses-library", std::is_base_of<UsesLibrary, T>::value},
+ {"uses-native-library", std::is_base_of<UsesNativeLibrary, T>::value},
+ {"uses-package", std::is_base_of<UsesPackage, T>::value},
+ {"uses-permission", std::is_base_of<UsesPermission, T>::value},
+ {"uses-permission-sdk-23", std::is_base_of<UsesPermissionSdk23, T>::value},
+ {"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
+ {"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value},
};
auto check = kTagCheck.find(element->tag());
@@ -2264,38 +2329,41 @@ std::unique_ptr<ManifestExtractor::Element> ManifestExtractor::Element::Inflate(
const std::unordered_map<std::string,
std::function<std::unique_ptr<ManifestExtractor::Element>()>>
kTagCheck = {
- {"action", &CreateType<Action>},
- {"activity", &CreateType<Activity>},
- {"application", &CreateType<Application>},
- {"category", &CreateType<Category>},
- {"compatible-screens", &CreateType<CompatibleScreens>},
- {"feature-group", &CreateType<FeatureGroup>},
- {"input-type", &CreateType<InputType>},
- {"intent-filter",&CreateType<IntentFilter>},
- {"manifest", &CreateType<Manifest>},
- {"meta-data", &CreateType<MetaData>},
- {"original-package", &CreateType<OriginalPackage>},
- {"overlay", &CreateType<Overlay>},
- {"package-verifier", &CreateType<PackageVerifier>},
- {"permission", &CreateType<Permission>},
- {"provider", &CreateType<Provider>},
- {"receiver", &CreateType<Receiver>},
- {"screen", &CreateType<Screen>},
- {"service", &CreateType<Service>},
- {"supports-gl-texture", &CreateType<SupportsGlTexture>},
- {"supports-input", &CreateType<SupportsInput>},
- {"supports-screens", &CreateType<SupportsScreen>},
- {"uses-configuration", &CreateType<UsesConfiguarion>},
- {"uses-feature", &CreateType<UsesFeature>},
- {"uses-permission", &CreateType<UsesPermission>},
- {"uses-permission-sdk-23", &CreateType<UsesPermissionSdk23>},
- {"uses-library", &CreateType<UsesLibrary>},
- {"static-library", &CreateType<StaticLibrary>},
- {"uses-static-library", &CreateType<UsesStaticLibrary>},
- {"uses-package", &CreateType<UsesPackage>},
- {"additional-certificate", &CreateType<AdditionalCertificate>},
- {"uses-sdk", &CreateType<UsesSdkBadging>},
- };
+ {"action", &CreateType<Action>},
+ {"activity", &CreateType<Activity>},
+ {"additional-certificate", &CreateType<AdditionalCertificate>},
+ {"application", &CreateType<Application>},
+ {"category", &CreateType<Category>},
+ {"compatible-screens", &CreateType<CompatibleScreens>},
+ {"feature-group", &CreateType<FeatureGroup>},
+ {"input-type", &CreateType<InputType>},
+ {"intent-filter", &CreateType<IntentFilter>},
+ {"manifest", &CreateType<Manifest>},
+ {"meta-data", &CreateType<MetaData>},
+ {"original-package", &CreateType<OriginalPackage>},
+ {"overlay", &CreateType<Overlay>},
+ {"package-verifier", &CreateType<PackageVerifier>},
+ {"permission", &CreateType<Permission>},
+ {"provider", &CreateType<Provider>},
+ {"receiver", &CreateType<Receiver>},
+ {"required-feature", &CreateType<RequiredFeature>},
+ {"required-not-feature", &CreateType<RequiredNotFeature>},
+ {"screen", &CreateType<Screen>},
+ {"service", &CreateType<Service>},
+ {"static-library", &CreateType<StaticLibrary>},
+ {"supports-gl-texture", &CreateType<SupportsGlTexture>},
+ {"supports-input", &CreateType<SupportsInput>},
+ {"supports-screens", &CreateType<SupportsScreen>},
+ {"uses-configuration", &CreateType<UsesConfiguarion>},
+ {"uses-feature", &CreateType<UsesFeature>},
+ {"uses-library", &CreateType<UsesLibrary>},
+ {"uses-native-library", &CreateType<UsesNativeLibrary>},
+ {"uses-package", &CreateType<UsesPackage>},
+ {"uses-permission", &CreateType<UsesPermission>},
+ {"uses-permission-sdk-23", &CreateType<UsesPermissionSdk23>},
+ {"uses-sdk", &CreateType<UsesSdkBadging>},
+ {"uses-static-library", &CreateType<UsesStaticLibrary>},
+ };
// Attempt to map the xml tag to a element inflater
std::unique_ptr<ManifestExtractor::Element> element;
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 4784ecf3d12c..eb0ade62d542 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -59,22 +59,10 @@ static void strcpy16_htod(uint16_t* dst, size_t len, const StringPiece16& src) {
dst[i] = 0;
}
-static bool cmp_style_ids(ResourceId a, ResourceId b) {
- // If one of a and b is from the framework package (package ID 0x01), and the
- // other is a dynamic ID (package ID 0x00), then put the dynamic ID after the
- // framework ID. This ensures that when AssetManager resolves the dynamic IDs,
- // they will be in sorted order as expected by AssetManager.
- if ((a.package_id() == kFrameworkPackageId && b.package_id() == 0x00) ||
- (a.package_id() == 0x00 && b.package_id() == kFrameworkPackageId)) {
- return b < a;
- }
- return a < b;
-}
-
static bool cmp_style_entries(const Style::Entry& a, const Style::Entry& b) {
if (a.key.id) {
if (b.key.id) {
- return cmp_style_ids(a.key.id.value(), b.key.id.value());
+ return cmp_ids_dynamic_after_framework(a.key.id.value(), b.key.id.value());
}
return true;
} else if (!b.key.id) {
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index 7eb8ebd9a043..06ac9e5dc5c4 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -449,9 +449,12 @@ static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStr
}
for (const pb::Entry& pb_entry : pb_type.entry()) {
- ResourceEntry* entry = type->FindOrCreateEntry(pb_entry.name());
+ ResourceEntry* entry;
if (pb_entry.has_entry_id()) {
- entry->id = static_cast<uint16_t>(pb_entry.entry_id().id());
+ auto entry_id = static_cast<uint16_t>(pb_entry.entry_id().id());
+ entry = type->FindOrCreateEntry(pb_entry.name(), entry_id);
+ } else {
+ entry = type->FindOrCreateEntry(pb_entry.name());
}
// Deserialize the symbol status (public/private with source and comments).
@@ -490,8 +493,10 @@ static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStr
// Find the overlayable to which this item belongs
pb::OverlayableItem pb_overlayable_item = pb_entry.overlayable_item();
if (pb_overlayable_item.overlayable_idx() >= overlayables.size()) {
- *out_error = android::base::StringPrintf("invalid overlayable_idx value %d",
- pb_overlayable_item.overlayable_idx());
+ *out_error =
+ android::base::StringPrintf("invalid overlayable_idx value %d for entry %s/%s",
+ pb_overlayable_item.overlayable_idx(),
+ pb_type.name().c_str(), pb_entry.name().c_str());
return false;
}
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 831229ffa383..98c517510028 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -359,12 +359,21 @@ void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table
}
pb_type->set_name(to_string(type->type).to_string());
+ // hardcoded string uses characters which make it an invalid resource name
+ static const char* obfuscated_resource_name = "0_resource_name_obfuscated";
for (const std::unique_ptr<ResourceEntry>& entry : type->entries) {
pb::Entry* pb_entry = pb_type->add_entry();
if (entry->id) {
pb_entry->mutable_entry_id()->set_id(entry->id.value());
}
- pb_entry->set_name(entry->name);
+ ResourceName resource_name({}, type->type, entry->name);
+ if (options.collapse_key_stringpool &&
+ options.name_collapse_exemptions.find(resource_name) ==
+ options.name_collapse_exemptions.end()) {
+ pb_entry->set_name(obfuscated_resource_name);
+ } else {
+ pb_entry->set_name(entry->name);
+ }
// Write the Visibility struct.
pb::Visibility* pb_visibility = pb_entry->mutable_visibility();
diff --git a/tools/aapt2/format/proto/ProtoSerialize.h b/tools/aapt2/format/proto/ProtoSerialize.h
index 7a3ea9903732..b0d56307fbe4 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.h
+++ b/tools/aapt2/format/proto/ProtoSerialize.h
@@ -38,6 +38,15 @@ struct SerializeXmlOptions {
struct SerializeTableOptions {
/** Prevent serializing the source pool and source protos. */
bool exclude_sources = false;
+
+ // When true, all the entry names in pb:ResourceTable are collapsed to a
+ // single entry name. When the proto table is converted to binary
+ // resources.arsc, the key string pool is collapsed to a single entry. All
+ // resource entries have name indices that point to this single value.
+ bool collapse_key_stringpool = false;
+
+ // Set of resources to avoid collapsing to a single entry in key stringpool.
+ std::set<ResourceName> name_collapse_exemptions;
};
// Serializes a Value to its protobuf representation. An optional StringPool will hold the
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 1a7de6dc1c48..fe4c8aa065f1 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -24,6 +24,7 @@ using ::android::ConfigDescription;
using ::android::StringPiece;
using ::testing::Eq;
using ::testing::IsEmpty;
+using ::testing::IsNull;
using ::testing::NotNull;
using ::testing::SizeIs;
using ::testing::StrEq;
@@ -39,6 +40,13 @@ class MockFileCollection : public io::IFileCollection {
MOCK_METHOD0(GetDirSeparator, char());
};
+ResourceEntry* GetEntry(ResourceTable* table, const ResourceNameRef& res_name,
+ uint32_t id) {
+ ResourceTablePackage* package = table->FindPackage(res_name.package);
+ ResourceTableType* type = package->FindType(res_name.type);
+ return type->FindEntry(res_name.entry, id);
+}
+
TEST(ProtoSerializeTest, SerializeSinglePackage) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<ResourceTable> table =
@@ -662,4 +670,167 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeNonDynamicReference) {
EXPECT_FALSE(actual_ref->is_dynamic);
}
+TEST(ProtoSerializeTest, CollapsingResourceNamesNoNameCollapseExemptionsSucceeds) {
+ const uint32_t id_one_id = 0x7f020000;
+ const uint32_t id_two_id = 0x7f020001;
+ const uint32_t id_three_id = 0x7f020002;
+ const uint32_t integer_three_id = 0x7f030000;
+ const uint32_t string_test_id = 0x7f040000;
+ const uint32_t layout_bar_id = 0x7f050000;
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.test", 0x7f)
+ .AddSimple("com.app.test:id/one", ResourceId(id_one_id))
+ .AddSimple("com.app.test:id/two", ResourceId(id_two_id))
+ .AddValue("com.app.test:id/three", ResourceId(id_three_id),
+ test::BuildReference("com.app.test:id/one", ResourceId(id_one_id)))
+ .AddValue("com.app.test:integer/one", ResourceId(integer_three_id),
+ util::make_unique<BinaryPrimitive>(
+ uint8_t(android::Res_value::TYPE_INT_DEC), 1u))
+ .AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"),
+ ResourceId(integer_three_id),
+ util::make_unique<BinaryPrimitive>(
+ uint8_t(android::Res_value::TYPE_INT_DEC), 2u))
+ .AddString("com.app.test:string/test", ResourceId(string_test_id), "foo")
+ .AddFileReference("com.app.test:layout/bar", ResourceId(layout_bar_id),
+ "res/layout/bar.xml")
+ .Build();
+
+ SerializeTableOptions options;
+ options.collapse_key_stringpool = true;
+
+ pb::ResourceTable pb_table;
+
+ SerializeTableToPb(*table, &pb_table, context->GetDiagnostics(), options);
+ test::TestFile file_a("res/layout/bar.xml");
+ MockFileCollection files;
+ EXPECT_CALL(files, FindFile(Eq("res/layout/bar.xml")))
+ .WillRepeatedly(::testing::Return(&file_a));
+ ResourceTable new_table;
+ std::string error;
+ ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error)) << error;
+ EXPECT_THAT(error, IsEmpty());
+
+ ResourceName real_id_resource(
+ "com.app.test", ResourceType::kId, "one");
+ EXPECT_THAT(GetEntry(&new_table, real_id_resource, id_one_id), IsNull());
+
+ ResourceName obfuscated_id_resource(
+ "com.app.test", ResourceType::kId, "0_resource_name_obfuscated");
+
+ EXPECT_THAT(GetEntry(&new_table, obfuscated_id_resource,
+ id_one_id), NotNull());
+ EXPECT_THAT(GetEntry(&new_table, obfuscated_id_resource,
+ id_two_id), NotNull());
+ ResourceEntry* entry = GetEntry(&new_table, obfuscated_id_resource, id_three_id);
+ EXPECT_THAT(entry, NotNull());
+ ResourceConfigValue* config_value = entry->FindValue({});
+ Reference* ref = ValueCast<Reference>(config_value->value.get());
+ EXPECT_THAT(ref->id.value(), Eq(id_one_id));
+
+ ResourceName obfuscated_integer_resource(
+ "com.app.test", ResourceType::kInteger, "0_resource_name_obfuscated");
+ entry = GetEntry(&new_table, obfuscated_integer_resource, integer_three_id);
+ EXPECT_THAT(entry, NotNull());
+ config_value = entry->FindValue({});
+ BinaryPrimitive* bp = ValueCast<BinaryPrimitive>(config_value->value.get());
+ EXPECT_THAT(bp->value.data, Eq(1u));
+
+ config_value = entry->FindValue(test::ParseConfigOrDie("v1"));
+ bp = ValueCast<BinaryPrimitive>(config_value->value.get());
+ EXPECT_THAT(bp->value.data, Eq(2u));
+
+ ResourceName obfuscated_string_resource(
+ "com.app.test", ResourceType::kString, "0_resource_name_obfuscated");
+ entry = GetEntry(&new_table, obfuscated_string_resource, string_test_id);
+ EXPECT_THAT(entry, NotNull());
+ config_value = entry->FindValue({});
+ String* s = ValueCast<String>(config_value->value.get());
+ EXPECT_THAT(*(s->value), Eq("foo"));
+
+ ResourceName obfuscated_layout_resource(
+ "com.app.test", ResourceType::kLayout, "0_resource_name_obfuscated");
+ entry = GetEntry(&new_table, obfuscated_layout_resource, layout_bar_id);
+ EXPECT_THAT(entry, NotNull());
+ config_value = entry->FindValue({});
+ FileReference* f = ValueCast<FileReference>(config_value->value.get());
+ EXPECT_THAT(*(f->path), Eq("res/layout/bar.xml"));
+}
+
+TEST(ProtoSerializeTest, ObfuscatingResourceNamesWithNameCollapseExemptionsSucceeds) {
+ const uint32_t id_one_id = 0x7f020000;
+ const uint32_t id_two_id = 0x7f020001;
+ const uint32_t id_three_id = 0x7f020002;
+ const uint32_t integer_three_id = 0x7f030000;
+ const uint32_t string_test_id = 0x7f040000;
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.test", 0x7f)
+ .AddSimple("com.app.test:id/one", ResourceId(id_one_id))
+ .AddSimple("com.app.test:id/two", ResourceId(id_two_id))
+ .AddValue("com.app.test:id/three", ResourceId(id_three_id),
+ test::BuildReference("com.app.test:id/one", ResourceId(id_one_id)))
+ .AddValue("com.app.test:integer/one", ResourceId(integer_three_id),
+ util::make_unique<BinaryPrimitive>(
+ uint8_t(android::Res_value::TYPE_INT_DEC), 1u))
+ .AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"),
+ ResourceId(integer_three_id),
+ util::make_unique<BinaryPrimitive>(
+ uint8_t(android::Res_value::TYPE_INT_DEC), 2u))
+ .AddString("com.app.test:string/test", ResourceId(string_test_id), "foo")
+ .Build();
+
+ SerializeTableOptions options;
+ options.collapse_key_stringpool = true;
+ options.name_collapse_exemptions.insert(ResourceName({}, ResourceType::kId, "one"));
+ options.name_collapse_exemptions.insert(ResourceName({}, ResourceType::kString, "test"));
+ pb::ResourceTable pb_table;
+
+ SerializeTableToPb(*table, &pb_table, context->GetDiagnostics(), options);
+ MockFileCollection files;
+ ResourceTable new_table;
+ std::string error;
+ ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error)) << error;
+ EXPECT_THAT(error, IsEmpty());
+
+ EXPECT_THAT(GetEntry(&new_table, ResourceName("com.app.test", ResourceType::kId, "one"),
+ id_one_id), NotNull());
+ ResourceName obfuscated_id_resource(
+ "com.app.test", ResourceType::kId, "0_resource_name_obfuscated");
+ EXPECT_THAT(GetEntry(&new_table, obfuscated_id_resource, id_one_id), IsNull());
+
+ ResourceName real_id_resource(
+ "com.app.test", ResourceType::kId, "two");
+ EXPECT_THAT(GetEntry(&new_table, real_id_resource, id_two_id), IsNull());
+ EXPECT_THAT(GetEntry(&new_table, obfuscated_id_resource, id_two_id), NotNull());
+
+ ResourceEntry* entry = GetEntry(&new_table, obfuscated_id_resource, id_three_id);
+ EXPECT_THAT(entry, NotNull());
+ ResourceConfigValue* config_value = entry->FindValue({});
+ Reference* ref = ValueCast<Reference>(config_value->value.get());
+ EXPECT_THAT(ref->id.value(), Eq(id_one_id));
+
+ // Note that this resource is also named "one", but it's a different type, so gets obfuscated.
+ ResourceName obfuscated_integer_resource(
+ "com.app.test", ResourceType::kInteger, "0_resource_name_obfuscated");
+ entry = GetEntry(&new_table, obfuscated_integer_resource, integer_three_id);
+ EXPECT_THAT(entry, NotNull());
+ config_value = entry->FindValue({});
+ BinaryPrimitive* bp = ValueCast<BinaryPrimitive>(config_value->value.get());
+ EXPECT_THAT(bp->value.data, Eq(1u));
+
+ config_value = entry->FindValue(test::ParseConfigOrDie("v1"));
+ bp = ValueCast<BinaryPrimitive>(config_value->value.get());
+ EXPECT_THAT(bp->value.data, Eq(2u));
+
+ entry = GetEntry(&new_table, ResourceName("com.app.test", ResourceType::kString, "test"),
+ string_test_id);
+ EXPECT_THAT(entry, NotNull());
+ config_value = entry->FindValue({});
+ String* s = ValueCast<String>(config_value->value.get());
+ EXPECT_THAT(*(s->value), Eq("foo"));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index dffad3b99c06..f0f839d968d5 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -218,13 +218,10 @@ struct StyleableAttr {
static bool operator<(const StyleableAttr& lhs, const StyleableAttr& rhs) {
const ResourceId lhs_id = lhs.attr_ref->id.value_or_default(ResourceId(0));
const ResourceId rhs_id = rhs.attr_ref->id.value_or_default(ResourceId(0));
- if (lhs_id < rhs_id) {
- return true;
- } else if (lhs_id > rhs_id) {
- return false;
- } else {
+ if (lhs_id == rhs_id) {
return lhs.attr_ref->name.value() < rhs.attr_ref->name.value();
}
+ return cmp_ids_dynamic_after_framework(lhs_id, rhs_id);
}
void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const ResourceId& id,
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 1e1fe4740c6b..04e20101a0dd 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -551,4 +551,39 @@ TEST(JavaClassGeneratorTest, OnlyGenerateRText) {
ASSERT_TRUE(generator.Generate("android", nullptr));
}
+TEST(JavaClassGeneratorTest, SortsDynamicAttributesAfterFrameworkAttributes) {
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("android", 0x01)
+ .SetPackageId("lib", 0x00)
+ .AddValue("android:attr/framework_attr", ResourceId(0x01010000),
+ test::AttributeBuilder().Build())
+ .AddValue("lib:attr/dynamic_attr", ResourceId(0x00010000),
+ test::AttributeBuilder().Build())
+ .AddValue("lib:styleable/MyStyleable", ResourceId(0x00030000),
+ test::StyleableBuilder()
+ .AddItem("android:attr/framework_attr", ResourceId(0x01010000))
+ .AddItem("lib:attr/dynamic_attr", ResourceId(0x00010000))
+ .Build())
+ .Build();
+
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+ .SetNameManglerPolicy(NameManglerPolicy{"custom"})
+ .SetCompilationPackage("custom")
+ .Build();
+ JavaClassGenerator generator(context.get(), table.get(), {});
+
+ std::string output;
+ StringOutputStream out(&output);
+ EXPECT_TRUE(generator.Generate("lib", &out));
+ out.Flush();
+
+ EXPECT_THAT(output, HasSubstr("public static final int[] MyStyleable={"));
+ EXPECT_THAT(output, HasSubstr("0x01010000, 0x00010000"));
+ EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_android_framework_attr=0;"));
+ EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_dynamic_attr=1;"));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/jni/aapt2_jni.cpp b/tools/aapt2/jni/aapt2_jni.cpp
index ba9646f9aeb4..ec3c5431c7a3 100644
--- a/tools/aapt2/jni/aapt2_jni.cpp
+++ b/tools/aapt2/jni/aapt2_jni.cpp
@@ -139,5 +139,5 @@ JNIEXPORT jint JNICALL Java_com_android_tools_aapt2_Aapt2Jni_nativeLink(JNIEnv*
JNIEXPORT void JNICALL Java_com_android_tools_aapt2_Aapt2Jni_ping(
JNIEnv *env, jclass aapt_obj) {
- // This is just a dummy method to see if the library has been loaded.
+ // This is just a no-op method to see if the library has been loaded.
}
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index c813a446b8db..3d8c25ebcbdd 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -376,10 +376,6 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
});
manifest_action["instrumentation"]["meta-data"] = meta_data_action;
- // TODO moltmann: Remove
- manifest_action["feature"];
- manifest_action["feature"]["inherit-from"];
-
manifest_action["attribution"];
manifest_action["attribution"]["inherit-from"];
manifest_action["original-package"];
@@ -397,6 +393,8 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
manifest_action["protected-broadcast"];
manifest_action["adopt-permissions"];
manifest_action["uses-permission"];
+ manifest_action["uses-permission"]["required-feature"].Action(RequiredNameIsNotEmpty);
+ manifest_action["uses-permission"]["required-not-feature"].Action(RequiredNameIsNotEmpty);
manifest_action["uses-permission-sdk-23"];
manifest_action["permission"];
manifest_action["permission"]["meta-data"] = meta_data_action;
@@ -426,6 +424,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
application_action.Action(OptionalNameIsJavaClassName);
application_action["uses-library"].Action(RequiredNameIsNotEmpty);
+ application_action["uses-native-library"].Action(RequiredNameIsNotEmpty);
application_action["library"].Action(RequiredNameIsNotEmpty);
application_action["profileable"];
@@ -573,8 +572,8 @@ bool ManifestFixer::Consume(IAaptContext* context, xml::XmlResource* doc) {
}
xml::XmlActionExecutorPolicy policy = options_.warn_validation
- ? xml::XmlActionExecutorPolicy::kWhitelistWarning
- : xml::XmlActionExecutorPolicy::kWhitelist;
+ ? xml::XmlActionExecutorPolicy::kAllowListWarning
+ : xml::XmlActionExecutorPolicy::kAllowList;
if (!executor.Execute(policy, context->GetDiagnostics(), doc)) {
return false;
}
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index c25e4503a208..ad56092754aa 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -31,11 +31,11 @@ namespace aapt {
TableMerger::TableMerger(IAaptContext* context, ResourceTable* out_table,
const TableMergerOptions& options)
- : context_(context), master_table_(out_table), options_(options) {
+ : context_(context), main_table_(out_table), options_(options) {
// Create the desired package that all tables will be merged into.
- master_package_ =
- master_table_->CreatePackage(context_->GetCompilationPackage(), context_->GetPackageId());
- CHECK(master_package_ != nullptr) << "package name or ID already taken";
+ main_package_ =
+ main_table_->CreatePackage(context_->GetCompilationPackage(), context_->GetPackageId());
+ CHECK(main_package_ != nullptr) << "package name or ID already taken";
}
bool TableMerger::Merge(const Source& src, ResourceTable* table, bool overlay) {
@@ -235,7 +235,7 @@ bool TableMerger::DoMerge(const Source& src, ResourceTablePackage* src_package,
bool error = false;
for (auto& src_type : src_package->types) {
- ResourceTableType* dst_type = master_package_->FindOrCreateType(src_type->type);
+ ResourceTableType* dst_type = main_package_->FindOrCreateType(src_type->type);
if (!MergeType(context_, src, dst_type, src_type.get())) {
error = true;
continue;
@@ -279,7 +279,7 @@ bool TableMerger::DoMerge(const Source& src, ResourceTablePackage* src_package,
if (dst_config_value) {
CollisionResult collision_result = MergeConfigValue(
context_, res_name, overlay, options_.override_styles_instead_of_overlaying,
- dst_config_value, src_config_value.get(), &master_table_->string_pool);
+ dst_config_value, src_config_value.get(), &main_table_->string_pool);
if (collision_result == CollisionResult::kConflict) {
error = true;
continue;
@@ -298,7 +298,7 @@ bool TableMerger::DoMerge(const Source& src, ResourceTablePackage* src_package,
if (mangle_package) {
new_file_ref = CloneAndMangleFile(src_package->name, *f);
} else {
- new_file_ref = std::unique_ptr<FileReference>(f->Clone(&master_table_->string_pool));
+ new_file_ref = std::unique_ptr<FileReference>(f->Clone(&main_table_->string_pool));
}
dst_config_value->value = std::move(new_file_ref);
@@ -307,7 +307,7 @@ bool TableMerger::DoMerge(const Source& src, ResourceTablePackage* src_package,
? dst_config_value->value->GetComment() : Maybe<std::string>();
dst_config_value->value = std::unique_ptr<Value>(
- src_config_value->value->Clone(&master_table_->string_pool));
+ src_config_value->value->Clone(&main_table_->string_pool));
// Keep the comment from the original resource and ignore all comments from overlaying
// resources
@@ -328,14 +328,14 @@ std::unique_ptr<FileReference> TableMerger::CloneAndMangleFile(
std::string mangled_entry = NameMangler::MangleEntry(package, entry.to_string());
std::string newPath = prefix.to_string() + mangled_entry + suffix.to_string();
std::unique_ptr<FileReference> new_file_ref =
- util::make_unique<FileReference>(master_table_->string_pool.MakeRef(newPath));
+ util::make_unique<FileReference>(main_table_->string_pool.MakeRef(newPath));
new_file_ref->SetComment(file_ref.GetComment());
new_file_ref->SetSource(file_ref.GetSource());
new_file_ref->type = file_ref.type;
new_file_ref->file = file_ref.file;
return new_file_ref;
}
- return std::unique_ptr<FileReference>(file_ref.Clone(&master_table_->string_pool));
+ return std::unique_ptr<FileReference>(file_ref.Clone(&main_table_->string_pool));
}
bool TableMerger::MergeFile(const ResourceFile& file_desc, bool overlay, io::IFile* file) {
diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h
index a35a134a887d..e01a0c186392 100644
--- a/tools/aapt2/link/TableMerger.h
+++ b/tools/aapt2/link/TableMerger.h
@@ -80,9 +80,9 @@ class TableMerger {
DISALLOW_COPY_AND_ASSIGN(TableMerger);
IAaptContext* context_;
- ResourceTable* master_table_;
+ ResourceTable* main_table_;
TableMergerOptions options_;
- ResourceTablePackage* master_package_;
+ ResourceTablePackage* main_package_;
std::set<std::string> merged_packages_;
bool MergeImpl(const Source& src, ResourceTable* src_table, bool overlay, bool allow_new);
diff --git a/tools/aapt2/link/XmlCompatVersioner.cpp b/tools/aapt2/link/XmlCompatVersioner.cpp
index 20ebdc696814..6937ca961f06 100644
--- a/tools/aapt2/link/XmlCompatVersioner.cpp
+++ b/tools/aapt2/link/XmlCompatVersioner.cpp
@@ -143,8 +143,8 @@ std::vector<std::unique_ptr<xml::XmlResource>> XmlCompatVersioner::Process(
// Iterate from smallest to largest API version.
for (ApiVersion api : apis_referenced) {
- std::set<ApiVersion> dummy;
- versioned_docs.push_back(ProcessDoc(api, api_range.end, doc, &dummy));
+ std::set<ApiVersion> tmp;
+ versioned_docs.push_back(ProcessDoc(api, api_range.end, doc, &tmp));
}
return versioned_docs;
}
diff --git a/tools/aapt2/optimize/ResourceFilter.cpp b/tools/aapt2/optimize/ResourceFilter.cpp
index 250b65197a7d..08c045bf68f7 100644
--- a/tools/aapt2/optimize/ResourceFilter.cpp
+++ b/tools/aapt2/optimize/ResourceFilter.cpp
@@ -20,8 +20,8 @@
namespace aapt {
-ResourceFilter::ResourceFilter(const std::unordered_set<ResourceName>& blacklist)
- : blacklist_(blacklist) {
+ResourceFilter::ResourceFilter(const std::unordered_set<ResourceName>& exclude_list)
+ : exclude_list_(exclude_list) {
}
bool ResourceFilter::Consume(IAaptContext* context, ResourceTable* table) {
@@ -29,7 +29,7 @@ bool ResourceFilter::Consume(IAaptContext* context, ResourceTable* table) {
for (auto& type : package->types) {
for (auto it = type->entries.begin(); it != type->entries.end(); ) {
ResourceName resource = ResourceName({}, type->type, (*it)->name);
- if (blacklist_.find(resource) != blacklist_.end()) {
+ if (exclude_list_.find(resource) != exclude_list_.end()) {
it = type->entries.erase(it);
} else {
++it;
diff --git a/tools/aapt2/optimize/ResourceFilter.h b/tools/aapt2/optimize/ResourceFilter.h
index d4baf654b0ff..a2645333e497 100644
--- a/tools/aapt2/optimize/ResourceFilter.h
+++ b/tools/aapt2/optimize/ResourceFilter.h
@@ -25,16 +25,16 @@
namespace aapt {
-// Removes non-whitelisted entries from resource table.
+// Removes exclude-listed entries from resource table.
class ResourceFilter : public IResourceTableConsumer {
public:
- explicit ResourceFilter(const std::unordered_set<ResourceName>& blacklist);
+ explicit ResourceFilter(const std::unordered_set<ResourceName>& exclude_list);
bool Consume(IAaptContext* context, ResourceTable* table) override;
private:
DISALLOW_COPY_AND_ASSIGN(ResourceFilter);
- std::unordered_set<ResourceName> blacklist_;
+ std::unordered_set<ResourceName> exclude_list_;
};
} // namespace aapt
diff --git a/tools/aapt2/optimize/ResourceFilter_test.cpp b/tools/aapt2/optimize/ResourceFilter_test.cpp
index ef57f9c56dab..34d8fd280fa9 100644
--- a/tools/aapt2/optimize/ResourceFilter_test.cpp
+++ b/tools/aapt2/optimize/ResourceFilter_test.cpp
@@ -31,22 +31,22 @@ TEST(ResourceFilterTest, SomeValuesAreFilteredOut) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .AddString("android:string/notblacklisted", ResourceId{}, default_config, "value")
- .AddString("android:string/blacklisted", ResourceId{}, default_config, "value")
- .AddString("android:string/notblacklisted2", ResourceId{}, default_config, "value")
- .AddString("android:string/blacklisted2", ResourceId{}, default_config, "value")
+ .AddString("android:string/notexclude_listed", ResourceId{}, default_config, "value")
+ .AddString("android:string/exclude_listed", ResourceId{}, default_config, "value")
+ .AddString("android:string/notexclude_listed2", ResourceId{}, default_config, "value")
+ .AddString("android:string/exclude_listed2", ResourceId{}, default_config, "value")
.Build();
- std::unordered_set<ResourceName> blacklist = {
- ResourceName({}, ResourceType::kString, "blacklisted"),
- ResourceName({}, ResourceType::kString, "blacklisted2"),
+ std::unordered_set<ResourceName> exclude_list = {
+ ResourceName({}, ResourceType::kString, "exclude_listed"),
+ ResourceName({}, ResourceType::kString, "exclude_listed2"),
};
- ASSERT_TRUE(ResourceFilter(blacklist).Consume(context.get(), table.get()));
- EXPECT_THAT(table, HasValue("android:string/notblacklisted", default_config));
- EXPECT_THAT(table, HasValue("android:string/notblacklisted2", default_config));
- EXPECT_THAT(table, Not(HasValue("android:string/blacklisted", default_config)));
- EXPECT_THAT(table, Not(HasValue("android:string/blacklisted2", default_config)));
+ ASSERT_TRUE(ResourceFilter(exclude_list).Consume(context.get(), table.get()));
+ EXPECT_THAT(table, HasValue("android:string/notexclude_listed", default_config));
+ EXPECT_THAT(table, HasValue("android:string/notexclude_listed2", default_config));
+ EXPECT_THAT(table, Not(HasValue("android:string/exclude_listed", default_config)));
+ EXPECT_THAT(table, Not(HasValue("android:string/exclude_listed2", default_config)));
}
TEST(ResourceFilterTest, TypeIsCheckedBeforeFiltering) {
@@ -55,21 +55,21 @@ TEST(ResourceFilterTest, TypeIsCheckedBeforeFiltering) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
- .AddString("android:string/notblacklisted", ResourceId{}, default_config, "value")
- .AddString("android:string/blacklisted", ResourceId{}, default_config, "value")
- .AddString("android:drawable/notblacklisted", ResourceId{}, default_config, "value")
- .AddString("android:drawable/blacklisted", ResourceId{}, default_config, "value")
+ .AddString("android:string/notexclude_listed", ResourceId{}, default_config, "value")
+ .AddString("android:string/exclude_listed", ResourceId{}, default_config, "value")
+ .AddString("android:drawable/notexclude_listed", ResourceId{}, default_config, "value")
+ .AddString("android:drawable/exclude_listed", ResourceId{}, default_config, "value")
.Build();
- std::unordered_set<ResourceName> blacklist = {
- ResourceName({}, ResourceType::kString, "blacklisted"),
+ std::unordered_set<ResourceName> exclude_list = {
+ ResourceName({}, ResourceType::kString, "exclude_listed"),
};
- ASSERT_TRUE(ResourceFilter(blacklist).Consume(context.get(), table.get()));
- EXPECT_THAT(table, HasValue("android:string/notblacklisted", default_config));
- EXPECT_THAT(table, HasValue("android:drawable/blacklisted", default_config));
- EXPECT_THAT(table, HasValue("android:drawable/notblacklisted", default_config));
- EXPECT_THAT(table, Not(HasValue("android:string/blacklisted", default_config)));
+ ASSERT_TRUE(ResourceFilter(exclude_list).Consume(context.get(), table.get()));
+ EXPECT_THAT(table, HasValue("android:string/notexclude_listed", default_config));
+ EXPECT_THAT(table, HasValue("android:drawable/exclude_listed", default_config));
+ EXPECT_THAT(table, HasValue("android:drawable/notexclude_listed", default_config));
+ EXPECT_THAT(table, Not(HasValue("android:string/exclude_listed", default_config)));
}
} // namespace aapt
diff --git a/tools/aapt2/test/Common.cpp b/tools/aapt2/test/Common.cpp
index b54c155ddc2f..23c22185a53f 100644
--- a/tools/aapt2/test/Common.cpp
+++ b/tools/aapt2/test/Common.cpp
@@ -21,7 +21,7 @@ using android::ConfigDescription;
namespace aapt {
namespace test {
-struct DummyDiagnosticsImpl : public IDiagnostics {
+struct TestDiagnosticsImpl : public IDiagnostics {
void Log(Level level, DiagMessageActual& actual_msg) override {
switch (level) {
case Level::Note:
@@ -39,7 +39,7 @@ struct DummyDiagnosticsImpl : public IDiagnostics {
};
IDiagnostics* GetDiagnostics() {
- static DummyDiagnosticsImpl diag;
+ static TestDiagnosticsImpl diag;
return &diag;
}
diff --git a/tools/aapt2/trace/TraceBuffer.h b/tools/aapt2/trace/TraceBuffer.h
index 8618e0eeb731..ba751dd72f41 100644
--- a/tools/aapt2/trace/TraceBuffer.h
+++ b/tools/aapt2/trace/TraceBuffer.h
@@ -40,7 +40,7 @@ public:
void BeginTrace(const std::string& tag);
void EndTrace();
-// A master trace is required to flush events to disk. Events are formatted in systrace
+// A main trace is required to flush events to disk. Events are formatted in systrace
// json format.
class FlushTrace {
public:
diff --git a/tools/aapt2/util/Maybe_test.cpp b/tools/aapt2/util/Maybe_test.cpp
index 2057ddcc9e45..4c921f13a3ca 100644
--- a/tools/aapt2/util/Maybe_test.cpp
+++ b/tools/aapt2/util/Maybe_test.cpp
@@ -22,32 +22,32 @@
namespace aapt {
-struct Dummy {
- Dummy() {
+struct Fake {
+ Fake() {
data = new int;
*data = 1;
- std::cerr << "Construct Dummy{0x" << (void*)this << "} with data=0x"
+ std::cerr << "Construct Fake{0x" << (void*)this << "} with data=0x"
<< (void*)data << std::endl;
}
- Dummy(const Dummy& rhs) {
+ Fake(const Fake& rhs) {
data = nullptr;
if (rhs.data) {
data = new int;
*data = *rhs.data;
}
- std::cerr << "CopyConstruct Dummy{0x" << (void*)this << "} from Dummy{0x"
+ std::cerr << "CopyConstruct Fake{0x" << (void*)this << "} from Fake{0x"
<< (const void*)&rhs << "}" << std::endl;
}
- Dummy(Dummy&& rhs) {
+ Fake(Fake&& rhs) {
data = rhs.data;
rhs.data = nullptr;
- std::cerr << "MoveConstruct Dummy{0x" << (void*)this << "} from Dummy{0x"
+ std::cerr << "MoveConstruct Fake{0x" << (void*)this << "} from Fake{0x"
<< (const void*)&rhs << "}" << std::endl;
}
- Dummy& operator=(const Dummy& rhs) {
+ Fake& operator=(const Fake& rhs) {
delete data;
data = nullptr;
@@ -55,22 +55,22 @@ struct Dummy {
data = new int;
*data = *rhs.data;
}
- std::cerr << "CopyAssign Dummy{0x" << (void*)this << "} from Dummy{0x"
+ std::cerr << "CopyAssign Fake{0x" << (void*)this << "} from Fake{0x"
<< (const void*)&rhs << "}" << std::endl;
return *this;
}
- Dummy& operator=(Dummy&& rhs) {
+ Fake& operator=(Fake&& rhs) {
delete data;
data = rhs.data;
rhs.data = nullptr;
- std::cerr << "MoveAssign Dummy{0x" << (void*)this << "} from Dummy{0x"
+ std::cerr << "MoveAssign Fake{0x" << (void*)this << "} from Fake{0x"
<< (const void*)&rhs << "}" << std::endl;
return *this;
}
- ~Dummy() {
- std::cerr << "Destruct Dummy{0x" << (void*)this << "} with data=0x"
+ ~Fake() {
+ std::cerr << "Destruct Fake{0x" << (void*)this << "} with data=0x"
<< (void*)data << std::endl;
delete data;
}
@@ -100,15 +100,15 @@ TEST(MaybeTest, MakeSomething) {
}
TEST(MaybeTest, Lifecycle) {
- Maybe<Dummy> val = make_nothing<Dummy>();
+ Maybe<Fake> val = make_nothing<Fake>();
- Maybe<Dummy> val2 = make_value(Dummy());
+ Maybe<Fake> val2 = make_value(Fake());
}
TEST(MaybeTest, MoveAssign) {
- Maybe<Dummy> val;
+ Maybe<Fake> val;
{
- Maybe<Dummy> val2 = Dummy();
+ Maybe<Fake> val2 = Fake();
val = std::move(val2);
}
}
diff --git a/tools/aapt2/xml/XmlActionExecutor.cpp b/tools/aapt2/xml/XmlActionExecutor.cpp
index cb844f085ecc..fab17c949dd8 100644
--- a/tools/aapt2/xml/XmlActionExecutor.cpp
+++ b/tools/aapt2/xml/XmlActionExecutor.cpp
@@ -74,11 +74,11 @@ bool XmlNodeAction::Execute(XmlActionExecutorPolicy policy, std::vector<StringPi
for (const StringPiece& element : *bread_crumb) {
error_msg << "<" << element << ">";
}
- if (policy == XmlActionExecutorPolicy::kWhitelistWarning) {
+ if (policy == XmlActionExecutorPolicy::kAllowListWarning) {
// Treat the error only as a warning.
diag->Warn(error_msg);
} else {
- // Policy is XmlActionExecutorPolicy::kWhitelist, we should fail.
+ // Policy is XmlActionExecutorPolicy::kAllowList, we should fail.
diag->Error(error_msg);
error = true;
}
@@ -94,7 +94,7 @@ bool XmlActionExecutor::Execute(XmlActionExecutorPolicy policy, IDiagnostics* di
Element* el = doc->root.get();
if (!el) {
- if (policy == XmlActionExecutorPolicy::kWhitelist) {
+ if (policy == XmlActionExecutorPolicy::kAllowList) {
source_diag.Error(DiagMessage() << "no root XML tag found");
return false;
}
@@ -109,7 +109,7 @@ bool XmlActionExecutor::Execute(XmlActionExecutorPolicy policy, IDiagnostics* di
return iter->second.Execute(policy, &bread_crumb, &source_diag, el);
}
- if (policy == XmlActionExecutorPolicy::kWhitelist) {
+ if (policy == XmlActionExecutorPolicy::kAllowList) {
DiagMessage error_msg(el->line_number);
error_msg << "unexpected root element ";
PrintElementToDiagMessage(el, &error_msg);
diff --git a/tools/aapt2/xml/XmlActionExecutor.h b/tools/aapt2/xml/XmlActionExecutor.h
index f689b2a3eaa8..a0ad1dadeddf 100644
--- a/tools/aapt2/xml/XmlActionExecutor.h
+++ b/tools/aapt2/xml/XmlActionExecutor.h
@@ -37,12 +37,12 @@ enum class XmlActionExecutorPolicy {
// The actions defined must match and run. If an element is found that does not match an action,
// an error occurs.
// Note: namespaced elements are always ignored.
- kWhitelist,
+ kAllowList,
// The actions defined should match and run. if an element is found that does not match an
// action, a warning is printed.
// Note: namespaced elements are always ignored.
- kWhitelistWarning,
+ kAllowListWarning,
};
// Contains the actions to perform at this XML node. This is a recursive data structure that
diff --git a/tools/aapt2/xml/XmlActionExecutor_test.cpp b/tools/aapt2/xml/XmlActionExecutor_test.cpp
index d39854e5fe4e..d47b49590f5c 100644
--- a/tools/aapt2/xml/XmlActionExecutor_test.cpp
+++ b/tools/aapt2/xml/XmlActionExecutor_test.cpp
@@ -60,10 +60,10 @@ TEST(XmlActionExecutorTest, FailsWhenUndefinedHierarchyExists) {
StdErrDiagnostics diag;
doc = test::BuildXmlDom("<manifest><application /><activity /></manifest>");
- ASSERT_FALSE(executor.Execute(XmlActionExecutorPolicy::kWhitelist, &diag, doc.get()));
+ ASSERT_FALSE(executor.Execute(XmlActionExecutorPolicy::kAllowList, &diag, doc.get()));
doc = test::BuildXmlDom("<manifest><application><activity /></application></manifest>");
- ASSERT_FALSE(executor.Execute(XmlActionExecutorPolicy::kWhitelist, &diag, doc.get()));
+ ASSERT_FALSE(executor.Execute(XmlActionExecutorPolicy::kAllowList, &diag, doc.get()));
}
} // namespace xml
diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh
index f25fcdcb7479..99aaa3c4d6e5 100755
--- a/tools/aosp/aosp_sha.sh
+++ b/tools/aosp/aosp_sha.sh
@@ -11,7 +11,7 @@ else
if (( count == 0 )); then
echo
fi
- echo -e "\033[0;31mThe source of truth for '$file' is in AOSP.\033[0m"
+ echo -e "\033[0;31;47mThe source of truth for '$file' is in AOSP.\033[0m"
(( count++ ))
done < <(git show --name-only --pretty=format: $1 | grep -- "$2")
if (( count != 0 )); then
diff --git a/tools/codegen/src/com/android/codegen/ClassInfo.kt b/tools/codegen/src/com/android/codegen/ClassInfo.kt
index bf95a2eb2193..056898c9eca1 100644
--- a/tools/codegen/src/com/android/codegen/ClassInfo.kt
+++ b/tools/codegen/src/com/android/codegen/ClassInfo.kt
@@ -1,12 +1,14 @@
package com.android.codegen
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration
+import com.github.javaparser.ast.body.TypeDeclaration
open class ClassInfo(val classAst: ClassOrInterfaceDeclaration, val fileInfo: FileInfo) {
val fileAst = fileInfo.fileAst
val nestedClasses = classAst.members.filterIsInstance<ClassOrInterfaceDeclaration>()
+ val nestedTypes = classAst.members.filterIsInstance<TypeDeclaration<*>>()
val superInterfaces = classAst.implementedTypes.map { it.asString() }
val superClass = classAst.extendedTypes.getOrNull(0)
diff --git a/tools/codegen/src/com/android/codegen/Debug.kt b/tools/codegen/src/com/android/codegen/Debug.kt
new file mode 100644
index 000000000000..de3184468540
--- /dev/null
+++ b/tools/codegen/src/com/android/codegen/Debug.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package com.android.codegen
+
+import com.github.javaparser.ast.Node
+
+fun Node.dump(indent: String = ""): String {
+ return buildString {
+ append(indent)
+ appendln(dumpOneLineNoChildren())
+ childNodes.forEach { child ->
+ append(child.dump(indent + " "))
+ }
+ }
+}
+
+private fun Node.dumpOneLineNoChildren(): String {
+ val node = this
+ return buildString {
+ append(node::class.java.simpleName)
+ if (childNodes.isEmpty()) {
+ append(": ").append(node.toString())
+ }
+ }
+}
diff --git a/tools/codegen/src/com/android/codegen/FileInfo.kt b/tools/codegen/src/com/android/codegen/FileInfo.kt
index 909472640f29..a1d0389b0041 100644
--- a/tools/codegen/src/com/android/codegen/FileInfo.kt
+++ b/tools/codegen/src/com/android/codegen/FileInfo.kt
@@ -272,7 +272,7 @@ class FileInfo(
/** Debug info */
fun summary(): String = when(this) {
is Code -> "${javaClass.simpleName}(${lines.size} lines): ${lines.getOrNull(0)?.take(70) ?: ""}..."
- is DataClass -> "DataClass ${ast.nameAsString}:\n" +
+ is DataClass -> "DataClass ${ast.nameAsString} nested:${ast.nestedTypes.map { it.nameAsString }}:\n" +
chunks.joinToString("\n") { it.summary() } +
"\n//end ${ast.nameAsString}"
}
diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt
index 5a96cf1d9bdb..6c6d011cfede 100644
--- a/tools/codegen/src/com/android/codegen/Generators.kt
+++ b/tools/codegen/src/com/android/codegen/Generators.kt
@@ -3,7 +3,10 @@ package com.android.codegen
import com.github.javaparser.ast.body.FieldDeclaration
import com.github.javaparser.ast.body.MethodDeclaration
import com.github.javaparser.ast.body.VariableDeclarator
-import com.github.javaparser.ast.expr.*
+import com.github.javaparser.ast.expr.AnnotationExpr
+import com.github.javaparser.ast.expr.ArrayInitializerExpr
+import com.github.javaparser.ast.expr.LiteralExpr
+import com.github.javaparser.ast.expr.UnaryExpr
import java.io.File
@@ -13,10 +16,7 @@ import java.io.File
fun ClassPrinter.generateConstDefs() {
val consts = classAst.fields.filter {
it.isStatic && it.isFinal && it.variables.all { variable ->
- val initializer = variable.initializer.orElse(null)
- val isLiteral = initializer is LiteralExpr
- || (initializer is UnaryExpr && initializer.expression is LiteralExpr)
- isLiteral && variable.type.asString() in listOf("int", "String")
+ variable.type.asString() in listOf("int", "String")
} && it.annotations.none { it.nameAsString == DataClassSuppressConstDefs }
}.flatMap { field -> field.variables.map { it to field } }
val intConsts = consts.filter { it.first.type.asString() == "int" }
@@ -703,7 +703,7 @@ fun ClassPrinter.generateSetters() {
generateFieldJavadoc(forceHide = FeatureFlag.SETTERS.hidden)
+GENERATED_MEMBER_HEADER
- "public $ClassType set$NameUpperCamel($annotatedTypeForSetterParam value)" {
+ "public @$NonNull $ClassType set$NameUpperCamel($annotatedTypeForSetterParam value)" {
generateSetFrom("value")
+"return this;"
}
diff --git a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
index d6953c00fc0b..83108e5ae109 100644
--- a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
+++ b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
@@ -41,7 +41,10 @@ private fun ClassPrinter.generateInputSignaturesForClass(classAst: ClassOrInterf
}
} + ("class ${classAst.nameAsString}" +
" extends ${classAst.extendedTypes.map { getFullClassName(it) }.ifEmpty { listOf("java.lang.Object") }.joinToString(", ")}" +
- " implements [${classAst.implementedTypes.joinToString(", ") { getFullClassName(it) }}]")
+ " implements [${classAst.implementedTypes.joinToString(", ") { getFullClassName(it) }}]") +
+ classAst.nestedNonDataClasses.flatMap { nestedClass ->
+ generateInputSignaturesForClass(nestedClass)
+ }
}
private fun ClassPrinter.annotationsToString(annotatedAst: NodeWithAnnotations<*>): String {
@@ -60,6 +63,7 @@ private fun ClassPrinter.annotationToString(ann: AnnotationExpr?): String {
append("@")
append(getFullClassName(ann.nameAsString))
if (ann is MarkerAnnotationExpr) return@buildString
+ if (!ann.nameAsString.startsWith("DataClass")) return@buildString
append("(")
@@ -125,7 +129,7 @@ private fun ClassPrinter.getFullClassName(className: String): String {
if (classAst.nameAsString == className) return thisPackagePrefix + classAst.nameAsString
- nestedClasses.find {
+ nestedTypes.find {
it.nameAsString == className
}?.let { return thisClassPrefix + it.nameAsString }
@@ -141,6 +145,8 @@ private fun ClassPrinter.getFullClassName(className: String): String {
if (className[0].isLowerCase()) return className //primitive
+ if (className[0] == '?') return className //wildcard
+
return thisPackagePrefix + className
}
diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt
index 6f740cd663e3..2e176c3d3bec 100644
--- a/tools/codegen/src/com/android/codegen/SharedConstants.kt
+++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt
@@ -1,7 +1,7 @@
package com.android.codegen
const val CODEGEN_NAME = "codegen"
-const val CODEGEN_VERSION = "1.0.15"
+const val CODEGEN_VERSION = "1.0.20"
const val CANONICAL_BUILDER_CLASS = "Builder"
const val BASE_BUILDER_CLASS = "BaseBuilder"
diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt
index c19ae3b0b11f..7cfa7847fcff 100644
--- a/tools/codegen/src/com/android/codegen/Utils.kt
+++ b/tools/codegen/src/com/android/codegen/Utils.kt
@@ -103,6 +103,10 @@ val TypeDeclaration<*>.nestedTypes get() = childNodes.filterIsInstance<TypeDecla
val TypeDeclaration<*>.nestedDataClasses get()
= nestedTypes.filterIsInstance<ClassOrInterfaceDeclaration>()
.filter { it.annotations.any { it.nameAsString.endsWith("DataClass") } }
+val TypeDeclaration<*>.nestedNonDataClasses get()
+ = nestedTypes.filterIsInstance<ClassOrInterfaceDeclaration>()
+ .filter { it.annotations.none { it.nameAsString.endsWith("DataClass") } }
+ .filterNot { it.isInterface }
val TypeDeclaration<*>.startLine get() = range.get()!!.begin.line
inline fun <T> List<T>.forEachSequentialPair(action: (T, T?) -> Unit) {
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index a4a315b7e371..f0b759547a93 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -316,20 +316,25 @@ def get_emoji_font():
def check_emoji_font_coverage(emoji_font, all_emoji, equivalent_emoji):
coverage = get_emoji_map(emoji_font)
+
+ errors = []
+
for sequence in all_emoji:
- assert sequence in coverage, (
- '%s is not supported in the emoji font.' % printable(sequence))
+ if not sequence in coverage:
+ errors.append('%s is not supported in the emoji font.' % printable(sequence))
for sequence in coverage:
if sequence in {0x0000, 0x000D, 0x0020}:
# The font needs to support a few extra characters, which is OK
continue
- assert sequence in all_emoji, (
- 'Emoji font should not support %s.' % printable(sequence))
+ if sequence not in all_emoji:
+ errors.append('%s support unexpected in the emoji font.' % printable(sequence))
for first, second in equivalent_emoji.items():
- assert coverage[first] == coverage[second], (
- '%s and %s should map to the same glyph.' % (
+ if first not in coverage or second not in coverage:
+ continue # sequence will be reported missing
+ if coverage[first] != coverage[second]:
+ errors.append('%s and %s should map to the same glyph.' % (
printable(first),
printable(second)))
@@ -344,11 +349,13 @@ def check_emoji_font_coverage(emoji_font, all_emoji, equivalent_emoji):
while equivalent_seq in equivalent_emoji:
equivalent_seq = equivalent_emoji[equivalent_seq]
equivalent_seqs.add(equivalent_seq)
- assert len(equivalent_seqs) == 1, (
- 'The sequences %s should not result in the same glyph %s' % (
+ if len(equivalent_seqs) != 1:
+ errors.append('The sequences %s should not result in the same glyph %s' % (
printable(equivalent_seqs),
glyph))
+ assert not errors, '%d emoji font errors:\n%s\n%d emoji font coverage errors' % (len(errors), '\n'.join(errors), len(errors))
+
def check_emoji_defaults(default_emoji):
missing_text_chars = _emoji_properties['Emoji'] - default_emoji
diff --git a/tools/powerstats/Android.bp b/tools/powerstats/Android.bp
new file mode 100644
index 000000000000..af41144167a9
--- /dev/null
+++ b/tools/powerstats/Android.bp
@@ -0,0 +1,10 @@
+java_binary_host {
+ name: "PowerStatsServiceProtoParser",
+ manifest: "PowerStatsServiceProtoParser_manifest.txt",
+ srcs: [
+ "*.java",
+ ],
+ static_libs: [
+ "platformprotos",
+ ],
+}
diff --git a/tools/powerstats/PowerStatsServiceProtoParser.java b/tools/powerstats/PowerStatsServiceProtoParser.java
new file mode 100644
index 000000000000..76edd6341b55
--- /dev/null
+++ b/tools/powerstats/PowerStatsServiceProtoParser.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package com.android.server.powerstats;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * This class implements a utility to parse ODPM data out
+ * of incident reports contained in bugreports. The data
+ * is output to STDOUT in csv format.
+ */
+public class PowerStatsServiceProtoParser {
+ private static void printEnergyMeterInfo(PowerStatsServiceMeterProto proto) {
+ String csvHeader = new String();
+ for (int i = 0; i < proto.getChannelInfoCount(); i++) {
+ ChannelInfoProto energyMeterInfo = proto.getChannelInfo(i);
+ csvHeader += "Index,Timestamp," + energyMeterInfo.getChannelId()
+ + "/" + energyMeterInfo.getChannelName() + ",";
+ }
+ System.out.println(csvHeader);
+ }
+
+ private static void printEnergyMeasurements(PowerStatsServiceMeterProto proto) {
+ int energyMeterInfoCount = proto.getChannelInfoCount();
+
+ if (energyMeterInfoCount > 0) {
+ int energyMeasurementCount = proto.getEnergyMeasurementCount();
+ int energyMeasurementSetCount = energyMeasurementCount / energyMeterInfoCount;
+
+ for (int i = 0; i < energyMeasurementSetCount; i++) {
+ String csvRow = new String();
+ for (int j = 0; j < energyMeterInfoCount; j++) {
+ EnergyMeasurementProto energyMeasurement =
+ proto.getEnergyMeasurement(i * energyMeterInfoCount + j);
+ csvRow += energyMeasurement.getChannelId() + ","
+ + energyMeasurement.getTimestampMs() + ","
+ + energyMeasurement.getEnergyUws() + ",";
+ }
+ System.out.println(csvRow);
+ }
+ } else {
+ System.out.println("Error: energyMeterInfoCount is zero");
+ }
+ }
+
+ private static void printEnergyConsumerId(PowerStatsServiceModelProto proto) {
+ String csvHeader = new String();
+ for (int i = 0; i < proto.getEnergyConsumerIdCount(); i++) {
+ EnergyConsumerIdProto energyConsumerId = proto.getEnergyConsumerId(i);
+ csvHeader += "Index,Timestamp," + energyConsumerId.getEnergyConsumerId() + ",";
+ }
+ System.out.println(csvHeader);
+ }
+
+ private static void printEnergyConsumerResults(PowerStatsServiceModelProto proto) {
+ int energyConsumerIdCount = proto.getEnergyConsumerIdCount();
+
+ if (energyConsumerIdCount > 0) {
+ int energyConsumerResultCount = proto.getEnergyConsumerResultCount();
+ int energyConsumerResultSetCount = energyConsumerResultCount / energyConsumerIdCount;
+
+ for (int i = 0; i < energyConsumerResultSetCount; i++) {
+ String csvRow = new String();
+ for (int j = 0; j < energyConsumerIdCount; j++) {
+ EnergyConsumerResultProto energyConsumerResult =
+ proto.getEnergyConsumerResult(i * energyConsumerIdCount + j);
+ csvRow += energyConsumerResult.getEnergyConsumerId() + ","
+ + energyConsumerResult.getTimestampMs() + ","
+ + energyConsumerResult.getEnergyUws() + ",";
+ }
+ System.out.println(csvRow);
+ }
+ } else {
+ System.out.println("Error: energyConsumerIdCount is zero");
+ }
+ }
+
+ private static void generateCsvFile(String pathToIncidentReport) {
+ try {
+ // Print power meter data.
+ IncidentReportMeterProto irMeterProto =
+ IncidentReportMeterProto.parseFrom(new FileInputStream(pathToIncidentReport));
+
+ if (irMeterProto.hasIncidentReport()) {
+ PowerStatsServiceMeterProto pssMeterProto = irMeterProto.getIncidentReport();
+ printEnergyMeterInfo(pssMeterProto);
+ printEnergyMeasurements(pssMeterProto);
+ } else {
+ System.out.println("Meter incident report not found. Exiting.");
+ }
+
+ // Print power model data.
+ IncidentReportModelProto irModelProto =
+ IncidentReportModelProto.parseFrom(new FileInputStream(pathToIncidentReport));
+
+ if (irModelProto.hasIncidentReport()) {
+ PowerStatsServiceModelProto pssModelProto = irModelProto.getIncidentReport();
+ printEnergyConsumerId(pssModelProto);
+ printEnergyConsumerResults(pssModelProto);
+ } else {
+ System.out.println("Model incident report not found. Exiting.");
+ }
+ } catch (IOException e) {
+ System.out.println("Unable to open incident report file: " + pathToIncidentReport);
+ System.out.println(e);
+ }
+ }
+
+ /**
+ * This is the entry point to parse the ODPM data out of incident reports.
+ * It requires one argument which is the path to the incident_report.proto
+ * file captured in a bugreport.
+ *
+ * @param args Path to incident_report.proto passed in from command line.
+ */
+ public static void main(String[] args) {
+ if (args.length > 0) {
+ generateCsvFile(args[0]);
+ } else {
+ System.err.println("Usage: PowerStatsServiceProtoParser <incident_report.proto>");
+ System.err.println("Missing path to incident_report.proto. Exiting.");
+ System.exit(1);
+ }
+ }
+}
diff --git a/tools/powerstats/PowerStatsServiceProtoParser_manifest.txt b/tools/powerstats/PowerStatsServiceProtoParser_manifest.txt
new file mode 100644
index 000000000000..5df12118ce80
--- /dev/null
+++ b/tools/powerstats/PowerStatsServiceProtoParser_manifest.txt
@@ -0,0 +1 @@
+Main-class: com.android.server.powerstats.PowerStatsServiceProtoParser
diff --git a/tools/processors/intdef_mappings/Android.bp b/tools/processors/intdef_mappings/Android.bp
new file mode 100644
index 000000000000..e255f7c784d3
--- /dev/null
+++ b/tools/processors/intdef_mappings/Android.bp
@@ -0,0 +1,33 @@
+java_plugin {
+ name: "intdef-annotation-processor",
+
+ processor_class: "android.processor.IntDefProcessor",
+
+ srcs: [
+ ":framework-annotations",
+ "src/**/*.java",
+ "src/**/*.kt"
+ ],
+
+ use_tools_jar: true,
+}
+
+java_test_host {
+ name: "intdef-annotation-processor-test",
+
+ srcs: [
+ "test/**/*.java",
+ "test/**/*.kt"
+ ],
+ java_resource_dirs: ["test/resources"],
+
+ static_libs: [
+ "compile-testing-prebuilt",
+ "truth-prebuilt",
+ "junit",
+ "guava",
+ "intdef-annotation-processor"
+ ],
+
+ test_suites: ["general-tests"],
+} \ No newline at end of file
diff --git a/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt
new file mode 100644
index 000000000000..84faeea36eea
--- /dev/null
+++ b/tools/processors/intdef_mappings/src/android/processor/IntDefProcessor.kt
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package android.processor
+
+import android.annotation.IntDef
+import com.sun.source.tree.IdentifierTree
+import com.sun.source.tree.MemberSelectTree
+import com.sun.source.tree.NewArrayTree
+import com.sun.source.util.SimpleTreeVisitor
+import com.sun.source.util.Trees
+import java.io.IOException
+import java.io.Writer
+import javax.annotation.processing.AbstractProcessor
+import javax.annotation.processing.RoundEnvironment
+import javax.lang.model.SourceVersion
+import javax.lang.model.element.AnnotationValue
+import javax.lang.model.element.TypeElement
+import javax.tools.Diagnostic.Kind
+import javax.tools.StandardLocation.CLASS_OUTPUT
+import kotlin.collections.set
+
+
+/**
+ * The IntDefProcessor is intended to generate a mapping from ints to their respective string
+ * identifier for each IntDef for use by Winscope or any other tool which requires such a mapping.
+ *
+ * The processor will run when building :frameworks-all and dump all the IntDef mappings found the
+ * files the make up :frameworks-all as json to outputPath.
+ */
+class IntDefProcessor : AbstractProcessor() {
+ private val outputName = "intDefMapping.json"
+
+ override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latest()
+
+ // Define what the annotation we care about are for compiler optimization
+ override fun getSupportedAnnotationTypes() = LinkedHashSet<String>().apply {
+ add(IntDef::class.java.name)
+ }
+
+ override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
+ // There should only be one matching annotation definition for intDef
+ val annotationType = annotations.firstOrNull() ?: return false
+ val annotatedElements = roundEnv.getElementsAnnotatedWith(annotationType)
+
+ val annotationTypeToIntDefMapping = annotatedElements.associate { annotatedElement ->
+ val type = (annotatedElement as TypeElement).qualifiedName.toString()
+ val mapping = generateIntDefMapping(annotatedElement, annotationType)
+ val intDef = annotatedElement.getAnnotation(IntDef::class.java)
+ type to IntDefMapping(mapping, intDef.flag)
+ }
+
+ try {
+ outputToFile(annotationTypeToIntDefMapping)
+ } catch (e: IOException) {
+ error("Failed to write IntDef mappings :: $e")
+ }
+ return false
+ }
+
+ private fun generateIntDefMapping(
+ annotatedElement: TypeElement,
+ annotationType: TypeElement
+ ): Map<Int, String> {
+ // LinkedHashMap makes sure ordering is the same as in the code
+ val mapping = LinkedHashMap<Int, String>()
+
+ val annotationMirror = annotatedElement.annotationMirrors
+ // Should only ever be one matching this condition
+ .first { it.annotationType.asElement() == annotationType }
+
+ val value = annotationMirror.elementValues.entries
+ .first { entry -> entry.key.simpleName.contentEquals("value") }
+ .value
+
+ val trees = Trees.instance(processingEnv)
+ val tree = trees.getTree(annotatedElement, annotationMirror, value)
+
+ val identifiers = ArrayList<String>()
+ tree.accept(IdentifierVisitor(), identifiers)
+
+ val values = value.value as List<AnnotationValue>
+
+ for (i in identifiers.indices) {
+ mapping[values[i].value as Int] = identifiers[i]
+ }
+
+ return mapping
+ }
+
+ private class IdentifierVisitor : SimpleTreeVisitor<Void, ArrayList<String>>() {
+ override fun visitNewArray(node: NewArrayTree, indentifiers: ArrayList<String>): Void? {
+ for (initializer in node.initializers) {
+ initializer.accept(this, indentifiers)
+ }
+
+ return null
+ }
+
+ override fun visitMemberSelect(node: MemberSelectTree, indentifiers: ArrayList<String>):
+ Void? {
+ indentifiers.add(node.identifier.toString())
+
+ return null
+ }
+
+ override fun visitIdentifier(node: IdentifierTree, indentifiers: ArrayList<String>): Void? {
+ indentifiers.add(node.name.toString())
+
+ return null
+ }
+ }
+
+ @Throws(IOException::class)
+ private fun outputToFile(annotationTypeToIntDefMapping: Map<String, IntDefMapping>) {
+ val resource = processingEnv.filer.createResource(
+ CLASS_OUTPUT, "com.android.winscope", outputName)
+ val writer = resource.openWriter()
+ serializeTo(annotationTypeToIntDefMapping, writer)
+ writer.close()
+ }
+
+ private fun error(message: String) {
+ processingEnv.messager.printMessage(Kind.ERROR, message)
+ }
+
+ private fun note(message: String) {
+ processingEnv.messager.printMessage(Kind.NOTE, message)
+ }
+
+ class IntDefMapping(val mapping: Map<Int, String>, val flag: Boolean) {
+ val size
+ get() = this.mapping.size
+
+ val entries
+ get() = this.mapping.entries
+ }
+
+ companion object {
+ fun serializeTo(
+ annotationTypeToIntDefMapping: Map<String, IntDefMapping>,
+ writer: Writer
+ ) {
+ val indent = " "
+
+ writer.appendln("{")
+
+ val intDefTypesCount = annotationTypeToIntDefMapping.size
+ var currentIntDefTypesCount = 0
+ for ((field, intDefMapping) in annotationTypeToIntDefMapping) {
+ writer.appendln("""$indent"$field": {""")
+
+ // Start IntDef
+
+ writer.appendln("""$indent$indent"flag": ${intDefMapping.flag},""")
+
+ writer.appendln("""$indent$indent"values": {""")
+ intDefMapping.entries.joinTo(writer, separator = ",\n") { (value, identifier) ->
+ """$indent$indent$indent"$value": "$identifier""""
+ }
+ writer.appendln()
+ writer.appendln("$indent$indent}")
+
+ // End IntDef
+
+ writer.append("$indent}")
+ if (++currentIntDefTypesCount < intDefTypesCount) {
+ writer.appendln(",")
+ } else {
+ writer.appendln("")
+ }
+ }
+
+ writer.appendln("}")
+ }
+ }
+}
diff --git a/tools/processors/intdef_mappings/test/android/processor/IntDefProcessorTest.kt b/tools/processors/intdef_mappings/test/android/processor/IntDefProcessorTest.kt
new file mode 100644
index 000000000000..c0c159c98aac
--- /dev/null
+++ b/tools/processors/intdef_mappings/test/android/processor/IntDefProcessorTest.kt
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+package android.processor
+
+import android.processor.IntDefProcessor.IntDefMapping
+import com.google.common.collect.ObjectArrays.concat
+import com.google.testing.compile.CompilationSubject.assertThat
+import com.google.testing.compile.Compiler.javac
+import com.google.testing.compile.JavaFileObjects
+import junit.framework.Assert.assertEquals
+import org.junit.Test
+import java.io.StringWriter
+import javax.tools.JavaFileObject
+import javax.tools.StandardLocation.CLASS_OUTPUT
+
+/**
+ * Tests for [IntDefProcessor]
+ */
+class IntDefProcessorTest {
+ private val mAnnotations = arrayOf<JavaFileObject>(
+ JavaFileObjects.forSourceLines("android.annotation.IntDef",
+ "package android.annotation;",
+ "import java.lang.annotation.Retention;",
+ "import java.lang.annotation.Target;",
+ "import static java.lang.annotation.ElementType.ANNOTATION_TYPE;",
+ "import static java.lang.annotation.RetentionPolicy.SOURCE;",
+ "@Retention(SOURCE)",
+ "@Target({ANNOTATION_TYPE})",
+ "public @interface IntDef {",
+ " String[] prefix() default {};",
+ " String[] suffix() default {};",
+ " int[] value() default {};",
+ " boolean flag() default false;",
+ "}")
+ )
+
+ @Test
+ public fun annotationProcessorGeneratesMapping() {
+ val sources: Array<JavaFileObject> = arrayOf(
+ JavaFileObjects.forSourceLines(
+ "com.android.server.accessibility.magnification.MagnificationGestureMatcher",
+ "package com.android.server.accessibility.magnification;",
+ "import android.annotation.IntDef;",
+ "import java.lang.annotation.Retention;",
+ "import java.lang.annotation.RetentionPolicy;",
+ "class MagnificationGestureMatcher {",
+ " private static final int GESTURE_BASE = 100;",
+ " public static final int GESTURE_TWO_FINGER_DOWN = GESTURE_BASE + 1;",
+ " public static final int GESTURE_SWIPE = GESTURE_BASE + 2;",
+ " @IntDef(prefix = {\"GESTURE_MAGNIFICATION_\"}, value = {",
+ " GESTURE_TWO_FINGER_DOWN,",
+ " GESTURE_SWIPE",
+ " })",
+ " @Retention(RetentionPolicy.SOURCE)",
+ " @interface GestureId {}",
+ "}"
+ ),
+ JavaFileObjects.forSourceLines(
+ "android.service.storage.ExternalStorageService",
+ "package android.service.storage;",
+ "import android.annotation.IntDef;",
+ "import java.lang.annotation.Retention;",
+ "import java.lang.annotation.RetentionPolicy;",
+ "class MagnificationGestureMatcher {",
+ " public static final int FLAG_SESSION_TYPE_FUSE = 1 << 0;",
+ " public static final int FLAG_SESSION_ATTRIBUTE_INDEXABLE = 1 << 1;",
+ " @IntDef(flag = true, prefix = {\"FLAG_SESSION_\"},",
+ " value = {FLAG_SESSION_TYPE_FUSE, FLAG_SESSION_ATTRIBUTE_INDEXABLE})",
+ " @Retention(RetentionPolicy.SOURCE)",
+ " public @interface SessionFlag {}",
+ "}"
+ )
+ )
+
+ val expectedFile = """
+ {
+ "com.android.server.accessibility.magnification.MagnificationGestureMatcher.GestureId": {
+ "flag": false,
+ "values": {
+ "101": "GESTURE_TWO_FINGER_DOWN",
+ "102": "GESTURE_SWIPE"
+ }
+ },
+ "android.service.storage.MagnificationGestureMatcher.SessionFlag": {
+ "flag": true,
+ "values": {
+ "1": "FLAG_SESSION_TYPE_FUSE",
+ "2": "FLAG_SESSION_ATTRIBUTE_INDEXABLE"
+ }
+ }
+ }
+
+ """.trimIndent()
+
+ val filesToCompile = concat(mAnnotations, sources, JavaFileObject::class.java)
+
+ val compilation = javac()
+ .withProcessors(IntDefProcessor())
+ .compile(filesToCompile.toMutableList())
+
+ assertThat(compilation).succeeded()
+ assertThat(compilation).generatedFile(CLASS_OUTPUT, "com.android.winscope",
+ "intDefMapping.json").contentsAsUtf8String().isEqualTo(expectedFile)
+ }
+
+ @Test
+ public fun serializesMappingCorrectly() {
+ val map = linkedMapOf(
+ "SimpleIntDef" to IntDefMapping(linkedMapOf(
+ 0x0001 to "VAL_1",
+ 0x0002 to "VAL_2",
+ 0x0003 to "VAL_3"
+ ), flag = false),
+ "Flags" to IntDefMapping(linkedMapOf(
+ 0b0001 to "PRIVATE_FLAG_1",
+ 0b0010 to "PRIVATE_FLAG_2",
+ 0b0100 to "PRIVATE_FLAG_3"
+ ), flag = true)
+ )
+
+ val writer = StringWriter()
+ IntDefProcessor.serializeTo(map, writer)
+
+ val actualOutput = writer.toString()
+ val expectedOutput = """
+ {
+ "SimpleIntDef": {
+ "flag": false,
+ "values": {
+ "1": "VAL_1",
+ "2": "VAL_2",
+ "3": "VAL_3"
+ }
+ },
+ "Flags": {
+ "flag": true,
+ "values": {
+ "1": "PRIVATE_FLAG_1",
+ "2": "PRIVATE_FLAG_2",
+ "4": "PRIVATE_FLAG_3"
+ }
+ }
+ }
+
+ """.trimIndent()
+
+ assertEquals(actualOutput, expectedOutput)
+ }
+} \ No newline at end of file
diff --git a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
index 51faa49a86cc..1aec9b812e61 100644
--- a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
+++ b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
@@ -98,8 +98,10 @@ class StaleDataclassProcessor: AbstractProcessor() {
private fun elemToString(elem: Element): String {
return buildString {
- append(elem.modifiers.joinToString(" ") { it.name.toLowerCase() }).append(" ")
- append(elem.annotationMirrors.joinToString(" ")).append(" ")
+ append(elem.modifiers.joinToString(" ") { it.name.toLowerCase() })
+ append(" ")
+ append(elem.annotationMirrors.joinToString(" ", transform = { annotationToString(it) }))
+ append(" ")
if (elem is Symbol) {
if (elem.type is Type.MethodType) {
append((elem.type as Type.MethodType).returnType)
@@ -112,6 +114,14 @@ class StaleDataclassProcessor: AbstractProcessor() {
}
}
+ private fun annotationToString(ann: AnnotationMirror): String {
+ return if (ann.annotationType.toString().startsWith("com.android.internal.util.DataClass")) {
+ ann.toString()
+ } else {
+ ann.toString().substringBefore("(")
+ }
+ }
+
private fun processSingleFile(elementAnnotatedWithGenerated: Element) {
val classElement = elementAnnotatedWithGenerated.enclosingElement
diff --git a/tools/protologtool/Android.bp b/tools/protologtool/Android.bp
index ce551bd0cc10..0be80d31990a 100644
--- a/tools/protologtool/Android.bp
+++ b/tools/protologtool/Android.bp
@@ -2,9 +2,9 @@ java_library_host {
name: "protologtool-lib",
srcs: [
"src/com/android/protolog/tool/**/*.kt",
+ ":protolog-common-src",
],
static_libs: [
- "protolog-common",
"javaparser",
"platformprotos",
"jsonlib",
diff --git a/tools/protologtool/src/com/android/protolog/tool/LogParser.kt b/tools/protologtool/src/com/android/protolog/tool/LogParser.kt
index a59038fc99a0..645c5672da64 100644
--- a/tools/protologtool/src/com/android/protolog/tool/LogParser.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/LogParser.kt
@@ -16,16 +16,15 @@
package com.android.protolog.tool
+import com.android.internal.protolog.ProtoLogFileProto
+import com.android.internal.protolog.ProtoLogMessage
+import com.android.internal.protolog.common.InvalidFormatStringException
+import com.android.internal.protolog.common.LogDataType
import com.android.json.stream.JsonReader
-import com.android.server.protolog.common.InvalidFormatStringException
-import com.android.server.protolog.common.LogDataType
-import com.android.server.protolog.ProtoLogMessage
-import com.android.server.protolog.ProtoLogFileProto
import java.io.BufferedReader
import java.io.InputStream
import java.io.InputStreamReader
import java.io.PrintStream
-import java.lang.Exception
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogGroupReader.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogGroupReader.kt
index 75493b6427cb..42b628b0e262 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogGroupReader.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogGroupReader.kt
@@ -17,7 +17,7 @@
package com.android.protolog.tool
import com.android.protolog.tool.Constants.ENUM_VALUES_METHOD
-import com.android.server.protolog.common.IProtoLogGroup
+import com.android.internal.protolog.common.IProtoLogGroup
import java.io.File
import java.net.URLClassLoader
diff --git a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
index 36ea41129450..27e61a139451 100644
--- a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
@@ -16,7 +16,7 @@
package com.android.protolog.tool
-import com.android.server.protolog.common.LogDataType
+import com.android.internal.protolog.common.LogDataType
import com.github.javaparser.StaticJavaParser
import com.github.javaparser.ast.CompilationUnit
import com.github.javaparser.ast.NodeList
@@ -89,7 +89,7 @@ class SourceTransformer(
// Out: ProtoLog.e(GROUP, 1234, 0, null, arg)
newCall.arguments.add(2, IntegerLiteralExpr(typeMask))
// Replace call to a stub method with an actual implementation.
- // Out: com.android.server.protolog.ProtoLogImpl.e(GROUP, 1234, null, arg)
+ // Out: ProtoLogImpl.e(GROUP, 1234, null, arg)
newCall.setScope(protoLogImplClassNode)
// Create a call to ProtoLog$Cache.GROUP_enabled
// Out: com.android.server.protolog.ProtoLog$Cache.GROUP_enabled
@@ -119,9 +119,9 @@ class SourceTransformer(
}
blockStmt.addStatement(ExpressionStmt(newCall))
// Create an IF-statement with the previously created condition.
- // Out: if (com.android.server.protolog.ProtoLogImpl.isEnabled(GROUP)) {
+ // Out: if (ProtoLogImpl.isEnabled(GROUP)) {
// long protoLogParam0 = arg;
- // com.android.server.protolog.ProtoLogImpl.e(GROUP, 1234, 0, null, protoLogParam0);
+ // ProtoLogImpl.e(GROUP, 1234, 0, null, protoLogParam0);
// }
ifStmt = IfStmt(isLogEnabled, blockStmt, null)
} else {
diff --git a/tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt b/tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt
index cf36651c3e39..3cfbb435a764 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/CommandOptionsTest.kt
@@ -31,7 +31,7 @@ class CommandOptionsTest {
private const val TEST_PROTOLOG_CLASS = "com.android.server.wm.ProtoLog"
private const val TEST_PROTOLOGIMPL_CLASS = "com.android.server.wm.ProtoLogImpl"
private const val TEST_PROTOLOGCACHE_CLASS = "com.android.server.wm.ProtoLog\$Cache"
- private const val TEST_PROTOLOGGROUP_CLASS = "com.android.server.wm.ProtoLogGroup"
+ private const val TEST_PROTOLOGGROUP_CLASS = "com.android.internal.protolog.ProtoLogGroup"
private const val TEST_PROTOLOGGROUP_JAR = "out/soong/.intermediates/frameworks/base/" +
"services/core/services.core.wm.protologgroups/android_common/javac/" +
"services.core.wm.protologgroups.jar"
diff --git a/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt b/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt
index dd8a0b1c50b4..0d2b91d6cfb8 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt
@@ -33,8 +33,8 @@ class EndToEndTest {
val output = run(
src = "frameworks/base/org/example/Example.java" to """
package org.example;
- import com.android.server.protolog.common.ProtoLog;
- import static com.android.server.wm.ProtoLogGroup.GROUP;
+ import com.android.internal.protolog.common.ProtoLog;
+ import static com.android.internal.protolog.ProtoLogGroup.GROUP;
class Example {
void method() {
@@ -46,11 +46,11 @@ class EndToEndTest {
""".trimIndent(),
logGroup = LogGroup("GROUP", true, false, "TAG_GROUP"),
commandOptions = CommandOptions(arrayOf("transform-protolog-calls",
- "--protolog-class", "com.android.server.protolog.common.ProtoLog",
- "--protolog-impl-class", "com.android.server.protolog.ProtoLogImpl",
+ "--protolog-class", "com.android.internal.protolog.common.ProtoLog",
+ "--protolog-impl-class", "com.android.internal.protolog.ProtoLogImpl",
"--protolog-cache-class",
- "com.android.server.protolog.ProtoLog${"\$\$"}Cache",
- "--loggroups-class", "com.android.server.wm.ProtoLogGroup",
+ "com.android.server.wm.ProtoLogCache",
+ "--loggroups-class", "com.android.internal.protolog.ProtoLogGroup",
"--loggroups-jar", "not_required.jar",
"--output-srcjar", "out.srcjar",
"frameworks/base/org/example/Example.java"))
@@ -64,8 +64,8 @@ class EndToEndTest {
val output = run(
src = "frameworks/base/org/example/Example.java" to """
package org.example;
- import com.android.server.protolog.common.ProtoLog;
- import static com.android.server.wm.ProtoLogGroup.GROUP;
+ import com.android.internal.protolog.common.ProtoLog;
+ import static com.android.internal.protolog.ProtoLogGroup.GROUP;
class Example {
void method() {
@@ -77,8 +77,8 @@ class EndToEndTest {
""".trimIndent(),
logGroup = LogGroup("GROUP", true, false, "TAG_GROUP"),
commandOptions = CommandOptions(arrayOf("generate-viewer-config",
- "--protolog-class", "com.android.server.protolog.common.ProtoLog",
- "--loggroups-class", "com.android.server.wm.ProtoLogGroup",
+ "--protolog-class", "com.android.internal.protolog.common.ProtoLog",
+ "--loggroups-class", "com.android.internal.protolog.ProtoLogGroup",
"--loggroups-jar", "not_required.jar",
"--viewer-conf", "out.json",
"frameworks/base/org/example/Example.java"))
diff --git a/tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt b/tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt
index 04a3bfa499d8..67a31da87081 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/LogParserTest.kt
@@ -17,8 +17,8 @@
package com.android.protolog.tool
import com.android.json.stream.JsonReader
-import com.android.server.protolog.ProtoLogMessage
-import com.android.server.protolog.ProtoLogFileProto
+import com.android.internal.protolog.ProtoLogMessage
+import com.android.internal.protolog.ProtoLogFileProto
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index e3b6db08c503..43387fc054b5 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -121,10 +121,26 @@ cc_library {
],
target: {
android: {
- shared_libs: ["libstatssocket"],
+ shared_libs: [
+ "libstatssocket",
+ "libstatspull",
+ ],
+ export_shared_lib_headers: [
+ "libstatssocket",
+ "libstatspull",
+ ],
},
host: {
- static_libs: ["libstatssocket"],
+ static_libs: [
+ "libstatssocket",
+ "libstatspull",
+ "statsd-aidl-ndk_platform",
+ ],
+ shared_libs: ["libbinder_ndk"],
+ export_static_lib_headers: [
+ "libstatssocket",
+ "libstatspull",
+ ],
},
},
}
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index a230de46dcf3..fe6ca558a0ee 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -25,6 +25,7 @@
namespace android {
namespace stats_log_api_gen {
+using google::protobuf::OneofDescriptor;
using google::protobuf::EnumDescriptor;
using google::protobuf::FieldDescriptor;
using google::protobuf::FileDescriptor;
@@ -71,7 +72,7 @@ static void print_error(const FieldDescriptor* field, const char* format, ...) {
SourceLocation loc;
if (field->GetSourceLocation(&loc)) {
- // TODO: this will work if we can figure out how to pass
+ // TODO(b/162454173): this will work if we can figure out how to pass
// --include_source_info to protoc
fprintf(stderr, "%s:%d: ", file->name().c_str(), loc.start_line);
} else {
@@ -110,7 +111,6 @@ static java_type_t java_type(const FieldDescriptor* field) {
case FieldDescriptor::TYPE_GROUP:
return JAVA_TYPE_UNKNOWN;
case FieldDescriptor::TYPE_MESSAGE:
- // TODO: not the final package name
if (field->message_type()->full_name() == "android.os.statsd.AttributionNode") {
return JAVA_TYPE_ATTRIBUTION_CHAIN;
} else if (field->message_type()->full_name() == "android.os.statsd.KeyValuePair") {
@@ -146,7 +146,7 @@ static java_type_t java_type(const FieldDescriptor* field) {
void collate_enums(const EnumDescriptor& enumDescriptor, AtomField* atomField) {
for (int i = 0; i < enumDescriptor.value_count(); i++) {
atomField->enumValues[enumDescriptor.value(i)->number()] =
- enumDescriptor.value(i)->name().c_str();
+ enumDescriptor.value(i)->name();
}
}
@@ -396,16 +396,14 @@ int collate_atom(const Descriptor* atom, AtomDecl* atomDecl, vector<java_type_t>
collate_enums(*field->enum_type(), &atField);
}
- // Generate signature for pushed atoms
- if (atomDecl->code < PULL_ATOM_START_ID) {
- if (javaType == JAVA_TYPE_ENUM) {
- // All enums are treated as ints when it comes to function signatures.
- signature->push_back(JAVA_TYPE_INT);
- } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) {
- signature->push_back(JAVA_TYPE_BYTE_ARRAY);
- } else {
- signature->push_back(javaType);
- }
+ // Generate signature for atom.
+ if (javaType == JAVA_TYPE_ENUM) {
+ // All enums are treated as ints when it comes to function signatures.
+ signature->push_back(JAVA_TYPE_INT);
+ } else if (javaType == JAVA_TYPE_OBJECT && isBinaryField) {
+ signature->push_back(JAVA_TYPE_BYTE_ARRAY);
+ } else {
+ signature->push_back(javaType);
}
atomDecl->fields.push_back(atField);
@@ -518,8 +516,7 @@ int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms*
shared_ptr<AtomDecl> atomDecl =
make_shared<AtomDecl>(atomField->number(), atomField->name(), atom->name());
- if (atomDecl->code < PULL_ATOM_START_ID &&
- atomField->options().GetExtension(os::statsd::truncate_timestamp)) {
+ if (atomField->options().GetExtension(os::statsd::truncate_timestamp)) {
addAnnotationToAtomDecl(atomDecl.get(), ATOM_ID_FIELD_NUMBER,
ANNOTATION_ID_TRUNCATE_TIMESTAMP, ANNOTATION_TYPE_BOOL,
AnnotationValue(true));
@@ -530,14 +527,30 @@ int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms*
vector<java_type_t> signature;
errorCount += collate_atom(atom, atomDecl.get(), &signature);
- if (atomDecl->primaryFields.size() != 0 && atomDecl->exclusiveField == 0) {
+ if (!atomDecl->primaryFields.empty() && atomDecl->exclusiveField == 0) {
print_error(atomField, "Cannot have a primary field without an exclusive field: %s\n",
atomField->name().c_str());
errorCount++;
continue;
}
- FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = atoms->signatureInfoMap[signature];
+ const OneofDescriptor* oneofAtom = atomField->containing_oneof();
+ if (oneofAtom == nullptr) {
+ print_error(atomField, "Atom is not declared in a `oneof` field: %s\n",
+ atomField->name().c_str());
+ errorCount++;
+ continue;
+ } else if ((oneofAtom->name() != ONEOF_PUSHED_ATOM_NAME) &&
+ (oneofAtom->name() != ONEOF_PULLED_ATOM_NAME)) {
+ print_error(atomField, "Atom is neither a pushed nor pulled atom: %s\n",
+ atomField->name().c_str());
+ errorCount++;
+ continue;
+ }
+
+ FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = oneofAtom->name() ==
+ ONEOF_PUSHED_ATOM_NAME ? atoms->signatureInfoMap[signature] :
+ atoms->pulledAtomsSignatureInfoMap[signature];
populateFieldNumberToAtomDeclSet(atomDecl, &fieldNumberToAtomDeclSet);
atoms->decls.insert(atomDecl);
@@ -556,13 +569,25 @@ int collate_atoms(const Descriptor* descriptor, const string& moduleName, Atoms*
}
if (dbg) {
+ // Signatures for pushed atoms.
printf("signatures = [\n");
for (SignatureInfoMap::const_iterator it = atoms->signatureInfoMap.begin();
it != atoms->signatureInfoMap.end(); it++) {
printf(" ");
for (vector<java_type_t>::const_iterator jt = it->first.begin(); jt != it->first.end();
jt++) {
- printf(" %d", (int)*jt);
+ printf(" %d", static_cast<int>(*jt));
+ }
+ printf("\n");
+ }
+
+ // Signatures for pull atoms.
+ for (SignatureInfoMap::const_iterator it = atoms->pulledAtomsSignatureInfoMap.begin();
+ it != atoms->pulledAtomsSignatureInfoMap.end(); it++) {
+ printf(" ");
+ for (vector<java_type_t>::const_iterator jt = it->first.begin(); jt != it->first.end();
+ jt++) {
+ printf(" %d", static_cast<int>(*jt));
}
printf("\n");
}
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index 10b34ecf5f54..5d196c4b8290 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -29,6 +29,7 @@
namespace android {
namespace stats_log_api_gen {
+using google::protobuf::OneofDescriptor;
using google::protobuf::Descriptor;
using google::protobuf::FieldDescriptor;
using std::map;
@@ -41,6 +42,14 @@ const int PULL_ATOM_START_ID = 10000;
const int FIRST_UID_IN_CHAIN_ID = 0;
+/**
+ * The types of oneof atoms.
+ *
+ * `OneofDescriptor::name()` returns the name of the oneof.
+ */
+const char ONEOF_PUSHED_ATOM_NAME[] = "pushed";
+const char ONEOF_PULLED_ATOM_NAME[] = "pulled";
+
enum AnnotationId : uint8_t {
ANNOTATION_ID_IS_UID = 1,
ANNOTATION_ID_TRUNCATE_TIMESTAMP = 2,
@@ -54,7 +63,7 @@ enum AnnotationId : uint8_t {
const int ATOM_ID_FIELD_NUMBER = -1;
-const string DEFAULT_MODULE_NAME = "DEFAULT";
+const char DEFAULT_MODULE_NAME[] = "DEFAULT";
/**
* The types for atom parameters.
@@ -86,9 +95,9 @@ union AnnotationValue {
int intValue;
bool boolValue;
- AnnotationValue(const int value) : intValue(value) {
+ explicit AnnotationValue(const int value) : intValue(value) {
}
- AnnotationValue(const bool value) : boolValue(value) {
+ explicit AnnotationValue(const bool value) : boolValue(value) {
}
};
@@ -184,6 +193,7 @@ using SignatureInfoMap = map<vector<java_type_t>, FieldNumberToAtomDeclSet>;
struct Atoms {
SignatureInfoMap signatureInfoMap;
+ SignatureInfoMap pulledAtomsSignatureInfoMap;
AtomDeclSet decls;
AtomDeclSet non_chained_decls;
SignatureInfoMap nonChainedSignatureInfoMap;
diff --git a/tools/stats_log_api_gen/java_writer.cpp b/tools/stats_log_api_gen/java_writer.cpp
index f4c937c3f599..6fcf267cf39c 100644
--- a/tools/stats_log_api_gen/java_writer.cpp
+++ b/tools/stats_log_api_gen/java_writer.cpp
@@ -42,6 +42,7 @@ static int write_java_q_logger_class(FILE* out, const SignatureInfoMap& signatur
static void write_java_annotation_constants(FILE* out) {
fprintf(out, " // Annotation constants.\n");
+ const map<AnnotationId, string>& ANNOTATION_ID_CONSTANTS = get_annotation_id_constants();
for (const auto& [id, name] : ANNOTATION_ID_CONSTANTS) {
fprintf(out, " public static final byte %s = %hhu;\n", name.c_str(), id);
}
@@ -56,6 +57,7 @@ static void write_annotations(FILE* out, int argIndex,
return;
}
const AtomDeclSet& atomDeclSet = fieldNumberToAtomDeclSetIt->second;
+ const map<AnnotationId, string>& ANNOTATION_ID_CONSTANTS = get_annotation_id_constants();
for (const shared_ptr<AtomDecl>& atomDecl : atomDeclSet) {
const string atomConstant = make_constant_name(atomDecl->name);
fprintf(out, " if (%s == code) {\n", atomConstant.c_str());
@@ -96,7 +98,154 @@ static void write_annotations(FILE* out, int argIndex,
}
}
-static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMap,
+static void write_method_signature(FILE* out, const vector<java_type_t>& signature,
+ const AtomDecl& attributionDecl) {
+ int argIndex = 1;
+ for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+ arg++) {
+ if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+ for (const auto& chainField : attributionDecl.fields) {
+ fprintf(out, ", %s[] %s", java_type_name(chainField.javaType),
+ chainField.name.c_str());
+ }
+ } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
+ fprintf(out, ", android.util.SparseArray<Object> valueMap");
+ } else {
+ fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
+ }
+ argIndex++;
+ }
+}
+
+static int write_method_body(FILE* out, const vector<java_type_t>& signature,
+ const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet,
+ const AtomDecl& attributionDecl, const string& indent) {
+ // Start StatsEvent.Builder.
+ fprintf(out,
+ "%s final StatsEvent.Builder builder = "
+ "StatsEvent.newBuilder();\n",
+ indent.c_str());
+
+ // Write atom code.
+ fprintf(out, "%s builder.setAtomId(code);\n", indent.c_str());
+ write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet);
+
+ // Write the args.
+ int argIndex = 1;
+ for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
+ arg++) {
+ switch (*arg) {
+ case JAVA_TYPE_BOOLEAN:
+ fprintf(out, "%s builder.writeBoolean(arg%d);\n", indent.c_str(),
+ argIndex);
+ break;
+ case JAVA_TYPE_INT:
+ case JAVA_TYPE_ENUM:
+ fprintf(out, "%s builder.writeInt(arg%d);\n", indent.c_str(), argIndex);
+ break;
+ case JAVA_TYPE_FLOAT:
+ fprintf(out, "%s builder.writeFloat(arg%d);\n", indent.c_str(),
+ argIndex);
+ break;
+ case JAVA_TYPE_LONG:
+ fprintf(out, "%s builder.writeLong(arg%d);\n", indent.c_str(), argIndex);
+ break;
+ case JAVA_TYPE_STRING:
+ fprintf(out, "%s builder.writeString(arg%d);\n", indent.c_str(),
+ argIndex);
+ break;
+ case JAVA_TYPE_BYTE_ARRAY:
+ fprintf(out,
+ "%s builder.writeByteArray(null == arg%d ? new byte[0] : "
+ "arg%d);\n",
+ indent.c_str(), argIndex, argIndex);
+ break;
+ case JAVA_TYPE_ATTRIBUTION_CHAIN: {
+ const char* uidName = attributionDecl.fields.front().name.c_str();
+ const char* tagName = attributionDecl.fields.back().name.c_str();
+
+ fprintf(out, "%s builder.writeAttributionChain(\n", indent.c_str());
+ fprintf(out, "%s null == %s ? new int[0] : %s,\n",
+ indent.c_str(), uidName, uidName);
+ fprintf(out, "%s null == %s ? new String[0] : %s);\n",
+ indent.c_str(), tagName, tagName);
+ break;
+ }
+ case JAVA_TYPE_KEY_VALUE_PAIR:
+ fprintf(out, "\n");
+ fprintf(out, "%s // Write KeyValuePairs.\n", indent.c_str());
+ fprintf(out, "%s final int count = valueMap.size();\n", indent.c_str());
+ fprintf(out, "%s android.util.SparseIntArray intMap = null;\n",
+ indent.c_str());
+ fprintf(out, "%s android.util.SparseLongArray longMap = null;\n",
+ indent.c_str());
+ fprintf(out, "%s android.util.SparseArray<String> stringMap = null;\n",
+ indent.c_str());
+ fprintf(out, "%s android.util.SparseArray<Float> floatMap = null;\n",
+ indent.c_str());
+ fprintf(out, "%s for (int i = 0; i < count; i++) {\n", indent.c_str());
+ fprintf(out, "%s final int key = valueMap.keyAt(i);\n",
+ indent.c_str());
+ fprintf(out, "%s final Object value = valueMap.valueAt(i);\n",
+ indent.c_str());
+ fprintf(out, "%s if (value instanceof Integer) {\n", indent.c_str());
+ fprintf(out, "%s if (null == intMap) {\n", indent.c_str());
+ fprintf(out,
+ "%s intMap = new "
+ "android.util.SparseIntArray();\n",
+ indent.c_str());
+ fprintf(out, "%s }\n", indent.c_str());
+ fprintf(out, "%s intMap.put(key, (Integer) value);\n",
+ indent.c_str());
+ fprintf(out, "%s } else if (value instanceof Long) {\n",
+ indent.c_str());
+ fprintf(out, "%s if (null == longMap) {\n", indent.c_str());
+ fprintf(out,
+ "%s longMap = new "
+ "android.util.SparseLongArray();\n",
+ indent.c_str());
+ fprintf(out, "%s }\n", indent.c_str());
+ fprintf(out, "%s longMap.put(key, (Long) value);\n",
+ indent.c_str());
+ fprintf(out, "%s } else if (value instanceof String) {\n",
+ indent.c_str());
+ fprintf(out, "%s if (null == stringMap) {\n", indent.c_str());
+ fprintf(out,
+ "%s stringMap = new "
+ "android.util.SparseArray<>();\n",
+ indent.c_str());
+ fprintf(out, "%s }\n", indent.c_str());
+ fprintf(out, "%s stringMap.put(key, (String) value);\n",
+ indent.c_str());
+ fprintf(out, "%s } else if (value instanceof Float) {\n",
+ indent.c_str());
+ fprintf(out, "%s if (null == floatMap) {\n", indent.c_str());
+ fprintf(out,
+ "%s floatMap = new "
+ "android.util.SparseArray<>();\n",
+ indent.c_str());
+ fprintf(out, "%s }\n", indent.c_str());
+ fprintf(out, "%s floatMap.put(key, (Float) value);\n",
+ indent.c_str());
+ fprintf(out, "%s }\n", indent.c_str());
+ fprintf(out, "%s }\n", indent.c_str());
+ fprintf(out,
+ "%s builder.writeKeyValuePairs("
+ "intMap, longMap, stringMap, floatMap);\n",
+ indent.c_str());
+ break;
+ default:
+ // Unsupported types: OBJECT, DOUBLE.
+ fprintf(stderr, "Encountered unsupported type.");
+ return 1;
+ }
+ write_annotations(out, argIndex, fieldNumberToAtomDeclSet);
+ argIndex++;
+ }
+ return 0;
+}
+
+static int write_java_pushed_methods(FILE* out, const SignatureInfoMap& signatureInfoMap,
const AtomDecl& attributionDecl, const bool supportQ) {
for (auto signatureInfoMapIt = signatureInfoMap.begin();
signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
@@ -104,21 +253,7 @@ static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMa
fprintf(out, " public static void write(int code");
const vector<java_type_t>& signature = signatureInfoMapIt->first;
const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second;
- int argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
- arg++) {
- if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
- fprintf(out, ", %s[] %s", java_type_name(chainField.javaType),
- chainField.name.c_str());
- }
- } else if (*arg == JAVA_TYPE_KEY_VALUE_PAIR) {
- fprintf(out, ", android.util.SparseArray<Object> valueMap");
- } else {
- fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
- }
- argIndex++;
- }
+ write_method_signature(out, signature, attributionDecl);
fprintf(out, ") {\n");
// Print method body.
@@ -128,130 +263,13 @@ static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMa
indent = " ";
}
- // Start StatsEvent.Builder.
- fprintf(out,
- "%s final StatsEvent.Builder builder = "
- "StatsEvent.newBuilder();\n",
- indent.c_str());
-
- // Write atom code.
- fprintf(out, "%s builder.setAtomId(code);\n", indent.c_str());
- write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet);
-
- // Write the args.
- argIndex = 1;
- for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
- arg++) {
- switch (*arg) {
- case JAVA_TYPE_BOOLEAN:
- fprintf(out, "%s builder.writeBoolean(arg%d);\n", indent.c_str(),
- argIndex);
- break;
- case JAVA_TYPE_INT:
- case JAVA_TYPE_ENUM:
- fprintf(out, "%s builder.writeInt(arg%d);\n", indent.c_str(), argIndex);
- break;
- case JAVA_TYPE_FLOAT:
- fprintf(out, "%s builder.writeFloat(arg%d);\n", indent.c_str(),
- argIndex);
- break;
- case JAVA_TYPE_LONG:
- fprintf(out, "%s builder.writeLong(arg%d);\n", indent.c_str(), argIndex);
- break;
- case JAVA_TYPE_STRING:
- fprintf(out, "%s builder.writeString(arg%d);\n", indent.c_str(),
- argIndex);
- break;
- case JAVA_TYPE_BYTE_ARRAY:
- fprintf(out,
- "%s builder.writeByteArray(null == arg%d ? new byte[0] : "
- "arg%d);\n",
- indent.c_str(), argIndex, argIndex);
- break;
- case JAVA_TYPE_ATTRIBUTION_CHAIN: {
- const char* uidName = attributionDecl.fields.front().name.c_str();
- const char* tagName = attributionDecl.fields.back().name.c_str();
-
- fprintf(out, "%s builder.writeAttributionChain(\n", indent.c_str());
- fprintf(out, "%s null == %s ? new int[0] : %s,\n",
- indent.c_str(), uidName, uidName);
- fprintf(out, "%s null == %s ? new String[0] : %s);\n",
- indent.c_str(), tagName, tagName);
- break;
- }
- case JAVA_TYPE_KEY_VALUE_PAIR:
- fprintf(out, "\n");
- fprintf(out, "%s // Write KeyValuePairs.\n", indent.c_str());
- fprintf(out, "%s final int count = valueMap.size();\n", indent.c_str());
- fprintf(out, "%s android.util.SparseIntArray intMap = null;\n",
- indent.c_str());
- fprintf(out, "%s android.util.SparseLongArray longMap = null;\n",
- indent.c_str());
- fprintf(out, "%s android.util.SparseArray<String> stringMap = null;\n",
- indent.c_str());
- fprintf(out, "%s android.util.SparseArray<Float> floatMap = null;\n",
- indent.c_str());
- fprintf(out, "%s for (int i = 0; i < count; i++) {\n", indent.c_str());
- fprintf(out, "%s final int key = valueMap.keyAt(i);\n",
- indent.c_str());
- fprintf(out, "%s final Object value = valueMap.valueAt(i);\n",
- indent.c_str());
- fprintf(out, "%s if (value instanceof Integer) {\n", indent.c_str());
- fprintf(out, "%s if (null == intMap) {\n", indent.c_str());
- fprintf(out,
- "%s intMap = new "
- "android.util.SparseIntArray();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s intMap.put(key, (Integer) value);\n",
- indent.c_str());
- fprintf(out, "%s } else if (value instanceof Long) {\n",
- indent.c_str());
- fprintf(out, "%s if (null == longMap) {\n", indent.c_str());
- fprintf(out,
- "%s longMap = new "
- "android.util.SparseLongArray();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s longMap.put(key, (Long) value);\n",
- indent.c_str());
- fprintf(out, "%s } else if (value instanceof String) {\n",
- indent.c_str());
- fprintf(out, "%s if (null == stringMap) {\n", indent.c_str());
- fprintf(out,
- "%s stringMap = new "
- "android.util.SparseArray<>();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s stringMap.put(key, (String) value);\n",
- indent.c_str());
- fprintf(out, "%s } else if (value instanceof Float) {\n",
- indent.c_str());
- fprintf(out, "%s if (null == floatMap) {\n", indent.c_str());
- fprintf(out,
- "%s floatMap = new "
- "android.util.SparseArray<>();\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s floatMap.put(key, (Float) value);\n",
- indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out, "%s }\n", indent.c_str());
- fprintf(out,
- "%s builder.writeKeyValuePairs("
- "intMap, longMap, stringMap, floatMap);\n",
- indent.c_str());
- break;
- default:
- // Unsupported types: OBJECT, DOUBLE.
- fprintf(stderr, "Encountered unsupported type.");
- return 1;
- }
- write_annotations(out, argIndex, fieldNumberToAtomDeclSet);
- argIndex++;
+ int ret = write_method_body(out, signature, fieldNumberToAtomDeclSet,
+ attributionDecl, indent);
+ if (ret != 0) {
+ return ret;
}
-
fprintf(out, "\n");
+
fprintf(out, "%s builder.usePooledBuffer();\n", indent.c_str());
fprintf(out, "%s StatsLog.write(builder.build());\n", indent.c_str());
@@ -259,7 +277,7 @@ static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMa
if (supportQ) {
fprintf(out, " } else {\n");
fprintf(out, " QLogger.write(code");
- argIndex = 1;
+ int argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature.begin();
arg != signature.end(); arg++) {
if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
@@ -285,6 +303,34 @@ static int write_java_methods(FILE* out, const SignatureInfoMap& signatureInfoMa
return 0;
}
+static int write_java_pulled_methods(FILE* out, const SignatureInfoMap& signatureInfoMap,
+ const AtomDecl& attributionDecl) {
+ for (auto signatureInfoMapIt = signatureInfoMap.begin();
+ signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
+ // Print method signature.
+ fprintf(out, " public static StatsEvent buildStatsEvent(int code");
+ const vector<java_type_t>& signature = signatureInfoMapIt->first;
+ const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second;
+ write_method_signature(out, signature, attributionDecl);
+ fprintf(out, ") {\n");
+
+ // Print method body.
+ string indent("");
+ int ret = write_method_body(out, signature, fieldNumberToAtomDeclSet,
+ attributionDecl, indent);
+ if (ret != 0) {
+ return ret;
+ }
+ fprintf(out, "\n");
+
+ fprintf(out, "%s return builder.build();\n", indent.c_str());
+
+ fprintf(out, " }\n"); // method
+ fprintf(out, "\n");
+ }
+ return 0;
+}
+
int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
const string& javaClass, const string& javaPackage, const bool supportQ,
const bool supportWorkSource) {
@@ -317,8 +363,10 @@ int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attribut
// Print write methods.
fprintf(out, " // Write methods\n");
- errors += write_java_methods(out, atoms.signatureInfoMap, attributionDecl, supportQ);
+ errors += write_java_pushed_methods(out, atoms.signatureInfoMap, attributionDecl, supportQ);
errors += write_java_non_chained_methods(out, atoms.nonChainedSignatureInfoMap);
+ errors += write_java_pulled_methods(out, atoms.pulledAtomsSignatureInfoMap,
+ attributionDecl);
if (supportWorkSource) {
errors += write_java_work_source_methods(out, atoms.signatureInfoMap);
}
diff --git a/tools/stats_log_api_gen/java_writer.h b/tools/stats_log_api_gen/java_writer.h
index 8b3b50588efc..afd992be6c5e 100644
--- a/tools/stats_log_api_gen/java_writer.h
+++ b/tools/stats_log_api_gen/java_writer.h
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-#pragma once
+#ifndef ANDROID_STATS_LOG_API_GEN_JAVA_WRITER_H
+#define ANDROID_STATS_LOG_API_GEN_JAVA_WRITER_H
#include <stdio.h>
#include <string.h>
@@ -28,11 +29,11 @@
namespace android {
namespace stats_log_api_gen {
-using namespace std;
-
int write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
const string& javaClass, const string& javaPackage, const bool supportQ,
const bool supportWorkSource);
} // namespace stats_log_api_gen
} // namespace android
+
+#endif // ANDROID_STATS_LOG_API_GEN_JAVA_WRITER_H
diff --git a/tools/stats_log_api_gen/java_writer_q.cpp b/tools/stats_log_api_gen/java_writer_q.cpp
index d21e2708b724..be7cb4aeb3f8 100644
--- a/tools/stats_log_api_gen/java_writer_q.cpp
+++ b/tools/stats_log_api_gen/java_writer_q.cpp
@@ -65,7 +65,7 @@ int write_java_methods_q_schema(FILE* out, const SignatureInfoMap& signatureInfo
for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
arg++) {
if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
+ for (const auto& chainField : attributionDecl.fields) {
fprintf(out, ", %s[] %s", java_type_name(chainField.javaType),
chainField.name.c_str());
}
@@ -407,7 +407,7 @@ void write_java_helpers_for_q_schema_methods(FILE* out, const AtomDecl& attribut
if (requiredHelpers & JAVA_MODULE_REQUIRES_ATTRIBUTION) {
fprintf(out, "%sprivate static void writeAttributionChain(byte[] buff, int pos",
indent.c_str());
- for (auto chainField : attributionDecl.fields) {
+ for (const auto& chainField : attributionDecl.fields) {
fprintf(out, ", %s[] %s", java_type_name(chainField.javaType), chainField.name.c_str());
}
fprintf(out, ") {\n");
diff --git a/tools/stats_log_api_gen/java_writer_q.h b/tools/stats_log_api_gen/java_writer_q.h
index c511a8436416..622ef3e37bad 100644
--- a/tools/stats_log_api_gen/java_writer_q.h
+++ b/tools/stats_log_api_gen/java_writer_q.h
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-#pragma once
+#ifndef ANDROID_STATS_LOG_API_GEN_JAVA_WRITER_Q_H
+#define ANDROID_STATS_LOG_API_GEN_JAVA_WRITER_Q_H
#include <stdio.h>
#include <string.h>
@@ -28,8 +29,6 @@
namespace android {
namespace stats_log_api_gen {
-using namespace std;
-
void write_java_q_logging_constants(FILE* out, const string& indent);
int write_java_methods_q_schema(FILE* out, const SignatureInfoMap& signatureInfoMap,
@@ -44,3 +43,5 @@ int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms,
} // namespace stats_log_api_gen
} // namespace android
+
+#endif // ANDROID_STATS_LOG_API_GEN_JAVA_WRITER_Q_H
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index b888ce904b31..28302493a8e1 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -15,9 +15,6 @@
#include "native_writer.h"
#include "utils.h"
-using namespace google::protobuf;
-using namespace std;
-
namespace android {
namespace stats_log_api_gen {
@@ -145,7 +142,7 @@ static int run(int argc, char const* const* argv) {
index++;
}
- if (cppFilename.size() == 0 && headerFilename.size() == 0 && javaFilename.size() == 0) {
+ if (cppFilename.empty() && headerFilename.empty() && javaFilename.empty()) {
print_usage();
return 1;
}
@@ -175,9 +172,9 @@ static int run(int argc, char const* const* argv) {
&attributionSignature);
// Write the .cpp file
- if (cppFilename.size() != 0) {
+ if (!cppFilename.empty()) {
FILE* out = fopen(cppFilename.c_str(), "w");
- if (out == NULL) {
+ if (out == nullptr) {
fprintf(stderr, "Unable to open file for write: %s\n", cppFilename.c_str());
return 1;
}
@@ -198,9 +195,9 @@ static int run(int argc, char const* const* argv) {
}
// Write the .h file
- if (headerFilename.size() != 0) {
+ if (!headerFilename.empty()) {
FILE* out = fopen(headerFilename.c_str(), "w");
- if (out == NULL) {
+ if (out == nullptr) {
fprintf(stderr, "Unable to open file for write: %s\n", headerFilename.c_str());
return 1;
}
@@ -214,24 +211,24 @@ static int run(int argc, char const* const* argv) {
}
// Write the .java file
- if (javaFilename.size() != 0) {
- if (javaClass.size() == 0) {
+ if (!javaFilename.empty()) {
+ if (javaClass.empty()) {
fprintf(stderr, "Must supply --javaClass if supplying a Java filename");
return 1;
}
- if (javaPackage.size() == 0) {
+ if (javaPackage.empty()) {
fprintf(stderr, "Must supply --javaPackage if supplying a Java filename");
return 1;
}
- if (moduleName.size() == 0) {
+ if (moduleName.empty()) {
fprintf(stderr, "Must supply --module if supplying a Java filename");
return 1;
}
FILE* out = fopen(javaFilename.c_str(), "w");
- if (out == NULL) {
+ if (out == nullptr) {
fprintf(stderr, "Unable to open file for write: %s\n", javaFilename.c_str());
return 1;
}
diff --git a/tools/stats_log_api_gen/native_writer.cpp b/tools/stats_log_api_gen/native_writer.cpp
index 0c6c0099e459..b4fb8dd8321b 100644
--- a/tools/stats_log_api_gen/native_writer.cpp
+++ b/tools/stats_log_api_gen/native_writer.cpp
@@ -24,6 +24,7 @@ namespace stats_log_api_gen {
static void write_native_annotation_constants(FILE* out) {
fprintf(out, "// Annotation constants.\n");
+ const map<AnnotationId, string>& ANNOTATION_ID_CONSTANTS = get_annotation_id_constants();
for (const auto& [id, name] : ANNOTATION_ID_CONSTANTS) {
fprintf(out, "const uint8_t %s = %hhu;\n", name.c_str(), id);
}
@@ -39,6 +40,7 @@ static void write_annotations(FILE* out, int argIndex,
return;
}
const AtomDeclSet& atomDeclSet = fieldNumberToAtomDeclSetIt->second;
+ const map<AnnotationId, string>& ANNOTATION_ID_CONSTANTS = get_annotation_id_constants();
for (const shared_ptr<AtomDecl>& atomDecl : atomDeclSet) {
const string atomConstant = make_constant_name(atomDecl->name);
fprintf(out, " if (%s == code) {\n", atomConstant.c_str());
@@ -60,8 +62,6 @@ static void write_annotations(FILE* out, int argIndex,
}
break;
case ANNOTATION_TYPE_BOOL:
- // TODO(b/151786433): Write annotation constant name instead of
- // annotation id literal.
fprintf(out, " %saddBoolAnnotation(%s%s, %s);\n", methodPrefix.c_str(),
methodSuffix.c_str(), annotationConstant.c_str(),
annotation->value.boolValue ? "true" : "false");
@@ -82,21 +82,78 @@ static void write_annotations(FILE* out, int argIndex,
}
}
-static int write_native_stats_write_methods(FILE* out, const Atoms& atoms,
+static int write_native_method_body(FILE* out, vector<java_type_t>& signature,
+ const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet,
+ const AtomDecl& attributionDecl) {
+ int argIndex = 1;
+ fprintf(out, " AStatsEvent_setAtomId(event, code);\n");
+ write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet, "AStatsEvent_",
+ "event, ");
+ for (vector<java_type_t>::const_iterator arg = signature.begin();
+ arg != signature.end(); arg++) {
+ switch (*arg) {
+ case JAVA_TYPE_ATTRIBUTION_CHAIN: {
+ const char* uidName = attributionDecl.fields.front().name.c_str();
+ const char* tagName = attributionDecl.fields.back().name.c_str();
+ fprintf(out,
+ " AStatsEvent_writeAttributionChain(event, "
+ "reinterpret_cast<const uint32_t*>(%s), %s.data(), "
+ "static_cast<uint8_t>(%s_length));\n",
+ uidName, tagName, uidName);
+ break;
+ }
+ case JAVA_TYPE_BYTE_ARRAY:
+ fprintf(out,
+ " AStatsEvent_writeByteArray(event, "
+ "reinterpret_cast<const uint8_t*>(arg%d.arg), "
+ "arg%d.arg_length);\n",
+ argIndex, argIndex);
+ break;
+ case JAVA_TYPE_BOOLEAN:
+ fprintf(out, " AStatsEvent_writeBool(event, arg%d);\n", argIndex);
+ break;
+ case JAVA_TYPE_INT: // Fall through.
+ case JAVA_TYPE_ENUM:
+ fprintf(out, " AStatsEvent_writeInt32(event, arg%d);\n", argIndex);
+ break;
+ case JAVA_TYPE_FLOAT:
+ fprintf(out, " AStatsEvent_writeFloat(event, arg%d);\n", argIndex);
+ break;
+ case JAVA_TYPE_LONG:
+ fprintf(out, " AStatsEvent_writeInt64(event, arg%d);\n", argIndex);
+ break;
+ case JAVA_TYPE_STRING:
+ fprintf(out, " AStatsEvent_writeString(event, arg%d);\n", argIndex);
+ break;
+ default:
+ // Unsupported types: OBJECT, DOUBLE, KEY_VALUE_PAIRS
+ fprintf(stderr, "Encountered unsupported type.");
+ return 1;
+ }
+ write_annotations(out, argIndex, fieldNumberToAtomDeclSet, "AStatsEvent_",
+ "event, ");
+ argIndex++;
+ }
+ return 0;
+}
+
+static int write_native_stats_write_methods(FILE* out, const SignatureInfoMap& signatureInfoMap,
const AtomDecl& attributionDecl, const bool supportQ) {
fprintf(out, "\n");
- for (auto signatureInfoMapIt = atoms.signatureInfoMap.begin();
- signatureInfoMapIt != atoms.signatureInfoMap.end(); signatureInfoMapIt++) {
+ for (auto signatureInfoMapIt = signatureInfoMap.begin();
+ signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
vector<java_type_t> signature = signatureInfoMapIt->first;
const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second;
// Key value pairs not supported in native.
- if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
+ if (std::find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) !=
+ signature.end()) {
continue;
}
- write_native_method_signature(out, "int stats_write", signature, attributionDecl, " {");
+ write_native_method_signature(out, "int stats_write(", signature, attributionDecl, " {");
- int argIndex = 1;
+ // Write method body.
if (supportQ) {
+ int argIndex = 1;
fprintf(out, " StatsEventCompat event;\n");
fprintf(out, " event.setAtomId(code);\n");
write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet, "event.", "");
@@ -138,78 +195,37 @@ static int write_native_stats_write_methods(FILE* out, const Atoms& atoms,
write_annotations(out, argIndex, fieldNumberToAtomDeclSet, "event.", "");
argIndex++;
}
- fprintf(out, " return event.writeToSocket();\n");
+ fprintf(out, " return event.writeToSocket();\n"); // end method body.
} else {
fprintf(out, " AStatsEvent* event = AStatsEvent_obtain();\n");
- fprintf(out, " AStatsEvent_setAtomId(event, code);\n");
- write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet, "AStatsEvent_",
- "event, ");
- for (vector<java_type_t>::const_iterator arg = signature.begin();
- arg != signature.end(); arg++) {
- switch (*arg) {
- case JAVA_TYPE_ATTRIBUTION_CHAIN: {
- const char* uidName = attributionDecl.fields.front().name.c_str();
- const char* tagName = attributionDecl.fields.back().name.c_str();
- fprintf(out,
- " AStatsEvent_writeAttributionChain(event, "
- "reinterpret_cast<const uint32_t*>(%s), %s.data(), "
- "static_cast<uint8_t>(%s_length));\n",
- uidName, tagName, uidName);
- break;
- }
- case JAVA_TYPE_BYTE_ARRAY:
- fprintf(out,
- " AStatsEvent_writeByteArray(event, "
- "reinterpret_cast<const uint8_t*>(arg%d.arg), "
- "arg%d.arg_length);\n",
- argIndex, argIndex);
- break;
- case JAVA_TYPE_BOOLEAN:
- fprintf(out, " AStatsEvent_writeBool(event, arg%d);\n", argIndex);
- break;
- case JAVA_TYPE_INT: // Fall through.
- case JAVA_TYPE_ENUM:
- fprintf(out, " AStatsEvent_writeInt32(event, arg%d);\n", argIndex);
- break;
- case JAVA_TYPE_FLOAT:
- fprintf(out, " AStatsEvent_writeFloat(event, arg%d);\n", argIndex);
- break;
- case JAVA_TYPE_LONG:
- fprintf(out, " AStatsEvent_writeInt64(event, arg%d);\n", argIndex);
- break;
- case JAVA_TYPE_STRING:
- fprintf(out, " AStatsEvent_writeString(event, arg%d);\n", argIndex);
- break;
- default:
- // Unsupported types: OBJECT, DOUBLE, KEY_VALUE_PAIRS
- fprintf(stderr, "Encountered unsupported type.");
- return 1;
- }
- write_annotations(out, argIndex, fieldNumberToAtomDeclSet, "AStatsEvent_",
- "event, ");
- argIndex++;
+ int ret = write_native_method_body(out, signature, fieldNumberToAtomDeclSet,
+ attributionDecl);
+ if (ret != 0) {
+ return ret;
}
fprintf(out, " const int ret = AStatsEvent_write(event);\n");
fprintf(out, " AStatsEvent_release(event);\n");
- fprintf(out, " return ret;\n");
+ fprintf(out, " return ret;\n"); // end method body.
}
- fprintf(out, "}\n\n");
+ fprintf(out, "}\n\n"); // end method.
}
return 0;
}
-static void write_native_stats_write_non_chained_methods(FILE* out, const Atoms& atoms,
+static void write_native_stats_write_non_chained_methods(FILE* out,
+ const SignatureInfoMap& signatureInfoMap,
const AtomDecl& attributionDecl) {
fprintf(out, "\n");
- for (auto signature_it = atoms.nonChainedSignatureInfoMap.begin();
- signature_it != atoms.nonChainedSignatureInfoMap.end(); signature_it++) {
+ for (auto signature_it = signatureInfoMap.begin();
+ signature_it != signatureInfoMap.end(); signature_it++) {
vector<java_type_t> signature = signature_it->first;
// Key value pairs not supported in native.
- if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
+ if (std::find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) !=
+ signature.end()) {
continue;
}
- write_native_method_signature(out, "int stats_write_non_chained", signature,
+ write_native_method_signature(out, "int stats_write_non_chained(", signature,
attributionDecl, " {");
vector<java_type_t> newSignature;
@@ -235,6 +251,35 @@ static void write_native_stats_write_non_chained_methods(FILE* out, const Atoms&
}
}
+static int write_native_build_stats_event_methods(FILE* out,
+ const SignatureInfoMap& signatureInfoMap,
+ const AtomDecl& attributionDecl) {
+ fprintf(out, "\n");
+ for (auto signatureInfoMapIt = signatureInfoMap.begin();
+ signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) {
+ vector<java_type_t> signature = signatureInfoMapIt->first;
+ const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second;
+ // Key value pairs not supported in native.
+ if (std::find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) !=
+ signature.end()) {
+ continue;
+ }
+ write_native_method_signature(out, "void addAStatsEvent(AStatsEventList* pulled_data, ",
+ signature, attributionDecl, " {");
+
+ fprintf(out, " AStatsEvent* event = AStatsEventList_addStatsEvent(pulled_data);\n");
+ int ret = write_native_method_body(out, signature, fieldNumberToAtomDeclSet,
+ attributionDecl);
+ if (ret != 0) {
+ return ret;
+ }
+ fprintf(out, " AStatsEvent_build(event);\n"); // end method body.
+
+ fprintf(out, "}\n\n"); // end method.
+ }
+ return 0;
+}
+
static void write_native_method_header(FILE* out, const string& methodName,
const SignatureInfoMap& signatureInfoMap,
const AtomDecl& attributionDecl) {
@@ -243,7 +288,8 @@ static void write_native_method_header(FILE* out, const string& methodName,
vector<java_type_t> signature = signatureInfoMapIt->first;
// Key value pairs not supported in native.
- if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) {
+ if (std::find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) !=
+ signature.end()) {
continue;
}
write_native_method_signature(out, methodName, signature, attributionDecl, ";");
@@ -262,13 +308,22 @@ int write_stats_log_cpp(FILE* out, const Atoms& atoms, const AtomDecl& attributi
fprintf(out, "#include <StatsEventCompat.h>\n");
} else {
fprintf(out, "#include <stats_event.h>\n");
+
+ if (!atoms.pulledAtomsSignatureInfoMap.empty()) {
+ fprintf(out, "#include <stats_pull_atom_callback.h>\n");
+ }
}
+
+
fprintf(out, "\n");
write_namespace(out, cppNamespace);
- write_native_stats_write_methods(out, atoms, attributionDecl, supportQ);
- write_native_stats_write_non_chained_methods(out, atoms, attributionDecl);
+ write_native_stats_write_methods(out, atoms.signatureInfoMap, attributionDecl, supportQ);
+ write_native_stats_write_non_chained_methods(out, atoms.nonChainedSignatureInfoMap,
+ attributionDecl);
+ write_native_build_stats_event_methods(out, atoms.pulledAtomsSignatureInfoMap,
+ attributionDecl);
// Print footer
fprintf(out, "\n");
@@ -288,6 +343,9 @@ int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl& attrib
fprintf(out, "#include <vector>\n");
fprintf(out, "#include <map>\n");
fprintf(out, "#include <set>\n");
+ if (!atoms.pulledAtomsSignatureInfoMap.empty()) {
+ fprintf(out, "#include <stats_pull_atom_callback.h>\n");
+ }
fprintf(out, "\n");
write_namespace(out, cppNamespace);
@@ -337,12 +395,22 @@ int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl& attrib
fprintf(out, "//\n");
fprintf(out, "// Write methods\n");
fprintf(out, "//\n");
- write_native_method_header(out, "int stats_write", atoms.signatureInfoMap, attributionDecl);
+ write_native_method_header(out, "int stats_write(", atoms.signatureInfoMap, attributionDecl);
+ fprintf(out, "\n");
fprintf(out, "//\n");
fprintf(out, "// Write flattened methods\n");
fprintf(out, "//\n");
- write_native_method_header(out, "int stats_write_non_chained", atoms.nonChainedSignatureInfoMap,
+ write_native_method_header(out, "int stats_write_non_chained(", atoms.nonChainedSignatureInfoMap,
+ attributionDecl);
+ fprintf(out, "\n");
+
+ // Print pulled atoms methods.
+ fprintf(out, "//\n");
+ fprintf(out, "// Add AStatsEvent methods\n");
+ fprintf(out, "//\n");
+ write_native_method_header(out, "void addAStatsEvent(AStatsEventList* pulled_data, ",
+ atoms.pulledAtomsSignatureInfoMap,
attributionDecl);
fprintf(out, "\n");
diff --git a/tools/stats_log_api_gen/native_writer.h b/tools/stats_log_api_gen/native_writer.h
index 264d4db29fc9..4e42d1fa2809 100644
--- a/tools/stats_log_api_gen/native_writer.h
+++ b/tools/stats_log_api_gen/native_writer.h
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-#pragma once
+#ifndef ANDROID_STATS_LOG_API_GEN_NATIVE_WRITER_H
+#define ANDROID_STATS_LOG_API_GEN_NATIVE_WRITER_H
#include <stdio.h>
#include <string.h>
@@ -24,8 +25,6 @@
namespace android {
namespace stats_log_api_gen {
-using namespace std;
-
int write_stats_log_cpp(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl,
const string& cppNamespace, const string& importHeader,
const bool supportQ);
@@ -35,3 +34,5 @@ int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl& attrib
} // namespace stats_log_api_gen
} // namespace android
+
+#endif // ANDROID_STATS_LOG_API_GEN_NATIVE_WRITER_H
diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto
index aaa488e44fee..e658b62b8daa 100644
--- a/tools/stats_log_api_gen/test.proto
+++ b/tools/stats_log_api_gen/test.proto
@@ -58,7 +58,7 @@ message AllTypesAtom {
}
message Event {
- oneof event {
+ oneof pushed {
OutOfOrderAtom out_of_order_atom = 2;
IntAtom int_atom = 1;
AnotherIntAtom another_int_atom = 3;
@@ -74,7 +74,7 @@ message BadTypesAtom {
}
message BadTypesEvent {
- oneof event {
+ oneof pushed {
BadTypesAtom bad_types_atom = 1;
}
}
@@ -84,7 +84,7 @@ message BadSkippedFieldSingleAtom {
}
message BadSkippedFieldSingle {
- oneof event {
+ oneof pushed {
BadSkippedFieldSingleAtom bad = 1;
}
}
@@ -96,7 +96,7 @@ message BadSkippedFieldMultipleAtom {
}
message BadSkippedFieldMultiple {
- oneof event {
+ oneof pushed {
BadSkippedFieldMultipleAtom bad = 1;
}
}
@@ -107,11 +107,11 @@ message BadAttributionNodePositionAtom {
}
message BadAttributionNodePosition {
- oneof event { BadAttributionNodePositionAtom bad = 1; }
+ oneof pushed { BadAttributionNodePositionAtom bad = 1; }
}
message GoodEventWithBinaryFieldAtom {
- oneof event { GoodBinaryFieldAtom field1 = 1; }
+ oneof pushed { GoodBinaryFieldAtom field1 = 1; }
}
message ComplexField {
@@ -124,7 +124,7 @@ message GoodBinaryFieldAtom {
}
message BadEventWithBinaryFieldAtom {
- oneof event { BadBinaryFieldAtom field1 = 1; }
+ oneof pushed { BadBinaryFieldAtom field1 = 1; }
}
message BadBinaryFieldAtom {
@@ -133,7 +133,7 @@ message BadBinaryFieldAtom {
}
message BadStateAtoms {
- oneof event {
+ oneof pushed {
BadStateAtom1 bad1 = 1;
BadStateAtom2 bad2 = 2;
BadStateAtom3 bad3 = 3;
@@ -141,7 +141,7 @@ message BadStateAtoms {
}
message GoodStateAtoms {
- oneof event {
+ oneof pushed {
GoodStateAtom1 good1 = 1;
GoodStateAtom2 good2 = 2;
}
@@ -204,7 +204,7 @@ message NoModuleAtom {
}
message ModuleAtoms {
- oneof event {
+ oneof pushed {
ModuleOneAtom module_one_atom = 1 [(android.os.statsd.module) = "module1"];
ModuleTwoAtom module_two_atom = 2 [(android.os.statsd.module) = "module2"];
ModuleOneAndTwoAtom module_one_and_two_atom = 3 [
@@ -213,3 +213,24 @@ message ModuleAtoms {
NoModuleAtom no_module_atom = 4;
}
}
+
+message NotAPushNorPullAtom {
+ oneof event {
+ IntAtom int_atom = 1;
+ }
+}
+
+message AtomNotInAOneof {
+ optional IntAtom int_atom = 1;
+}
+
+message PushedAndPulledAtoms {
+ oneof pushed {
+ IntAtom int_atom_1 = 1;
+ }
+
+ oneof pulled {
+ OutOfOrderAtom out_of_order_atom = 11;
+ AnotherIntAtom another_int_atom = 10;
+ }
+}
diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp
index dbae58889333..6f78921d8f1c 100644
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ b/tools/stats_log_api_gen/test_collation.cpp
@@ -24,7 +24,6 @@ namespace android {
namespace stats_log_api_gen {
using std::map;
-using std::set;
using std::vector;
/**
@@ -32,11 +31,11 @@ using std::vector;
*/
static bool map_contains_vector(const SignatureInfoMap& s, int count, ...) {
va_list args;
- vector<java_type_t> v;
+ vector<java_type_t> v(count);
va_start(args, count);
for (int i = 0; i < count; i++) {
- v.push_back((java_type_t)va_arg(args, int));
+ v[i] = static_cast<java_type_t>(va_arg(args, int));
}
va_end(args);
@@ -222,7 +221,7 @@ TEST(CollationTest, FailOnBadBinaryFieldAtom) {
Atoms atoms;
int errorCount =
collate_atoms(BadEventWithBinaryFieldAtom::descriptor(), DEFAULT_MODULE_NAME, &atoms);
- EXPECT_TRUE(errorCount > 0);
+ EXPECT_GT(errorCount, 0);
}
TEST(CollationTest, PassOnLogFromModuleAtom) {
@@ -365,5 +364,69 @@ TEST(CollationTest, RecognizeModule1Atom) {
EXPECT_TRUE(annotation->value.boolValue);
}
+/**
+ * Test that an atom is not a pushed nor pulled atom.
+ */
+TEST(CollationTest, InvalidAtomType) {
+ Atoms atoms;
+ int errorCount = collate_atoms(NotAPushNorPullAtom::descriptor(), DEFAULT_MODULE_NAME, &atoms);
+
+ EXPECT_EQ(1, errorCount);
+}
+
+/**
+ * Test that an atom was not declared in a `oneof` field.
+ */
+TEST(CollationTest, AtomNotDeclaredInAOneof) {
+ Atoms atoms;
+ int errorCount = collate_atoms(AtomNotInAOneof::descriptor(), DEFAULT_MODULE_NAME, &atoms);
+
+ EXPECT_EQ(1, errorCount);
+}
+
+/**
+ * Test a correct collation with pushed and pulled atoms.
+ */
+TEST(CollationTest, CollatePushedAndPulledAtoms) {
+ Atoms atoms;
+ int errorCount = collate_atoms(PushedAndPulledAtoms::descriptor(), DEFAULT_MODULE_NAME, &atoms);
+
+ EXPECT_EQ(0, errorCount);
+ EXPECT_EQ(1ul, atoms.signatureInfoMap.size());
+ EXPECT_EQ(2ul, atoms.pulledAtomsSignatureInfoMap.size());
+
+ // IntAtom
+ EXPECT_MAP_CONTAINS_SIGNATURE(atoms.signatureInfoMap, JAVA_TYPE_INT);
+
+ // AnotherIntAtom
+ EXPECT_MAP_CONTAINS_SIGNATURE(atoms.pulledAtomsSignatureInfoMap, JAVA_TYPE_INT);
+
+ // OutOfOrderAtom
+ EXPECT_MAP_CONTAINS_SIGNATURE(atoms.pulledAtomsSignatureInfoMap, JAVA_TYPE_INT, JAVA_TYPE_INT);
+
+ EXPECT_EQ(3ul, atoms.decls.size());
+
+ AtomDeclSet::const_iterator atomIt = atoms.decls.begin();
+ EXPECT_EQ(1, (*atomIt)->code);
+ EXPECT_EQ("int_atom_1", (*atomIt)->name);
+ EXPECT_EQ("IntAtom", (*atomIt)->message);
+ EXPECT_NO_ENUM_FIELD((*atomIt));
+ atomIt++;
+
+ EXPECT_EQ(10, (*atomIt)->code);
+ EXPECT_EQ("another_int_atom", (*atomIt)->name);
+ EXPECT_EQ("AnotherIntAtom", (*atomIt)->message);
+ EXPECT_NO_ENUM_FIELD((*atomIt));
+ atomIt++;
+
+ EXPECT_EQ(11, (*atomIt)->code);
+ EXPECT_EQ("out_of_order_atom", (*atomIt)->name);
+ EXPECT_EQ("OutOfOrderAtom", (*atomIt)->message);
+ EXPECT_NO_ENUM_FIELD((*atomIt));
+ atomIt++;
+
+ EXPECT_EQ(atoms.decls.end(), atomIt);
+}
+
} // namespace stats_log_api_gen
} // namespace android
diff --git a/tools/stats_log_api_gen/utils.cpp b/tools/stats_log_api_gen/utils.cpp
index abb89133e58e..1eaf42acf153 100644
--- a/tools/stats_log_api_gen/utils.cpp
+++ b/tools/stats_log_api_gen/utils.cpp
@@ -16,11 +16,30 @@
#include "utils.h"
-#include "android-base/strings.h"
-
namespace android {
namespace stats_log_api_gen {
+/**
+ * Inlining this method because "android-base/strings.h" is not available on
+ * google3.
+ */
+static vector<string> Split(const string& s, const string& delimiters) {
+ GOOGLE_CHECK_NE(delimiters.size(), 0U);
+
+ vector<string> result;
+
+ size_t base = 0;
+ size_t found;
+ while (true) {
+ found = s.find_first_of(delimiters, base);
+ result.push_back(s.substr(base, found - base));
+ if (found == s.npos) break;
+ base = found + 1;
+ }
+
+ return result;
+}
+
static void build_non_chained_decl_map(const Atoms& atoms,
std::map<int, AtomDeclSet::const_iterator>* decl_map) {
for (AtomDeclSet::const_iterator atomIt = atoms.non_chained_decls.begin();
@@ -29,6 +48,20 @@ static void build_non_chained_decl_map(const Atoms& atoms,
}
}
+const map<AnnotationId, string>& get_annotation_id_constants() {
+ static const map<AnnotationId, string>* ANNOTATION_ID_CONSTANTS =
+ new map<AnnotationId, string>{
+ {ANNOTATION_ID_IS_UID, "ANNOTATION_ID_IS_UID"},
+ {ANNOTATION_ID_TRUNCATE_TIMESTAMP, "ANNOTATION_ID_TRUNCATE_TIMESTAMP"},
+ {ANNOTATION_ID_PRIMARY_FIELD, "ANNOTATION_ID_PRIMARY_FIELD"},
+ {ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, "ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID"},
+ {ANNOTATION_ID_EXCLUSIVE_STATE, "ANNOTATION_ID_EXCLUSIVE_STATE"},
+ {ANNOTATION_ID_TRIGGER_STATE_RESET, "ANNOTATION_ID_TRIGGER_STATE_RESET"},
+ {ANNOTATION_ID_STATE_NESTED, "ANNOTATION_ID_STATE_NESTED"}};
+
+ return *ANNOTATION_ID_CONSTANTS;
+}
+
/**
* Turn lower and camel case into upper case with underscores.
*/
@@ -102,15 +135,15 @@ const char* java_type_name(java_type_t type) {
// Writes namespaces for the cpp and header files, returning the number of
// namespaces written.
void write_namespace(FILE* out, const string& cppNamespaces) {
- vector<string> cppNamespaceVec = android::base::Split(cppNamespaces, ",");
- for (string cppNamespace : cppNamespaceVec) {
+ vector<string> cppNamespaceVec = Split(cppNamespaces, ",");
+ for (const string& cppNamespace : cppNamespaceVec) {
fprintf(out, "namespace %s {\n", cppNamespace.c_str());
}
}
// Writes namespace closing brackets for cpp and header files.
void write_closing_namespace(FILE* out, const string& cppNamespaces) {
- vector<string> cppNamespaceVec = android::base::Split(cppNamespaces, ",");
+ vector<string> cppNamespaceVec = Split(cppNamespaces, ",");
for (auto it = cppNamespaceVec.rbegin(); it != cppNamespaceVec.rend(); ++it) {
fprintf(out, "} // namespace %s\n", it->c_str());
}
@@ -123,7 +156,7 @@ static void write_cpp_usage(FILE* out, const string& method_name, const string&
for (vector<AtomField>::const_iterator field = atom->fields.begin();
field != atom->fields.end(); field++) {
if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
+ for (const auto& chainField : attributionDecl.fields) {
if (chainField.javaType == JAVA_TYPE_STRING) {
fprintf(out, ", const std::vector<%s>& %s", cpp_type_name(chainField.javaType),
chainField.name.c_str());
@@ -182,15 +215,15 @@ void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl&
fprintf(out, "\n");
}
-void write_native_method_signature(FILE* out, const string& methodName,
+void write_native_method_signature(FILE* out, const string& signaturePrefix,
const vector<java_type_t>& signature,
const AtomDecl& attributionDecl, const string& closer) {
- fprintf(out, "%s(int32_t code", methodName.c_str());
+ fprintf(out, "%sint32_t code", signaturePrefix.c_str());
int argIndex = 1;
for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
arg++) {
if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
+ for (const auto& chainField : attributionDecl.fields) {
if (chainField.javaType == JAVA_TYPE_STRING) {
fprintf(out, ", const std::vector<%s>& %s", cpp_type_name(chainField.javaType),
chainField.name.c_str());
@@ -222,7 +255,7 @@ void write_native_method_call(FILE* out, const string& methodName,
for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end();
arg++) {
if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
- for (auto chainField : attributionDecl.fields) {
+ for (const auto& chainField : attributionDecl.fields) {
if (chainField.javaType == JAVA_TYPE_STRING) {
fprintf(out, ", %s", chainField.name.c_str());
} else {
diff --git a/tools/stats_log_api_gen/utils.h b/tools/stats_log_api_gen/utils.h
index 73e0cb838227..13a7e2d8a54e 100644
--- a/tools/stats_log_api_gen/utils.h
+++ b/tools/stats_log_api_gen/utils.h
@@ -14,7 +14,8 @@
* limitations under the License.
*/
-#pragma once
+#ifndef ANDROID_STATS_LOG_API_GEN_UTILS_H
+#define ANDROID_STATS_LOG_API_GEN_UTILS_H
#include <stdio.h>
#include <string.h>
@@ -28,23 +29,14 @@
namespace android {
namespace stats_log_api_gen {
-using namespace std;
-
-const string DEFAULT_CPP_NAMESPACE = "android,util";
-const string DEFAULT_CPP_HEADER_IMPORT = "statslog.h";
+const char DEFAULT_CPP_NAMESPACE[] = "android,util";
+const char DEFAULT_CPP_HEADER_IMPORT[] = "statslog.h";
const int JAVA_MODULE_REQUIRES_FLOAT = 0x01;
const int JAVA_MODULE_REQUIRES_ATTRIBUTION = 0x02;
const int JAVA_MODULE_REQUIRES_KEY_VALUE_PAIRS = 0x04;
-const map<AnnotationId, string> ANNOTATION_ID_CONSTANTS = {
- {ANNOTATION_ID_IS_UID, "ANNOTATION_ID_IS_UID"},
- {ANNOTATION_ID_TRUNCATE_TIMESTAMP, "ANNOTATION_ID_TRUNCATE_TIMESTAMP"},
- {ANNOTATION_ID_PRIMARY_FIELD, "ANNOTATION_ID_PRIMARY_FIELD"},
- {ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID, "ANNOTATION_ID_PRIMARY_FIELD_FIRST_UID"},
- {ANNOTATION_ID_EXCLUSIVE_STATE, "ANNOTATION_ID_EXCLUSIVE_STATE"},
- {ANNOTATION_ID_TRIGGER_STATE_RESET, "ANNOTATION_ID_TRIGGER_STATE_RESET"},
- {ANNOTATION_ID_STATE_NESTED, "ANNOTATION_ID_STATE_NESTED"}};
+const map<AnnotationId, string>& get_annotation_id_constants();
string make_constant_name(const string& str);
@@ -59,7 +51,7 @@ void write_closing_namespace(FILE* out, const string& cppNamespaces);
void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl);
-void write_native_method_signature(FILE* out, const string& methodName,
+void write_native_method_signature(FILE* out, const string& signaturePrefix,
const vector<java_type_t>& signature,
const AtomDecl& attributionDecl, const string& closer);
@@ -81,3 +73,5 @@ int write_java_work_source_methods(FILE* out, const SignatureInfoMap& signatureI
} // namespace stats_log_api_gen
} // namespace android
+
+#endif // ANDROID_STATS_LOG_API_GEN_UTILS_H
diff --git a/tools/stringslint/stringslint.py b/tools/stringslint/stringslint.py
index afe91cda37b0..15088fc81e88 100644
--- a/tools/stringslint/stringslint.py
+++ b/tools/stringslint/stringslint.py
@@ -1,4 +1,5 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
+#-*- coding: utf-8 -*-
# Copyright (C) 2018 The Android Open Source Project
#
@@ -33,9 +34,6 @@ In general:
import re, sys, codecs
import lxml.etree as ET
-reload(sys)
-sys.setdefaultencoding('utf8')
-
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False):
@@ -118,7 +116,7 @@ def lint(path):
raw = f.read()
if len(raw.strip()) == 0:
return warnings
- tree = ET.fromstring(raw)
+ tree = ET.fromstring(bytes(raw, encoding='utf-8'))
root = tree #tree.getroot()
last_comment = None
@@ -231,6 +229,6 @@ for b in before:
if len(after) > 0:
for a in sorted(after.keys()):
- print after[a]
- print
+ print(after[a])
+ print()
sys.exit(1)
diff --git a/tools/stringslint/stringslint_sha.sh b/tools/stringslint/stringslint_sha.sh
index bd80bb4e6f3f..bd0569873197 100755
--- a/tools/stringslint/stringslint_sha.sh
+++ b/tools/stringslint/stringslint_sha.sh
@@ -1,5 +1,5 @@
#!/bin/bash
LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
git show --name-only --pretty=format: $1 | grep values/strings.xml | while read file; do
- python $LOCAL_DIR/stringslint.py <(git show $1:$file) <(git show $1^:$file)
+ python3 $LOCAL_DIR/stringslint.py <(git show $1:$file) <(git show $1^:$file)
done
diff --git a/tools/validatekeymaps/Android.bp b/tools/validatekeymaps/Android.bp
index 819e75ba1fed..15b8b4105713 100644
--- a/tools/validatekeymaps/Android.bp
+++ b/tools/validatekeymaps/Android.bp
@@ -20,12 +20,21 @@ cc_binary_host {
"libutils",
"libcutils",
"liblog",
+ "libui-types",
],
+ target: {
+ linux_glibc: {
+ static_libs: [
+ // libbinder is only available for linux
+ "libbinder",
+ ],
+ },
+ },
// This tool is prebuilt if we're doing an app-only build.
product_variables: {
unbundled_build: {
- enabled: false,
+ enabled: false,
},
},
}
diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp
index 877715a66f6d..3865076e8484 100644
--- a/tools/validatekeymaps/Main.cpp
+++ b/tools/validatekeymaps/Main.cpp
@@ -96,34 +96,32 @@ static bool validateFile(const char* filename) {
return false;
case FILETYPE_KEYLAYOUT: {
- sp<KeyLayoutMap> map;
- status_t status = KeyLayoutMap::load(filename, &map);
- if (status) {
- error("Error %d parsing key layout file.\n\n", status);
+ base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(filename);
+ if (!ret) {
+ error("Error %s parsing key layout file.\n\n", ret.error().message().c_str());
return false;
}
break;
}
case FILETYPE_KEYCHARACTERMAP: {
- sp<KeyCharacterMap> map;
- status_t status = KeyCharacterMap::load(filename,
- KeyCharacterMap::FORMAT_ANY, &map);
- if (status) {
- error("Error %d parsing key character map file.\n\n", status);
+ base::Result<std::shared_ptr<KeyCharacterMap>> ret =
+ KeyCharacterMap::load(filename, KeyCharacterMap::Format::ANY);
+ if (!ret) {
+ error("Error %s parsing key character map file.\n\n", ret.error().message().c_str());
return false;
}
break;
}
case FILETYPE_INPUTDEVICECONFIGURATION: {
- PropertyMap* map;
- status_t status = PropertyMap::load(String8(filename), &map);
- if (status) {
- error("Error %d parsing input device configuration file.\n\n", status);
+ android::base::Result<std::unique_ptr<PropertyMap>> propertyMap =
+ PropertyMap::load(String8(filename));
+ if (!propertyMap.ok()) {
+ error("Error %d parsing input device configuration file.\n\n",
+ propertyMap.error().code());
return false;
}
- delete map;
break;
}
diff --git a/tools/xmlpersistence/Android.bp b/tools/xmlpersistence/Android.bp
new file mode 100644
index 000000000000..d58d0dcdc45a
--- /dev/null
+++ b/tools/xmlpersistence/Android.bp
@@ -0,0 +1,11 @@
+java_binary_host {
+ name: "xmlpersistence_cli",
+ manifest: "manifest.txt",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "javaparser-symbol-solver",
+ "javapoet",
+ ],
+}
diff --git a/tools/xmlpersistence/OWNERS b/tools/xmlpersistence/OWNERS
new file mode 100644
index 000000000000..4f4d06a32676
--- /dev/null
+++ b/tools/xmlpersistence/OWNERS
@@ -0,0 +1 @@
+zhanghai@google.com
diff --git a/tools/xmlpersistence/manifest.txt b/tools/xmlpersistence/manifest.txt
new file mode 100644
index 000000000000..6d9771998efc
--- /dev/null
+++ b/tools/xmlpersistence/manifest.txt
@@ -0,0 +1 @@
+Main-class: MainKt
diff --git a/tools/xmlpersistence/src/main/kotlin/Generator.kt b/tools/xmlpersistence/src/main/kotlin/Generator.kt
new file mode 100644
index 000000000000..b2c5f4ac767b
--- /dev/null
+++ b/tools/xmlpersistence/src/main/kotlin/Generator.kt
@@ -0,0 +1,576 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.NameAllocator
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.TypeSpec
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
+import java.nio.charset.StandardCharsets
+import java.time.Year
+import java.util.Objects
+import javax.lang.model.element.Modifier
+
+// JavaPoet only supports line comments, and can't add a newline after file level comments.
+val FILE_HEADER = """
+ /*
+ * Copyright (C) ${Year.now().value} 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.
+ */
+
+ // Generated by xmlpersistence. DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ // @formatter:off
+""".trimIndent() + "\n\n"
+
+private val atomicFileType = ClassName.get("android.util", "AtomicFile")
+
+fun generate(persistence: PersistenceInfo): JavaFile {
+ val distinctClassFields = persistence.root.allClassFields.distinctBy { it.type }
+ val type = TypeSpec.classBuilder(persistence.name)
+ .addJavadoc(
+ """
+ Generated class implementing XML persistence for${'$'}W{@link $1T}.
+ <p>
+ This class provides atomicity for persistence via {@link $2T}, however it does not provide
+ thread safety, so please bring your own synchronization mechanism.
+ """.trimIndent(), persistence.root.type, atomicFileType
+ )
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .addField(generateFileField())
+ .addMethod(generateConstructor())
+ .addMethod(generateReadMethod(persistence.root))
+ .addMethod(generateParseMethod(persistence.root))
+ .addMethods(distinctClassFields.map { generateParseClassMethod(it) })
+ .addMethod(generateWriteMethod(persistence.root))
+ .addMethod(generateSerializeMethod(persistence.root))
+ .addMethods(distinctClassFields.map { generateSerializeClassMethod(it) })
+ .addMethod(generateDeleteMethod())
+ .build()
+ return JavaFile.builder(persistence.root.type.packageName(), type)
+ .skipJavaLangImports(true)
+ .indent(" ")
+ .build()
+}
+
+private val nonNullType = ClassName.get("android.annotation", "NonNull")
+
+private fun generateFileField(): FieldSpec =
+ FieldSpec.builder(atomicFileType, "mFile", Modifier.PRIVATE, Modifier.FINAL)
+ .addAnnotation(nonNullType)
+ .build()
+
+private fun generateConstructor(): MethodSpec =
+ MethodSpec.constructorBuilder()
+ .addJavadoc(
+ """
+ Create an instance of this class.
+
+ @param file the XML file for persistence
+ """.trimIndent()
+ )
+ .addModifiers(Modifier.PUBLIC)
+ .addParameter(
+ ParameterSpec.builder(File::class.java, "file").addAnnotation(nonNullType).build()
+ )
+ .addStatement("mFile = new \$1T(file)", atomicFileType)
+ .build()
+
+private val nullableType = ClassName.get("android.annotation", "Nullable")
+
+private val xmlPullParserType = ClassName.get("org.xmlpull.v1", "XmlPullParser")
+
+private val xmlType = ClassName.get("android.util", "Xml")
+
+private val xmlPullParserExceptionType = ClassName.get("org.xmlpull.v1", "XmlPullParserException")
+
+private fun generateReadMethod(rootField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder("read")
+ .addJavadoc(
+ """
+ Read${'$'}W{@link $1T}${'$'}Wfrom${'$'}Wthe${'$'}WXML${'$'}Wfile.
+
+ @return the persisted${'$'}W{@link $1T},${'$'}Wor${'$'}W{@code null}${'$'}Wif${'$'}Wthe${'$'}WXML${'$'}Wfile${'$'}Wdoesn't${'$'}Wexist
+ @throws IllegalArgumentException if an error occurred while reading
+ """.trimIndent(), rootField.type
+ )
+ .addAnnotation(nullableType)
+ .addModifiers(Modifier.PUBLIC)
+ .returns(rootField.type)
+ .addControlFlow("try (\$1T inputStream = mFile.openRead())", FileInputStream::class.java) {
+ addStatement("final \$1T parser = \$2T.newPullParser()", xmlPullParserType, xmlType)
+ addStatement("parser.setInput(inputStream, null)")
+ addStatement("return parse(parser)")
+ nextControlFlow("catch (\$1T e)", FileNotFoundException::class.java)
+ addStatement("return null")
+ nextControlFlow(
+ "catch (\$1T | \$2T e)", IOException::class.java, xmlPullParserExceptionType
+ )
+ addStatement("throw new IllegalArgumentException(e)")
+ }
+ .build()
+
+private val ClassFieldInfo.allClassFields: List<ClassFieldInfo>
+ get() =
+ mutableListOf<ClassFieldInfo>().apply {
+ this += this@allClassFields
+ for (field in fields) {
+ when (field) {
+ is ClassFieldInfo -> this += field.allClassFields
+ is ListFieldInfo -> this += field.element.allClassFields
+ }
+ }
+ }
+
+private fun generateParseMethod(rootField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder("parse")
+ .addAnnotation(nonNullType)
+ .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+ .returns(rootField.type)
+ .addParameter(
+ ParameterSpec.builder(xmlPullParserType, "parser").addAnnotation(nonNullType).build()
+ )
+ .addExceptions(listOf(ClassName.get(IOException::class.java), xmlPullParserExceptionType))
+ .apply {
+ addStatement("int type")
+ addStatement("int depth")
+ addStatement("int innerDepth = parser.getDepth() + 1")
+ addControlFlow(
+ "while ((type = parser.next()) != \$1T.END_DOCUMENT\$W"
+ + "&& ((depth = parser.getDepth()) >= innerDepth || type != \$1T.END_TAG))",
+ xmlPullParserType
+ ) {
+ addControlFlow(
+ "if (depth > innerDepth || type != \$1T.START_TAG)", xmlPullParserType
+ ) {
+ addStatement("continue")
+ }
+ addControlFlow(
+ "if (\$1T.equals(parser.getName(),\$W\$2S))", Objects::class.java,
+ rootField.tagName
+ ) {
+ addStatement("return \$1L(parser)", rootField.parseMethodName)
+ }
+ }
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Missing root tag <${rootField.tagName}>"
+ )
+ }
+ .build()
+
+private fun generateParseClassMethod(classField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder(classField.parseMethodName)
+ .addAnnotation(nonNullType)
+ .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+ .returns(classField.type)
+ .addParameter(
+ ParameterSpec.builder(xmlPullParserType, "parser").addAnnotation(nonNullType).build()
+ )
+ .apply {
+ val (attributeFields, tagFields) = classField.fields
+ .partition { it is PrimitiveFieldInfo || it is StringFieldInfo }
+ if (tagFields.isNotEmpty()) {
+ addExceptions(
+ listOf(ClassName.get(IOException::class.java), xmlPullParserExceptionType)
+ )
+ }
+ val nameAllocator = NameAllocator().apply {
+ newName("parser")
+ newName("type")
+ newName("depth")
+ newName("innerDepth")
+ }
+ for (field in attributeFields) {
+ val variableName = nameAllocator.newName(field.variableName, field)
+ when (field) {
+ is PrimitiveFieldInfo -> {
+ val stringVariableName =
+ nameAllocator.newName("${field.variableName}String")
+ addStatement(
+ "final String \$1L =\$Wparser.getAttributeValue(null,\$W\$2S)",
+ stringVariableName, field.attributeName
+ )
+ if (field.isRequired) {
+ addControlFlow("if (\$1L == null)", stringVariableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Missing attribute \"${field.attributeName}\""
+ )
+ }
+ }
+ val boxedType = field.type.box()
+ val parseTypeMethodName = if (field.type.isPrimitive) {
+ "parse${field.type.toString().capitalize()}"
+ } else {
+ "valueOf"
+ }
+ if (field.isRequired) {
+ addStatement(
+ "final \$1T \$2L =\$W\$3T.\$4L($5L)", field.type, variableName,
+ boxedType, parseTypeMethodName, stringVariableName
+ )
+ } else {
+ addStatement(
+ "final \$1T \$2L =\$W$3L != null ?\$W\$4T.\$5L($3L)\$W: null",
+ field.type, variableName, stringVariableName, boxedType,
+ parseTypeMethodName
+ )
+ }
+ }
+ is StringFieldInfo ->
+ addStatement(
+ "final String \$1L =\$Wparser.getAttributeValue(null,\$W\$2S)",
+ variableName, field.attributeName
+ )
+ else -> error(field)
+ }
+ }
+ if (tagFields.isNotEmpty()) {
+ for (field in tagFields) {
+ val variableName = nameAllocator.newName(field.variableName, field)
+ when (field) {
+ is ClassFieldInfo ->
+ addStatement("\$1T \$2L =\$Wnull", field.type, variableName)
+ is ListFieldInfo ->
+ addStatement(
+ "final \$1T \$2L =\$Wnew \$3T<>()", field.type, variableName,
+ ArrayList::class.java
+ )
+ else -> error(field)
+ }
+ }
+ addStatement("int type")
+ addStatement("int depth")
+ addStatement("int innerDepth = parser.getDepth() + 1")
+ addControlFlow(
+ "while ((type = parser.next()) != \$1T.END_DOCUMENT\$W"
+ + "&& ((depth = parser.getDepth()) >= innerDepth || type != \$1T.END_TAG))",
+ xmlPullParserType
+ ) {
+ addControlFlow(
+ "if (depth > innerDepth || type != \$1T.START_TAG)", xmlPullParserType
+ ) {
+ addStatement("continue")
+ }
+ addControlFlow("switch (parser.getName())") {
+ for (field in tagFields) {
+ addControlFlow("case \$1S:", field.tagName) {
+ val variableName = nameAllocator.get(field)
+ when (field) {
+ is ClassFieldInfo -> {
+ addControlFlow("if (\$1L != null)", variableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Duplicate tag \"${field.tagName}\""
+ )
+ }
+ addStatement(
+ "\$1L =\$W\$2L(parser)", variableName,
+ field.parseMethodName
+ )
+ addStatement("break")
+ }
+ is ListFieldInfo -> {
+ val elementNameAllocator = nameAllocator.clone()
+ val elementVariableName = elementNameAllocator.newName(
+ field.element.xmlName!!.toLowerCamelCase()
+ )
+ addStatement(
+ "final \$1T \$2L =\$W\$3L(parser)", field.element.type,
+ elementVariableName, field.element.parseMethodName
+ )
+ addStatement(
+ "\$1L.add(\$2L)", variableName, elementVariableName
+ )
+ addStatement("break")
+ }
+ else -> error(field)
+ }
+ }
+ }
+ }
+ }
+ }
+ for (field in tagFields.filter { it is ClassFieldInfo && it.isRequired }) {
+ addControlFlow("if ($1L == null)", nameAllocator.get(field)) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)", "Missing tag <${field.tagName}>"
+ )
+ }
+ }
+ addStatement(
+ classField.fields.joinToString(",\$W", "return new \$1T(", ")") {
+ nameAllocator.get(it)
+ }, classField.type
+ )
+ }
+ .build()
+
+private val ClassFieldInfo.parseMethodName: String
+ get() = "parse${type.simpleName().toUpperCamelCase()}"
+
+private val xmlSerializerType = ClassName.get("org.xmlpull.v1", "XmlSerializer")
+
+private fun generateWriteMethod(rootField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder("write")
+ .apply {
+ val nameAllocator = NameAllocator().apply {
+ newName("outputStream")
+ newName("serializer")
+ }
+ val parameterName = nameAllocator.newName(rootField.variableName)
+ addJavadoc(
+ """
+ Write${'$'}W{@link $1T}${'$'}Wto${'$'}Wthe${'$'}WXML${'$'}Wfile.
+
+ @param $2L the${'$'}W{@link ${'$'}1T}${'$'}Wto${'$'}Wpersist
+ """.trimIndent(), rootField.type, parameterName
+ )
+ addAnnotation(nullableType)
+ addModifiers(Modifier.PUBLIC)
+ addParameter(
+ ParameterSpec.builder(rootField.type, parameterName)
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ addStatement("\$1T outputStream = null", FileOutputStream::class.java)
+ addControlFlow("try") {
+ addStatement("outputStream = mFile.startWrite()")
+ addStatement(
+ "final \$1T serializer =\$W\$2T.newSerializer()", xmlSerializerType, xmlType
+ )
+ addStatement(
+ "serializer.setOutput(outputStream, \$1T.UTF_8.name())",
+ StandardCharsets::class.java
+ )
+ addStatement(
+ "serializer.setFeature(\$1S, true)",
+ "http://xmlpull.org/v1/doc/features.html#indent-output"
+ )
+ addStatement("serializer.startDocument(null, true)")
+ addStatement("serialize(serializer,\$W\$1L)", parameterName)
+ addStatement("serializer.endDocument()")
+ addStatement("mFile.finishWrite(outputStream)")
+ nextControlFlow("catch (Exception e)")
+ addStatement("e.printStackTrace()")
+ addStatement("mFile.failWrite(outputStream)")
+ }
+ }
+ .build()
+
+private fun generateSerializeMethod(rootField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder("serialize")
+ .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+ .addParameter(
+ ParameterSpec.builder(xmlSerializerType, "serializer")
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ .apply {
+ val nameAllocator = NameAllocator().apply { newName("serializer") }
+ val parameterName = nameAllocator.newName(rootField.variableName)
+ addParameter(
+ ParameterSpec.builder(rootField.type, parameterName)
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ addException(IOException::class.java)
+ addStatement("serializer.startTag(null, \$1S)", rootField.tagName)
+ addStatement("\$1L(serializer, \$2L)", rootField.serializeMethodName, parameterName)
+ addStatement("serializer.endTag(null, \$1S)", rootField.tagName)
+ }
+ .build()
+
+private fun generateSerializeClassMethod(classField: ClassFieldInfo): MethodSpec =
+ MethodSpec.methodBuilder(classField.serializeMethodName)
+ .addModifiers(Modifier.PRIVATE, Modifier.STATIC)
+ .addParameter(
+ ParameterSpec.builder(xmlSerializerType, "serializer")
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ .apply {
+ val nameAllocator = NameAllocator().apply {
+ newName("serializer")
+ newName("i")
+ }
+ val parameterName = nameAllocator.newName(classField.serializeParameterName)
+ addParameter(
+ ParameterSpec.builder(classField.type, parameterName)
+ .addAnnotation(nonNullType)
+ .build()
+ )
+ addException(IOException::class.java)
+ val (attributeFields, tagFields) = classField.fields
+ .partition { it is PrimitiveFieldInfo || it is StringFieldInfo }
+ for (field in attributeFields) {
+ val variableName = "$parameterName.${field.name}"
+ if (!field.isRequired) {
+ beginControlFlow("if (\$1L != null)", variableName)
+ }
+ when (field) {
+ is PrimitiveFieldInfo -> {
+ if (field.isRequired && !field.type.isPrimitive) {
+ addControlFlow("if (\$1L == null)", variableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Field \"${field.name}\" is null"
+ )
+ }
+ }
+ val stringVariableName =
+ nameAllocator.newName("${field.variableName}String")
+ addStatement(
+ "final String \$1L =\$WString.valueOf(\$2L)", stringVariableName,
+ variableName
+ )
+ addStatement(
+ "serializer.attribute(null, \$1S, \$2L)", field.attributeName,
+ stringVariableName
+ )
+ }
+ is StringFieldInfo -> {
+ if (field.isRequired) {
+ addControlFlow("if (\$1L == null)", variableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Field \"${field.name}\" is null"
+ )
+ }
+ }
+ addStatement(
+ "serializer.attribute(null, \$1S, \$2L)", field.attributeName,
+ variableName
+ )
+ }
+ else -> error(field)
+ }
+ if (!field.isRequired) {
+ endControlFlow()
+ }
+ }
+ for (field in tagFields) {
+ val variableName = "$parameterName.${field.name}"
+ if (field.isRequired) {
+ addControlFlow("if (\$1L == null)", variableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S)",
+ "Field \"${field.name}\" is null"
+ )
+ }
+ }
+ when (field) {
+ is ClassFieldInfo -> {
+ addStatement("serializer.startTag(null, \$1S)", field.tagName)
+ addStatement(
+ "\$1L(serializer, \$2L)", field.serializeMethodName, variableName
+ )
+ addStatement("serializer.endTag(null, \$1S)", field.tagName)
+ }
+ is ListFieldInfo -> {
+ val sizeVariableName = nameAllocator.newName("${field.variableName}Size")
+ addStatement(
+ "final int \$1L =\$W\$2L.size()", sizeVariableName, variableName
+ )
+ addControlFlow("for (int i = 0;\$Wi < \$1L;\$Wi++)", sizeVariableName) {
+ val elementNameAllocator = nameAllocator.clone()
+ val elementVariableName = elementNameAllocator.newName(
+ field.element.xmlName!!.toLowerCamelCase()
+ )
+ addStatement(
+ "final \$1T \$2L =\$W\$3L.get(i)", field.element.type,
+ elementVariableName, variableName
+ )
+ addControlFlow("if (\$1L == null)", elementVariableName) {
+ addStatement(
+ "throw new IllegalArgumentException(\$1S\$W+ i\$W+ \$2S)",
+ "Field element \"${field.name}[", "]\" is null"
+ )
+ }
+ addStatement("serializer.startTag(null, \$1S)", field.element.tagName)
+ addStatement(
+ "\$1L(serializer,\$W\$2L)", field.element.serializeMethodName,
+ elementVariableName
+ )
+ addStatement("serializer.endTag(null, \$1S)", field.element.tagName)
+ }
+ }
+ else -> error(field)
+ }
+ }
+ }
+ .build()
+
+private val ClassFieldInfo.serializeMethodName: String
+ get() = "serialize${type.simpleName().toUpperCamelCase()}"
+
+private val ClassFieldInfo.serializeParameterName: String
+ get() = type.simpleName().toLowerCamelCase()
+
+private val FieldInfo.variableName: String
+ get() = name.toLowerCamelCase()
+
+private val FieldInfo.attributeName: String
+ get() {
+ check(this is PrimitiveFieldInfo || this is StringFieldInfo)
+ return xmlNameOrName.toLowerCamelCase()
+ }
+
+private val FieldInfo.tagName: String
+ get() {
+ check(this is ClassFieldInfo || this is ListFieldInfo)
+ return xmlNameOrName.toLowerKebabCase()
+ }
+
+private val FieldInfo.xmlNameOrName: String
+ get() = xmlName ?: name
+
+private fun generateDeleteMethod(): MethodSpec =
+ MethodSpec.methodBuilder("delete")
+ .addJavadoc("Delete the XML file, if any.")
+ .addModifiers(Modifier.PUBLIC)
+ .addStatement("mFile.delete()")
+ .build()
+
+private inline fun MethodSpec.Builder.addControlFlow(
+ controlFlow: String,
+ vararg args: Any,
+ block: MethodSpec.Builder.() -> Unit
+): MethodSpec.Builder {
+ beginControlFlow(controlFlow, *args)
+ block()
+ endControlFlow()
+ return this
+}
diff --git a/tools/xmlpersistence/src/main/kotlin/Main.kt b/tools/xmlpersistence/src/main/kotlin/Main.kt
new file mode 100644
index 000000000000..e271f8cb9361
--- /dev/null
+++ b/tools/xmlpersistence/src/main/kotlin/Main.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+import java.io.File
+import java.nio.file.Files
+
+fun main(args: Array<String>) {
+ val showUsage = args.isEmpty() || when (args.singleOrNull()) {
+ "-h", "--help" -> true
+ else -> false
+ }
+ if (showUsage) {
+ usage()
+ return
+ }
+
+ val files = args.flatMap {
+ File(it).walk().filter { it.isFile && it.extension == "java" }.map { it.toPath() }
+ }
+ val persistences = parse(files)
+ for (persistence in persistences) {
+ val file = generate(persistence)
+ Files.newBufferedWriter(persistence.path).use {
+ it.write(FILE_HEADER)
+ file.writeTo(it)
+ }
+ }
+}
+
+private fun usage() {
+ println("Usage: xmlpersistence <FILES>")
+}
diff --git a/tools/xmlpersistence/src/main/kotlin/Parser.kt b/tools/xmlpersistence/src/main/kotlin/Parser.kt
new file mode 100644
index 000000000000..3ea12a9aa389
--- /dev/null
+++ b/tools/xmlpersistence/src/main/kotlin/Parser.kt
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+import com.github.javaparser.JavaParser
+import com.github.javaparser.ParseProblemException
+import com.github.javaparser.ParseResult
+import com.github.javaparser.ParserConfiguration
+import com.github.javaparser.ast.Node
+import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration
+import com.github.javaparser.ast.body.FieldDeclaration
+import com.github.javaparser.ast.body.TypeDeclaration
+import com.github.javaparser.ast.expr.AnnotationExpr
+import com.github.javaparser.ast.expr.Expression
+import com.github.javaparser.ast.expr.NormalAnnotationExpr
+import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr
+import com.github.javaparser.ast.expr.StringLiteralExpr
+import com.github.javaparser.resolution.declarations.ResolvedReferenceTypeDeclaration
+import com.github.javaparser.resolution.types.ResolvedPrimitiveType
+import com.github.javaparser.resolution.types.ResolvedReferenceType
+import com.github.javaparser.symbolsolver.JavaSymbolSolver
+import com.github.javaparser.symbolsolver.javaparsermodel.declarations.JavaParserClassDeclaration
+import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver
+import com.github.javaparser.symbolsolver.resolution.typesolvers.MemoryTypeSolver
+import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import java.nio.file.Path
+import java.util.Optional
+
+class PersistenceInfo(
+ val name: String,
+ val root: ClassFieldInfo,
+ val path: Path
+)
+
+sealed class FieldInfo {
+ abstract val name: String
+ abstract val xmlName: String?
+ abstract val type: TypeName
+ abstract val isRequired: Boolean
+}
+
+class PrimitiveFieldInfo(
+ override val name: String,
+ override val xmlName: String?,
+ override val type: TypeName,
+ override val isRequired: Boolean
+) : FieldInfo()
+
+class StringFieldInfo(
+ override val name: String,
+ override val xmlName: String?,
+ override val isRequired: Boolean
+) : FieldInfo() {
+ override val type: TypeName = ClassName.get(String::class.java)
+}
+
+class ClassFieldInfo(
+ override val name: String,
+ override val xmlName: String?,
+ override val type: ClassName,
+ override val isRequired: Boolean,
+ val fields: List<FieldInfo>
+) : FieldInfo()
+
+class ListFieldInfo(
+ override val name: String,
+ override val xmlName: String?,
+ override val type: ParameterizedTypeName,
+ val element: ClassFieldInfo
+) : FieldInfo() {
+ override val isRequired: Boolean = true
+}
+
+fun parse(files: List<Path>): List<PersistenceInfo> {
+ val typeSolver = CombinedTypeSolver().apply { add(ReflectionTypeSolver()) }
+ val javaParser = JavaParser(ParserConfiguration()
+ .setSymbolResolver(JavaSymbolSolver(typeSolver)))
+ val compilationUnits = files.map { javaParser.parse(it).getOrThrow() }
+ val memoryTypeSolver = MemoryTypeSolver().apply {
+ for (compilationUnit in compilationUnits) {
+ for (typeDeclaration in compilationUnit.getNodesByClass<TypeDeclaration<*>>()) {
+ val name = typeDeclaration.fullyQualifiedName.getOrNull() ?: continue
+ addDeclaration(name, typeDeclaration.resolve())
+ }
+ }
+ }
+ typeSolver.add(memoryTypeSolver)
+ return mutableListOf<PersistenceInfo>().apply {
+ for (compilationUnit in compilationUnits) {
+ val classDeclarations = compilationUnit
+ .getNodesByClass<ClassOrInterfaceDeclaration>()
+ .filter { !it.isInterface && (!it.isNestedType || it.isStatic) }
+ this += classDeclarations.mapNotNull { parsePersistenceInfo(it) }
+ }
+ }
+}
+
+private fun parsePersistenceInfo(classDeclaration: ClassOrInterfaceDeclaration): PersistenceInfo? {
+ val annotation = classDeclaration.getAnnotationByName("XmlPersistence").getOrNull()
+ ?: return null
+ val rootClassName = classDeclaration.nameAsString
+ val name = annotation.getMemberValue("value")?.stringLiteralValue
+ ?: "${rootClassName}Persistence"
+ val rootXmlName = classDeclaration.getAnnotationByName("XmlName").getOrNull()
+ ?.getMemberValue("value")?.stringLiteralValue
+ val root = parseClassFieldInfo(
+ rootXmlName ?: rootClassName, rootXmlName, true, classDeclaration
+ )
+ val path = classDeclaration.findCompilationUnit().get().storage.get().path
+ .resolveSibling("$name.java")
+ return PersistenceInfo(name, root, path)
+}
+
+private fun parseClassFieldInfo(
+ name: String,
+ xmlName: String?,
+ isRequired: Boolean,
+ classDeclaration: ClassOrInterfaceDeclaration
+): ClassFieldInfo {
+ val fields = classDeclaration.fields.filterNot { it.isStatic }.map { parseFieldInfo(it) }
+ val type = classDeclaration.resolve().typeName
+ return ClassFieldInfo(name, xmlName, type, isRequired, fields)
+}
+
+private fun parseFieldInfo(field: FieldDeclaration): FieldInfo {
+ require(field.isPublic && field.isFinal)
+ val variable = field.variables.single()
+ val name = variable.nameAsString
+ val annotations = field.annotations + variable.type.annotations
+ val annotation = annotations.getByName("XmlName")
+ val xmlName = annotation?.getMemberValue("value")?.stringLiteralValue
+ val isRequired = annotations.getByName("NonNull") != null
+ return when (val type = variable.type.resolve()) {
+ is ResolvedPrimitiveType -> {
+ val primitiveType = type.typeName
+ PrimitiveFieldInfo(name, xmlName, primitiveType, true)
+ }
+ is ResolvedReferenceType -> {
+ when (type.qualifiedName) {
+ Boolean::class.javaObjectType.name, Byte::class.javaObjectType.name,
+ Short::class.javaObjectType.name, Char::class.javaObjectType.name,
+ Integer::class.javaObjectType.name, Long::class.javaObjectType.name,
+ Float::class.javaObjectType.name, Double::class.javaObjectType.name ->
+ PrimitiveFieldInfo(name, xmlName, type.typeName, isRequired)
+ String::class.java.name -> StringFieldInfo(name, xmlName, isRequired)
+ List::class.java.name -> {
+ requireNotNull(xmlName)
+ val elementType = type.typeParametersValues().single()
+ require(elementType is ResolvedReferenceType)
+ val listType = ParameterizedTypeName.get(
+ ClassName.get(List::class.java), elementType.typeName
+ )
+ val element = parseClassFieldInfo(
+ "(element)", xmlName, true, elementType.classDeclaration
+ )
+ ListFieldInfo(name, xmlName, listType, element)
+ }
+ else -> parseClassFieldInfo(name, xmlName, isRequired, type.classDeclaration)
+ }
+ }
+ else -> error(type)
+ }
+}
+
+private fun <T> ParseResult<T>.getOrThrow(): T =
+ if (isSuccessful) {
+ result.get()
+ } else {
+ throw ParseProblemException(problems)
+ }
+
+private inline fun <reified T : Node> Node.getNodesByClass(): List<T> =
+ getNodesByClass(T::class.java)
+
+private fun <T : Node> Node.getNodesByClass(klass: Class<T>): List<T> = mutableListOf<T>().apply {
+ if (klass.isInstance(this@getNodesByClass)) {
+ this += klass.cast(this@getNodesByClass)
+ }
+ for (childNode in childNodes) {
+ this += childNode.getNodesByClass(klass)
+ }
+}
+
+private fun <T> Optional<T>.getOrNull(): T? = orElse(null)
+
+private fun List<AnnotationExpr>.getByName(name: String): AnnotationExpr? =
+ find { it.name.identifier == name }
+
+private fun AnnotationExpr.getMemberValue(name: String): Expression? =
+ when (this) {
+ is NormalAnnotationExpr -> pairs.find { it.nameAsString == name }?.value
+ is SingleMemberAnnotationExpr -> if (name == "value") memberValue else null
+ else -> null
+ }
+
+private val Expression.stringLiteralValue: String
+ get() {
+ require(this is StringLiteralExpr)
+ return value
+ }
+
+private val ResolvedReferenceType.classDeclaration: ClassOrInterfaceDeclaration
+ get() {
+ val resolvedClassDeclaration = typeDeclaration
+ require(resolvedClassDeclaration is JavaParserClassDeclaration)
+ return resolvedClassDeclaration.wrappedNode
+ }
+
+private val ResolvedPrimitiveType.typeName: TypeName
+ get() =
+ when (this) {
+ ResolvedPrimitiveType.BOOLEAN -> TypeName.BOOLEAN
+ ResolvedPrimitiveType.BYTE -> TypeName.BYTE
+ ResolvedPrimitiveType.SHORT -> TypeName.SHORT
+ ResolvedPrimitiveType.CHAR -> TypeName.CHAR
+ ResolvedPrimitiveType.INT -> TypeName.INT
+ ResolvedPrimitiveType.LONG -> TypeName.LONG
+ ResolvedPrimitiveType.FLOAT -> TypeName.FLOAT
+ ResolvedPrimitiveType.DOUBLE -> TypeName.DOUBLE
+ }
+
+// This doesn't support type parameters.
+private val ResolvedReferenceType.typeName: TypeName
+ get() = typeDeclaration.typeName
+
+private val ResolvedReferenceTypeDeclaration.typeName: ClassName
+ get() {
+ val packageName = packageName
+ val classNames = className.split(".")
+ val topLevelClassName = classNames.first()
+ val nestedClassNames = classNames.drop(1)
+ return ClassName.get(packageName, topLevelClassName, *nestedClassNames.toTypedArray())
+ }
diff --git a/tools/xmlpersistence/src/main/kotlin/StringCaseExtensions.kt b/tools/xmlpersistence/src/main/kotlin/StringCaseExtensions.kt
new file mode 100644
index 000000000000..b4bdbba7170b
--- /dev/null
+++ b/tools/xmlpersistence/src/main/kotlin/StringCaseExtensions.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+import java.util.Locale
+
+private val camelHumpBoundary = Regex(
+ "-"
+ + "|_"
+ + "|(?<=[0-9])(?=[^0-9])"
+ + "|(?<=[A-Z])(?=[^A-Za-z]|[A-Z][a-z])"
+ + "|(?<=[a-z])(?=[^a-z])"
+)
+
+private fun String.toCamelHumps(): List<String> = split(camelHumpBoundary)
+
+fun String.toUpperCamelCase(): String =
+ toCamelHumps().joinToString("") { it.toLowerCase(Locale.ROOT).capitalize(Locale.ROOT) }
+
+fun String.toLowerCamelCase(): String = toUpperCamelCase().decapitalize(Locale.ROOT)
+
+fun String.toUpperKebabCase(): String =
+ toCamelHumps().joinToString("-") { it.toUpperCase(Locale.ROOT) }
+
+fun String.toLowerKebabCase(): String =
+ toCamelHumps().joinToString("-") { it.toLowerCase(Locale.ROOT) }
+
+fun String.toUpperSnakeCase(): String =
+ toCamelHumps().joinToString("_") { it.toUpperCase(Locale.ROOT) }
+
+fun String.toLowerSnakeCase(): String =
+ toCamelHumps().joinToString("_") { it.toLowerCase(Locale.ROOT) }