diff options
Diffstat (limited to 'tools')
68 files changed, 1640 insertions, 497 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/Resources.proto b/tools/aapt2/Resources.proto index ab9ce66b0ae3..b1e1a77e1224 100644 --- a/tools/aapt2/Resources.proto +++ b/tools/aapt2/Resources.proto @@ -168,6 +168,7 @@ message OverlayableItem { ODM = 6; OEM = 7; ACTOR = 8; + CONFIG_SIGNATURE = 9; } // The location of the <item> declaration in source. 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..71c70da96109 100644 --- a/tools/aapt2/dump/DumpManifest.cpp +++ b/tools/aapt2/dump/DumpManifest.cpp @@ -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; @@ -1405,6 +1405,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. @@ -1848,7 +1871,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 +1880,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)); } @@ -2245,6 +2268,7 @@ T* ElementCast(ManifestExtractor::Element* element) { {"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}, + {"uses-native-library", std::is_base_of<UsesNativeLibrary, T>::value}, }; auto check = kTagCheck.find(element->tag()); @@ -2295,6 +2319,7 @@ std::unique_ptr<ManifestExtractor::Element> ManifestExtractor::Element::Inflate( {"uses-package", &CreateType<UsesPackage>}, {"additional-certificate", &CreateType<AdditionalCertificate>}, {"uses-sdk", &CreateType<UsesSdkBadging>}, + {"uses-native-library", &CreateType<UsesNativeLibrary>}, }; // Attempt to map the xml tag to a element inflater 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/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp index 59627ce579af..6932baf76c75 100644 --- a/tools/aapt2/format/binary/TableFlattener_test.cpp +++ b/tools/aapt2/format/binary/TableFlattener_test.cpp @@ -776,6 +776,7 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) { OverlayableItem overlayable_item_three(group_one); overlayable_item_three.policies |= PolicyFlags::SIGNATURE; overlayable_item_three.policies |= PolicyFlags::ACTOR_SIGNATURE; + overlayable_item_three.policies |= PolicyFlags::CONFIG_SIGNATURE; std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() @@ -830,7 +831,8 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) { EXPECT_EQ(result_overlayable.overlayable->name, "OtherName"); EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization"); EXPECT_EQ(result_overlayable.policies, PolicyFlags::SIGNATURE - | PolicyFlags::ACTOR_SIGNATURE); + | PolicyFlags::ACTOR_SIGNATURE + | PolicyFlags::CONFIG_SIGNATURE); } TEST_F(TableFlattenerTest, FlattenOverlayableNoPolicyFails) { diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp index 2fd01d7f3dee..06ac9e5dc5c4 100644 --- a/tools/aapt2/format/proto/ProtoDeserialize.cpp +++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp @@ -404,6 +404,9 @@ bool DeserializeOverlayableItemFromPb(const pb::OverlayableItem& pb_overlayable, case pb::OverlayableItem::ACTOR: out_overlayable->policies |= PolicyFlags::ACTOR_SIGNATURE; break; + case pb::OverlayableItem::CONFIG_SIGNATURE: + out_overlayable->policies |= PolicyFlags::CONFIG_SIGNATURE; + break; default: *out_error = "unknown overlayable policy"; return false; @@ -446,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). @@ -487,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 ba6df22af9d3..98c517510028 100644 --- a/tools/aapt2/format/proto/ProtoSerialize.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize.cpp @@ -325,6 +325,9 @@ static void SerializeOverlayableItemToPb(const OverlayableItem& overlayable_item if (overlayable_item.policies & PolicyFlags::ACTOR_SIGNATURE) { pb_overlayable_item->add_policy(pb::OverlayableItem::ACTOR); } + if (overlayable_item.policies & PolicyFlags::CONFIG_SIGNATURE) { + pb_overlayable_item->add_policy(pb::OverlayableItem::CONFIG_SIGNATURE); + } if (source_pool != nullptr) { SerializeSourceToPb(overlayable_item.source, source_pool, @@ -356,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..dac21d7e9200 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"]; @@ -426,6 +422,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 +570,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/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..8ab302a50662 --- /dev/null +++ b/tools/powerstats/PowerStatsServiceProtoParser.java @@ -0,0 +1,95 @@ +/* + * 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 printRailInfo(PowerStatsServiceProto proto) { + String csvHeader = new String(); + for (int i = 0; i < proto.getRailInfoCount(); i++) { + RailInfoProto railInfo = proto.getRailInfo(i); + csvHeader += "Index" + "," + + "Timestamp" + "," + + railInfo.getRailName() + "/" + railInfo.getSubsysName() + ","; + } + System.out.println(csvHeader); + } + + private static void printEnergyData(PowerStatsServiceProto proto) { + int railInfoCount = proto.getRailInfoCount(); + + if (railInfoCount > 0) { + int energyDataCount = proto.getEnergyDataCount(); + int energyDataSetCount = energyDataCount / railInfoCount; + + for (int i = 0; i < energyDataSetCount; i++) { + String csvRow = new String(); + for (int j = 0; j < railInfoCount; j++) { + EnergyDataProto energyData = proto.getEnergyData(i * railInfoCount + j); + csvRow += energyData.getIndex() + "," + + energyData.getTimestampMs() + "," + + energyData.getEnergyUws() + ","; + } + System.out.println(csvRow); + } + } else { + System.out.println("Error: railInfoCount is zero"); + } + } + + private static void generateCsvFile(String pathToIncidentReport) { + try { + IncidentReportProto irProto = + IncidentReportProto.parseFrom(new FileInputStream(pathToIncidentReport)); + + if (irProto.hasIncidentReport()) { + PowerStatsServiceProto pssProto = irProto.getIncidentReport(); + printRailInfo(pssProto); + printEnergyData(pssProto); + } else { + System.out.println("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/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/validatekeymaps/Android.bp b/tools/validatekeymaps/Android.bp index 819e75ba1fed..61ce44c3b5b9 100644 --- a/tools/validatekeymaps/Android.bp +++ b/tools/validatekeymaps/Android.bp @@ -20,6 +20,7 @@ cc_binary_host { "libutils", "libcutils", "liblog", + "libui-types", ], // This tool is prebuilt if we're doing an app-only build. diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp index 877715a66f6d..7c150f9c8db9 100644 --- a/tools/validatekeymaps/Main.cpp +++ b/tools/validatekeymaps/Main.cpp @@ -96,21 +96,19 @@ 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; |