summaryrefslogtreecommitdiff
path: root/tools/aapt2
diff options
context:
space:
mode:
Diffstat (limited to 'tools/aapt2')
-rw-r--r--tools/aapt2/Android.bp5
-rw-r--r--tools/aapt2/AppInfo.h4
-rw-r--r--tools/aapt2/Configuration.proto1
-rw-r--r--tools/aapt2/Debug.cpp88
-rw-r--r--tools/aapt2/Debug.h1
-rw-r--r--tools/aapt2/Resource.h9
-rw-r--r--tools/aapt2/ResourceParser.cpp41
-rw-r--r--tools/aapt2/ResourceParser_test.cpp44
-rw-r--r--tools/aapt2/ResourceTable.cpp30
-rw-r--r--tools/aapt2/ResourceTable.h38
-rw-r--r--tools/aapt2/ResourceTable_test.cpp20
-rw-r--r--tools/aapt2/ResourceUtils.cpp19
-rw-r--r--tools/aapt2/ResourceUtils_test.cpp14
-rw-r--r--tools/aapt2/ResourceValues.cpp10
-rw-r--r--tools/aapt2/ResourceValues.h1
-rw-r--r--tools/aapt2/ResourceValues_test.cpp52
-rw-r--r--tools/aapt2/Resources.proto13
-rw-r--r--tools/aapt2/ResourcesInternal.proto1
-rw-r--r--tools/aapt2/SdkConstants.cpp2
-rw-r--r--tools/aapt2/SdkConstants.h1
-rw-r--r--tools/aapt2/cmd/Compile.cpp18
-rw-r--r--tools/aapt2/cmd/Compile_test.cpp2
-rw-r--r--tools/aapt2/cmd/Convert.cpp6
-rw-r--r--tools/aapt2/cmd/Diff.cpp6
-rw-r--r--tools/aapt2/cmd/Dump.cpp17
-rw-r--r--tools/aapt2/cmd/Dump.h12
-rw-r--r--tools/aapt2/cmd/Link.cpp199
-rw-r--r--tools/aapt2/cmd/Link.h31
-rw-r--r--tools/aapt2/cmd/Link_test.cpp244
-rw-r--r--tools/aapt2/cmd/Optimize.cpp56
-rw-r--r--tools/aapt2/cmd/Optimize.h17
-rw-r--r--tools/aapt2/cmd/Optimize_test.cpp68
-rw-r--r--tools/aapt2/cmd/Util.cpp4
-rw-r--r--tools/aapt2/cmd/Util_test.cpp22
-rw-r--r--tools/aapt2/dump/DumpManifest.cpp20
-rw-r--r--tools/aapt2/format/Archive.cpp9
-rw-r--r--tools/aapt2/format/Archive_test.cpp209
-rw-r--r--tools/aapt2/format/binary/BinaryResourceParser.cpp32
-rw-r--r--tools/aapt2/format/binary/TableFlattener.cpp62
-rw-r--r--tools/aapt2/format/binary/TableFlattener.h5
-rw-r--r--tools/aapt2/format/binary/TableFlattener_test.cpp124
-rw-r--r--tools/aapt2/format/binary/XmlFlattener_test.cpp6
-rw-r--r--tools/aapt2/format/proto/ProtoDeserialize.cpp22
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize.cpp64
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize.h8
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize_test.cpp94
-rw-r--r--tools/aapt2/integration-tests/MergeOnlyTest/Android.mk2
-rw-r--r--tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk29
-rw-r--r--tools/aapt2/integration-tests/MergeOnlyTest/App/AndroidManifest.xml23
-rw-r--r--tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk28
-rw-r--r--tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/AndroidManifest.xml17
-rw-r--r--tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/res/layout/activity.xml25
-rw-r--r--tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/res/values/values.xml43
-rw-r--r--tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk28
-rw-r--r--tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/AndroidManifest.xml28
-rw-r--r--tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/layout/activity.xml25
-rw-r--r--tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/layout/includer.xml25
-rw-r--r--tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/values/values.xml29
-rw-r--r--tools/aapt2/io/Util.cpp2
-rw-r--r--tools/aapt2/io/Util.h10
-rw-r--r--tools/aapt2/java/AnnotationProcessor.cpp31
-rw-r--r--tools/aapt2/java/AnnotationProcessor.h3
-rw-r--r--tools/aapt2/java/AnnotationProcessor_test.cpp15
-rw-r--r--tools/aapt2/java/JavaClassGenerator.cpp17
-rw-r--r--tools/aapt2/java/JavaClassGenerator_test.cpp12
-rw-r--r--tools/aapt2/java/ProguardRules.cpp46
-rw-r--r--tools/aapt2/java/ProguardRules.h6
-rw-r--r--tools/aapt2/java/ProguardRules_test.cpp21
-rw-r--r--tools/aapt2/link/ManifestFixer.cpp46
-rw-r--r--tools/aapt2/link/ManifestFixer_test.cpp30
-rw-r--r--tools/aapt2/link/ReferenceLinker.cpp43
-rw-r--r--tools/aapt2/link/ReferenceLinker.h7
-rw-r--r--tools/aapt2/link/ReferenceLinker_test.cpp65
-rw-r--r--tools/aapt2/link/TableMerger.cpp37
-rw-r--r--tools/aapt2/link/TableMerger.h2
-rw-r--r--tools/aapt2/link/TableMerger_test.cpp137
-rw-r--r--tools/aapt2/link/XmlReferenceLinker.cpp11
-rw-r--r--tools/aapt2/link/XmlReferenceLinker_test.cpp63
-rw-r--r--tools/aapt2/optimize/MultiApkGenerator.cpp4
-rw-r--r--tools/aapt2/optimize/ResourceDeduper.cpp9
-rw-r--r--tools/aapt2/optimize/ResourceDeduper_test.cpp47
-rw-r--r--tools/aapt2/optimize/ResourcePathShortener.cpp33
-rw-r--r--tools/aapt2/optimize/ResourcePathShortener_test.cpp99
-rw-r--r--tools/aapt2/process/IResourceTableConsumer.h2
-rw-r--r--tools/aapt2/process/SymbolTable.cpp20
-rw-r--r--tools/aapt2/process/SymbolTable.h2
-rw-r--r--tools/aapt2/test/Context.h14
-rw-r--r--tools/aapt2/test/Fixture.cpp25
-rw-r--r--tools/aapt2/test/Fixture.h5
89 files changed, 2297 insertions, 620 deletions
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index a2709bd545b2..ade0dc4d9c42 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -57,9 +57,10 @@ cc_defaults {
"libziparchive",
"libpng",
"libbase",
- "libprotobuf-cpp-lite",
+ "libprotobuf-cpp-full",
"libz",
"libbuildversion",
+ "libidmap2_policies",
],
stl: "libc++_static",
group_static_libs: true,
@@ -198,6 +199,7 @@ cc_test_host {
cc_binary_host {
name: "aapt2",
srcs: ["Main.cpp"] + toolSources,
+ use_version_lib: true,
static_libs: ["libaapt2"],
defaults: ["aapt2_defaults"],
}
@@ -210,6 +212,7 @@ genrule {
tools: [":soong_zip"],
srcs: [
"Configuration.proto",
+ "ResourcesInternal.proto",
"Resources.proto",
],
out: ["aapt2-protos.zip"],
diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h
index 75123537116f..d3ca357b0305 100644
--- a/tools/aapt2/AppInfo.h
+++ b/tools/aapt2/AppInfo.h
@@ -17,6 +17,7 @@
#ifndef AAPT_APP_INFO_H
#define AAPT_APP_INFO_H
+#include <set>
#include <string>
#include "util/Maybe.h"
@@ -42,6 +43,9 @@ struct AppInfo {
// The app's split name, if it is a split.
Maybe<std::string> split_name;
+
+ // The split names that this split depends on.
+ std::set<std::string> split_name_dependencies;
};
} // namespace aapt
diff --git a/tools/aapt2/Configuration.proto b/tools/aapt2/Configuration.proto
index fc636a43ec40..8a4644c9a219 100644
--- a/tools/aapt2/Configuration.proto
+++ b/tools/aapt2/Configuration.proto
@@ -19,7 +19,6 @@ syntax = "proto3";
package aapt.pb;
option java_package = "com.android.aapt";
-option optimize_for = LITE_RUNTIME;
// A description of the requirements a device must have in order for a
// resource to be matched and selected.
diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp
index 3da22b4fb9fa..439f231193df 100644
--- a/tools/aapt2/Debug.cpp
+++ b/tools/aapt2/Debug.cpp
@@ -32,10 +32,16 @@
#include "text/Printer.h"
#include "util/Util.h"
+#include "idmap2/Policies.h"
+
using ::aapt::text::Printer;
using ::android::StringPiece;
using ::android::base::StringPrintf;
+using android::idmap2::policy::kPolicyStringToFlag;
+
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
namespace {
@@ -178,19 +184,17 @@ class ValueBodyPrinter : public ConstValueVisitor {
void Visit(const Array* array) override {
const size_t count = array->elements.size();
printer_->Print("[");
- if (count > 0) {
- for (size_t i = 0u; i < count; i++) {
- if (i != 0u && i % 4u == 0u) {
- printer_->Println();
- printer_->Print(" ");
- }
- PrintItem(*array->elements[i]);
- if (i != count - 1) {
- printer_->Print(", ");
- }
+ for (size_t i = 0u; i < count; i++) {
+ if (i != 0u && i % 4u == 0u) {
+ printer_->Println();
+ printer_->Print(" ");
+ }
+ PrintItem(*array->elements[i]);
+ if (i != count - 1) {
+ printer_->Print(", ");
}
- printer_->Println("]");
}
+ printer_->Println("]");
}
void Visit(const Plural* plural) override {
@@ -314,6 +318,10 @@ void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions&
break;
}
+ if (entry->overlayable_item) {
+ printer->Print(" OVERLAYABLE");
+ }
+
printer->Println();
if (options.show_values) {
@@ -527,4 +535,62 @@ void Debug::DumpXml(const xml::XmlResource& doc, Printer* printer) {
doc.root->Accept(&xml_visitor);
}
+struct DumpOverlayableEntry {
+ std::string overlayable_section;
+ std::string policy_subsection;
+ std::string resource_name;
+};
+
+void Debug::DumpOverlayable(const ResourceTable& table, text::Printer* printer) {
+ std::vector<DumpOverlayableEntry> items;
+ for (const auto& package : table.packages) {
+ for (const auto& type : package->types) {
+ for (const auto& entry : type->entries) {
+ if (entry->overlayable_item) {
+ const auto& overlayable_item = entry->overlayable_item.value();
+ const auto overlayable_section = StringPrintf(R"(name="%s" actor="%s")",
+ overlayable_item.overlayable->name.c_str(),
+ overlayable_item.overlayable->actor.c_str());
+ const auto policy_subsection = StringPrintf(R"(policies="%s")",
+ android::idmap2::policy::PoliciesToDebugString(overlayable_item.policies).c_str());
+ const auto value =
+ StringPrintf("%s/%s", to_string(type->type).data(), entry->name.c_str());
+ items.push_back(DumpOverlayableEntry{overlayable_section, policy_subsection, value});
+ }
+ }
+ }
+ }
+
+ std::sort(items.begin(), items.end(),
+ [](const DumpOverlayableEntry& a, const DumpOverlayableEntry& b) {
+ if (a.overlayable_section != b.overlayable_section) {
+ return a.overlayable_section < b.overlayable_section;
+ }
+ if (a.policy_subsection != b.policy_subsection) {
+ return a.policy_subsection < b.policy_subsection;
+ }
+ return a.resource_name < b.resource_name;
+ });
+
+ std::string last_overlayable_section;
+ std::string last_policy_subsection;
+ for (const auto& item : items) {
+ if (last_overlayable_section != item.overlayable_section) {
+ printer->Println(item.overlayable_section);
+ last_overlayable_section = item.overlayable_section;
+ }
+ if (last_policy_subsection != item.policy_subsection) {
+ printer->Indent();
+ printer->Println(item.policy_subsection);
+ last_policy_subsection = item.policy_subsection;
+ printer->Undent();
+ }
+ printer->Indent();
+ printer->Indent();
+ printer->Println(item.resource_name);
+ printer->Undent();
+ printer->Undent();
+ }
+}
+
} // namespace aapt
diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h
index a43197cacf7b..9443d606d7e5 100644
--- a/tools/aapt2/Debug.h
+++ b/tools/aapt2/Debug.h
@@ -39,6 +39,7 @@ struct Debug {
static void DumpHex(const void* data, size_t len);
static void DumpXml(const xml::XmlResource& doc, text::Printer* printer);
static void DumpResStringPool(const android::ResStringPool* pool, text::Printer* printer);
+ static void DumpOverlayable(const ResourceTable& table, text::Printer* printer);
};
} // namespace aapt
diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h
index a8ba202d08f7..4e051a37f3ed 100644
--- a/tools/aapt2/Resource.h
+++ b/tools/aapt2/Resource.h
@@ -142,10 +142,11 @@ struct ResourceId {
ResourceId(uint32_t res_id); // NOLINT(google-explicit-constructor)
ResourceId(uint8_t p, uint8_t t, uint16_t e);
- bool is_valid() const;
+ // Returns true if the ID is a valid ID that is not dynamic (package ID cannot be 0)
+ bool is_valid_static() const;
// Returns true if the ID is a valid ID or dynamic ID (package ID can be 0).
- bool is_valid_dynamic() const;
+ bool is_valid() const;
uint8_t package_id() const;
uint8_t type_id() const;
@@ -228,11 +229,11 @@ inline ResourceId::ResourceId(uint32_t res_id) : id(res_id) {}
inline ResourceId::ResourceId(uint8_t p, uint8_t t, uint16_t e)
: id((p << 24) | (t << 16) | e) {}
-inline bool ResourceId::is_valid() const {
+inline bool ResourceId::is_valid_static() const {
return (id & 0xff000000u) != 0 && (id & 0x00ff0000u) != 0;
}
-inline bool ResourceId::is_valid_dynamic() const {
+inline bool ResourceId::is_valid() const {
return (id & 0x00ff0000u) != 0;
}
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index b34c3d59d6a5..931a14b1f650 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -32,11 +32,15 @@
#include "util/Util.h"
#include "xml/XmlPullParser.h"
+#include "idmap2/Policies.h"
+
using ::aapt::ResourceUtils::StringBuilder;
using ::aapt::text::Utf8Iterator;
using ::android::ConfigDescription;
using ::android::StringPiece;
+using android::idmap2::policy::kPolicyStringToFlag;
+
namespace aapt {
constexpr const char* sXliffNamespaceUri = "urn:oasis:names:tc:xliff:document:1.2";
@@ -770,16 +774,14 @@ std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser,
return std::move(string);
}
- // If the text is empty, and the value is not allowed to be a string, encode it as a @null.
- if (util::TrimWhitespace(raw_value).empty()) {
- return ResourceUtils::MakeNull();
- }
-
if (allow_raw_value) {
// We can't parse this so return a RawString if we are allowed.
return util::make_unique<RawString>(
table_->string_pool.MakeRef(util::TrimWhitespace(raw_value),
StringPool::Context(config_)));
+ } else if (util::TrimWhitespace(raw_value).empty()) {
+ // If the text is empty, and the value is not allowed to be a string, encode it as a @null.
+ return ResourceUtils::MakeNull();
}
return {};
}
@@ -1067,7 +1069,7 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
bool error = false;
std::string comment;
- OverlayableItem::PolicyFlags current_policies = OverlayableItem::Policy::kNone;
+ PolicyFlags current_policies = PolicyFlags::NONE;
const size_t start_depth = parser->depth();
while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
xml::XmlPullParser::Event event = parser->event();
@@ -1077,7 +1079,7 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
} else if (event == xml::XmlPullParser::Event::kEndElement
&& parser->depth() == start_depth + 1) {
// Clear the current policies when exiting the <policy> tags
- current_policies = OverlayableItem::Policy::kNone;
+ current_policies = PolicyFlags::NONE;
continue;
} else if (event == xml::XmlPullParser::Event::kComment) {
// Retrieve the comment of individual <item> tags
@@ -1092,7 +1094,7 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
const std::string& element_name = parser->element_name();
const std::string& element_namespace = parser->element_namespace();
if (element_namespace.empty() && element_name == "item") {
- if (current_policies == OverlayableItem::Policy::kNone) {
+ if (current_policies == PolicyFlags::NONE) {
diag_->Error(DiagMessage(element_source)
<< "<item> within an <overlayable> must be inside a <policy> block");
error = true;
@@ -1137,7 +1139,7 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
out_resource->child_resources.push_back(std::move(child_resource));
} else if (element_namespace.empty() && element_name == "policy") {
- if (current_policies != OverlayableItem::Policy::kNone) {
+ if (current_policies != PolicyFlags::NONE) {
// If the policy list is not empty, then we are currently inside a policy element
diag_->Error(DiagMessage(element_source) << "<policy> blocks cannot be recursively nested");
error = true;
@@ -1145,21 +1147,14 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
} else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
// Parse the polices separated by vertical bar characters to allow for specifying multiple
// policies. Items within the policy tag will have the specified policy.
- static const auto kPolicyMap =
- ImmutableMap<StringPiece, OverlayableItem::Policy>::CreatePreSorted({
- {"odm", OverlayableItem::Policy::kOdm},
- {"oem", OverlayableItem::Policy::kOem},
- {"product", OverlayableItem::Policy::kProduct},
- {"public", OverlayableItem::Policy::kPublic},
- {"signature", OverlayableItem::Policy::kSignature},
- {"system", OverlayableItem::Policy::kSystem},
- {"vendor", OverlayableItem::Policy::kVendor},
- });
-
for (const StringPiece& part : util::Tokenize(maybe_type.value(), '|')) {
StringPiece trimmed_part = util::TrimWhitespace(part);
- const auto policy = kPolicyMap.find(trimmed_part);
- if (policy == kPolicyMap.end()) {
+ const auto policy = std::find_if(kPolicyStringToFlag.begin(),
+ kPolicyStringToFlag.end(),
+ [trimmed_part](const auto& it) {
+ return trimmed_part == it.first;
+ });
+ if (policy == kPolicyStringToFlag.end()) {
diag_->Error(DiagMessage(element_source)
<< "<policy> has unsupported type '" << trimmed_part << "'");
error = true;
@@ -1391,7 +1386,7 @@ Maybe<Attribute::Symbol> ResourceParser::ParseEnumOrFlagItem(
return Attribute::Symbol{
Reference(ResourceNameRef({}, ResourceType::kId, maybe_name.value())),
- val.data};
+ val.data, val.dataType};
}
bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) {
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 46ad7cbe49e9..9b70079a98c9 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -41,6 +41,8 @@ using ::testing::Pointee;
using ::testing::SizeIs;
using ::testing::StrEq;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
constexpr const char* kXmlPreamble = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
@@ -401,7 +403,7 @@ TEST_F(ResourceParserTest, ParseEnumAttr) {
std::string input = R"(
<attr name="foo">
<enum name="bar" value="0"/>
- <enum name="bat" value="1"/>
+ <enum name="bat" value="0x1"/>
<enum name="baz" value="2"/>
</attr>)";
ASSERT_TRUE(TestParse(input));
@@ -414,14 +416,17 @@ TEST_F(ResourceParserTest, ParseEnumAttr) {
ASSERT_TRUE(enum_attr->symbols[0].symbol.name);
EXPECT_THAT(enum_attr->symbols[0].symbol.name.value().entry, Eq("bar"));
EXPECT_THAT(enum_attr->symbols[0].value, Eq(0u));
+ EXPECT_THAT(enum_attr->symbols[0].type, Eq(Res_value::TYPE_INT_DEC));
ASSERT_TRUE(enum_attr->symbols[1].symbol.name);
EXPECT_THAT(enum_attr->symbols[1].symbol.name.value().entry, Eq("bat"));
EXPECT_THAT(enum_attr->symbols[1].value, Eq(1u));
+ EXPECT_THAT(enum_attr->symbols[1].type, Eq(Res_value::TYPE_INT_HEX));
ASSERT_TRUE(enum_attr->symbols[2].symbol.name);
EXPECT_THAT(enum_attr->symbols[2].symbol.name.value().entry, Eq("baz"));
EXPECT_THAT(enum_attr->symbols[2].value, Eq(2u));
+ EXPECT_THAT(enum_attr->symbols[2].type, Eq(Res_value::TYPE_INT_DEC));
}
TEST_F(ResourceParserTest, ParseFlagAttr) {
@@ -956,7 +961,7 @@ TEST_F(ResourceParserTest, ParseOverlayable) {
OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::SIGNATURE));
search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar"));
ASSERT_TRUE(search_result);
@@ -965,7 +970,7 @@ TEST_F(ResourceParserTest, ParseOverlayable) {
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::SIGNATURE));
}
TEST_F(ResourceParserTest, ParseOverlayableRequiresName) {
@@ -1002,6 +1007,9 @@ TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
<policy type="oem">
<item type="string" name="buz" />
</policy>
+ <policy type="actor">
+ <item type="string" name="actor" />
+ </policy>
</overlayable>)";
ASSERT_TRUE(TestParse(input));
@@ -1011,7 +1019,7 @@ TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
ASSERT_TRUE(search_result.value().entry->overlayable_item);
OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::PRODUCT_PARTITION));
search_result = table_.FindResource(test::ParseNameOrDie("string/fiz"));
ASSERT_TRUE(search_result);
@@ -1019,7 +1027,7 @@ TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSystem));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::SYSTEM_PARTITION));
search_result = table_.FindResource(test::ParseNameOrDie("string/fuz"));
ASSERT_TRUE(search_result);
@@ -1027,7 +1035,7 @@ TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::VENDOR_PARTITION));
search_result = table_.FindResource(test::ParseNameOrDie("string/faz"));
ASSERT_TRUE(search_result);
@@ -1035,7 +1043,7 @@ TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::PUBLIC));
search_result = table_.FindResource(test::ParseNameOrDie("string/foz"));
ASSERT_TRUE(search_result);
@@ -1043,7 +1051,7 @@ TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::SIGNATURE));
search_result = table_.FindResource(test::ParseNameOrDie("string/biz"));
ASSERT_TRUE(search_result);
@@ -1051,7 +1059,7 @@ TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kOdm));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::ODM_PARTITION));
search_result = table_.FindResource(test::ParseNameOrDie("string/buz"));
ASSERT_TRUE(search_result);
@@ -1059,7 +1067,15 @@ TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kOem));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::OEM_PARTITION));
+
+ search_result = table_.FindResource(test::ParseNameOrDie("string/actor"));
+ ASSERT_TRUE(search_result);
+ ASSERT_THAT(search_result.value().entry, NotNull());
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ result_overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::ACTOR_SIGNATURE));
}
TEST_F(ResourceParserTest, ParseOverlayableNoPolicyError) {
@@ -1122,8 +1138,8 @@ TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) {
ASSERT_TRUE(search_result.value().entry->overlayable_item);
OverlayableItem result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kVendor
- | OverlayableItem::Policy::kPublic));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::VENDOR_PARTITION
+ | PolicyFlags::PUBLIC));
search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
ASSERT_TRUE(search_result);
@@ -1131,8 +1147,8 @@ TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) {
ASSERT_TRUE(search_result.value().entry->overlayable_item);
result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
- | OverlayableItem::Policy::kSystem));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::PRODUCT_PARTITION
+ | PolicyFlags::SYSTEM_PARTITION));
}
TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp
index 836e199593fc..e0a9a31eee8b 100644
--- a/tools/aapt2/ResourceTable.cpp
+++ b/tools/aapt2/ResourceTable.cpp
@@ -267,8 +267,7 @@ bool ResourceEntry::HasDefaultValue() const {
// A DECL will override a USE without error. Two DECLs must match in their format for there to be
// no error.
ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* existing,
- Value* incoming,
- bool overlay) {
+ Value* incoming) {
Attribute* existing_attr = ValueCast<Attribute>(existing);
Attribute* incoming_attr = ValueCast<Attribute>(incoming);
if (!incoming_attr) {
@@ -282,7 +281,7 @@ ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* exist
}
// The existing and incoming values are strong, this is an error
// if the values are not both attributes.
- return overlay ? CollisionResult::kTakeNew : CollisionResult::kConflict;
+ return CollisionResult::kConflict;
}
if (!existing_attr) {
@@ -293,7 +292,7 @@ ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* exist
}
// The existing value is not an attribute and it is strong,
// so the incoming attribute value is an error.
- return overlay ? CollisionResult::kTakeNew : CollisionResult::kConflict;
+ return CollisionResult::kConflict;
}
CHECK(incoming_attr != nullptr && existing_attr != nullptr);
@@ -324,9 +323,8 @@ ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* exist
return CollisionResult::kConflict;
}
-ResourceTable::CollisionResult ResourceTable::IgnoreCollision(Value* /* existing */,
- Value* /* incoming */,
- bool /* overlay */) {
+ResourceTable::CollisionResult ResourceTable::IgnoreCollision(Value* /** existing **/,
+ Value* /** incoming **/) {
return CollisionResult::kKeepBoth;
}
@@ -400,7 +398,7 @@ bool ResourceTable::AddResourceImpl(const ResourceNameRef& name, const ResourceI
// Check for package names appearing twice with two different package ids
ResourceTablePackage* package = FindOrCreatePackage(name.package);
- if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
+ if (res_id.is_valid() && package->id && package->id.value() != res_id.package_id()) {
diag->Error(DiagMessage(source)
<< "trying to add resource '" << name << "' with ID " << res_id
<< " but package '" << package->name << "' already has ID "
@@ -409,9 +407,9 @@ bool ResourceTable::AddResourceImpl(const ResourceNameRef& name, const ResourceI
}
// Whether or not to error on duplicate resources
- bool check_id = validate_resources_ && res_id.is_valid_dynamic();
+ bool check_id = validate_resources_ && res_id.is_valid();
// Whether or not to create a duplicate resource if the id does not match
- bool use_id = !validate_resources_ && res_id.is_valid_dynamic();
+ bool use_id = !validate_resources_ && res_id.is_valid();
ResourceTableType* type = package->FindOrCreateType(name.type, use_id ? res_id.type_id()
: Maybe<uint8_t>());
@@ -442,7 +440,7 @@ bool ResourceTable::AddResourceImpl(const ResourceNameRef& name, const ResourceI
// Resource does not exist, add it now.
config_value->value = std::move(value);
} else {
- switch (conflict_resolver(config_value->value.get(), value.get(), false /* overlay */)) {
+ switch (conflict_resolver(config_value->value.get(), value.get())) {
case CollisionResult::kKeepBoth:
// Insert the value ignoring for duplicate configurations
entry->values.push_back(util::make_unique<ResourceConfigValue>(config, product));
@@ -465,7 +463,7 @@ bool ResourceTable::AddResourceImpl(const ResourceNameRef& name, const ResourceI
}
}
- if (res_id.is_valid_dynamic()) {
+ if (res_id.is_valid()) {
package->id = res_id.package_id();
type->id = res_id.type_id();
entry->id = res_id.entry_id();
@@ -506,7 +504,7 @@ bool ResourceTable::SetVisibilityImpl(const ResourceNameRef& name, const Visibil
// Check for package names appearing twice with two different package ids
ResourceTablePackage* package = FindOrCreatePackage(name.package);
- if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
+ if (res_id.is_valid() && package->id && package->id.value() != res_id.package_id()) {
diag->Error(DiagMessage(source)
<< "trying to add resource '" << name << "' with ID " << res_id
<< " but package '" << package->name << "' already has ID "
@@ -515,9 +513,9 @@ bool ResourceTable::SetVisibilityImpl(const ResourceNameRef& name, const Visibil
}
// Whether or not to error on duplicate resources
- bool check_id = validate_resources_ && res_id.is_valid_dynamic();
+ bool check_id = validate_resources_ && res_id.is_valid();
// Whether or not to create a duplicate resource if the id does not match
- bool use_id = !validate_resources_ && res_id.is_valid_dynamic();
+ bool use_id = !validate_resources_ && res_id.is_valid();
ResourceTableType* type = package->FindOrCreateType(name.type, use_id ? res_id.type_id()
: Maybe<uint8_t>());
@@ -543,7 +541,7 @@ bool ResourceTable::SetVisibilityImpl(const ResourceNameRef& name, const Visibil
return false;
}
- if (res_id.is_valid_dynamic()) {
+ if (res_id.is_valid()) {
package->id = res_id.package_id();
type->id = res_id.type_id();
entry->id = res_id.entry_id();
diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h
index e8793800b148..93a7a314d6ba 100644
--- a/tools/aapt2/ResourceTable.h
+++ b/tools/aapt2/ResourceTable.h
@@ -36,6 +36,8 @@
#include <unordered_map>
#include <vector>
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
// The Public status of a resource.
@@ -75,36 +77,8 @@ struct Overlayable {
struct OverlayableItem {
explicit OverlayableItem(const std::shared_ptr<Overlayable>& overlayable)
: overlayable(overlayable) {}
-
- // Represents the types overlays that are allowed to overlay the resource.
- typedef uint32_t PolicyFlags;
- enum Policy : uint32_t {
- kNone = 0x00000000,
-
- // The resource can be overlaid by any overlay.
- kPublic = 0x00000001,
-
- // The resource can be overlaid by any overlay on the system partition.
- kSystem = 0x00000002,
-
- // The resource can be overlaid by any overlay on the vendor partition.
- kVendor = 0x00000004,
-
- // The resource can be overlaid by any overlay on the product partition.
- kProduct = 0x00000008,
-
- // The resource can be overlaid by any overlay signed with the same signature as its actor.
- kSignature = 0x00000010,
-
- // The resource can be overlaid by any overlay on the odm partition.
- kOdm = 0x00000020,
-
- // The resource can be overlaid by any overlay on the oem partition.
- kOem = 0x00000040,
- };
-
std::shared_ptr<Overlayable> overlayable;
- PolicyFlags policies = Policy::kNone;
+ PolicyFlags policies = PolicyFlags::NONE;
std::string comment;
Source source;
};
@@ -228,13 +202,13 @@ class ResourceTable {
enum class CollisionResult { kKeepBoth, kKeepOriginal, kConflict, kTakeNew };
- using CollisionResolverFunc = std::function<CollisionResult(Value*, Value*, bool)>;
+ using CollisionResolverFunc = std::function<CollisionResult(Value*, Value*)>;
// When a collision of resources occurs, this method decides which value to keep.
- static CollisionResult ResolveValueCollision(Value* existing, Value* incoming, bool overlay);
+ static CollisionResult ResolveValueCollision(Value* existing, Value* incoming);
// When a collision of resources occurs, this method keeps both values
- static CollisionResult IgnoreCollision(Value* existing, Value* incoming, bool overlay);
+ static CollisionResult IgnoreCollision(Value* existing, Value* incoming);
bool AddResource(const ResourceNameRef& name, const android::ConfigDescription& config,
const android::StringPiece& product, std::unique_ptr<Value> value,
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index b97dc6b205ca..9271a7e6bae1 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -30,6 +30,8 @@ using ::testing::Eq;
using ::testing::NotNull;
using ::testing::StrEq;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
TEST(ResourceTableTest, FailToAddResourceWithBadName) {
@@ -247,8 +249,8 @@ TEST(ResourceTableTest, SetOverlayable) {
auto overlayable = std::make_shared<Overlayable>("Name", "overlay://theme",
Source("res/values/overlayable.xml", 40));
OverlayableItem overlayable_item(overlayable);
- overlayable_item.policies |= OverlayableItem::Policy::kProduct;
- overlayable_item.policies |= OverlayableItem::Policy::kVendor;
+ overlayable_item.policies |= PolicyFlags::PRODUCT_PARTITION;
+ overlayable_item.policies |= PolicyFlags::VENDOR_PARTITION;
overlayable_item.comment = "comment";
overlayable_item.source = Source("res/values/overlayable.xml", 42);
@@ -264,8 +266,8 @@ TEST(ResourceTableTest, SetOverlayable) {
EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
EXPECT_THAT(result_overlayable_item.overlayable->source.path, Eq("res/values/overlayable.xml"));
EXPECT_THAT(result_overlayable_item.overlayable->source.line, 40);
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
- | OverlayableItem::Policy::kVendor));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::PRODUCT_PARTITION
+ | PolicyFlags::VENDOR_PARTITION));
ASSERT_THAT(result_overlayable_item.comment, StrEq("comment"));
EXPECT_THAT(result_overlayable_item.source.path, Eq("res/values/overlayable.xml"));
EXPECT_THAT(result_overlayable_item.source.line, 42);
@@ -277,17 +279,17 @@ TEST(ResourceTableTest, SetMultipleOverlayableResources) {
const ResourceName foo = test::ParseNameOrDie("android:string/foo");
auto group = std::make_shared<Overlayable>("Name", "overlay://theme");
OverlayableItem overlayable(group);
- overlayable.policies = OverlayableItem::Policy::kProduct;
+ overlayable.policies = PolicyFlags::PRODUCT_PARTITION;
ASSERT_TRUE(table.SetOverlayable(foo, overlayable, test::GetDiagnostics()));
const ResourceName bar = test::ParseNameOrDie("android:string/bar");
OverlayableItem overlayable2(group);
- overlayable2.policies = OverlayableItem::Policy::kProduct;
+ overlayable2.policies = PolicyFlags::PRODUCT_PARTITION;
ASSERT_TRUE(table.SetOverlayable(bar, overlayable2, test::GetDiagnostics()));
const ResourceName baz = test::ParseNameOrDie("android:string/baz");
OverlayableItem overlayable3(group);
- overlayable3.policies = OverlayableItem::Policy::kVendor;
+ overlayable3.policies = PolicyFlags::VENDOR_PARTITION;
ASSERT_TRUE(table.SetOverlayable(baz, overlayable3, test::GetDiagnostics()));
}
@@ -296,12 +298,12 @@ TEST(ResourceTableTest, SetOverlayableDifferentResourcesDifferentName) {
const ResourceName foo = test::ParseNameOrDie("android:string/foo");
OverlayableItem overlayable_item(std::make_shared<Overlayable>("Name", "overlay://theme"));
- overlayable_item.policies = OverlayableItem::Policy::kProduct;
+ overlayable_item.policies = PolicyFlags::PRODUCT_PARTITION;
ASSERT_TRUE(table.SetOverlayable(foo, overlayable_item, test::GetDiagnostics()));
const ResourceName bar = test::ParseNameOrDie("android:string/bar");
OverlayableItem overlayable_item2(std::make_shared<Overlayable>("Name2", "overlay://theme"));
- overlayable_item2.policies = OverlayableItem::Policy::kProduct;
+ overlayable_item2.policies = PolicyFlags::PRODUCT_PARTITION;
ASSERT_TRUE(table.SetOverlayable(bar, overlayable_item2, test::GetDiagnostics()));
}
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index e0040e486a23..469128b1e50b 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -378,7 +378,7 @@ std::unique_ptr<BinaryPrimitive> TryParseEnumSymbol(const Attribute* enum_attr,
const ResourceName& enum_symbol_resource_name = symbol.symbol.name.value();
if (trimmed_str == enum_symbol_resource_name.entry) {
android::Res_value value = {};
- value.dataType = android::Res_value::TYPE_INT_DEC;
+ value.dataType = symbol.type;
value.data = symbol.value;
return util::make_unique<BinaryPrimitive>(value);
}
@@ -516,7 +516,7 @@ Maybe<ResourceId> ParseResourceId(const StringPiece& str) {
if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) {
if (value.dataType == android::Res_value::TYPE_INT_HEX) {
ResourceId id(value.data);
- if (id.is_valid_dynamic()) {
+ if (id.is_valid()) {
return id;
}
}
@@ -738,7 +738,13 @@ std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const Config
const android::Res_value& res_value,
StringPool* dst_pool) {
if (type == ResourceType::kId) {
- return util::make_unique<Id>();
+ 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,
+ // while aapt2 uses a false boolean).
+ return util::make_unique<Id>();
+ }
+ // fall through to regular reference deserialization logic
}
const uint32_t data = util::DeviceToHost32(res_value.data);
@@ -794,7 +800,12 @@ std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const Config
}
// This is a normal reference.
- return util::make_unique<Reference>(data, ref_type);
+ auto reference = util::make_unique<Reference>(data, ref_type);
+ if (res_value.dataType == android::Res_value::TYPE_DYNAMIC_REFERENCE ||
+ res_value.dataType == android::Res_value::TYPE_DYNAMIC_ATTRIBUTE) {
+ reference->is_dynamic = true;
+ }
+ return reference;
} break;
}
diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp
index c016cb44af00..b08bf9a1ff17 100644
--- a/tools/aapt2/ResourceUtils_test.cpp
+++ b/tools/aapt2/ResourceUtils_test.cpp
@@ -109,6 +109,20 @@ TEST(ResourceUtilsTest, ParsePrivateReference) {
EXPECT_TRUE(private_ref);
}
+TEST(ResourceUtilsTest, ParseBinaryDynamicReference) {
+ android::Res_value value = {};
+ value.data = util::HostToDevice32(0x01);
+ value.dataType = android::Res_value::TYPE_DYNAMIC_REFERENCE;
+ std::unique_ptr<Item> item = ResourceUtils::ParseBinaryResValue(ResourceType::kId,
+ android::ConfigDescription(),
+ android::ResStringPool(), value,
+ nullptr);
+
+ Reference* ref = ValueCast<Reference>(item.get());
+ EXPECT_TRUE(ref->is_dynamic);
+ EXPECT_EQ(ref->id.value().id, 0x01);
+}
+
TEST(ResourceUtilsTest, FailToParseAutoCreateNonIdReference) {
bool create = false;
bool private_ref = false;
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 696012786e6d..4f0fa8ae29ba 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -117,7 +117,7 @@ bool Reference::Equals(const Value* value) const {
bool Reference::Flatten(android::Res_value* out_value) const {
const ResourceId resid = id.value_or_default(ResourceId(0));
- const bool dynamic = resid.is_valid_dynamic() && is_dynamic;
+ const bool dynamic = resid.is_valid() && is_dynamic;
if (reference_type == Reference::Type::kResource) {
if (dynamic) {
@@ -159,7 +159,7 @@ void Reference::Print(std::ostream* out) const {
*out << name.value();
}
- if (id && id.value().is_valid_dynamic()) {
+ if (id && id.value().is_valid()) {
if (name) {
*out << " ";
}
@@ -196,7 +196,7 @@ static void PrettyPrintReferenceImpl(const Reference& ref, bool print_package, P
printer->Print("/");
printer->Print(name.entry);
}
- } else if (ref.id && ref.id.value().is_valid_dynamic()) {
+ } else if (ref.id && ref.id.value().is_valid()) {
printer->Print(ref.id.value().to_string());
}
}
@@ -574,10 +574,6 @@ bool Attribute::Equals(const Value* value) const {
}
bool Attribute::IsCompatibleWith(const Attribute& attr) const {
- if (Equals(&attr)) {
- return true;
- }
-
// If the high bits are set on any of these attribute type masks, then they are incompatible.
// We don't check that flags and enums are identical.
if ((type_mask & ~android::ResTable_map::TYPE_ANY) != 0 ||
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 168ad61784e7..fe0883be50aa 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -292,6 +292,7 @@ struct Attribute : public BaseValue<Attribute> {
struct Symbol {
Reference symbol;
uint32_t value;
+ uint8_t type;
friend std::ostream& operator<<(std::ostream& out, const Symbol& symbol);
};
diff --git a/tools/aapt2/ResourceValues_test.cpp b/tools/aapt2/ResourceValues_test.cpp
index dbf51143f720..c4a1108ac62a 100644
--- a/tools/aapt2/ResourceValues_test.cpp
+++ b/tools/aapt2/ResourceValues_test.cpp
@@ -284,58 +284,8 @@ TEST(ResourcesValuesTest, AttributeIsCompatible) {
EXPECT_FALSE(attr_three.IsCompatibleWith(attr_one));
EXPECT_FALSE(attr_three.IsCompatibleWith(attr_two));
- EXPECT_TRUE(attr_three.IsCompatibleWith(attr_three));
+ EXPECT_FALSE(attr_three.IsCompatibleWith(attr_three));
EXPECT_FALSE(attr_three.IsCompatibleWith(attr_four));
-
- EXPECT_FALSE(attr_four.IsCompatibleWith(attr_one));
- EXPECT_FALSE(attr_four.IsCompatibleWith(attr_two));
- EXPECT_FALSE(attr_four.IsCompatibleWith(attr_three));
- EXPECT_TRUE(attr_four.IsCompatibleWith(attr_four));
-}
-
-TEST(ResourcesValuesTest, AttributeEnumIsCompatible) {
- Attribute attr_one(TYPE_ENUM);
- attr_one.symbols.push_back(
- Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/foo")), 0x01u});
- attr_one.symbols.push_back(
- Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/bar")), 0x07u});
-
- Attribute attr_two(TYPE_ENUM);
- attr_two.symbols.push_back(
- Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/foo")), 0x01u});
- attr_two.symbols.push_back(
- Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/bar")), 0x07u});
- EXPECT_TRUE(attr_one.IsCompatibleWith(attr_two));
-}
-
-TEST(ResourcesValuesTest, DifferentAttributeEnumDifferentNameIsNotCompatible) {
- Attribute attr_one(TYPE_ENUM);
- attr_one.symbols.push_back(
- Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/foo")), 0x01u});
- attr_one.symbols.push_back(
- Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/bar")), 0x07u});
-
- Attribute attr_two(TYPE_ENUM);
- attr_two.symbols.push_back(
- Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/foo")), 0x01u});
- attr_one.symbols.push_back(
- Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/baz")), 0x07u});
- EXPECT_FALSE(attr_one.IsCompatibleWith(attr_two));
-}
-
-TEST(ResourcesValuesTest, DifferentAttributeEnumDifferentValueIsNotCompatible) {
- Attribute attr_one(TYPE_ENUM);
- attr_one.symbols.push_back(
- Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/foo")), 0x01u});
- attr_one.symbols.push_back(
- Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/bar")), 0x07u});
-
- Attribute attr_two(TYPE_ENUM);
- attr_two.symbols.push_back(
- Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/foo")), 0x01u});
- attr_two.symbols.push_back(
- Attribute::Symbol{Reference(test::ParseNameOrDie("android:id/bar")), 0x09u});
- EXPECT_FALSE(attr_one.IsCompatibleWith(attr_two));
}
} // namespace aapt
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index b2fc08423d34..ab9ce66b0ae3 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -21,7 +21,6 @@ import "frameworks/base/tools/aapt2/Configuration.proto";
package aapt.pb;
option java_package = "com.android.aapt";
-option optimize_for = LITE_RUNTIME;
// A string pool that wraps the binary form of the C++ class android::ResStringPool.
message StringPool {
@@ -168,6 +167,7 @@ message OverlayableItem {
SIGNATURE = 5;
ODM = 6;
OEM = 7;
+ ACTOR = 8;
}
// The location of the <item> declaration in source.
@@ -270,6 +270,11 @@ message CompoundValue {
}
}
+// Message holding a boolean, so it can be optionally encoded.
+message Boolean {
+ bool value = 1;
+}
+
// A value that is a reference to another resource. This reference can be by name or resource ID.
message Reference {
enum Type {
@@ -290,6 +295,9 @@ message Reference {
// Whether this reference is referencing a private resource (@*package:type/entry).
bool private = 4;
+
+ // Whether this reference is dynamic.
+ Boolean is_dynamic = 5;
}
// A value that represents an ID. This is just a placeholder, as ID values are used to occupy a
@@ -388,6 +396,9 @@ message Attribute {
// The value of the enum/flag.
uint32 value = 4;
+
+ // The data type of the enum/flag as defined in android::Res_value.
+ uint32 type = 5;
}
// Bitmask of formats allowed for an attribute.
diff --git a/tools/aapt2/ResourcesInternal.proto b/tools/aapt2/ResourcesInternal.proto
index 520b242ee509..b0ed3da33368 100644
--- a/tools/aapt2/ResourcesInternal.proto
+++ b/tools/aapt2/ResourcesInternal.proto
@@ -22,7 +22,6 @@ import "frameworks/base/tools/aapt2/Resources.proto";
package aapt.pb.internal;
option java_package = "android.aapt.pb.internal";
-option optimize_for = LITE_RUNTIME;
// The top level message representing an external resource file (layout XML, PNG, etc).
// This is used to represent a compiled file before it is linked. Only useful to aapt2.
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index 304bc4925831..e8873bf2d81b 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -57,6 +57,8 @@ static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = {
{0x0568, SDK_O},
{0x056d, SDK_O_MR1},
{0x0586, SDK_P},
+ {0x0606, SDK_Q},
+ {0x0617, SDK_R},
};
static bool less_entry_id(const std::pair<uint16_t, ApiVersion>& p, uint16_t entryId) {
diff --git a/tools/aapt2/SdkConstants.h b/tools/aapt2/SdkConstants.h
index a00d978565ad..aa9aa12d2cee 100644
--- a/tools/aapt2/SdkConstants.h
+++ b/tools/aapt2/SdkConstants.h
@@ -55,6 +55,7 @@ enum : ApiVersion {
SDK_O_MR1 = 27,
SDK_P = 28,
SDK_Q = 29,
+ SDK_R = 30,
};
ApiVersion FindAttributeSdkLevel(const ResourceId& id);
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 21719705838d..32686538c10d 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -630,6 +630,12 @@ class CompileContext : public IAaptContext {
return 0;
}
+ const std::set<std::string>& GetSplitNameDependencies() override {
+ UNIMPLEMENTED(FATAL) << "No Split Name Dependencies be needed in compile phase";
+ static std::set<std::string> empty;
+ return empty;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(CompileContext);
@@ -735,7 +741,6 @@ int CompileCommand::Action(const std::vector<std::string>& args) {
}
std::unique_ptr<io::IFileCollection> file_collection;
- std::unique_ptr<IArchiveWriter> archive_writer;
// Collect the resources files to compile
if (options_.res_dir && options_.res_zip) {
@@ -756,8 +761,6 @@ int CompileCommand::Action(const std::vector<std::string>& args) {
context.GetDiagnostics()->Error(DiagMessage(options_.res_dir.value()) << err);
return 1;
}
-
- archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options_.output_path);
} else if (options_.res_zip) {
if (!args.empty()) {
context.GetDiagnostics()->Error(DiagMessage() << "files given but --zip specified");
@@ -772,8 +775,6 @@ int CompileCommand::Action(const std::vector<std::string>& args) {
context.GetDiagnostics()->Error(DiagMessage(options_.res_zip.value()) << err);
return 1;
}
-
- archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options_.output_path);
} else {
auto collection = util::make_unique<io::FileCollection>();
@@ -786,7 +787,14 @@ int CompileCommand::Action(const std::vector<std::string>& args) {
}
file_collection = std::move(collection);
+ }
+
+ std::unique_ptr<IArchiveWriter> archive_writer;
+ file::FileType output_file_type = file::GetFileType(options_.output_path);
+ if (output_file_type == file::FileType::kDirectory) {
archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options_.output_path);
+ } else {
+ archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options_.output_path);
}
if (!archive_writer) {
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index 5f637bd8d582..fb786a31360e 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -200,7 +200,7 @@ static void AssertTranslations(CommandTestFixture *ctf, std::string file_name,
const std::string compiled_files_dir = ctf->GetTestPath("/compiled_" + file_name);
const std::string out_apk = ctf->GetTestPath("/" + file_name + ".apk");
- CHECK(ctf->WriteFile(source_file, sTranslatableXmlContent));
+ ctf->WriteFile(source_file, sTranslatableXmlContent);
CHECK(file::mkdirs(compiled_files_dir.data()));
ASSERT_EQ(CompileCommand(&diag).Execute({
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index 0cf86ccdd59f..22bcd8589ce9 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -243,6 +243,12 @@ class Context : public IAaptContext {
return 0u;
}
+ const std::set<std::string>& GetSplitNameDependencies() override {
+ UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary";
+ static std::set<std::string> empty;
+ return empty;
+ }
+
bool verbose_ = false;
std::string package_;
diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp
index 262f4fc4e394..d56994e3ae24 100644
--- a/tools/aapt2/cmd/Diff.cpp
+++ b/tools/aapt2/cmd/Diff.cpp
@@ -65,6 +65,12 @@ class DiffContext : public IAaptContext {
return 0;
}
+ const std::set<std::string>& GetSplitNameDependencies() override {
+ UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary";
+ static std::set<std::string> empty;
+ return empty;
+ }
+
private:
std::string empty_;
StdErrDiagnostics diagnostics_;
diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp
index a23a6a46cf0f..3982d12f6036 100644
--- a/tools/aapt2/cmd/Dump.cpp
+++ b/tools/aapt2/cmd/Dump.cpp
@@ -118,6 +118,12 @@ class DumpContext : public IAaptContext {
return 0;
}
+ const std::set<std::string>& GetSplitNameDependencies() override {
+ UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary";
+ static std::set<std::string> empty;
+ return empty;
+ }
+
private:
StdErrDiagnostics diagnostics_;
bool verbose_ = false;
@@ -388,6 +394,17 @@ int DumpXmlTreeCommand::Dump(LoadedApk* apk) {
return 0;
}
+int DumpOverlayableCommand::Dump(LoadedApk* apk) {
+ ResourceTable* table = apk->GetResourceTable();
+ if (!table) {
+ GetDiagnostics()->Error(DiagMessage() << "Failed to retrieve resource table");
+ return 1;
+ }
+
+ Debug::DumpOverlayable(*table, GetPrinter());
+ return 0;
+}
+
const char DumpBadgerCommand::kBadgerData[2925] = {
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 32, 32, 32, 32, 32, 95, 46, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
diff --git a/tools/aapt2/cmd/Dump.h b/tools/aapt2/cmd/Dump.h
index 7ded9bcf8470..cd51f7a7718c 100644
--- a/tools/aapt2/cmd/Dump.h
+++ b/tools/aapt2/cmd/Dump.h
@@ -240,6 +240,16 @@ class DumpXmlTreeCommand : public DumpApkCommand {
std::vector<std::string> files_;
};
+class DumpOverlayableCommand : public DumpApkCommand {
+ public:
+ explicit DumpOverlayableCommand(text::Printer* printer, IDiagnostics* diag)
+ : DumpApkCommand("overlayable", printer, diag) {
+ SetDescription("Print the <overlayable> resources of an APK.");
+ }
+
+ int Dump(LoadedApk* apk) override;
+};
+
/** The default dump command. Performs no action because a subcommand is required. */
class DumpCommand : public Command {
public:
@@ -255,8 +265,8 @@ class DumpCommand : public Command {
AddOptionalSubcommand(util::make_unique<DumpTableCommand>(printer, diag_));
AddOptionalSubcommand(util::make_unique<DumpXmlStringsCommand>(printer, diag_));
AddOptionalSubcommand(util::make_unique<DumpXmlTreeCommand>(printer, diag_));
+ AddOptionalSubcommand(util::make_unique<DumpOverlayableCommand>(printer, diag_));
AddOptionalSubcommand(util::make_unique<DumpBadgerCommand>(printer), /* hidden */ true);
- // TODO(b/120609160): Add aapt2 overlayable dump command
}
int Action(const std::vector<std::string>& args) override {
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 6e5b7a191287..fd12d02434fa 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -140,6 +140,14 @@ class LinkContext : public IAaptContext {
min_sdk_version_ = minSdk;
}
+ const std::set<std::string>& GetSplitNameDependencies() override {
+ return split_name_dependencies_;
+ }
+
+ void SetSplitNameDependencies(const std::set<std::string>& split_name_dependencies) {
+ split_name_dependencies_ = split_name_dependencies;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(LinkContext);
@@ -151,6 +159,7 @@ class LinkContext : public IAaptContext {
SymbolTable symbols_;
bool verbose_ = false;
int min_sdk_version_ = 0;
+ std::set<std::string> split_name_dependencies_;
};
// A custom delegate that generates compatible pre-O IDs for use with feature splits.
@@ -269,6 +278,7 @@ struct ResourceFileFlattenerOptions {
bool keep_raw_values = false;
bool do_not_compress_anything = false;
bool update_proguard_spec = false;
+ bool do_not_fail_on_missing_resources = false;
OutputFormat output_format = OutputFormat::kApk;
std::unordered_set<std::string> extensions_to_not_compress;
Maybe<std::regex> regex_to_not_compress;
@@ -297,6 +307,25 @@ struct R {
};
};
+template <typename T>
+uint32_t GetCompressionFlags(const StringPiece& str, T options) {
+ if (options.do_not_compress_anything) {
+ return 0;
+ }
+
+ if (options.regex_to_not_compress
+ && std::regex_search(str.to_string(), options.regex_to_not_compress.value())) {
+ return 0;
+ }
+
+ for (const std::string& extension : options.extensions_to_not_compress) {
+ if (util::EndsWith(str, extension)) {
+ return 0;
+ }
+ }
+ return ArchiveEntry::kCompress;
+}
+
class ResourceFileFlattener {
public:
ResourceFileFlattener(const ResourceFileFlattenerOptions& options, IAaptContext* context,
@@ -321,8 +350,6 @@ class ResourceFileFlattener {
std::string dst_path;
};
- uint32_t GetCompressionFlags(const StringPiece& str);
-
std::vector<std::unique_ptr<xml::XmlResource>> LinkAndVersionXmlFile(ResourceTable* table,
FileOperation* file_op);
@@ -381,26 +408,6 @@ ResourceFileFlattener::ResourceFileFlattener(const ResourceFileFlattenerOptions&
}
}
-// TODO(rtmitchell): turn this function into a variable that points to a method that retrieves the
-// compression flag
-uint32_t ResourceFileFlattener::GetCompressionFlags(const StringPiece& str) {
- if (options_.do_not_compress_anything) {
- return 0;
- }
-
- if (options_.regex_to_not_compress
- && std::regex_search(str.to_string(), options_.regex_to_not_compress.value())) {
- return 0;
- }
-
- for (const std::string& extension : options_.extensions_to_not_compress) {
- if (util::EndsWith(str, extension)) {
- return 0;
- }
- }
- return ArchiveEntry::kCompress;
-}
-
static bool IsTransitionElement(const std::string& name) {
return name == "fade" || name == "changeBounds" || name == "slide" || name == "explode" ||
name == "changeImageTransform" || name == "changeTransform" ||
@@ -438,7 +445,7 @@ std::vector<std::unique_ptr<xml::XmlResource>> ResourceFileFlattener::LinkAndVer
xml::StripAndroidStudioAttributes(doc->root.get());
XmlReferenceLinker xml_linker;
- if (!xml_linker.Consume(context_, doc)) {
+ if (!options_.do_not_fail_on_missing_resources && !xml_linker.Consume(context_, doc)) {
return {};
}
@@ -640,7 +647,8 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
}
} else {
error |= !io::CopyFileToArchive(context_, file_op.file_to_copy, file_op.dst_path,
- GetCompressionFlags(file_op.dst_path), archive_writer);
+ GetCompressionFlags(file_op.dst_path, options_),
+ archive_writer);
}
}
}
@@ -887,7 +895,7 @@ class Linker {
// android:versionCode from the framework AndroidManifest.xml.
ExtractCompileSdkVersions(asset_source->GetAssetManager());
}
- } else if (asset_source->IsPackageDynamic(entry.first)) {
+ } else if (asset_source->IsPackageDynamic(entry.first, entry.second)) {
final_table_.included_packages_[entry.first] = entry.second;
}
}
@@ -965,6 +973,17 @@ class Linker {
app_info.min_sdk_version = ResourceUtils::ParseSdkVersion(min_sdk->value);
}
}
+
+ for (const xml::Element* child_el : manifest_el->GetChildElements()) {
+ if (child_el->namespace_uri.empty() && child_el->name == "uses-split") {
+ if (const xml::Attribute* split_name =
+ child_el->FindAttribute(xml::kSchemaAndroid, "name")) {
+ if (!split_name->value.empty()) {
+ app_info.split_name_dependencies.insert(split_name->value);
+ }
+ }
+ }
+ }
return app_info;
}
@@ -1065,7 +1084,8 @@ class Linker {
case OutputFormat::kProto: {
pb::ResourceTable pb_table;
- SerializeTableToPb(*table, &pb_table, context_->GetDiagnostics());
+ SerializeTableToPb(*table, &pb_table, context_->GetDiagnostics(),
+ options_.proto_table_flattener_options);
return io::CopyProtoToArchive(context_, &pb_table, kProtoResourceTablePath,
ArchiveEntry::kCompress, writer);
} break;
@@ -1278,7 +1298,8 @@ class Linker {
return false;
}
- proguard::WriteKeepSet(keep_set, &fout, options_.generate_minimal_proguard_rules);
+ proguard::WriteKeepSet(keep_set, &fout, options_.generate_minimal_proguard_rules,
+ options_.no_proguard_location_reference);
fout.Flush();
if (fout.HadError()) {
@@ -1548,16 +1569,7 @@ class Linker {
}
for (auto& entry : merged_assets) {
- uint32_t compression_flags = ArchiveEntry::kCompress;
- std::string extension = file::GetExtension(entry.first).to_string();
-
- if (options_.do_not_compress_anything
- || options_.extensions_to_not_compress.count(extension) > 0
- || (options_.regex_to_not_compress
- && std::regex_search(extension, options_.regex_to_not_compress.value()))) {
- compression_flags = 0u;
- }
-
+ uint32_t compression_flags = GetCompressionFlags(entry.first, options_);
if (!io::CopyFileToArchive(context_, entry.second.get(), entry.first, compression_flags,
writer)) {
return false;
@@ -1566,6 +1578,93 @@ class Linker {
return true;
}
+ void AliasAdaptiveIcon(xml::XmlResource* manifest, ResourceTable* table) {
+ 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");
+ if (!icon || !round_icon) {
+ return;
+ }
+
+ // Find the icon resource defined within the application.
+ 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_entry = type->FindEntry(icon_reference->name.value().entry);
+ if (!icon_entry) {
+ return;
+ }
+
+ int icon_max_sdk = 0;
+ for (auto& config_value : icon_entry->values) {
+ icon_max_sdk = (icon_max_sdk < config_value->config.sdkVersion)
+ ? config_value->config.sdkVersion : icon_max_sdk;
+ }
+ if (icon_max_sdk < SDK_O) {
+ // Adaptive icons must be versioned with v26 qualifiers, so this is not an adaptive icon.
+ return;
+ }
+
+ // Find the roundIcon resource defined within the application.
+ 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_entry = type->FindEntry(round_icon_reference->name.value().entry);
+ if (!round_icon_entry) {
+ return;
+ }
+
+ int round_icon_max_sdk = 0;
+ for (auto& config_value : round_icon_entry->values) {
+ round_icon_max_sdk = (round_icon_max_sdk < config_value->config.sdkVersion)
+ ? config_value->config.sdkVersion : round_icon_max_sdk;
+ }
+ if (round_icon_max_sdk >= SDK_O) {
+ // The developer explicitly used a v26 compatible drawable as the roundIcon, meaning we should
+ // not generate an alias to the icon drawable.
+ return;
+ }
+
+ // Add an equivalent v26 entry to the roundIcon for each v26 variant of the regular icon.
+ for (auto& config_value : icon_entry->values) {
+ if (config_value->config.sdkVersion < SDK_O) {
+ continue;
+ }
+
+ context_->GetDiagnostics()->Note(DiagMessage() << "generating "
+ << round_icon_reference->name.value()
+ << " with config \"" << config_value->config
+ << "\" for round icon compatibility");
+
+ auto value = icon_reference->Clone(&table->string_pool);
+ auto round_config_value = round_icon_entry->FindOrCreateValue(
+ config_value->config, config_value->product);
+ round_config_value->value.reset(value);
+ }
+ }
+
// Writes the AndroidManifest, ResourceTable, and all XML files referenced by the ResourceTable
// to the IArchiveWriter.
bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set, xml::XmlResource* manifest,
@@ -1579,6 +1678,14 @@ class Linker {
return false;
}
+ // When a developer specifies an adaptive application icon, and a non-adaptive round application
+ // icon, create an alias from the round icon to the regular icon for v26 APIs and up. We do this
+ // because certain devices prefer android:roundIcon over android:icon regardless of the API
+ // levels of the drawables set for either. This auto-aliasing behaviour allows an app to prefer
+ // the android:roundIcon on API 25 devices, and prefer the adaptive icon on API 26 devices.
+ // See (b/34829129)
+ AliasAdaptiveIcon(manifest, table);
+
ResourceFileFlattenerOptions file_flattener_options;
file_flattener_options.keep_raw_values = keep_raw_values;
file_flattener_options.do_not_compress_anything = options_.do_not_compress_anything;
@@ -1591,9 +1698,9 @@ class Linker {
file_flattener_options.update_proguard_spec =
static_cast<bool>(options_.generate_proguard_rules_path);
file_flattener_options.output_format = options_.output_format;
+ file_flattener_options.do_not_fail_on_missing_resources = options_.merge_only;
ResourceFileFlattener file_flattener(file_flattener_options, context_, keep_set);
-
if (!file_flattener.Flatten(table, writer)) {
context_->GetDiagnostics()->Error(DiagMessage() << "failed linking file resources");
return false;
@@ -1660,10 +1767,9 @@ class Linker {
return 1;
}
- // First extract the package name without modifying it (via --rename-manifest-package).
+ // First extract the Package name without modifying it (via --rename-manifest-package).
if (Maybe<AppInfo> maybe_app_info =
ExtractAppInfoFromManifest(manifest_xml.get(), context_->GetDiagnostics())) {
- // Extract the package name from the manifest ignoring the value of --rename-manifest-package.
const AppInfo& app_info = maybe_app_info.value();
context_->SetCompilationPackage(app_info.package);
}
@@ -1699,6 +1805,7 @@ class Linker {
context_->SetMinSdkVersion(app_info_.min_sdk_version.value_or_default(0));
context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()});
+ context_->SetSplitNameDependencies(app_info_.split_name_dependencies);
// Override the package ID when it is "android".
if (context_->GetCompilationPackage() == "android") {
@@ -1714,6 +1821,8 @@ class Linker {
TableMergerOptions table_merger_options;
table_merger_options.auto_add_overlay = options_.auto_add_overlay;
+ table_merger_options.override_styles_instead_of_overlaying =
+ options_.override_styles_instead_of_overlaying;
table_merger_options.strict_visibility = options_.strict_visibility;
table_merger_ = util::make_unique<TableMerger>(context_, &final_table_, table_merger_options);
@@ -1828,7 +1937,7 @@ class Linker {
}
ReferenceLinker linker;
- if (!linker.Consume(context_, &final_table_)) {
+ if (!options_.merge_only && !linker.Consume(context_, &final_table_)) {
context_->GetDiagnostics()->Error(DiagMessage() << "failed linking references");
return 1;
}
@@ -1980,7 +2089,7 @@ class Linker {
manifest_xml->file.name.package = context_->GetCompilationPackage();
XmlReferenceLinker manifest_linker;
- if (manifest_linker.Consume(context_, manifest_xml.get())) {
+ if (options_.merge_only || manifest_linker.Consume(context_, manifest_xml.get())) {
if (options_.generate_proguard_rules_path &&
!proguard::CollectProguardRulesForManifest(manifest_xml.get(), &proguard_keep_set)) {
error = true;
@@ -2114,6 +2223,12 @@ int LinkCommand::Action(const std::vector<std::string>& args) {
return 1;
}
+ if (options_.merge_only && !static_lib_) {
+ context.GetDiagnostics()->Error(
+ DiagMessage() << "the --merge-only flag can be only used when building a static library");
+ return 1;
+ }
+
// The default build type.
context.SetPackageType(PackageType::kApp);
context.SetPackageId(kAppPackageId);
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index e62e0a6b9f62..852b1244cd6e 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -24,6 +24,7 @@
#include "Resource.h"
#include "split/TableSplitter.h"
#include "format/binary/TableFlattener.h"
+#include "format/proto/ProtoSerialize.h"
#include "link/ManifestFixer.h"
#include "trace/TraceBuffer.h"
@@ -42,6 +43,7 @@ struct LinkOptions {
std::vector<std::string> assets_dirs;
bool output_to_directory = false;
bool auto_add_overlay = false;
+ bool override_styles_instead_of_overlaying = false;
OutputFormat output_format = OutputFormat::kApk;
Maybe<std::string> rename_resources_package;
@@ -55,6 +57,7 @@ struct LinkOptions {
bool generate_conditional_proguard_rules = false;
bool generate_minimal_proguard_rules = false;
bool generate_non_final_ids = false;
+ bool no_proguard_location_reference = false;
std::vector<std::string> javadoc_annotations;
Maybe<std::string> private_symbols;
@@ -71,6 +74,7 @@ struct LinkOptions {
// Static lib options.
bool no_static_lib_packages = false;
+ bool merge_only = false;
// AndroidManifest.xml massaging options.
ManifestFixerOptions manifest_fixer_options;
@@ -80,6 +84,7 @@ struct LinkOptions {
// Flattening options.
TableFlattenerOptions table_flattener_options;
+ SerializeTableOptions proto_table_flattener_options;
bool keep_raw_values = false;
// Split APK options.
@@ -212,6 +217,9 @@ class LinkCommand : public Command {
"Generates R.java without the final modifier. This is implied when\n"
"--static-lib is specified.",
&options_.generate_non_final_ids);
+ AddOptionalSwitch("--no-proguard-location-reference",
+ "Keep proguard rules files from having a reference to the source file",
+ &options_.no_proguard_location_reference);
AddOptionalFlag("--stable-ids", "File containing a list of name to ID mapping.",
&stable_id_file_path_);
AddOptionalFlag("--emit-ids",
@@ -243,6 +251,10 @@ class LinkCommand : public Command {
"Allows the addition of new resources in overlays without\n"
"<add-resource> tags.",
&options_.auto_add_overlay);
+ AddOptionalSwitch("--override-styles-instead-of-overlaying",
+ "Causes styles defined in -R resources to replace previous definitions\n"
+ "instead of merging into them\n",
+ &options_.override_styles_instead_of_overlaying);
AddOptionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml.",
&options_.manifest_fixer_options.rename_manifest_package);
AddOptionalFlag("--rename-resources-package", "Renames the package in resources table",
@@ -255,7 +267,7 @@ class LinkCommand : public Command {
"Changes the name of the target package for overlay. Most useful\n"
"when used in conjunction with --rename-manifest-package.",
&options_.manifest_fixer_options.rename_overlay_target_package);
- AddOptionalFlagList("-0", "File extensions not to compress.",
+ AddOptionalFlagList("-0", "File suffix not to compress.",
&options_.extensions_to_not_compress);
AddOptionalSwitch("--no-compress", "Do not compress any resources.",
&options_.do_not_compress_anything);
@@ -263,8 +275,8 @@ class LinkCommand : public Command {
&options_.keep_raw_values);
AddOptionalFlag("--no-compress-regex",
"Do not compress extensions matching the regular expression. Remember to\n"
- " use the '$' symbol for end of line. Uses a non case-sensitive\n"
- " ECMAScript regular expression grammar.",
+ "use the '$' symbol for end of line. Uses a case-sensitive ECMAScript"
+ "regular expression grammar.",
&no_compress_regex);
AddOptionalSwitch("--warn-manifest-validation",
"Treat manifest validation errors as warnings.",
@@ -284,9 +296,18 @@ class LinkCommand : public Command {
AddOptionalSwitch("--strict-visibility",
"Do not allow overlays with different visibility levels.",
&options_.strict_visibility);
+ AddOptionalSwitch("--exclude-sources",
+ "Do not serialize source file information when generating resources in\n"
+ "Protobuf format.",
+ &options_.proto_table_flattener_options.exclude_sources);
+ AddOptionalFlag("--trace-folder",
+ "Generate systrace json trace fragment to specified folder.",
+ &trace_folder_);
+ AddOptionalSwitch("--merge-only",
+ "Only merge the resources, without verifying resource references. This flag\n"
+ "should only be used together with the --static-lib flag.",
+ &options_.merge_only);
AddOptionalSwitch("-v", "Enables verbose logging.", &verbose_);
- AddOptionalFlag("--trace-folder", "Generate systrace json trace fragment to specified folder.",
- &trace_folder_);
}
int Action(const std::vector<std::string>& args) override;
diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp
index 9ea93f638aff..062dd8eac975 100644
--- a/tools/aapt2/cmd/Link_test.cpp
+++ b/tools/aapt2/cmd/Link_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "AppInfo.h"
#include "Link.h"
#include "LoadedApk.h"
@@ -43,10 +44,8 @@ TEST_F(LinkTest, RemoveRawXmlStrings) {
// Load the binary xml tree
android::ResXMLTree tree;
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
-
std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
ASSERT_THAT(data, Ne(nullptr));
-
AssertLoadXml(apk.get(), data.get(), &tree);
// Check that the raw string index has not been assigned
@@ -71,10 +70,8 @@ TEST_F(LinkTest, KeepRawXmlStrings) {
// Load the binary xml tree
android::ResXMLTree tree;
std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
-
std::unique_ptr<io::IData> data = OpenFileAsData(apk.get(), "res/xml/test.xml");
ASSERT_THAT(data, Ne(nullptr));
-
AssertLoadXml(apk.get(), data.get(), &tree);
// Check that the raw string index has been set to the correct string pool entry
@@ -83,4 +80,241 @@ TEST_F(LinkTest, KeepRawXmlStrings) {
EXPECT_THAT(util::GetString(tree.getStrings(), static_cast<size_t>(raw_index)), Eq("007"));
}
-} // namespace aapt \ No newline at end of file
+TEST_F(LinkTest, NoCompressAssets) {
+ StdErrDiagnostics diag;
+ std::string content(500, 'a');
+ WriteFile(GetTestPath("assets/testtxt"), content);
+ WriteFile(GetTestPath("assets/testtxt2"), content);
+ WriteFile(GetTestPath("assets/test.txt"), content);
+ WriteFile(GetTestPath("assets/test.hello.txt"), content);
+ WriteFile(GetTestPath("assets/test.hello.xml"), content);
+
+ const std::string out_apk = GetTestPath("out.apk");
+ std::vector<std::string> link_args = {
+ "--manifest", GetDefaultManifest(),
+ "-o", out_apk,
+ "-0", ".txt",
+ "-0", "txt2",
+ "-0", ".hello.txt",
+ "-0", "hello.xml",
+ "-A", GetTestPath("assets")
+ };
+
+ ASSERT_TRUE(Link(link_args, &diag));
+
+ std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
+ ASSERT_THAT(apk, Ne(nullptr));
+ io::IFileCollection* zip = apk->GetFileCollection();
+ ASSERT_THAT(zip, Ne(nullptr));
+
+ auto file = zip->FindFile("assets/testtxt");
+ ASSERT_THAT(file, Ne(nullptr));
+ EXPECT_TRUE(file->WasCompressed());
+
+ file = zip->FindFile("assets/testtxt2");
+ ASSERT_THAT(file, Ne(nullptr));
+ EXPECT_FALSE(file->WasCompressed());
+
+ file = zip->FindFile("assets/test.txt");
+ ASSERT_THAT(file, Ne(nullptr));
+ EXPECT_FALSE(file->WasCompressed());
+
+ file = zip->FindFile("assets/test.hello.txt");
+ ASSERT_THAT(file, Ne(nullptr));
+ EXPECT_FALSE(file->WasCompressed());
+
+ file = zip->FindFile("assets/test.hello.xml");
+ ASSERT_THAT(file, Ne(nullptr));
+ EXPECT_FALSE(file->WasCompressed());
+}
+
+TEST_F(LinkTest, NoCompressResources) {
+ StdErrDiagnostics diag;
+ std::string content(500, 'a');
+ const std::string compiled_files_dir = GetTestPath("compiled");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/raw/testtxt"), content, compiled_files_dir, &diag));
+ ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test.txt"), content, compiled_files_dir, &diag));
+ ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test1.hello.txt"), content, compiled_files_dir,
+ &diag));
+ ASSERT_TRUE(CompileFile(GetTestPath("res/raw/test2.goodbye.xml"), content, compiled_files_dir,
+ &diag));
+
+ const std::string out_apk = GetTestPath("out.apk");
+ std::vector<std::string> link_args = {
+ "--manifest", GetDefaultManifest(),
+ "-o", out_apk,
+ "-0", ".txt",
+ "-0", ".hello.txt",
+ "-0", "goodbye.xml",
+ };
+
+ ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
+
+ std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
+ ASSERT_THAT(apk, Ne(nullptr));
+ io::IFileCollection* zip = apk->GetFileCollection();
+ ASSERT_THAT(zip, Ne(nullptr));
+
+ auto file = zip->FindFile("res/raw/testtxt");
+ ASSERT_THAT(file, Ne(nullptr));
+ EXPECT_TRUE(file->WasCompressed());
+
+ file = zip->FindFile("res/raw/test.txt");
+ ASSERT_THAT(file, Ne(nullptr));
+ EXPECT_FALSE(file->WasCompressed());
+
+ file = zip->FindFile("res/raw/test1.hello.hello.txt");
+ ASSERT_THAT(file, Ne(nullptr));
+ EXPECT_FALSE(file->WasCompressed());
+
+ file = zip->FindFile("res/raw/test2.goodbye.goodbye.xml");
+ ASSERT_THAT(file, Ne(nullptr));
+ EXPECT_FALSE(file->WasCompressed());
+}
+
+TEST_F(LinkTest, OverlayStyles) {
+ StdErrDiagnostics diag;
+ const std::string compiled_files_dir = GetTestPath("compiled");
+ const std::string override_files_dir = GetTestPath("compiled-override");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
+ R"(<resources>
+ <style name="MyStyle">
+ <item name="android:textColor">#123</item>
+ </style>
+ </resources>)",
+ compiled_files_dir, &diag));
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values-override.xml"),
+ R"(<resources>
+ <style name="MyStyle">
+ <item name="android:background">#456</item>
+ </style>
+ </resources>)",
+ override_files_dir, &diag));
+
+
+ const std::string out_apk = GetTestPath("out.apk");
+ std::vector<std::string> link_args = {
+ "--manifest", GetDefaultManifest(kDefaultPackageName),
+ "-o", out_apk,
+ };
+ const auto override_files = file::FindFiles(override_files_dir, &diag);
+ for (const auto &override_file : override_files.value()) {
+ link_args.push_back("-R");
+ link_args.push_back(file::BuildPath({override_files_dir, override_file}));
+ }
+ ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
+
+ std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
+ const Style* actual_style = test::GetValue<Style>(
+ apk->GetResourceTable(), std::string(kDefaultPackageName) + ":style/MyStyle");
+ ASSERT_NE(actual_style, nullptr);
+ ASSERT_EQ(actual_style->entries.size(), 2);
+ EXPECT_EQ(actual_style->entries[0].key.id, 0x01010098); // android:textColor
+ EXPECT_EQ(actual_style->entries[1].key.id, 0x010100d4); // android:background
+}
+
+TEST_F(LinkTest, OverrideStylesInsteadOfOverlaying) {
+ StdErrDiagnostics diag;
+ const std::string compiled_files_dir = GetTestPath("compiled");
+ const std::string override_files_dir = GetTestPath("compiled-override");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
+ R"(<resources>
+ <style name="MyStyle">
+ <item name="android:textColor">#123</item>
+ </style>
+ </resources>)",
+ compiled_files_dir, &diag));
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values-override.xml"),
+ R"(<resources>
+ <style name="MyStyle">
+ <item name="android:background">#456</item>
+ </style>
+ </resources>)",
+ override_files_dir, &diag));
+
+
+ const std::string out_apk = GetTestPath("out.apk");
+ std::vector<std::string> link_args = {
+ "--manifest", GetDefaultManifest(kDefaultPackageName),
+ "--override-styles-instead-of-overlaying",
+ "-o", out_apk,
+ };
+ const auto override_files = file::FindFiles(override_files_dir, &diag);
+ for (const auto &override_file : override_files.value()) {
+ link_args.push_back("-R");
+ link_args.push_back(file::BuildPath({override_files_dir, override_file}));
+ }
+ ASSERT_TRUE(Link(link_args, compiled_files_dir, &diag));
+
+ std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(out_apk, &diag);
+ const Style* actual_style = test::GetValue<Style>(
+ apk->GetResourceTable(), std::string(kDefaultPackageName) + ":style/MyStyle");
+ ASSERT_NE(actual_style, nullptr);
+ ASSERT_EQ(actual_style->entries.size(), 1);
+ EXPECT_EQ(actual_style->entries[0].key.id, 0x010100d4); // android:background
+}
+
+TEST_F(LinkTest, AppInfoWithUsesSplit) {
+ StdErrDiagnostics diag;
+ const std::string base_files_dir = GetTestPath("base");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
+ R"(<resources>
+ <string name="bar">bar</string>
+ </resources>)",
+ base_files_dir, &diag));
+ const std::string base_apk = GetTestPath("base.apk");
+ std::vector<std::string> link_args = {
+ "--manifest", GetDefaultManifest("com.aapt2.app"),
+ "-o", base_apk,
+ };
+ ASSERT_TRUE(Link(link_args, base_files_dir, &diag));
+
+ const std::string feature_manifest = GetTestPath("feature_manifest.xml");
+ WriteFile(feature_manifest, android::base::StringPrintf(R"(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.aapt2.app" split="feature1">
+ </manifest>)"));
+ const std::string feature_files_dir = GetTestPath("feature");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
+ R"(<resources>
+ <string name="foo">foo</string>
+ </resources>)",
+ feature_files_dir, &diag));
+ const std::string feature_apk = GetTestPath("feature.apk");
+ const std::string feature_package_id = "0x80";
+ link_args = {
+ "--manifest", feature_manifest,
+ "-I", base_apk,
+ "--package-id", feature_package_id,
+ "-o", feature_apk,
+ };
+ ASSERT_TRUE(Link(link_args, feature_files_dir, &diag));
+
+ const std::string feature2_manifest = GetTestPath("feature2_manifest.xml");
+ WriteFile(feature2_manifest, android::base::StringPrintf(R"(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.aapt2.app" split="feature2">
+ <uses-split android:name="feature1"/>
+ </manifest>)"));
+ const std::string feature2_files_dir = GetTestPath("feature2");
+ ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"),
+ R"(<resources>
+ <string-array name="string_array">
+ <item>@string/bar</item>
+ <item>@string/foo</item>
+ </string-array>
+ </resources>)",
+ feature2_files_dir, &diag));
+ const std::string feature2_apk = GetTestPath("feature2.apk");
+ const std::string feature2_package_id = "0x81";
+ link_args = {
+ "--manifest", feature2_manifest,
+ "-I", base_apk,
+ "-I", feature_apk,
+ "--package-id", feature2_package_id,
+ "-o", feature2_apk,
+ };
+ ASSERT_TRUE(Link(link_args, feature2_files_dir, &diag));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 2e6af18c1948..e36668e5a043 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -53,9 +53,9 @@ using ::android::ConfigDescription;
using ::android::ResTable_config;
using ::android::StringPiece;
using ::android::base::ReadFileToString;
-using ::android::base::WriteStringToFile;
using ::android::base::StringAppendF;
using ::android::base::StringPrintf;
+using ::android::base::WriteStringToFile;
namespace aapt {
@@ -108,6 +108,12 @@ class OptimizeContext : public IAaptContext {
return sdk_version_;
}
+ const std::set<std::string>& GetSplitNameDependencies() override {
+ UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary";
+ static std::set<std::string> empty;
+ return empty;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(OptimizeContext);
@@ -294,29 +300,7 @@ class Optimizer {
OptimizeContext* context_;
};
-bool ExtractObfuscationWhitelistFromConfig(const std::string& path, OptimizeContext* context,
- OptimizeOptions* options) {
- std::string contents;
- if (!ReadFileToString(path, &contents, true)) {
- context->GetDiagnostics()->Error(DiagMessage()
- << "failed to parse whitelist from config file: " << path);
- return false;
- }
- for (StringPiece resource_name : util::Tokenize(contents, ',')) {
- options->table_flattener_options.whitelisted_resources.insert(
- resource_name.to_string());
- }
- return true;
-}
-
-bool ExtractConfig(const std::string& path, OptimizeContext* context,
- OptimizeOptions* options) {
- std::string content;
- if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) {
- context->GetDiagnostics()->Error(DiagMessage(path) << "failed reading whitelist");
- return false;
- }
-
+bool ParseConfig(const std::string& content, IAaptContext* context, OptimizeOptions* options) {
size_t line_no = 0;
for (StringPiece line : util::Tokenize(content, '\n')) {
line_no++;
@@ -345,15 +329,24 @@ bool ExtractConfig(const std::string& path, OptimizeContext* context,
for (StringPiece directive : util::Tokenize(directives, ',')) {
if (directive == "remove") {
options->resources_blacklist.insert(resource_name.ToResourceName());
- } else if (directive == "no_obfuscate") {
- options->table_flattener_options.whitelisted_resources.insert(
- resource_name.entry.to_string());
+ } else if (directive == "no_collapse" || directive == "no_obfuscate") {
+ options->table_flattener_options.name_collapse_exemptions.insert(
+ resource_name.ToResourceName());
}
}
}
return true;
}
+bool ExtractConfig(const std::string& path, IAaptContext* context, OptimizeOptions* options) {
+ std::string content;
+ if (!android::base::ReadFileToString(path, &content, true /*follow_symlinks*/)) {
+ context->GetDiagnostics()->Error(DiagMessage(path) << "failed reading config file");
+ return false;
+ }
+ return ParseConfig(content, context, options);
+}
+
bool ExtractAppDataFromManifest(OptimizeContext* context, const LoadedApk* apk,
OptimizeOptions* out_options) {
const xml::XmlResource* manifest = apk->GetManifest();
@@ -461,15 +454,6 @@ int OptimizeCommand::Action(const std::vector<std::string>& args) {
}
}
- if (options_.table_flattener_options.collapse_key_stringpool) {
- if (whitelist_path_) {
- std::string& path = whitelist_path_.value();
- if (!ExtractObfuscationWhitelistFromConfig(path, &context, &options_)) {
- return 1;
- }
- }
- }
-
if (resources_config_path_) {
std::string& path = resources_config_path_.value();
if (!ExtractConfig(path, &context, &options_)) {
diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h
index 7f4a3ed85364..5070ccc8afbf 100644
--- a/tools/aapt2/cmd/Optimize.h
+++ b/tools/aapt2/cmd/Optimize.h
@@ -57,7 +57,7 @@ struct OptimizeOptions {
std::unordered_set<std::string> kept_artifacts;
// Whether or not to shorten resource paths in the APK.
- bool shorten_resource_paths;
+ bool shorten_resource_paths = false;
// Path to the output map of original resource paths to shortened paths.
Maybe<std::string> shortened_paths_map_path;
@@ -78,10 +78,6 @@ class OptimizeCommand : public Command {
"All the resources that would be unused on devices of the given densities will be \n"
"removed from the APK.",
&target_densities_);
- AddOptionalFlag("--whitelist-path",
- "Path to the whitelist.cfg file containing whitelisted resources \n"
- "whose names should not be altered in final resource tables.",
- &whitelist_path_);
AddOptionalFlag("--resources-config-path",
"Path to the resources.cfg file containing the list of resources and \n"
"directives to each resource. \n"
@@ -104,11 +100,13 @@ class OptimizeCommand : public Command {
"Enables encoding sparse entries using a binary search tree.\n"
"This decreases APK size at the cost of resource retrieval performance.",
&options_.table_flattener_options.use_sparse_entries);
- AddOptionalSwitch("--enable-resource-obfuscation",
- "Enables obfuscation of key string pool to single value",
+ AddOptionalSwitch("--collapse-resource-names",
+ "Collapses resource names to a single value in the key string pool. Resources can \n"
+ "be exempted using the \"no_collapse\" directive in a file specified by "
+ "--resources-config-path.",
&options_.table_flattener_options.collapse_key_stringpool);
- AddOptionalSwitch("--enable-resource-path-shortening",
- "Enables shortening of the path of the resources inside the APK.",
+ AddOptionalSwitch("--shorten-resource-paths",
+ "Shortens the paths of resources inside the APK.",
&options_.shorten_resource_paths);
AddOptionalFlag("--resource-path-shortening-map",
"Path to output the map of old resource paths to shortened paths.",
@@ -125,7 +123,6 @@ class OptimizeCommand : public Command {
const std::string &file_path);
Maybe<std::string> config_path_;
- Maybe<std::string> whitelist_path_;
Maybe<std::string> resources_config_path_;
Maybe<std::string> target_densities_;
std::vector<std::string> configs_;
diff --git a/tools/aapt2/cmd/Optimize_test.cpp b/tools/aapt2/cmd/Optimize_test.cpp
new file mode 100644
index 000000000000..ac681e85b3d6
--- /dev/null
+++ b/tools/aapt2/cmd/Optimize_test.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Optimize.h"
+
+#include "AppInfo.h"
+#include "Diagnostics.h"
+#include "LoadedApk.h"
+#include "Resource.h"
+#include "test/Test.h"
+
+using testing::Contains;
+using testing::Eq;
+
+namespace aapt {
+
+bool ParseConfig(const std::string&, IAaptContext*, OptimizeOptions*);
+
+using OptimizeTest = CommandTestFixture;
+
+TEST_F(OptimizeTest, ParseConfigWithNoCollapseExemptions) {
+ const std::string& content = R"(
+string/foo#no_collapse
+dimen/bar#no_collapse
+)";
+ aapt::test::Context context;
+ OptimizeOptions options;
+ ParseConfig(content, &context, &options);
+
+ const std::set<ResourceName>& name_collapse_exemptions =
+ options.table_flattener_options.name_collapse_exemptions;
+
+ ASSERT_THAT(name_collapse_exemptions.size(), Eq(2));
+ EXPECT_THAT(name_collapse_exemptions, Contains(ResourceName({}, ResourceType::kString, "foo")));
+ EXPECT_THAT(name_collapse_exemptions, Contains(ResourceName({}, ResourceType::kDimen, "bar")));
+}
+
+TEST_F(OptimizeTest, ParseConfigWithNoObfuscateExemptions) {
+ const std::string& content = R"(
+string/foo#no_obfuscate
+dimen/bar#no_obfuscate
+)";
+ aapt::test::Context context;
+ OptimizeOptions options;
+ ParseConfig(content, &context, &options);
+
+ const std::set<ResourceName>& name_collapse_exemptions =
+ options.table_flattener_options.name_collapse_exemptions;
+
+ ASSERT_THAT(name_collapse_exemptions.size(), Eq(2));
+ EXPECT_THAT(name_collapse_exemptions, Contains(ResourceName({}, ResourceType::kString, "foo")));
+ EXPECT_THAT(name_collapse_exemptions, Contains(ResourceName({}, ResourceType::kDimen, "bar")));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/cmd/Util.cpp b/tools/aapt2/cmd/Util.cpp
index e2c65ba74271..7214f1a68d2c 100644
--- a/tools/aapt2/cmd/Util.cpp
+++ b/tools/aapt2/cmd/Util.cpp
@@ -436,9 +436,9 @@ void SetLongVersionCode(xml::Element* manifest, uint64_t version) {
}
std::regex GetRegularExpression(const std::string &input) {
- // Standard ECMAScript grammar plus case insensitive.
+ // Standard ECMAScript grammar.
std::regex case_insensitive(
- input, std::regex_constants::icase | std::regex_constants::ECMAScript);
+ input, std::regex_constants::ECMAScript);
return case_insensitive;
}
diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp
index 7e492610b658..ac1f981d753c 100644
--- a/tools/aapt2/cmd/Util_test.cpp
+++ b/tools/aapt2/cmd/Util_test.cpp
@@ -383,7 +383,7 @@ TEST (UtilTest, AdjustSplitConstraintsForMinSdk) {
EXPECT_NE(*adjusted_contraints[1].configs.begin(), ConfigDescription::DefaultConfig());
}
-TEST(UtilTest, RegularExperssions) {
+TEST (UtilTest, RegularExperssionsSimple) {
std::string valid(".bc$");
std::regex expression = GetRegularExpression(valid);
EXPECT_TRUE(std::regex_search("file.abc", expression));
@@ -391,4 +391,24 @@ TEST(UtilTest, RegularExperssions) {
EXPECT_FALSE(std::regex_search("abc.zip", expression));
}
+TEST (UtilTest, RegularExpressionComplex) {
+ std::string valid("\\.(d|D)(e|E)(x|X)$");
+ std::regex expression = GetRegularExpression(valid);
+ EXPECT_TRUE(std::regex_search("file.dex", expression));
+ EXPECT_TRUE(std::regex_search("file.DEX", expression));
+ EXPECT_TRUE(std::regex_search("file.dEx", expression));
+ EXPECT_FALSE(std::regex_search("file.dexx", expression));
+ EXPECT_FALSE(std::regex_search("dex.file", expression));
+ EXPECT_FALSE(std::regex_search("file.adex", expression));
+}
+
+TEST (UtilTest, RegularExpressionNonEnglish) {
+ std::string valid("\\.(k|K)(o|O)(ń|Ń)(c|C)(ó|Ó)(w|W)(k|K)(a|A)$");
+ std::regex expression = GetRegularExpression(valid);
+ EXPECT_TRUE(std::regex_search("file.końcówka", expression));
+ EXPECT_TRUE(std::regex_search("file.KOŃCÓWKA", expression));
+ EXPECT_TRUE(std::regex_search("file.kOńcÓwkA", expression));
+ EXPECT_FALSE(std::regex_search("file.koncowka", expression));
+}
+
} // namespace aapt
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 42a64716701d..4a6bfd031284 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -91,6 +91,7 @@ enum {
};
const std::string& kAndroidNamespace = "http://schemas.android.com/apk/res/android";
+constexpr int kCurrentDevelopmentVersion = 10000;
/** Retrieves the attribute of the element with the specified attribute resource id. */
static xml::Attribute* FindAttribute(xml::Element *el, uint32_t resd_id) {
@@ -291,7 +292,10 @@ class ManifestExtractor {
}
}
}
- return &attr->value;
+
+ if (!attr->value.empty()) {
+ return &attr->value;
+ }
}
return nullptr;
}
@@ -322,7 +326,7 @@ class ManifestExtractor {
ConfigDescription config;
config.orientation = android::ResTable_config::ORIENTATION_PORT;
config.density = android::ResTable_config::DENSITY_MEDIUM;
- config.sdkVersion = 10000; // Very high.
+ config.sdkVersion = kCurrentDevelopmentVersion; // Very high.
config.screenWidthDp = 320;
config.screenHeightDp = 480;
config.smallestScreenWidthDp = 320;
@@ -425,6 +429,8 @@ class Manifest : public ManifestExtractor::Element {
const std::string* split = nullptr;
const std::string* platformVersionName = nullptr;
const std::string* platformVersionCode = nullptr;
+ const int32_t* platformVersionNameInt = nullptr;
+ const int32_t* platformVersionCodeInt = nullptr;
const int32_t* compilesdkVersion = nullptr;
const std::string* compilesdkVersionCodename = nullptr;
const int32_t* installLocation = nullptr;
@@ -440,6 +446,10 @@ class Manifest : public ManifestExtractor::Element {
"platformBuildVersionName"));
platformVersionCode = GetAttributeString(FindAttribute(manifest, {},
"platformBuildVersionCode"));
+ platformVersionNameInt = GetAttributeInteger(FindAttribute(manifest, {},
+ "platformBuildVersionName"));
+ platformVersionCodeInt = GetAttributeInteger(FindAttribute(manifest, {},
+ "platformBuildVersionCode"));
// Extract the compile sdk info
compilesdkVersion = GetAttributeInteger(FindAttribute(manifest, COMPILE_SDK_VERSION_ATTR));
@@ -459,9 +469,13 @@ class Manifest : public ManifestExtractor::Element {
}
if (platformVersionName) {
printer->Print(StringPrintf(" platformBuildVersionName='%s'", platformVersionName->data()));
+ } else if (platformVersionNameInt) {
+ printer->Print(StringPrintf(" platformBuildVersionName='%d'", *platformVersionNameInt));
}
if (platformVersionCode) {
printer->Print(StringPrintf(" platformBuildVersionCode='%s'", platformVersionCode->data()));
+ } else if (platformVersionCodeInt) {
+ printer->Print(StringPrintf(" platformBuildVersionCode='%d'", *platformVersionCodeInt));
}
if (compilesdkVersion) {
printer->Print(StringPrintf(" compileSdkVersion='%d'", *compilesdkVersion));
@@ -609,6 +623,8 @@ class UsesSdkBadging : public ManifestExtractor::Element {
}
if (target_sdk) {
extractor()->RaiseTargetSdk(*target_sdk);
+ } else if (target_sdk_name) {
+ extractor()->RaiseTargetSdk(kCurrentDevelopmentVersion);
}
}
diff --git a/tools/aapt2/format/Archive.cpp b/tools/aapt2/format/Archive.cpp
index d152a9cc7e62..41f01a01ed7c 100644
--- a/tools/aapt2/format/Archive.cpp
+++ b/tools/aapt2/format/Archive.cpp
@@ -103,7 +103,13 @@ class DirectoryWriter : public IArchiveWriter {
return false;
}
}
- return !in->HadError();
+
+ if (in->HadError()) {
+ error_ = in->GetError();
+ return false;
+ }
+
+ return FinishEntry();
}
bool HadError() const override {
@@ -191,6 +197,7 @@ class ZipFileWriter : public IArchiveWriter {
}
if (in->HadError()) {
+ error_ = in->GetError();
return false;
}
diff --git a/tools/aapt2/format/Archive_test.cpp b/tools/aapt2/format/Archive_test.cpp
new file mode 100644
index 000000000000..ceed3740f37a
--- /dev/null
+++ b/tools/aapt2/format/Archive_test.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "test/Test.h"
+
+namespace aapt {
+
+using ArchiveTest = TestDirectoryFixture;
+
+constexpr size_t kTestDataLength = 100;
+
+class TestData : public io::MallocData {
+ public:
+ TestData(std::unique_ptr<uint8_t[]>& data, size_t size)
+ : MallocData(std::move(data), size) {}
+
+ bool HadError() const override { return !error_.empty(); }
+
+ std::string GetError() const override { return error_; }
+
+ std::string error_;
+};
+
+std::unique_ptr<uint8_t[]> MakeTestArray() {
+ auto array = std::make_unique<uint8_t[]>(kTestDataLength);
+ for (int index = 0; index < kTestDataLength; ++index) {
+ array[index] = static_cast<uint8_t>(rand());
+ }
+ return array;
+}
+
+std::unique_ptr<IArchiveWriter> MakeDirectoryWriter(const std::string& output_path) {
+ file::mkdirs(output_path);
+
+ StdErrDiagnostics diag;
+ return CreateDirectoryArchiveWriter(&diag, output_path);
+}
+
+std::unique_ptr<IArchiveWriter> MakeZipFileWriter(const std::string& output_path) {
+ file::mkdirs(file::GetStem(output_path).to_string());
+ std::remove(output_path.c_str());
+
+ StdErrDiagnostics diag;
+ return CreateZipFileArchiveWriter(&diag, output_path);
+}
+
+void VerifyDirectory(const std::string& path, const std::string& file, const uint8_t array[]) {
+ std::string file_path = file::BuildPath({path, file});
+ auto buffer = std::make_unique<char[]>(kTestDataLength);
+ std::ifstream stream(file_path);
+ stream.read(buffer.get(), kTestDataLength);
+
+ for (int index = 0; index < kTestDataLength; ++index) {
+ ASSERT_EQ(array[index], static_cast<uint8_t>(buffer[index]));
+ }
+}
+
+void VerifyZipFile(const std::string& output_path, const std::string& file, const uint8_t array[]) {
+ std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(output_path, nullptr);
+ std::unique_ptr<io::InputStream> stream = zip->FindFile(file)->OpenInputStream();
+
+ std::vector<uint8_t> buffer;
+ const void* data;
+ size_t size;
+
+ while (stream->Next(&data, &size)) {
+ auto pointer = static_cast<const uint8_t*>(data);
+ buffer.insert(buffer.end(), pointer, pointer + size);
+ }
+
+ for (int index = 0; index < kTestDataLength; ++index) {
+ ASSERT_EQ(array[index], buffer[index]);
+ }
+}
+
+TEST_F(ArchiveTest, DirectoryWriteEntrySuccess) {
+ std::string output_path = GetTestPath("output");
+ std::unique_ptr<IArchiveWriter> writer = MakeDirectoryWriter(output_path);
+ std::unique_ptr<uint8_t[]> data1 = MakeTestArray();
+ std::unique_ptr<uint8_t[]> data2 = MakeTestArray();
+
+ ASSERT_TRUE(writer->StartEntry("test1", 0));
+ ASSERT_TRUE(writer->Write(static_cast<const void*>(data1.get()), kTestDataLength));
+ ASSERT_TRUE(writer->FinishEntry());
+ ASSERT_FALSE(writer->HadError());
+
+ ASSERT_TRUE(writer->StartEntry("test2", 0));
+ ASSERT_TRUE(writer->Write(static_cast<const void*>(data2.get()), kTestDataLength));
+ ASSERT_TRUE(writer->FinishEntry());
+ ASSERT_FALSE(writer->HadError());
+
+ writer.reset();
+
+ VerifyDirectory(output_path, "test1", data1.get());
+ VerifyDirectory(output_path, "test2", data2.get());
+}
+
+TEST_F(ArchiveTest, DirectoryWriteFileSuccess) {
+ std::string output_path = GetTestPath("output");
+ std::unique_ptr<IArchiveWriter> writer = MakeDirectoryWriter(output_path);
+
+ std::unique_ptr<uint8_t[]> data1 = MakeTestArray();
+ auto data1_copy = std::make_unique<uint8_t[]>(kTestDataLength);
+ std::copy(data1.get(), data1.get() + kTestDataLength, data1_copy.get());
+
+ std::unique_ptr<uint8_t[]> data2 = MakeTestArray();
+ auto data2_copy = std::make_unique<uint8_t[]>(kTestDataLength);
+ std::copy(data2.get(), data2.get() + kTestDataLength, data2_copy.get());
+
+ auto input1 = std::make_unique<TestData>(data1_copy, kTestDataLength);
+ auto input2 = std::make_unique<TestData>(data2_copy, kTestDataLength);
+
+ ASSERT_TRUE(writer->WriteFile("test1", 0, input1.get()));
+ ASSERT_FALSE(writer->HadError());
+ ASSERT_TRUE(writer->WriteFile("test2", 0, input2.get()));
+ ASSERT_FALSE(writer->HadError());
+
+ writer.reset();
+
+ VerifyDirectory(output_path, "test1", data1.get());
+ VerifyDirectory(output_path, "test2", data2.get());
+}
+
+TEST_F(ArchiveTest, DirectoryWriteFileError) {
+ std::string output_path = GetTestPath("output");
+ std::unique_ptr<IArchiveWriter> writer = MakeDirectoryWriter(output_path);
+ std::unique_ptr<uint8_t[]> data = MakeTestArray();
+ auto input = std::make_unique<TestData>(data, kTestDataLength);
+ input->error_ = "DirectoryWriteFileError";
+
+ ASSERT_FALSE(writer->WriteFile("test", 0, input.get()));
+ ASSERT_TRUE(writer->HadError());
+ ASSERT_EQ("DirectoryWriteFileError", writer->GetError());
+}
+
+TEST_F(ArchiveTest, ZipFileWriteEntrySuccess) {
+ std::string output_path = GetTestPath("output.apk");
+ std::unique_ptr<IArchiveWriter> writer = MakeZipFileWriter(output_path);
+ std::unique_ptr<uint8_t[]> data1 = MakeTestArray();
+ std::unique_ptr<uint8_t[]> data2 = MakeTestArray();
+
+ ASSERT_TRUE(writer->StartEntry("test1", 0));
+ ASSERT_TRUE(writer->Write(static_cast<const void*>(data1.get()), kTestDataLength));
+ ASSERT_TRUE(writer->FinishEntry());
+ ASSERT_FALSE(writer->HadError());
+
+ ASSERT_TRUE(writer->StartEntry("test2", 0));
+ ASSERT_TRUE(writer->Write(static_cast<const void*>(data2.get()), kTestDataLength));
+ ASSERT_TRUE(writer->FinishEntry());
+ ASSERT_FALSE(writer->HadError());
+
+ writer.reset();
+
+ VerifyZipFile(output_path, "test1", data1.get());
+ VerifyZipFile(output_path, "test2", data2.get());
+}
+
+TEST_F(ArchiveTest, ZipFileWriteFileSuccess) {
+ std::string output_path = GetTestPath("output.apk");
+ std::unique_ptr<IArchiveWriter> writer = MakeZipFileWriter(output_path);
+
+ std::unique_ptr<uint8_t[]> data1 = MakeTestArray();
+ auto data1_copy = std::make_unique<uint8_t[]>(kTestDataLength);
+ std::copy(data1.get(), data1.get() + kTestDataLength, data1_copy.get());
+
+ std::unique_ptr<uint8_t[]> data2 = MakeTestArray();
+ auto data2_copy = std::make_unique<uint8_t[]>(kTestDataLength);
+ std::copy(data2.get(), data2.get() + kTestDataLength, data2_copy.get());
+
+ auto input1 = std::make_unique<TestData>(data1_copy, kTestDataLength);
+ auto input2 = std::make_unique<TestData>(data2_copy, kTestDataLength);
+
+ ASSERT_TRUE(writer->WriteFile("test1", 0, input1.get()));
+ ASSERT_FALSE(writer->HadError());
+ ASSERT_TRUE(writer->WriteFile("test2", 0, input2.get()));
+ ASSERT_FALSE(writer->HadError());
+
+ writer.reset();
+
+ VerifyZipFile(output_path, "test1", data1.get());
+ VerifyZipFile(output_path, "test2", data2.get());
+}
+
+TEST_F(ArchiveTest, ZipFileWriteFileError) {
+ std::string output_path = GetTestPath("output.apk");
+ std::unique_ptr<IArchiveWriter> writer = MakeZipFileWriter(output_path);
+ std::unique_ptr<uint8_t[]> data = MakeTestArray();
+ auto input = std::make_unique<TestData>(data, kTestDataLength);
+ input->error_ = "ZipFileWriteFileError";
+
+ ASSERT_FALSE(writer->WriteFile("test", 0, input.get()));
+ ASSERT_TRUE(writer->HadError());
+ ASSERT_EQ("ZipFileWriteFileError", writer->GetError());
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/format/binary/BinaryResourceParser.cpp b/tools/aapt2/format/binary/BinaryResourceParser.cpp
index 82c21a66b9f9..cccd9faa9b39 100644
--- a/tools/aapt2/format/binary/BinaryResourceParser.cpp
+++ b/tools/aapt2/format/binary/BinaryResourceParser.cpp
@@ -454,35 +454,6 @@ bool BinaryResourceParser::ParseOverlayable(const ResChunk_header* chunk) {
const ResTable_overlayable_policy_header* policy_header =
ConvertTo<ResTable_overlayable_policy_header>(parser.chunk());
- OverlayableItem::PolicyFlags policies = OverlayableItem::Policy::kNone;
- if (policy_header->policy_flags & ResTable_overlayable_policy_header::POLICY_PUBLIC) {
- policies |= OverlayableItem::Policy::kPublic;
- }
- if (policy_header->policy_flags
- & ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION) {
- policies |= OverlayableItem::Policy::kSystem;
- }
- if (policy_header->policy_flags
- & ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION) {
- policies |= OverlayableItem::Policy::kVendor;
- }
- if (policy_header->policy_flags
- & ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION) {
- policies |= OverlayableItem::Policy::kProduct;
- }
- if (policy_header->policy_flags
- & ResTable_overlayable_policy_header::POLICY_SIGNATURE) {
- policies |= OverlayableItem::Policy::kSignature;
- }
- if (policy_header->policy_flags
- & ResTable_overlayable_policy_header::POLICY_ODM_PARTITION) {
- policies |= OverlayableItem::Policy::kOdm;
- }
- if (policy_header->policy_flags
- & ResTable_overlayable_policy_header::POLICY_OEM_PARTITION) {
- policies |= OverlayableItem::Policy::kOem;
- }
-
const ResTable_ref* const ref_begin = reinterpret_cast<const ResTable_ref*>(
((uint8_t *)policy_header) + util::DeviceToHost32(policy_header->header.headerSize));
const ResTable_ref* const ref_end = ref_begin
@@ -500,7 +471,7 @@ bool BinaryResourceParser::ParseOverlayable(const ResChunk_header* chunk) {
}
OverlayableItem overlayable_item(overlayable);
- overlayable_item.policies = policies;
+ overlayable_item.policies = policy_header->policy_flags;
if (!table_->SetOverlayable(iter->second, overlayable_item, diag_)) {
return false;
}
@@ -613,6 +584,7 @@ std::unique_ptr<Attribute> BinaryResourceParser::ParseAttr(const ResourceNameRef
if (attr->type_mask & (ResTable_map::TYPE_ENUM | ResTable_map::TYPE_FLAGS)) {
Attribute::Symbol symbol;
symbol.value = util::DeviceToHost32(map_entry.value.data);
+ symbol.type = map_entry.value.dataType;
symbol.symbol = Reference(util::DeviceToHost32(map_entry.name.ident));
attr->symbols.push_back(std::move(symbol));
}
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index f2e72da4056a..4784ecf3d12c 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -59,10 +59,22 @@ 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 a.key.id.value() < b.key.id.value();
+ return cmp_style_ids(a.key.id.value(), b.key.id.value());
}
return true;
} else if (!b.key.id) {
@@ -107,7 +119,7 @@ class MapFlattenVisitor : public ValueVisitor {
}
for (Attribute::Symbol& s : attr->symbols) {
- BinaryPrimitive val(Res_value::TYPE_INT_DEC, s.value);
+ BinaryPrimitive val(s.type, s.value);
FlattenEntry(&s.symbol, &val);
}
}
@@ -221,21 +233,22 @@ class MapFlattenVisitor : public ValueVisitor {
struct OverlayableChunk {
std::string actor;
Source source;
- std::map<OverlayableItem::PolicyFlags, std::set<ResourceId>> policy_ids;
+ std::map<PolicyFlags, std::set<ResourceId>> policy_ids;
};
class PackageFlattener {
public:
PackageFlattener(IAaptContext* context, ResourceTablePackage* package,
const std::map<size_t, std::string>* shared_libs, bool use_sparse_entries,
- bool collapse_key_stringpool, const std::set<std::string>& whitelisted_resources)
+ bool collapse_key_stringpool,
+ const std::set<ResourceName>& name_collapse_exemptions)
: context_(context),
diag_(context->GetDiagnostics()),
package_(package),
shared_libs_(shared_libs),
use_sparse_entries_(use_sparse_entries),
collapse_key_stringpool_(collapse_key_stringpool),
- whitelisted_resources_(whitelisted_resources) {
+ name_collapse_exemptions_(name_collapse_exemptions) {
}
bool FlattenPackage(BigBuffer* buffer) {
@@ -480,35 +493,12 @@ class PackageFlattener {
return false;
}
- uint32_t policy_flags = 0;
- if (item.policies & OverlayableItem::Policy::kPublic) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_PUBLIC;
- }
- if (item.policies & OverlayableItem::Policy::kSystem) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_SYSTEM_PARTITION;
- }
- if (item.policies & OverlayableItem::Policy::kVendor) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_VENDOR_PARTITION;
- }
- if (item.policies & OverlayableItem::Policy::kProduct) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_PRODUCT_PARTITION;
- }
- if (item.policies & OverlayableItem::Policy::kSignature) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_SIGNATURE;
- }
- if (item.policies & OverlayableItem::Policy::kOdm) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_ODM_PARTITION;
- }
- if (item.policies & OverlayableItem::Policy::kOem) {
- policy_flags |= ResTable_overlayable_policy_header::POLICY_OEM_PARTITION;
- }
-
- auto policy = overlayable_chunk->policy_ids.find(policy_flags);
+ auto policy = overlayable_chunk->policy_ids.find(item.policies);
if (policy != overlayable_chunk->policy_ids.end()) {
policy->second.insert(id);
} else {
overlayable_chunk->policy_ids.insert(
- std::make_pair(policy_flags, std::set<ResourceId>{id}));
+ std::make_pair(item.policies, std::set<ResourceId>{id}));
}
}
}
@@ -546,7 +536,8 @@ class PackageFlattener {
ChunkWriter policy_writer(buffer);
auto* policy_type = policy_writer.StartChunk<ResTable_overlayable_policy_header>(
RES_TABLE_OVERLAYABLE_POLICY_TYPE);
- policy_type->policy_flags = util::HostToDevice32(static_cast<uint32_t>(policy_ids.first));
+ policy_type->policy_flags =
+ static_cast<PolicyFlags>(util::HostToDevice32(static_cast<uint32_t>(policy_ids.first)));
policy_type->entry_count = util::HostToDevice32(static_cast<uint32_t>(
policy_ids.second.size()));
// Write the ids after the policy header
@@ -652,11 +643,12 @@ class PackageFlattener {
for (ResourceEntry* entry : sorted_entries) {
uint32_t local_key_index;
+ ResourceName resource_name({}, type->type, entry->name);
if (!collapse_key_stringpool_ ||
- whitelisted_resources_.find(entry->name) != whitelisted_resources_.end()) {
+ name_collapse_exemptions_.find(resource_name) != name_collapse_exemptions_.end()) {
local_key_index = (uint32_t)key_pool_.MakeRef(entry->name).index();
} else {
- // resource isn't whitelisted, add it as obfuscated value
+ // resource isn't exempt from collapse, add it as obfuscated value
local_key_index = (uint32_t)key_pool_.MakeRef(obfuscated_resource_name).index();
}
// Group values by configuration.
@@ -712,7 +704,7 @@ class PackageFlattener {
StringPool type_pool_;
StringPool key_pool_;
bool collapse_key_stringpool_;
- const std::set<std::string>& whitelisted_resources_;
+ const std::set<ResourceName>& name_collapse_exemptions_;
};
} // namespace
@@ -760,7 +752,7 @@ bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) {
PackageFlattener flattener(context, package.get(), &table->included_packages_,
options_.use_sparse_entries, options_.collapse_key_stringpool,
- options_.whitelisted_resources);
+ options_.name_collapse_exemptions);
if (!flattener.FlattenPackage(&package_buffer)) {
return false;
}
diff --git a/tools/aapt2/format/binary/TableFlattener.h b/tools/aapt2/format/binary/TableFlattener.h
index 73c17295556b..4360db190146 100644
--- a/tools/aapt2/format/binary/TableFlattener.h
+++ b/tools/aapt2/format/binary/TableFlattener.h
@@ -19,6 +19,7 @@
#include "android-base/macros.h"
+#include "Resource.h"
#include "ResourceTable.h"
#include "process/IResourceTableConsumer.h"
#include "util/BigBuffer.h"
@@ -41,8 +42,8 @@ struct TableFlattenerOptions {
// have name indices that point to this single value
bool collapse_key_stringpool = false;
- // Set of whitelisted resource names to avoid altering in key stringpool
- std::set<std::string> whitelisted_resources;
+ // Set of resources to avoid collapsing to a single entry in key stringpool.
+ std::set<ResourceName> name_collapse_exemptions;
// Map from original resource paths to shortened resource paths.
std::map<std::string, std::string> shortened_path_map;
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index a9409235e07a..59627ce579af 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -32,6 +32,8 @@ using ::testing::Gt;
using ::testing::IsNull;
using ::testing::NotNull;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
class TableFlattenerTest : public ::testing::Test {
@@ -431,6 +433,47 @@ TEST_F(TableFlattenerTest, FlattenSharedLibrary) {
EXPECT_EQ("lib", iter->second);
}
+TEST_F(TableFlattenerTest, FlattenSharedLibraryWithStyle) {
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder().SetCompilationPackage("lib").SetPackageId(0x00).Build();
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .SetPackageId("lib", 0x00)
+ .AddValue("lib:style/Theme",
+ ResourceId(0x00030001),
+ test::StyleBuilder()
+ .AddItem("lib:attr/bar", ResourceId(0x00010002),
+ ResourceUtils::TryParseInt("2"))
+ .AddItem("lib:attr/foo", ResourceId(0x00010001),
+ ResourceUtils::TryParseInt("1"))
+ .AddItem("android:attr/bar", ResourceId(0x01010002),
+ ResourceUtils::TryParseInt("4"))
+ .AddItem("android:attr/foo", ResourceId(0x01010001),
+ ResourceUtils::TryParseInt("3"))
+ .Build())
+ .Build();
+ ResourceTable result;
+ ASSERT_TRUE(Flatten(context.get(), {}, table.get(), &result));
+
+ Maybe<ResourceTable::SearchResult> search_result =
+ result.FindResource(test::ParseNameOrDie("lib:style/Theme"));
+ ASSERT_TRUE(search_result);
+ EXPECT_EQ(0x00u, search_result.value().package->id.value());
+ EXPECT_EQ(0x03u, search_result.value().type->id.value());
+ EXPECT_EQ(0x01u, search_result.value().entry->id.value());
+ ASSERT_EQ(1u, search_result.value().entry->values.size());
+ Value* value = search_result.value().entry->values[0]->value.get();
+ Style* style = ValueCast<Style>(value);
+ ASSERT_TRUE(style);
+ ASSERT_EQ(4u, style->entries.size());
+ // Ensure the attributes from the shared library come after the items from
+ // android.
+ EXPECT_EQ(0x01010001, style->entries[0].key.id.value());
+ EXPECT_EQ(0x01010002, style->entries[1].key.id.value());
+ EXPECT_EQ(0x00010001, style->entries[2].key.id.value());
+ EXPECT_EQ(0x00010002, style->entries[3].key.id.value());
+}
+
TEST_F(TableFlattenerTest, FlattenTableReferencingSharedLibraries) {
std::unique_ptr<IAaptContext> context =
test::ContextBuilder().SetCompilationPackage("app").SetPackageId(0x7f).Build();
@@ -518,7 +561,7 @@ TEST_F(TableFlattenerTest, LongSharedLibraryPackageNameIsIllegal) {
ASSERT_FALSE(Flatten(context.get(), {}, table.get(), &result));
}
-TEST_F(TableFlattenerTest, ObfuscatingResourceNamesNoWhitelistSucceeds) {
+TEST_F(TableFlattenerTest, ObfuscatingResourceNamesNoNameCollapseExemptionsSucceeds) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("com.app.test", 0x7f)
@@ -572,7 +615,7 @@ TEST_F(TableFlattenerTest, ObfuscatingResourceNamesNoWhitelistSucceeds) {
ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u));
}
-TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithWhitelistSucceeds) {
+TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithNameCollapseExemptionsSucceeds) {
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("com.app.test", 0x7f)
@@ -591,21 +634,22 @@ TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithWhitelistSucceeds) {
TableFlattenerOptions options;
options.collapse_key_stringpool = true;
- options.whitelisted_resources.insert("test");
- options.whitelisted_resources.insert("three");
+ options.name_collapse_exemptions.insert(ResourceName({}, ResourceType::kId, "one"));
+ options.name_collapse_exemptions.insert(ResourceName({}, ResourceType::kString, "test"));
ResTable res_table;
ASSERT_TRUE(Flatten(context_.get(), options, table.get(), &res_table));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one",
ResourceId(0x7f020000), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
ResourceId(0x7f020001), {}, Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
- EXPECT_TRUE(Exists(&res_table, "com.app.test:id/three", ResourceId(0x7f020002), {},
- Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
+ EXPECT_TRUE(Exists(&res_table, "com.app.test:id/0_resource_name_obfuscated",
+ ResourceId(0x7f020002), {}, Res_value::TYPE_REFERENCE, 0x7f020000u, 0u));
+ // Note that this resource is also named "one", but it's a different type, so gets obfuscated.
EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/0_resource_name_obfuscated",
ResourceId(0x7f030000), {}, Res_value::TYPE_INT_DEC, 1u,
ResTable_config::CONFIG_VERSION));
@@ -629,9 +673,9 @@ TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithWhitelistSucceeds) {
TEST_F(TableFlattenerTest, FlattenOverlayable) {
OverlayableItem overlayable_item(std::make_shared<Overlayable>("TestName", "overlay://theme"));
- overlayable_item.policies |= OverlayableItem::Policy::kProduct;
- overlayable_item.policies |= OverlayableItem::Policy::kSystem;
- overlayable_item.policies |= OverlayableItem::Policy::kVendor;
+ overlayable_item.policies |= PolicyFlags::PRODUCT_PARTITION;
+ overlayable_item.policies |= PolicyFlags::SYSTEM_PARTITION;
+ overlayable_item.policies |= PolicyFlags::VENDOR_PARTITION;
std::string name = "com.app.test:integer/overlayable";
std::unique_ptr<ResourceTable> table =
@@ -649,27 +693,27 @@ TEST_F(TableFlattenerTest, FlattenOverlayable) {
ASSERT_THAT(search_result.value().entry, NotNull());
ASSERT_TRUE(search_result.value().entry->overlayable_item);
OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
- EXPECT_EQ(result_overlayable_item.policies, OverlayableItem::Policy::kSystem
- | OverlayableItem::Policy::kVendor
- | OverlayableItem::Policy::kProduct);
+ EXPECT_EQ(result_overlayable_item.policies, PolicyFlags::SYSTEM_PARTITION
+ | PolicyFlags::VENDOR_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION);
}
TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
auto overlayable = std::make_shared<Overlayable>("TestName", "overlay://theme");
std::string name_zero = "com.app.test:integer/overlayable_zero_item";
OverlayableItem overlayable_item_zero(overlayable);
- overlayable_item_zero.policies |= OverlayableItem::Policy::kProduct;
- overlayable_item_zero.policies |= OverlayableItem::Policy::kSystem;
+ overlayable_item_zero.policies |= PolicyFlags::PRODUCT_PARTITION;
+ overlayable_item_zero.policies |= PolicyFlags::SYSTEM_PARTITION;
std::string name_one = "com.app.test:integer/overlayable_one_item";
OverlayableItem overlayable_item_one(overlayable);
- overlayable_item_one.policies |= OverlayableItem::Policy::kPublic;
+ overlayable_item_one.policies |= PolicyFlags::PUBLIC;
std::string name_two = "com.app.test:integer/overlayable_two_item";
OverlayableItem overlayable_item_two(overlayable);
- overlayable_item_two.policies |= OverlayableItem::Policy::kProduct;
- overlayable_item_two.policies |= OverlayableItem::Policy::kSystem;
- overlayable_item_two.policies |= OverlayableItem::Policy::kVendor;
+ overlayable_item_two.policies |= PolicyFlags::PRODUCT_PARTITION;
+ overlayable_item_two.policies |= PolicyFlags::SYSTEM_PARTITION;
+ overlayable_item_two.policies |= PolicyFlags::VENDOR_PARTITION;
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
@@ -690,47 +734,48 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayablePolicies) {
ASSERT_THAT(search_result.value().entry, NotNull());
ASSERT_TRUE(search_result.value().entry->overlayable_item);
OverlayableItem& overlayable_item = search_result.value().entry->overlayable_item.value();
- EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
- | OverlayableItem::Policy::kProduct);
+ EXPECT_EQ(overlayable_item.policies, PolicyFlags::SYSTEM_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION);
search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
ASSERT_TRUE(search_result.value().entry->overlayable_item);
overlayable_item = search_result.value().entry->overlayable_item.value();
- EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kPublic);
+ EXPECT_EQ(overlayable_item.policies, PolicyFlags::PUBLIC);
search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
ASSERT_TRUE(search_result);
ASSERT_THAT(search_result.value().entry, NotNull());
ASSERT_TRUE(search_result.value().entry->overlayable_item);
overlayable_item = search_result.value().entry->overlayable_item.value();
- EXPECT_EQ(overlayable_item.policies, OverlayableItem::Policy::kSystem
- | OverlayableItem::Policy::kProduct
- | OverlayableItem::Policy::kVendor);
+ EXPECT_EQ(overlayable_item.policies, PolicyFlags::SYSTEM_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION
+ | PolicyFlags::VENDOR_PARTITION);
}
TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) {
auto group = std::make_shared<Overlayable>("TestName", "overlay://theme");
std::string name_zero = "com.app.test:integer/overlayable_zero";
OverlayableItem overlayable_item_zero(group);
- overlayable_item_zero.policies |= OverlayableItem::Policy::kProduct;
- overlayable_item_zero.policies |= OverlayableItem::Policy::kSystem;
+ overlayable_item_zero.policies |= PolicyFlags::PRODUCT_PARTITION;
+ overlayable_item_zero.policies |= PolicyFlags::SYSTEM_PARTITION;
auto group_one = std::make_shared<Overlayable>("OtherName", "overlay://customization");
std::string name_one = "com.app.test:integer/overlayable_one";
OverlayableItem overlayable_item_one(group_one);
- overlayable_item_one.policies |= OverlayableItem::Policy::kPublic;
+ overlayable_item_one.policies |= PolicyFlags::PUBLIC;
std::string name_two = "com.app.test:integer/overlayable_two";
OverlayableItem overlayable_item_two(group);
- overlayable_item_two.policies |= OverlayableItem::Policy::kOdm;
- overlayable_item_two.policies |= OverlayableItem::Policy::kOem;
- overlayable_item_two.policies |= OverlayableItem::Policy::kVendor;
+ overlayable_item_two.policies |= PolicyFlags::ODM_PARTITION;
+ overlayable_item_two.policies |= PolicyFlags::OEM_PARTITION;
+ overlayable_item_two.policies |= PolicyFlags::VENDOR_PARTITION;
std::string name_three = "com.app.test:integer/overlayable_three";
OverlayableItem overlayable_item_three(group_one);
- overlayable_item_three.policies |= OverlayableItem::Policy::kSignature;
+ overlayable_item_three.policies |= PolicyFlags::SIGNATURE;
+ overlayable_item_three.policies |= PolicyFlags::ACTOR_SIGNATURE;
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
@@ -754,8 +799,8 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) {
OverlayableItem& result_overlayable = search_result.value().entry->overlayable_item.value();
EXPECT_EQ(result_overlayable.overlayable->name, "TestName");
EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://theme");
- EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kSystem
- | OverlayableItem::Policy::kProduct);
+ EXPECT_EQ(result_overlayable.policies, PolicyFlags::SYSTEM_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION);
search_result = output_table.FindResource(test::ParseNameOrDie(name_one));
ASSERT_TRUE(search_result);
@@ -764,7 +809,7 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) {
result_overlayable = search_result.value().entry->overlayable_item.value();
EXPECT_EQ(result_overlayable.overlayable->name, "OtherName");
EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization");
- EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kPublic);
+ EXPECT_EQ(result_overlayable.policies, PolicyFlags::PUBLIC);
search_result = output_table.FindResource(test::ParseNameOrDie(name_two));
ASSERT_TRUE(search_result);
@@ -773,9 +818,9 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) {
result_overlayable = search_result.value().entry->overlayable_item.value();
EXPECT_EQ(result_overlayable.overlayable->name, "TestName");
EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://theme");
- EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kOdm
- | OverlayableItem::Policy::kOem
- | OverlayableItem::Policy::kVendor);
+ EXPECT_EQ(result_overlayable.policies, PolicyFlags::ODM_PARTITION
+ | PolicyFlags::OEM_PARTITION
+ | PolicyFlags::VENDOR_PARTITION);
search_result = output_table.FindResource(test::ParseNameOrDie(name_three));
ASSERT_TRUE(search_result);
@@ -784,7 +829,8 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) {
result_overlayable = search_result.value().entry->overlayable_item.value();
EXPECT_EQ(result_overlayable.overlayable->name, "OtherName");
EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization");
- EXPECT_EQ(result_overlayable.policies, OverlayableItem::Policy::kSignature);
+ EXPECT_EQ(result_overlayable.policies, PolicyFlags::SIGNATURE
+ | PolicyFlags::ACTOR_SIGNATURE);
}
TEST_F(TableFlattenerTest, FlattenOverlayableNoPolicyFails) {
diff --git a/tools/aapt2/format/binary/XmlFlattener_test.cpp b/tools/aapt2/format/binary/XmlFlattener_test.cpp
index 1dac493359e4..c24488b16153 100644
--- a/tools/aapt2/format/binary/XmlFlattener_test.cpp
+++ b/tools/aapt2/format/binary/XmlFlattener_test.cpp
@@ -226,10 +226,10 @@ TEST_F(XmlFlattenerTest, FlattenNonStandardPackageId) {
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
// The tree needs a custom DynamicRefTable since it is not using a standard app ID (0x7f).
- android::DynamicRefTable dynamic_ref_table;
- dynamic_ref_table.addMapping(0x80, 0x80);
+ auto dynamic_ref_table = std::make_shared<android::DynamicRefTable>();
+ dynamic_ref_table->addMapping(0x80, 0x80);
- android::ResXMLTree tree(&dynamic_ref_table);
+ auto tree = android::ResXMLTree(std::move(dynamic_ref_table));
ASSERT_TRUE(Flatten(doc.get(), &tree));
while (tree.next() != android::ResXMLTree::START_TAG) {
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index bb21c1c539fb..2fd01d7f3dee 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -30,6 +30,8 @@ using ::android::ConfigDescription;
using ::android::LocaleValue;
using ::android::ResStringPool;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
namespace {
@@ -379,25 +381,28 @@ bool DeserializeOverlayableItemFromPb(const pb::OverlayableItem& pb_overlayable,
for (const int policy : pb_overlayable.policy()) {
switch (policy) {
case pb::OverlayableItem::PUBLIC:
- out_overlayable->policies |= OverlayableItem::Policy::kPublic;
+ out_overlayable->policies |= PolicyFlags::PUBLIC;
break;
case pb::OverlayableItem::SYSTEM:
- out_overlayable->policies |= OverlayableItem::Policy::kSystem;
+ out_overlayable->policies |= PolicyFlags::SYSTEM_PARTITION;
break;
case pb::OverlayableItem::VENDOR:
- out_overlayable->policies |= OverlayableItem::Policy::kVendor;
+ out_overlayable->policies |= PolicyFlags::VENDOR_PARTITION;
break;
case pb::OverlayableItem::PRODUCT:
- out_overlayable->policies |= OverlayableItem::Policy::kProduct;
+ out_overlayable->policies |= PolicyFlags::PRODUCT_PARTITION;
break;
case pb::OverlayableItem::SIGNATURE:
- out_overlayable->policies |= OverlayableItem::Policy::kSignature;
+ out_overlayable->policies |= PolicyFlags::SIGNATURE;
break;
case pb::OverlayableItem::ODM:
- out_overlayable->policies |= OverlayableItem::Policy::kOdm;
+ out_overlayable->policies |= PolicyFlags::ODM_PARTITION;
break;
case pb::OverlayableItem::OEM:
- out_overlayable->policies |= OverlayableItem::Policy::kOem;
+ out_overlayable->policies |= PolicyFlags::OEM_PARTITION;
+ break;
+ case pb::OverlayableItem::ACTOR:
+ out_overlayable->policies |= PolicyFlags::ACTOR_SIGNATURE;
break;
default:
*out_error = "unknown overlayable policy";
@@ -634,6 +639,7 @@ static bool DeserializeReferenceFromPb(const pb::Reference& pb_ref, Reference* o
std::string* out_error) {
out_ref->reference_type = DeserializeReferenceTypeFromPb(pb_ref.type());
out_ref->private_reference = pb_ref.private_();
+ out_ref->is_dynamic = pb_ref.is_dynamic().value();
if (pb_ref.id() != 0) {
out_ref->id = ResourceId(pb_ref.id());
@@ -708,6 +714,8 @@ std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value,
return {};
}
symbol.value = pb_symbol.value();
+ symbol.type = pb_symbol.type() != 0U ? pb_symbol.type()
+ : android::Res_value::TYPE_INT_DEC;
attr->symbols.push_back(std::move(symbol));
}
value = std::move(attr);
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index a54822b20302..ba6df22af9d3 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -21,6 +21,8 @@
using android::ConfigDescription;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool, IDiagnostics* diag) {
@@ -290,43 +292,51 @@ static void SerializeOverlayableItemToPb(const OverlayableItem& overlayable_item
pb::Overlayable* pb_overlayable = pb_table->add_overlayable();
pb_overlayable->set_name(overlayable_item.overlayable->name);
pb_overlayable->set_actor(overlayable_item.overlayable->actor);
- SerializeSourceToPb(overlayable_item.overlayable->source, source_pool,
- pb_overlayable->mutable_source());
+ if (source_pool != nullptr) {
+ SerializeSourceToPb(overlayable_item.overlayable->source, source_pool,
+ pb_overlayable->mutable_source());
+ }
}
pb::OverlayableItem* pb_overlayable_item = pb_entry->mutable_overlayable_item();
pb_overlayable_item->set_overlayable_idx(i);
- if (overlayable_item.policies & OverlayableItem::Policy::kPublic) {
+ if (overlayable_item.policies & PolicyFlags::PUBLIC) {
pb_overlayable_item->add_policy(pb::OverlayableItem::PUBLIC);
}
- if (overlayable_item.policies & OverlayableItem::Policy::kProduct) {
+ if (overlayable_item.policies & PolicyFlags::PRODUCT_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::PRODUCT);
}
- if (overlayable_item.policies & OverlayableItem::Policy::kSystem) {
+ if (overlayable_item.policies & PolicyFlags::SYSTEM_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::SYSTEM);
}
- if (overlayable_item.policies & OverlayableItem::Policy::kVendor) {
+ if (overlayable_item.policies & PolicyFlags::VENDOR_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::VENDOR);
}
- if (overlayable_item.policies & OverlayableItem::Policy::kSignature) {
+ if (overlayable_item.policies & PolicyFlags::SIGNATURE) {
pb_overlayable_item->add_policy(pb::OverlayableItem::SIGNATURE);
}
- if (overlayable_item.policies & OverlayableItem::Policy::kOdm) {
+ if (overlayable_item.policies & PolicyFlags::ODM_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::ODM);
}
- if (overlayable_item.policies & OverlayableItem::Policy::kOem) {
+ if (overlayable_item.policies & PolicyFlags::OEM_PARTITION) {
pb_overlayable_item->add_policy(pb::OverlayableItem::OEM);
}
+ if (overlayable_item.policies & PolicyFlags::ACTOR_SIGNATURE) {
+ pb_overlayable_item->add_policy(pb::OverlayableItem::ACTOR);
+ }
- SerializeSourceToPb(overlayable_item.source, source_pool,
- pb_overlayable_item->mutable_source());
+ if (source_pool != nullptr) {
+ SerializeSourceToPb(overlayable_item.source, source_pool,
+ pb_overlayable_item->mutable_source());
+ }
pb_overlayable_item->set_comment(overlayable_item.comment);
}
void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table,
- IDiagnostics* diag) {
- StringPool source_pool;
+ IDiagnostics* diag, SerializeTableOptions options) {
+ auto source_pool = (options.exclude_sources) ? nullptr : util::make_unique<StringPool>();
+
pb::ToolFingerprint* pb_fingerprint = out_table->add_tool_fingerprint();
pb_fingerprint->set_tool(util::GetToolName());
pb_fingerprint->set_version(util::GetToolFingerprint());
@@ -356,32 +366,40 @@ void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table
// Write the Visibility struct.
pb::Visibility* pb_visibility = pb_entry->mutable_visibility();
pb_visibility->set_level(SerializeVisibilityToPb(entry->visibility.level));
- SerializeSourceToPb(entry->visibility.source, &source_pool,
- pb_visibility->mutable_source());
+ if (source_pool != nullptr) {
+ SerializeSourceToPb(entry->visibility.source, source_pool.get(),
+ pb_visibility->mutable_source());
+ }
pb_visibility->set_comment(entry->visibility.comment);
if (entry->allow_new) {
pb::AllowNew* pb_allow_new = pb_entry->mutable_allow_new();
- SerializeSourceToPb(entry->allow_new.value().source, &source_pool,
- pb_allow_new->mutable_source());
+ if (source_pool != nullptr) {
+ SerializeSourceToPb(entry->allow_new.value().source, source_pool.get(),
+ pb_allow_new->mutable_source());
+ }
pb_allow_new->set_comment(entry->allow_new.value().comment);
}
if (entry->overlayable_item) {
- SerializeOverlayableItemToPb(entry->overlayable_item.value(), overlayables, &source_pool,
- pb_entry, out_table);
+ SerializeOverlayableItemToPb(entry->overlayable_item.value(), overlayables,
+ source_pool.get(), pb_entry, out_table);
}
for (const std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
pb::ConfigValue* pb_config_value = pb_entry->add_config_value();
SerializeConfig(config_value->config, pb_config_value->mutable_config());
pb_config_value->mutable_config()->set_product(config_value->product);
- SerializeValueToPb(*config_value->value, pb_config_value->mutable_value(), &source_pool);
+ SerializeValueToPb(*config_value->value, pb_config_value->mutable_value(),
+ source_pool.get());
}
}
}
}
- SerializeStringPoolToPb(source_pool, out_table->mutable_source_pool(), diag);
+
+ if (source_pool != nullptr) {
+ SerializeStringPoolToPb(*source_pool, out_table->mutable_source_pool(), diag);
+ }
}
static pb::Reference_Type SerializeReferenceTypeToPb(Reference::Type type) {
@@ -405,6 +423,9 @@ static void SerializeReferenceToPb(const Reference& ref, pb::Reference* pb_ref)
pb_ref->set_private_(ref.private_reference);
pb_ref->set_type(SerializeReferenceTypeToPb(ref.reference_type));
+ if (ref.is_dynamic) {
+ pb_ref->mutable_is_dynamic()->set_value(ref.is_dynamic);
+ }
}
template <typename T>
@@ -552,6 +573,7 @@ class ValueSerializer : public ConstValueVisitor {
SerializeItemMetaDataToPb(symbol.symbol, pb_symbol, src_pool_);
SerializeReferenceToPb(symbol.symbol, pb_symbol->mutable_name());
pb_symbol->set_value(symbol.value);
+ pb_symbol->set_type(symbol.type);
}
}
diff --git a/tools/aapt2/format/proto/ProtoSerialize.h b/tools/aapt2/format/proto/ProtoSerialize.h
index 33ffd182435b..7a3ea9903732 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.h
+++ b/tools/aapt2/format/proto/ProtoSerialize.h
@@ -35,6 +35,11 @@ struct SerializeXmlOptions {
bool remove_empty_text_nodes = false;
};
+struct SerializeTableOptions {
+ /** Prevent serializing the source pool and source protos. */
+ bool exclude_sources = false;
+};
+
// Serializes a Value to its protobuf representation. An optional StringPool will hold the
// source path string.
void SerializeValueToPb(const Value& value, pb::Value* out_value, StringPool* src_pool = nullptr);
@@ -59,7 +64,8 @@ void SerializeStringPoolToPb(const StringPool& pool, pb::StringPool* out_pb_pool
void SerializeConfig(const android::ConfigDescription& config, pb::Configuration* out_pb_config);
// Serializes a ResourceTable into its protobuf representation.
-void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table, IDiagnostics* diag);
+void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table,
+ IDiagnostics* diag, SerializeTableOptions options = {});
// Serializes a ResourceFile into its protobuf representation.
void SerializeCompiledFileToPb(const ResourceFile& file, pb::internal::CompiledFile* out_file);
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index f252f33f44fb..1a7de6dc1c48 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -28,6 +28,8 @@ using ::testing::NotNull;
using ::testing::SizeIs;
using ::testing::StrEq;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
class MockFileCollection : public io::IFileCollection {
@@ -80,7 +82,7 @@ TEST(ProtoSerializeTest, SerializeSinglePackage) {
test::BuildPrimitive(android::Res_value::TYPE_INT_DEC, 123u), context->GetDiagnostics()));
ASSERT_TRUE(table->AddResource(
test::ParseNameOrDie("com.app.a:integer/one"), test::ParseConfigOrDie("land"), "tablet",
- test::BuildPrimitive(android::Res_value::TYPE_INT_DEC, 321u), context->GetDiagnostics()));
+ test::BuildPrimitive(android::Res_value::TYPE_INT_HEX, 321u), context->GetDiagnostics()));
// Make a reference with both resource name and resource ID.
// The reference should point to a resource outside of this table to test that both name and id
@@ -133,11 +135,13 @@ TEST(ProtoSerializeTest, SerializeSinglePackage) {
&new_table, "com.app.a:integer/one", test::ParseConfigOrDie("land"), "");
ASSERT_THAT(prim, NotNull());
EXPECT_THAT(prim->value.data, Eq(123u));
+ EXPECT_THAT(prim->value.dataType, Eq(0x10));
prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
&new_table, "com.app.a:integer/one", test::ParseConfigOrDie("land"), "tablet");
ASSERT_THAT(prim, NotNull());
EXPECT_THAT(prim->value.data, Eq(321u));
+ EXPECT_THAT(prim->value.dataType, Eq(0x11));
Reference* actual_ref = test::GetValue<Reference>(&new_table, "com.app.a:layout/abc");
ASSERT_THAT(actual_ref, NotNull());
@@ -169,7 +173,7 @@ TEST(ProtoSerializeTest, SerializeSinglePackage) {
EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://theme"));
EXPECT_THAT(result_overlayable_item.overlayable->source.path, Eq("res/values/overlayable.xml"));
EXPECT_THAT(result_overlayable_item.overlayable->source.line, Eq(40));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::NONE));
EXPECT_THAT(result_overlayable_item.source.path, Eq("res/values/overlayable.xml"));
EXPECT_THAT(result_overlayable_item.source.line, Eq(42));
}
@@ -514,23 +518,28 @@ TEST(ProtoSerializeTest, SerializeDeserializeConfiguration) {
TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
OverlayableItem overlayable_item_foo(std::make_shared<Overlayable>(
"CustomizableResources", "overlay://customization"));
- overlayable_item_foo.policies |= OverlayableItem::Policy::kSystem;
- overlayable_item_foo.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_foo.policies |= PolicyFlags::SYSTEM_PARTITION;
+ overlayable_item_foo.policies |= PolicyFlags::PRODUCT_PARTITION;
OverlayableItem overlayable_item_bar(std::make_shared<Overlayable>(
"TaskBar", "overlay://theme"));
- overlayable_item_bar.policies |= OverlayableItem::Policy::kPublic;
- overlayable_item_bar.policies |= OverlayableItem::Policy::kVendor;
+ overlayable_item_bar.policies |= PolicyFlags::PUBLIC;
+ overlayable_item_bar.policies |= PolicyFlags::VENDOR_PARTITION;
OverlayableItem overlayable_item_baz(std::make_shared<Overlayable>(
"FontPack", "overlay://theme"));
- overlayable_item_baz.policies |= OverlayableItem::Policy::kPublic;
+ overlayable_item_baz.policies |= PolicyFlags::PUBLIC;
OverlayableItem overlayable_item_boz(std::make_shared<Overlayable>(
"IconPack", "overlay://theme"));
- overlayable_item_boz.policies |= OverlayableItem::Policy::kSignature;
- overlayable_item_boz.policies |= OverlayableItem::Policy::kOdm;
- overlayable_item_boz.policies |= OverlayableItem::Policy::kOem;
+ overlayable_item_boz.policies |= PolicyFlags::SIGNATURE;
+ overlayable_item_boz.policies |= PolicyFlags::ODM_PARTITION;
+ overlayable_item_boz.policies |= PolicyFlags::OEM_PARTITION;
+
+ OverlayableItem overlayable_item_actor_config(std::make_shared<Overlayable>(
+ "ActorConfig", "overlay://theme"));
+ overlayable_item_actor_config.policies |= PolicyFlags::SIGNATURE;
+ overlayable_item_actor_config.policies |= PolicyFlags::ACTOR_SIGNATURE;
OverlayableItem overlayable_item_biz(std::make_shared<Overlayable>(
"Other", "overlay://customization"));
@@ -544,6 +553,7 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
.SetOverlayable("com.app.a:bool/baz", overlayable_item_baz)
.SetOverlayable("com.app.a:bool/boz", overlayable_item_boz)
.SetOverlayable("com.app.a:bool/biz", overlayable_item_biz)
+ .SetOverlayable("com.app.a:bool/actor_config", overlayable_item_actor_config)
.AddValue("com.app.a:bool/fiz", ResourceUtils::TryParseBool("true"))
.Build();
@@ -563,8 +573,8 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
OverlayableItem& overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(overlayable_item.overlayable->name, Eq("CustomizableResources"));
EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://customization"));
- EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kSystem
- | OverlayableItem::Policy::kProduct));
+ EXPECT_THAT(overlayable_item.policies, Eq(PolicyFlags::SYSTEM_PARTITION
+ | PolicyFlags::PRODUCT_PARTITION));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/bar"));
ASSERT_TRUE(search_result);
@@ -572,8 +582,8 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(overlayable_item.overlayable->name, Eq("TaskBar"));
EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
- EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic
- | OverlayableItem::Policy::kVendor));
+ EXPECT_THAT(overlayable_item.policies, Eq(PolicyFlags::PUBLIC
+ | PolicyFlags::VENDOR_PARTITION));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/baz"));
ASSERT_TRUE(search_result);
@@ -581,7 +591,7 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(overlayable_item.overlayable->name, Eq("FontPack"));
EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
- EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic));
+ EXPECT_THAT(overlayable_item.policies, Eq(PolicyFlags::PUBLIC));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/boz"));
ASSERT_TRUE(search_result);
@@ -589,16 +599,25 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(overlayable_item.overlayable->name, Eq("IconPack"));
EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
- EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kSignature
- | OverlayableItem::Policy::kOdm
- | OverlayableItem::Policy::kOem));
+ EXPECT_THAT(overlayable_item.policies, Eq(PolicyFlags::SIGNATURE
+ | PolicyFlags::ODM_PARTITION
+ | PolicyFlags::OEM_PARTITION));
+
+ search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/actor_config"));
+ ASSERT_TRUE(search_result);
+ ASSERT_TRUE(search_result.value().entry->overlayable_item);
+ overlayable_item = search_result.value().entry->overlayable_item.value();
+ EXPECT_THAT(overlayable_item.overlayable->name, Eq("ActorConfig"));
+ EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
+ EXPECT_THAT(overlayable_item.policies, Eq(PolicyFlags::SIGNATURE
+ | PolicyFlags::ACTOR_SIGNATURE));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz"));
ASSERT_TRUE(search_result);
ASSERT_TRUE(search_result.value().entry->overlayable_item);
overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(overlayable_item.overlayable->name, Eq("Other"));
- EXPECT_THAT(overlayable_item.policies, Eq(OverlayableItem::Policy::kNone));
+ EXPECT_THAT(overlayable_item.policies, Eq(PolicyFlags::NONE));
EXPECT_THAT(overlayable_item.comment, Eq("comment"));
search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/fiz"));
@@ -606,4 +625,41 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
ASSERT_FALSE(search_result.value().entry->overlayable_item);
}
+TEST(ProtoSerializeTest, SerializeAndDeserializeDynamicReference) {
+ Reference ref(ResourceId(0x00010001));
+ ref.is_dynamic = true;
+
+ pb::Item pb_item;
+ SerializeItemToPb(ref, &pb_item);
+
+ ASSERT_TRUE(pb_item.has_ref());
+ EXPECT_EQ(pb_item.ref().id(), ref.id.value().id);
+ EXPECT_TRUE(pb_item.ref().is_dynamic().value());
+
+ std::unique_ptr<Item> item = DeserializeItemFromPb(pb_item, android::ResStringPool(),
+ android::ConfigDescription(), nullptr,
+ nullptr, nullptr);
+ Reference* actual_ref = ValueCast<Reference>(item.get());
+ EXPECT_EQ(actual_ref->id.value().id, ref.id.value().id);
+ EXPECT_TRUE(actual_ref->is_dynamic);
+}
+
+TEST(ProtoSerializeTest, SerializeAndDeserializeNonDynamicReference) {
+ Reference ref(ResourceId(0x00010001));
+
+ pb::Item pb_item;
+ SerializeItemToPb(ref, &pb_item);
+
+ ASSERT_TRUE(pb_item.has_ref());
+ EXPECT_EQ(pb_item.ref().id(), ref.id.value().id);
+ EXPECT_FALSE(pb_item.ref().has_is_dynamic());
+
+ std::unique_ptr<Item> item = DeserializeItemFromPb(pb_item, android::ResStringPool(),
+ android::ConfigDescription(), nullptr,
+ nullptr, nullptr);
+ Reference* actual_ref = ValueCast<Reference>(item.get());
+ EXPECT_EQ(actual_ref->id.value().id, ref.id.value().id);
+ EXPECT_FALSE(actual_ref->is_dynamic);
+}
+
} // namespace aapt
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/Android.mk b/tools/aapt2/integration-tests/MergeOnlyTest/Android.mk
new file mode 100644
index 000000000000..6361f9b8ae7d
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/Android.mk
@@ -0,0 +1,2 @@
+LOCAL_PATH := $(call my-dir)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk b/tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk
new file mode 100644
index 000000000000..6bc2064c6e63
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/App/Android.mk
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_AAPT_NAMESPACES := true
+LOCAL_PACKAGE_NAME := AaptTestMergeOnly_App
+LOCAL_SDK_VERSION := current
+LOCAL_EXPORT_PACKAGE_RESOURCES := true
+LOCAL_MODULE_TAGS := tests
+LOCAL_STATIC_ANDROID_LIBRARIES := \
+ AaptTestMergeOnly_LeafLib \
+ AaptTestMergeOnly_LocalLib
+include $(BUILD_PACKAGE) \ No newline at end of file
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/App/AndroidManifest.xml b/tools/aapt2/integration-tests/MergeOnlyTest/App/AndroidManifest.xml
new file mode 100644
index 000000000000..bc3a7e5ebd21
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/App/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.app">
+
+ <application
+ android:label="@*com.local.lib:string/lib_string"/>
+
+</manifest> \ No newline at end of file
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk
new file mode 100644
index 000000000000..7bf8cf84426c
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/Android.mk
@@ -0,0 +1,28 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_AAPT_NAMESPACES := true
+LOCAL_MODULE := AaptTestMergeOnly_LeafLib
+LOCAL_SDK_VERSION := current
+LOCAL_MODULE_TAGS := tests
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_MIN_SDK_VERSION := 21
+LOCAL_AAPT_FLAGS := --merge-only
+include $(BUILD_STATIC_JAVA_LIBRARY) \ No newline at end of file
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/AndroidManifest.xml b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/AndroidManifest.xml
new file mode 100644
index 000000000000..9907bd98790d
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest package="com.leaf.lib" />
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/res/layout/activity.xml b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/res/layout/activity.xml
new file mode 100644
index 000000000000..07de87fa1d33
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/res/layout/activity.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:leaf="http://schemas.android.com/apk/res/com.leaf.lib">
+
+ <TextView android:text="@string/leaf_string"
+ leaf:leaf_attr="hello"
+ style="@style/LeafChildStyle"/>
+
+</RelativeLayout> \ No newline at end of file
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/res/values/values.xml b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/res/values/values.xml
new file mode 100644
index 000000000000..7f94c26de23c
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LeafLib/res/values/values.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <attr format="string" name="leaf_attr"/>
+ <attr format="string" name="leaf_attr2"/>
+
+ <string name="leaf_string">I am a leaf</string>
+
+ <style name="LeafParentStyle">
+ <item type="attr" name="leaf_attr"/>
+ <item type="attr" name="leaf_attr2"/>
+ </style>
+
+ <style name="LeafChildStyle" parent="LeafParentStyle">
+ <item type="attr" name="leaf_attr2">hello</item>
+ </style>
+
+ <style name="LeafParentStyle.DottedChild"/>
+
+ <declare-styleable name="leaf_ds">
+ <attr name="leaf_attr">hello</attr>
+ </declare-styleable>
+
+ <public type="attr" name="leaf_attr"/>
+ <public type="attr" name="leaf_attr2"/>
+ <public type="style" name="LeafParentStyle"/>
+ <public type="style" name="LeafChildStyle"/>
+ <public type="style" name="LeafParentStyle.DottedChild"/>
+</resources> \ No newline at end of file
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk
new file mode 100644
index 000000000000..ba781c56a913
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/Android.mk
@@ -0,0 +1,28 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_AAPT_NAMESPACES := true
+LOCAL_MODULE := AaptTestMergeOnly_LocalLib
+LOCAL_SDK_VERSION := current
+LOCAL_MODULE_TAGS := tests
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+LOCAL_MIN_SDK_VERSION := 21
+LOCAL_AAPT_FLAGS := --merge-only
+include $(BUILD_STATIC_JAVA_LIBRARY) \ No newline at end of file
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/AndroidManifest.xml b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/AndroidManifest.xml
new file mode 100644
index 000000000000..aa0ff5dcb4b6
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.local.lib">
+
+ <application>
+
+ <activity
+ android:name="com.myapp.MyActivity"
+ android:theme="@com.leaf.lib:style/LeafParentStyle.DottedChild"/>
+
+ </application>
+
+</manifest>
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/layout/activity.xml b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/layout/activity.xml
new file mode 100644
index 000000000000..80d2fd6bcd09
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/layout/activity.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:leaf="http://schemas.android.com/apk/res/com.leaf.lib">
+
+ <TextView android:text="@*com.leaf.lib:string/leaf_string"
+ leaf:leaf_attr="hello"
+ style="@com.leaf.lib:style/LeafChildStyle"/>
+
+</RelativeLayout> \ No newline at end of file
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/layout/includer.xml b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/layout/includer.xml
new file mode 100644
index 000000000000..f06371874a45
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/layout/includer.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:leaf="http://schemas.android.com/apk/res/com.leaf.lib">
+
+ <include layout="@layout/activity"/>
+
+ <include layout="@*com.leaf.lib:layout/activity"/>
+
+</RelativeLayout> \ No newline at end of file
diff --git a/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/values/values.xml b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/values/values.xml
new file mode 100644
index 000000000000..2f9704df0570
--- /dev/null
+++ b/tools/aapt2/integration-tests/MergeOnlyTest/LocalLib/res/values/values.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <string name="lib_string">@*com.leaf.lib:string/leaf_string</string>
+
+ <style name="lib_style" parent="@com.leaf.lib:style/LeafChildStyle">
+ <item name="com.leaf.lib:leaf_attr">hello</item>
+ </style>
+
+ <style name="LeafParentStyle.DottedChild.LocalLibStyle"
+ parent="@com.leaf.lib:style/LeafParentStyle.DottedChild"/>
+
+ <public type="style" name="LeafParentStyle.DottedChild.LocalLibStyle"/>
+
+</resources> \ No newline at end of file
diff --git a/tools/aapt2/io/Util.cpp b/tools/aapt2/io/Util.cpp
index ce6d9352180d..bb925c9b3f8e 100644
--- a/tools/aapt2/io/Util.cpp
+++ b/tools/aapt2/io/Util.cpp
@@ -58,7 +58,7 @@ bool CopyFileToArchivePreserveCompression(IAaptContext* context, io::IFile* file
return CopyFileToArchive(context, file, out_path, compression_flags, writer);
}
-bool CopyProtoToArchive(IAaptContext* context, ::google::protobuf::MessageLite* proto_msg,
+bool CopyProtoToArchive(IAaptContext* context, ::google::protobuf::Message* proto_msg,
const std::string& out_path, uint32_t compression_flags,
IArchiveWriter* writer) {
TRACE_CALL();
diff --git a/tools/aapt2/io/Util.h b/tools/aapt2/io/Util.h
index 5f978a8e2c35..5cb8206db23c 100644
--- a/tools/aapt2/io/Util.h
+++ b/tools/aapt2/io/Util.h
@@ -19,7 +19,7 @@
#include <string>
-#include "google/protobuf/message_lite.h"
+#include "google/protobuf/message.h"
#include "google/protobuf/io/coded_stream.h"
#include "format/Archive.h"
@@ -39,7 +39,7 @@ bool CopyFileToArchive(IAaptContext* context, IFile* file, const std::string& ou
bool CopyFileToArchivePreserveCompression(IAaptContext* context, IFile* file,
const std::string& out_path, IArchiveWriter* writer);
-bool CopyProtoToArchive(IAaptContext* context, ::google::protobuf::MessageLite* proto_msg,
+bool CopyProtoToArchive(IAaptContext* context, ::google::protobuf::Message* proto_msg,
const std::string& out_path, uint32_t compression_flags,
IArchiveWriter* writer);
@@ -127,13 +127,13 @@ class ProtoInputStreamReader {
public:
explicit ProtoInputStreamReader(io::InputStream* in) : in_(in) { }
- /** Deserializes a MessageLite proto from the current position in the input stream.*/
- template <typename T> bool ReadMessage(T *message_lite) {
+ /** Deserializes a Message proto from the current position in the input stream.*/
+ template <typename T> bool ReadMessage(T *message) {
ZeroCopyInputAdaptor adapter(in_);
google::protobuf::io::CodedInputStream coded_stream(&adapter);
coded_stream.SetTotalBytesLimit(std::numeric_limits<int32_t>::max(),
coded_stream.BytesUntilTotalBytesLimit());
- return message_lite->ParseFromCodedStream(&coded_stream);
+ return message->ParseFromCodedStream(&coded_stream);
}
private:
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
index bb667d6fa539..482d91aeb491 100644
--- a/tools/aapt2/java/AnnotationProcessor.cpp
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -18,6 +18,7 @@
#include <algorithm>
#include <array>
+#include <regex>
#include "text/Unicode.h"
#include "text/Utf8Iterator.h"
@@ -65,14 +66,26 @@ void AnnotationProcessor::AppendCommentLine(std::string comment) {
// Treat deprecated specially, since we don't remove it from the source comment.
if (comment.find(sDeprecated) != std::string::npos) {
- annotation_bit_mask_ |= AnnotationRule::kDeprecated;
+ annotation_parameter_map_[AnnotationRule::kDeprecated] = "";
}
for (const AnnotationRule& rule : sAnnotationRules) {
std::string::size_type idx = comment.find(rule.doc_str.data());
if (idx != std::string::npos) {
- annotation_bit_mask_ |= rule.bit_mask;
- comment.erase(comment.begin() + idx, comment.begin() + idx + rule.doc_str.size());
+ // Captures all parameters associated with the specified annotation rule
+ // by matching the first pair of parantheses after the rule.
+ std::regex re(rule.doc_str.to_string() + "\\s*\\((.+)\\)");
+ std::smatch match_result;
+ const bool is_match = std::regex_search(comment, match_result, re);
+ // We currently only capture and preserve parameters for SystemApi.
+ if (is_match && rule.bit_mask == AnnotationRule::kSystemApi) {
+ annotation_parameter_map_[rule.bit_mask] = match_result[1].str();
+ comment.erase(comment.begin() + match_result.position(),
+ comment.begin() + match_result.position() + match_result.length());
+ } else {
+ annotation_parameter_map_[rule.bit_mask] = "";
+ comment.erase(comment.begin() + idx, comment.begin() + idx + rule.doc_str.size());
+ }
}
}
@@ -119,7 +132,8 @@ void AnnotationProcessor::Print(Printer* printer, bool strip_api_annotations) co
printer->Println(" */");
}
- if (annotation_bit_mask_ & AnnotationRule::kDeprecated) {
+ if (annotation_parameter_map_.find(AnnotationRule::kDeprecated) !=
+ annotation_parameter_map_.end()) {
printer->Println("@Deprecated");
}
@@ -127,8 +141,13 @@ void AnnotationProcessor::Print(Printer* printer, bool strip_api_annotations) co
return;
}
for (const AnnotationRule& rule : sAnnotationRules) {
- if (annotation_bit_mask_ & rule.bit_mask) {
- printer->Println(rule.annotation);
+ const auto& it = annotation_parameter_map_.find(rule.bit_mask);
+ if (it != annotation_parameter_map_.end()) {
+ printer->Print(rule.annotation);
+ if (!it->second.empty()) {
+ printer->Print("(").Print(it->second).Print(")");
+ }
+ printer->Print("\n");
}
}
}
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
index 6d768be8e7dd..f217afb16f32 100644
--- a/tools/aapt2/java/AnnotationProcessor.h
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -19,6 +19,7 @@
#include <sstream>
#include <string>
+#include <unordered_map>
#include "androidfw/StringPiece.h"
@@ -70,7 +71,7 @@ class AnnotationProcessor {
std::stringstream comment_;
std::stringstream mAnnotations;
bool has_comments_ = false;
- uint32_t annotation_bit_mask_ = 0;
+ std::unordered_map<uint32_t, std::string> annotation_parameter_map_;
void AppendCommentLine(std::string line);
};
diff --git a/tools/aapt2/java/AnnotationProcessor_test.cpp b/tools/aapt2/java/AnnotationProcessor_test.cpp
index c4c8ff934348..6bc8902a6dcf 100644
--- a/tools/aapt2/java/AnnotationProcessor_test.cpp
+++ b/tools/aapt2/java/AnnotationProcessor_test.cpp
@@ -61,6 +61,21 @@ TEST(AnnotationProcessorTest, EmitsSystemApiAnnotationAndRemovesFromComment) {
EXPECT_THAT(annotations, HasSubstr("This is a system API"));
}
+TEST(AnnotationProcessorTest, EmitsSystemApiAnnotationParamsAndRemovesFromComment) {
+ AnnotationProcessor processor;
+ processor.AppendComment("@SystemApi (p1=k1,p2=k2) This is a system API");
+
+ std::string annotations;
+ StringOutputStream out(&annotations);
+ Printer printer(&out);
+ processor.Print(&printer);
+ out.Flush();
+
+ EXPECT_THAT(annotations, HasSubstr("@android.annotation.SystemApi(p1=k1,p2=k2)"));
+ EXPECT_THAT(annotations, Not(HasSubstr("@SystemApi")));
+ EXPECT_THAT(annotations, HasSubstr("This is a system API"));
+}
+
TEST(AnnotationProcessorTest, EmitsTestApiAnnotationAndRemovesFromComment) {
AnnotationProcessor processor;
processor.AppendComment("@TestApi This is a test API");
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 9788f64995fb..dffad3b99c06 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -304,9 +304,11 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res
auto documentation_remove_iter = std::remove_if(documentation_attrs.begin(),
documentation_attrs.end(),
[&](StyleableAttr entry) -> bool {
- StringPiece attr_comment_line = entry.symbol.value().attribute->GetComment();
- return SkipSymbol(entry.symbol) || attr_comment_line.contains("@removed")
- || attr_comment_line.contains("@hide");
+ if (SkipSymbol(entry.symbol)) {
+ return true;
+ }
+ const StringPiece attr_comment_line = entry.symbol.value().attribute->GetComment();
+ return attr_comment_line.contains("@removed") || attr_comment_line.contains("@hide");
});
documentation_attrs.erase(documentation_remove_iter, documentation_attrs.end());
@@ -428,7 +430,7 @@ void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const Res
out_rewrite_method->AppendStatement(
StringPrintf(" if ((styleable.%s[i] & 0xff000000) == 0) {", array_field_name.data()));
out_rewrite_method->AppendStatement(
- StringPrintf(" styleable.%s[i] = (styleable.%s[i] & 0x00ffffff) | (p << 24);",
+ StringPrintf(" styleable.%s[i] = (styleable.%s[i] & 0x00ffffff) | packageIdBits;",
array_field_name.data(), array_field_name.data()));
out_rewrite_method->AppendStatement(" }");
out_rewrite_method->AppendStatement("}");
@@ -487,9 +489,9 @@ void JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const Reso
if (out_rewrite_method != nullptr) {
const StringPiece& type_str = to_string(name.type);
- out_rewrite_method->AppendStatement(StringPrintf("%s.%s = (%s.%s & 0x00ffffff) | (p << 24);",
- type_str.data(), field_name.data(),
- type_str.data(), field_name.data()));
+ out_rewrite_method->AppendStatement(
+ StringPrintf("%s.%s = (%s.%s & 0x00ffffff) | packageIdBits;", type_str.data(),
+ field_name.data(), type_str.data(), field_name.data()));
}
}
@@ -599,6 +601,7 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
rewrite_method->AppendStatement(
StringPrintf("%s.R.onResourcesLoaded(p);", package_to_callback.data()));
}
+ rewrite_method->AppendStatement("final int packageIdBits = p << 24;");
}
const bool is_public = (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 4f51fc48c80e..1e1fe4740c6b 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -522,9 +522,15 @@ TEST(JavaClassGeneratorTest, GenerateOnResourcesLoadedCallbackForSharedLibrary)
ASSERT_TRUE(generator.Generate("android", &out));
out.Flush();
- EXPECT_THAT(output, HasSubstr("void onResourcesLoaded"));
- EXPECT_THAT(output, HasSubstr("com.foo.R.onResourcesLoaded"));
- EXPECT_THAT(output, HasSubstr("com.boo.R.onResourcesLoaded"));
+ EXPECT_THAT(output, HasSubstr(
+ R"( public static void onResourcesLoaded(int p) {
+ com.foo.R.onResourcesLoaded(p);
+ com.boo.R.onResourcesLoaded(p);
+ final int packageIdBits = p << 24;
+ attr.foo = (attr.foo & 0x00ffffff) | packageIdBits;
+ id.foo = (id.foo & 0x00ffffff) | packageIdBits;
+ style.foo = (style.foo & 0x00ffffff) | packageIdBits;
+ })"));
}
TEST(JavaClassGeneratorTest, OnlyGenerateRText) {
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index ff7f5c12404c..d9a4caa34e0d 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -155,13 +155,19 @@ class MenuVisitor : public BaseVisitor {
void Visit(xml::Element* node) override {
if (node->namespace_uri.empty() && node->name == "item") {
for (const auto& attr : node->attributes) {
- if (attr.namespace_uri == xml::kSchemaAndroid) {
- if ((attr.name == "actionViewClass" || attr.name == "actionProviderClass") &&
- util::IsJavaClassName(attr.value)) {
- AddClass(node->line_number, attr.value, "android.content.Context");
- } else if (attr.name == "onClick") {
- AddMethod(node->line_number, attr.value, "android.view.MenuItem");
- }
+ // AppCompat-v7 defines its own versions of Android attributes if
+ // they're defined after SDK 7 (the below are from 11 and 14,
+ // respectively), so don't bother checking the XML namespace.
+ //
+ // Given the names of the containing XML files and the attribute
+ // names, it's unlikely that keeping these classes would be wrong.
+ if ((attr.name == "actionViewClass" || attr.name == "actionProviderClass") &&
+ util::IsJavaClassName(attr.value)) {
+ AddClass(node->line_number, attr.value, "android.content.Context");
+ }
+
+ if (attr.namespace_uri == xml::kSchemaAndroid && attr.name == "onClick") {
+ AddMethod(node->line_number, attr.value, "android.view.MenuItem");
}
}
}
@@ -388,11 +394,15 @@ bool CollectProguardRules(IAaptContext* context_, xml::XmlResource* res, KeepSet
return true;
}
-void WriteKeepSet(const KeepSet& keep_set, OutputStream* out, bool minimal_keep) {
+void WriteKeepSet(const KeepSet& keep_set, OutputStream* out, bool minimal_keep,
+ bool no_location_reference) {
+
Printer printer(out);
for (const auto& entry : keep_set.manifest_class_set_) {
- for (const UsageLocation& location : entry.second) {
- printer.Print("# Referenced at ").Println(location.source.to_string());
+ if (!no_location_reference) {
+ for (const UsageLocation& location : entry.second) {
+ printer.Print("# Referenced at ").Println(location.source.to_string());
+ }
}
printer.Print("-keep class ").Print(entry.first).Println(" { <init>(); }");
}
@@ -409,7 +419,9 @@ void WriteKeepSet(const KeepSet& keep_set, OutputStream* out, bool minimal_keep)
if (can_be_conditional) {
for (const UsageLocation& location : locations) {
- printer.Print("# Referenced at ").Println(location.source.to_string());
+ if (!no_location_reference) {
+ printer.Print("# Referenced at ").Println(location.source.to_string());
+ }
printer.Print("-if class **.R$layout { int ")
.Print(JavaClassGenerator::TransformToFieldName(location.name.entry))
.Println("; }");
@@ -419,8 +431,10 @@ void WriteKeepSet(const KeepSet& keep_set, OutputStream* out, bool minimal_keep)
printer.Println("); }");
}
} else {
- for (const UsageLocation& location : entry.second) {
- printer.Print("# Referenced at ").Println(location.source.to_string());
+ if (!no_location_reference) {
+ for (const UsageLocation& location : entry.second) {
+ printer.Print("# Referenced at ").Println(location.source.to_string());
+ }
}
printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>(");
@@ -431,8 +445,10 @@ void WriteKeepSet(const KeepSet& keep_set, OutputStream* out, bool minimal_keep)
}
for (const auto& entry : keep_set.method_set_) {
- for (const UsageLocation& location : entry.second) {
- printer.Print("# Referenced at ").Println(location.source.to_string());
+ if (!no_location_reference) {
+ for (const UsageLocation& location : entry.second) {
+ printer.Print("# Referenced at ").Println(location.source.to_string());
+ }
}
printer.Print("-keepclassmembers class * { *** ").Print(entry.first.name)
.Print("(").Print(entry.first.signature).Println("); }");
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
index b15df59f56a6..a01b64d024d2 100644
--- a/tools/aapt2/java/ProguardRules.h
+++ b/tools/aapt2/java/ProguardRules.h
@@ -70,7 +70,8 @@ class KeepSet {
}
private:
- friend void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out, bool minimal_keep);
+ friend void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out, bool minimal_keep,
+ bool no_location_reference);
friend bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
std::set<UsageLocation>* locations);
@@ -89,7 +90,8 @@ bool CollectProguardRules(IAaptContext* context, xml::XmlResource* res, KeepSet*
bool CollectResourceReferences(IAaptContext* context, ResourceTable* table, KeepSet* keep_set);
-void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out, bool minimal_keep);
+void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out, bool minimal_keep,
+ bool no_location_reference);
bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
std::set<UsageLocation>* locations);
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index 7c3cda089b40..c7ae0b6a75cc 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -30,7 +30,7 @@ namespace aapt {
std::string GetKeepSetString(const proguard::KeepSet& set, bool minimal_rules) {
std::string out;
StringOutputStream sout(&out);
- proguard::WriteKeepSet(set, &sout, minimal_rules);
+ proguard::WriteKeepSet(set, &sout, minimal_rules, false);
sout.Flush();
return out;
}
@@ -381,6 +381,25 @@ TEST(ProguardRulesTest, MenuRulesAreEmitted) {
EXPECT_THAT(actual, Not(HasSubstr("com.foo.Bat")));
}
+TEST(ProguardRulesTest, MenuRulesAreEmittedForActionClasses) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ std::unique_ptr<xml::XmlResource> menu = test::BuildXmlDom(R"(
+ <menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item android:id="@+id/my_item"
+ app:actionViewClass="com.foo.Bar"
+ app:actionProviderClass="com.foo.Baz" />
+ </menu>)");
+ menu->file.name = test::ParseNameOrDie("menu/foo");
+
+ proguard::KeepSet set;
+ ASSERT_TRUE(proguard::CollectProguardRules(context.get(), menu.get(), &set));
+
+ std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar"));
+ EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz"));
+}
+
TEST(ProguardRulesTest, TransitionPathMotionRulesAreEmitted) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<xml::XmlResource> transition = test::BuildXmlDom(R"(
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 06303c273261..c813a446b8db 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -214,6 +214,33 @@ static bool VerifyUsesFeature(xml::Element* el, SourcePathDiagnostics* diag) {
return true;
}
+// Ensure that 'ns_decls' contains a declaration for 'uri', using 'prefix' as
+// the xmlns prefix if possible.
+static void EnsureNamespaceIsDeclared(const std::string& prefix, const std::string& uri,
+ std::vector<xml::NamespaceDecl>* ns_decls) {
+ if (std::find_if(ns_decls->begin(), ns_decls->end(), [&](const xml::NamespaceDecl& ns_decl) {
+ return ns_decl.uri == uri;
+ }) != ns_decls->end()) {
+ return;
+ }
+
+ std::set<std::string> used_prefixes;
+ for (const auto& ns_decl : *ns_decls) {
+ used_prefixes.insert(ns_decl.prefix);
+ }
+
+ // Make multiple attempts in the unlikely event that 'prefix' is already taken.
+ std::string disambiguator;
+ for (int i = 0; i < used_prefixes.size() + 1; i++) {
+ std::string attempted_prefix = prefix + disambiguator;
+ if (used_prefixes.find(attempted_prefix) == used_prefixes.end()) {
+ ns_decls->push_back(xml::NamespaceDecl{attempted_prefix, uri});
+ return;
+ }
+ disambiguator = std::to_string(i);
+ }
+}
+
bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
IDiagnostics* diag) {
// First verify some options.
@@ -272,6 +299,8 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
manifest_action.Action(VerifyManifest);
manifest_action.Action(FixCoreAppAttribute);
manifest_action.Action([&](xml::Element* el) -> bool {
+ EnsureNamespaceIsDeclared("android", xml::kSchemaAndroid, &el->namespace_decls);
+
if (options_.version_name_default) {
if (options_.replace_version) {
el->RemoveAttribute(xml::kSchemaAndroid, "versionName");
@@ -330,6 +359,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
}
return true;
});
+ manifest_action["uses-sdk"]["extension-sdk"];
// Instrumentation actions.
manifest_action["instrumentation"].Action(RequiredNameIsJavaClassName);
@@ -346,6 +376,12 @@ 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"];
manifest_action["overlay"].Action([&](xml::Element* el) -> bool {
if (!options_.rename_overlay_target_package) {
@@ -377,6 +413,10 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
manifest_action["package-verifier"];
manifest_action["meta-data"] = meta_data_action;
manifest_action["uses-split"].Action(RequiredNameIsJavaPackage);
+ manifest_action["queries"]["package"].Action(RequiredNameIsJavaPackage);
+ manifest_action["queries"]["intent"] = intent_filter_action;
+ manifest_action["queries"]["provider"].Action(RequiredAndroidAttribute("authorities"));
+ // TODO: more complicated component name tag
manifest_action["key-sets"]["key-set"]["public-key"];
manifest_action["key-sets"]["upgrade-key-set"];
@@ -413,6 +453,12 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
application_action["meta-data"] = meta_data_action;
+ application_action["processes"];
+ application_action["processes"]["deny-permission"];
+ application_action["processes"]["allow-permission"];
+ application_action["processes"]["process"]["deny-permission"];
+ application_action["processes"]["process"]["allow-permission"];
+
application_action["activity"] = component_action;
application_action["activity"]["layout"];
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 3aba4e2ec49f..0791805e2506 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -753,8 +753,7 @@ TEST_F(ManifestFixerTest, SupportKeySets) {
}
TEST_F(ManifestFixerTest, InsertCompileSdkVersions) {
- std::string input = R"(
- <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android" />)";
+ std::string input = R"(<manifest package="com.pkg" />)";
ManifestFixerOptions options;
options.compile_sdk_version = {"28"};
options.compile_sdk_version_codename = {"P"};
@@ -762,6 +761,12 @@ TEST_F(ManifestFixerTest, InsertCompileSdkVersions) {
std::unique_ptr<xml::XmlResource> manifest = VerifyWithOptions(input, options);
ASSERT_THAT(manifest, NotNull());
+ // There should be a declaration of kSchemaAndroid, even when the input
+ // didn't have one.
+ EXPECT_EQ(manifest->root->namespace_decls.size(), 1);
+ EXPECT_EQ(manifest->root->namespace_decls[0].prefix, "android");
+ EXPECT_EQ(manifest->root->namespace_decls[0].uri, xml::kSchemaAndroid);
+
xml::Attribute* attr = manifest->root->FindAttribute(xml::kSchemaAndroid, "compileSdkVersion");
ASSERT_THAT(attr, NotNull());
EXPECT_THAT(attr->value, StrEq("28"));
@@ -808,6 +813,27 @@ TEST_F(ManifestFixerTest, OverrideCompileSdkVersions) {
EXPECT_THAT(attr->value, StrEq("P"));
}
+TEST_F(ManifestFixerTest, AndroidPrefixAlreadyUsed) {
+ std::string input =
+ R"(<manifest package="com.pkg"
+ xmlns:android="http://schemas.android.com/apk/prv/res/android"
+ android:private_attr="foo" />)";
+ ManifestFixerOptions options;
+ options.compile_sdk_version = {"28"};
+ options.compile_sdk_version_codename = {"P"};
+
+ std::unique_ptr<xml::XmlResource> manifest = VerifyWithOptions(input, options);
+ ASSERT_THAT(manifest, NotNull());
+
+ // Make sure that we don't redefine "android".
+ EXPECT_EQ(manifest->root->namespace_decls.size(), 2);
+ EXPECT_EQ(manifest->root->namespace_decls[0].prefix, "android");
+ EXPECT_EQ(manifest->root->namespace_decls[0].uri,
+ "http://schemas.android.com/apk/prv/res/android");
+ EXPECT_EQ(manifest->root->namespace_decls[1].prefix, "android0");
+ EXPECT_EQ(manifest->root->namespace_decls[1].uri, xml::kSchemaAndroid);
+}
+
TEST_F(ManifestFixerTest, UnexpectedElementsInManifest) {
std::string input = R"(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 28f09aa48365..8e49fabe6a5c 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -17,6 +17,7 @@
#include "link/ReferenceLinker.h"
#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
#include "androidfw/ResourceTypes.h"
#include "Diagnostics.h"
@@ -33,6 +34,7 @@
using ::aapt::ResourceUtils::StringBuilder;
using ::android::StringPiece;
+using ::android::base::StringPrintf;
namespace aapt {
@@ -81,7 +83,7 @@ class ReferenceLinkerVisitor : public DescendingValueVisitor {
// Find the attribute in the symbol table and check if it is visible from this callsite.
const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
- transformed_reference, callsite_, symbols_, &err_str);
+ transformed_reference, callsite_, context_, symbols_, &err_str);
if (symbol) {
// Assign our style key the correct ID. The ID may not exist.
entry.key.id = symbol->id;
@@ -203,12 +205,35 @@ bool IsSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& reference,
const CallSite& callsite,
+ IAaptContext* context,
SymbolTable* symbols) {
if (reference.name) {
const ResourceName& name = reference.name.value();
if (name.package.empty()) {
// Use the callsite's package name if no package name was defined.
- return symbols->FindByName(ResourceName(callsite.package, name.type, name.entry));
+ const SymbolTable::Symbol* symbol = symbols->FindByName(
+ ResourceName(callsite.package, name.type, name.entry));
+ if (symbol) {
+ return symbol;
+ }
+
+ // If the callsite package is the same as the current compilation package,
+ // check the feature split dependencies as well. Feature split resources
+ // can be referenced without a namespace, just like the base package.
+ // TODO: modify the package name of included splits instead of having the
+ // symbol table look up the resource in in every package. b/136105066
+ if (callsite.package == context->GetCompilationPackage()) {
+ const auto& split_name_dependencies = context->GetSplitNameDependencies();
+ for (const std::string& split_name : split_name_dependencies) {
+ std::string split_package =
+ StringPrintf("%s.%s", callsite.package.c_str(), split_name.c_str());
+ symbol = symbols->FindByName(ResourceName(split_package, name.type, name.entry));
+ if (symbol) {
+ return symbol;
+ }
+ }
+ }
+ return nullptr;
}
return symbols->FindByName(name);
} else if (reference.id) {
@@ -220,9 +245,10 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& refer
const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const Reference& reference,
const CallSite& callsite,
+ IAaptContext* context,
SymbolTable* symbols,
std::string* out_error) {
- const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, symbols);
+ const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, context, symbols);
if (!symbol) {
if (out_error) *out_error = "not found";
return nullptr;
@@ -236,10 +262,10 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const R
}
const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility(
- const Reference& reference, const CallSite& callsite, SymbolTable* symbols,
- std::string* out_error) {
+ const Reference& reference, const CallSite& callsite, IAaptContext* context,
+ SymbolTable* symbols, std::string* out_error) {
const SymbolTable::Symbol* symbol =
- ResolveSymbolCheckVisibility(reference, callsite, symbols, out_error);
+ ResolveSymbolCheckVisibility(reference, callsite, context, symbols, out_error);
if (!symbol) {
return nullptr;
}
@@ -253,10 +279,11 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility(
Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference& reference,
const CallSite& callsite,
+ IAaptContext* context,
SymbolTable* symbols,
std::string* out_error) {
const SymbolTable::Symbol* symbol =
- ResolveAttributeCheckVisibility(reference, callsite, symbols, out_error);
+ ResolveAttributeCheckVisibility(reference, callsite, context, symbols, out_error);
if (!symbol) {
return {};
}
@@ -335,7 +362,7 @@ bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* referen
std::string err_str;
const SymbolTable::Symbol* s =
- ResolveSymbolCheckVisibility(transformed_reference, callsite, symbols, &err_str);
+ ResolveSymbolCheckVisibility(transformed_reference, callsite, context, symbols, &err_str);
if (s) {
// The ID may not exist. This is fine because of the possibility of building
// against libraries without assigned IDs.
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index b0b49457e5dd..1256709edbf4 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -39,13 +39,16 @@ class ReferenceLinker : public IResourceTableConsumer {
// package if the reference has no package name defined (implicit).
// Returns nullptr if the symbol was not found.
static const SymbolTable::Symbol* ResolveSymbol(const Reference& reference,
- const CallSite& callsite, SymbolTable* symbols);
+ const CallSite& callsite,
+ IAaptContext* context,
+ SymbolTable* symbols);
// Performs name mangling and looks up the resource in the symbol table. If the symbol is not
// visible by the reference at the callsite, nullptr is returned.
// `out_error` holds the error message.
static const SymbolTable::Symbol* ResolveSymbolCheckVisibility(const Reference& reference,
const CallSite& callsite,
+ IAaptContext* context,
SymbolTable* symbols,
std::string* out_error);
@@ -53,6 +56,7 @@ class ReferenceLinker : public IResourceTableConsumer {
// That is, the return value will have a non-null value for ISymbolTable::Symbol::attribute.
static const SymbolTable::Symbol* ResolveAttributeCheckVisibility(const Reference& reference,
const CallSite& callsite,
+ IAaptContext* context,
SymbolTable* symbols,
std::string* out_error);
@@ -60,6 +64,7 @@ class ReferenceLinker : public IResourceTableConsumer {
// If resolution fails, outError holds the error message.
static Maybe<xml::AaptAttribute> CompileXmlAttribute(const Reference& reference,
const CallSite& callsite,
+ IAaptContext* context,
SymbolTable* symbols,
std::string* out_error);
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index be38b967c986..a31ce9496d0c 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -266,8 +266,13 @@ TEST(ReferenceLinkerTest, AppsWithSamePackageButDifferentIdAreVisibleNonPublic)
std::string error;
const CallSite call_site{"com.app.test"};
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetPackageId(0x7f)
+ .Build();
const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveSymbolCheckVisibility(
- *test::BuildReference("com.app.test:string/foo"), call_site, &table, &error);
+ *test::BuildReference("com.app.test:string/foo"), call_site, context.get(), &table, &error);
ASSERT_THAT(symbol, NotNull());
EXPECT_TRUE(error.empty());
}
@@ -281,17 +286,23 @@ TEST(ReferenceLinkerTest, AppsWithDifferentPackageCanNotUseEachOthersAttribute)
.AddPublicSymbol("com.app.test:attr/public_foo", ResourceId(0x7f010001),
test::AttributeBuilder().Build())
.Build());
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.ext")
+ .SetPackageId(0x7f)
+ .Build();
std::string error;
const CallSite call_site{"com.app.ext"};
EXPECT_FALSE(ReferenceLinker::CompileXmlAttribute(
- *test::BuildReference("com.app.test:attr/foo"), call_site, &table, &error));
+ *test::BuildReference("com.app.test:attr/foo"), call_site, context.get(), &table, &error));
EXPECT_FALSE(error.empty());
error = "";
ASSERT_TRUE(ReferenceLinker::CompileXmlAttribute(
- *test::BuildReference("com.app.test:attr/public_foo"), call_site, &table, &error));
+ *test::BuildReference("com.app.test:attr/public_foo"), call_site, context.get(), &table,
+ &error));
EXPECT_TRUE(error.empty());
}
@@ -302,20 +313,62 @@ TEST(ReferenceLinkerTest, ReferenceWithNoPackageUsesCallSitePackage) {
.AddSymbol("com.app.test:string/foo", ResourceId(0x7f010000))
.AddSymbol("com.app.lib:string/foo", ResourceId(0x7f010001))
.Build());
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetPackageId(0x7f)
+ .Build();
const SymbolTable::Symbol* s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
- CallSite{"com.app.test"}, &table);
+ CallSite{"com.app.test"},
+ context.get(), &table);
ASSERT_THAT(s, NotNull());
EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010000)));
s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), CallSite{"com.app.lib"},
- &table);
+ context.get(), &table);
ASSERT_THAT(s, NotNull());
EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010001)));
EXPECT_THAT(ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
- CallSite{"com.app.bad"}, &table),
+ CallSite{"com.app.bad"}, context.get(), &table),
IsNull());
}
+TEST(ReferenceLinkerTest, ReferenceSymbolFromOtherSplit) {
+ NameMangler mangler(NameManglerPolicy{"com.app.test"});
+ SymbolTable table(&mangler);
+ table.AppendSource(test::StaticSymbolSourceBuilder()
+ .AddSymbol("com.app.test.feature:string/bar", ResourceId(0x80010000))
+ .Build());
+ std::set<std::string> split_name_dependencies;
+ split_name_dependencies.insert("feature");
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetPackageId(0x81)
+ .SetSplitNameDependencies(split_name_dependencies)
+ .Build();
+
+ const SymbolTable::Symbol* s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/bar"),
+ CallSite{"com.app.test"},
+ context.get(), &table);
+ ASSERT_THAT(s, NotNull());
+ EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x80010000)));
+
+ s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), CallSite{"com.app.lib"},
+ context.get(), &table);
+ EXPECT_THAT(s, IsNull());
+
+ context =
+ test::ContextBuilder()
+ .SetCompilationPackage("com.app.test")
+ .SetPackageId(0x81)
+ .Build();
+ s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/bar"),CallSite{"com.app.test"},
+ context.get(), &table);
+
+ EXPECT_THAT(s, IsNull());
+}
+
} // namespace aapt
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 3f65e868505d..c25e4503a208 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -172,28 +172,32 @@ static bool MergeEntry(IAaptContext* context, const Source& src,
//
// Styleables and Styles don't simply overlay each other, their definitions merge and accumulate.
// If both values are Styleables/Styles, we just merge them into the existing value.
-static ResourceTable::CollisionResult ResolveMergeCollision(Value* existing, Value* incoming,
- StringPool* pool) {
+static ResourceTable::CollisionResult ResolveMergeCollision(
+ bool override_styles_instead_of_overlaying, Value* existing, Value* incoming,
+ StringPool* pool) {
if (Styleable* existing_styleable = ValueCast<Styleable>(existing)) {
if (Styleable* incoming_styleable = ValueCast<Styleable>(incoming)) {
// Styleables get merged.
existing_styleable->MergeWith(incoming_styleable);
return ResourceTable::CollisionResult::kKeepOriginal;
}
- } else if (Style* existing_style = ValueCast<Style>(existing)) {
- if (Style* incoming_style = ValueCast<Style>(incoming)) {
- // Styles get merged.
- existing_style->MergeWith(incoming_style, pool);
- return ResourceTable::CollisionResult::kKeepOriginal;
+ } else if (!override_styles_instead_of_overlaying) {
+ if (Style* existing_style = ValueCast<Style>(existing)) {
+ if (Style* incoming_style = ValueCast<Style>(incoming)) {
+ // Styles get merged.
+ existing_style->MergeWith(incoming_style, pool);
+ return ResourceTable::CollisionResult::kKeepOriginal;
+ }
}
}
// Delegate to the default handler.
- return ResourceTable::ResolveValueCollision(existing, incoming, true /* overlay */);
+ return ResourceTable::ResolveValueCollision(existing, incoming);
}
static ResourceTable::CollisionResult MergeConfigValue(IAaptContext* context,
const ResourceNameRef& res_name,
bool overlay,
+ bool override_styles_instead_of_overlaying,
ResourceConfigValue* dst_config_value,
ResourceConfigValue* src_config_value,
StringPool* pool) {
@@ -204,13 +208,18 @@ static ResourceTable::CollisionResult MergeConfigValue(IAaptContext* context,
CollisionResult collision_result;
if (overlay) {
- collision_result = ResolveMergeCollision(dst_value, src_value, pool);
+ collision_result =
+ ResolveMergeCollision(override_styles_instead_of_overlaying, dst_value, src_value, pool);
} else {
- collision_result = ResourceTable::ResolveValueCollision(dst_value, src_value,
- false /* overlay */);
+ collision_result = ResourceTable::ResolveValueCollision(dst_value, src_value);
}
if (collision_result == CollisionResult::kConflict) {
+ if (overlay) {
+ return CollisionResult::kTakeNew;
+ }
+
+ // Error!
context->GetDiagnostics()->Error(DiagMessage(src_value->GetSource())
<< "resource '" << res_name << "' has a conflicting value for "
<< "configuration (" << src_config_value->config << ")");
@@ -268,9 +277,9 @@ bool TableMerger::DoMerge(const Source& src, ResourceTablePackage* src_package,
ResourceConfigValue* dst_config_value = dst_entry->FindValue(
src_config_value->config, src_config_value->product);
if (dst_config_value) {
- CollisionResult collision_result =
- MergeConfigValue(context_, res_name, overlay, dst_config_value,
- src_config_value.get(), &master_table_->string_pool);
+ 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);
if (collision_result == CollisionResult::kConflict) {
error = true;
continue;
diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h
index 51305cfcdd25..a35a134a887d 100644
--- a/tools/aapt2/link/TableMerger.h
+++ b/tools/aapt2/link/TableMerger.h
@@ -37,6 +37,8 @@ struct TableMergerOptions {
bool auto_add_overlay = false;
// If true, resource overlays with conflicting visibility are not allowed.
bool strict_visibility = false;
+ // If true, styles specified via "aapt2 link -R" completely replace any previously-seen resources.
+ bool override_styles_instead_of_overlaying = false;
};
// TableMerger takes resource tables and merges all packages within the tables that have the same
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index 78d42a160e21..69cf5ee7002b 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -29,6 +29,8 @@ using ::testing::Pointee;
using ::testing::StrEq;
using ::testing::UnorderedElementsAreArray;
+using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
+
namespace aapt {
struct TableMergerTest : public ::testing::Test {
@@ -352,62 +354,6 @@ TEST_F(TableMergerTest, MergeAddResourceFromOverlayWithAutoAddOverlay) {
ASSERT_TRUE(merger.Merge({}, table_b.get(), false /*overlay*/));
}
-TEST_F(TableMergerTest, OverrideAttributeSameFormatsWithOverlay) {
- std::unique_ptr<ResourceTable> base =
- test::ResourceTableBuilder()
- .SetPackageId("", 0x7f)
- .AddValue("attr/foo", test::AttributeBuilder()
- .SetTypeMask(android::ResTable_map::TYPE_STRING)
- .SetWeak(false)
- .Build())
- .Build();
-
- std::unique_ptr<ResourceTable> overlay =
- test::ResourceTableBuilder()
- .SetPackageId("", 0x7f)
- .AddValue("attr/foo", test::AttributeBuilder()
- .SetTypeMask(android::ResTable_map::TYPE_STRING)
- .SetWeak(false)
- .Build())
- .Build();
-
- ResourceTable final_table;
- TableMergerOptions options;
- options.auto_add_overlay = false;
- TableMerger merger(context_.get(), &final_table, options);
-
- ASSERT_TRUE(merger.Merge({}, base.get(), false /*overlay*/));
- ASSERT_TRUE(merger.Merge({}, overlay.get(), true /*overlay*/));
-}
-
-TEST_F(TableMergerTest, FailToOverrideConflictingAttributeFormatsWithOverlay) {
- std::unique_ptr<ResourceTable> base =
- test::ResourceTableBuilder()
- .SetPackageId("", 0x7f)
- .AddValue("attr/foo", test::AttributeBuilder()
- .SetTypeMask(android::ResTable_map::TYPE_ANY)
- .SetWeak(false)
- .Build())
- .Build();
-
- std::unique_ptr<ResourceTable> overlay =
- test::ResourceTableBuilder()
- .SetPackageId("", 0x7f)
- .AddValue("attr/foo", test::AttributeBuilder()
- .SetTypeMask(android::ResTable_map::TYPE_STRING)
- .SetWeak(false)
- .Build())
- .Build();
-
- ResourceTable final_table;
- TableMergerOptions options;
- options.auto_add_overlay = false;
- TableMerger merger(context_.get(), &final_table, options);
-
- ASSERT_TRUE(merger.Merge({}, base.get(), false /*overlay*/));
- ASSERT_FALSE(merger.Merge({}, overlay.get(), true /*overlay*/));
-}
-
TEST_F(TableMergerTest, FailToMergeNewResourceWithoutAutoAddOverlay) {
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder().SetPackageId("", 0x7f).Build();
@@ -492,12 +438,59 @@ TEST_F(TableMergerTest, OverlaidStyleablesAndStylesShouldBeMerged) {
Eq(make_value(Reference(test::ParseNameOrDie("com.app.a:style/OverlayParent")))));
}
+TEST_F(TableMergerTest, OverrideStyleInsteadOfOverlaying) {
+ std::unique_ptr<ResourceTable> table_a =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.a", 0x7f)
+ .AddValue(
+ "com.app.a:styleable/MyWidget",
+ test::StyleableBuilder().AddItem("com.app.a:attr/foo", ResourceId(0x1234)).Build())
+ .AddValue("com.app.a:style/Theme",
+ test::StyleBuilder()
+ .AddItem("com.app.a:attr/foo", ResourceUtils::MakeBool(false))
+ .Build())
+ .Build();
+ std::unique_ptr<ResourceTable> table_b =
+ test::ResourceTableBuilder()
+ .SetPackageId("com.app.a", 0x7f)
+ .AddValue(
+ "com.app.a:styleable/MyWidget",
+ test::StyleableBuilder().AddItem("com.app.a:attr/bar", ResourceId(0x5678)).Build())
+ .AddValue(
+ "com.app.a:style/Theme",
+ test::StyleBuilder().AddItem("com.app.a:attr/bat", util::make_unique<Id>()).Build())
+ .Build();
+
+ ResourceTable final_table;
+ TableMergerOptions options;
+ options.auto_add_overlay = true;
+ options.override_styles_instead_of_overlaying = true;
+ TableMerger merger(context_.get(), &final_table, options);
+ ASSERT_TRUE(merger.Merge({}, table_a.get(), false /*overlay*/));
+ ASSERT_TRUE(merger.Merge({}, table_b.get(), true /*overlay*/));
+
+ // Styleables are always overlaid
+ std::unique_ptr<Styleable> expected_styleable = test::StyleableBuilder()
+ // The merged Styleable has its entries ordered by name.
+ .AddItem("com.app.a:attr/bar", ResourceId(0x5678))
+ .AddItem("com.app.a:attr/foo", ResourceId(0x1234))
+ .Build();
+ const Styleable* actual_styleable =
+ test::GetValue<Styleable>(&final_table, "com.app.a:styleable/MyWidget");
+ ASSERT_NE(actual_styleable, nullptr);
+ EXPECT_TRUE(actual_styleable->Equals(expected_styleable.get()));
+ // Style should be overridden
+ const Style* actual_style = test::GetValue<Style>(&final_table, "com.app.a:style/Theme");
+ ASSERT_NE(actual_style, nullptr);
+ EXPECT_TRUE(actual_style->Equals(test::GetValue<Style>(table_b.get(), "com.app.a:style/Theme")));
+}
+
TEST_F(TableMergerTest, SetOverlayable) {
auto overlayable = std::make_shared<Overlayable>("CustomizableResources",
"overlay://customization");
OverlayableItem overlayable_item(overlayable);
- overlayable_item.policies |= OverlayableItem::Policy::kProduct;
- overlayable_item.policies |= OverlayableItem::Policy::kVendor;
+ overlayable_item.policies |= PolicyFlags::PRODUCT_PARTITION;
+ overlayable_item.policies |= PolicyFlags::VENDOR_PARTITION;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
@@ -525,8 +518,8 @@ TEST_F(TableMergerTest, SetOverlayable) {
OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources"));
EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kProduct
- | OverlayableItem::Policy::kVendor));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::PRODUCT_PARTITION
+ | PolicyFlags::VENDOR_PARTITION));
}
TEST_F(TableMergerTest, SetOverlayableLater) {
@@ -539,8 +532,8 @@ TEST_F(TableMergerTest, SetOverlayableLater) {
.Build();
OverlayableItem overlayable_item(overlayable);
- overlayable_item.policies |= OverlayableItem::Policy::kPublic;
- overlayable_item.policies |= OverlayableItem::Policy::kSystem;
+ overlayable_item.policies |= PolicyFlags::PUBLIC;
+ overlayable_item.policies |= PolicyFlags::SYSTEM_PARTITION;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -561,15 +554,15 @@ TEST_F(TableMergerTest, SetOverlayableLater) {
OverlayableItem& result_overlayable_item = search_result.value().entry->overlayable_item.value();
EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("CustomizableResources"));
EXPECT_THAT(result_overlayable_item.overlayable->actor, Eq("overlay://customization"));
- EXPECT_THAT(result_overlayable_item.policies, Eq(OverlayableItem::Policy::kPublic
- | OverlayableItem::Policy::kSystem));
+ EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::PUBLIC
+ | PolicyFlags::SYSTEM_PARTITION));
}
TEST_F(TableMergerTest, SameResourceDifferentNameFail) {
auto overlayable_first = std::make_shared<Overlayable>("CustomizableResources",
"overlay://customization");
OverlayableItem overlayable_item_first(overlayable_first);
- overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_first.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -579,7 +572,7 @@ TEST_F(TableMergerTest, SameResourceDifferentNameFail) {
auto overlayable_second = std::make_shared<Overlayable>("ThemeResources",
"overlay://customization");
OverlayableItem overlayable_item_second(overlayable_second);
- overlayable_item_second.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_second.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -598,7 +591,7 @@ TEST_F(TableMergerTest, SameResourceDifferentActorFail) {
auto overlayable_first = std::make_shared<Overlayable>("CustomizableResources",
"overlay://customization");
OverlayableItem overlayable_item_first(overlayable_first);
- overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_first.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -608,7 +601,7 @@ TEST_F(TableMergerTest, SameResourceDifferentActorFail) {
auto overlayable_second = std::make_shared<Overlayable>("CustomizableResources",
"overlay://theme");
OverlayableItem overlayable_item_second(overlayable_second);
- overlayable_item_second.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_second.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -627,7 +620,7 @@ TEST_F(TableMergerTest, SameResourceDifferentPoliciesFail) {
auto overlayable_first = std::make_shared<Overlayable>("CustomizableResources",
"overlay://customization");
OverlayableItem overlayable_item_first(overlayable_first);
- overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_first.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -637,7 +630,7 @@ TEST_F(TableMergerTest, SameResourceDifferentPoliciesFail) {
auto overlayable_second = std::make_shared<Overlayable>("CustomizableResources",
"overlay://customization");
OverlayableItem overlayable_item_second(overlayable_second);
- overlayable_item_second.policies |= OverlayableItem::Policy::kSignature;
+ overlayable_item_second.policies |= PolicyFlags::SIGNATURE;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -657,7 +650,7 @@ TEST_F(TableMergerTest, SameResourceSameOverlayable) {
"overlay://customization");
OverlayableItem overlayable_item_first(overlayable);
- overlayable_item_first.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_first.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_a =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
@@ -665,7 +658,7 @@ TEST_F(TableMergerTest, SameResourceSameOverlayable) {
.Build();
OverlayableItem overlayable_item_second(overlayable);
- overlayable_item_second.policies |= OverlayableItem::Policy::kProduct;
+ overlayable_item_second.policies |= PolicyFlags::PRODUCT_PARTITION;
std::unique_ptr<ResourceTable> table_b =
test::ResourceTableBuilder()
.SetPackageId("com.app.a", 0x7f)
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index d68f7dd44c9f..c3c16b92f712 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -83,6 +83,15 @@ class XmlVisitor : public xml::PackageAwareVisitor {
Attribute default_attribute(android::ResTable_map::TYPE_ANY);
default_attribute.SetWeak(true);
+ // The default orientation of gradients in android Q is different than previous android
+ // versions. Set the android:angle attribute to "0" to ensure that the default gradient
+ // orientation will remain left-to-right in android Q.
+ if (el->name == "gradient" && context_->GetMinSdkVersion() <= SDK_Q) {
+ if (!el->FindAttribute(xml::kSchemaAndroid, "angle")) {
+ el->attributes.push_back(xml::Attribute{xml::kSchemaAndroid, "angle", "0"});
+ }
+ }
+
const Source source = source_.WithLine(el->line_number);
for (xml::Attribute& attr : el->attributes) {
// If the attribute has no namespace, interpret values as if
@@ -99,7 +108,7 @@ class XmlVisitor : public xml::PackageAwareVisitor {
std::string err_str;
attr.compiled_attribute =
- ReferenceLinker::CompileXmlAttribute(attr_ref, callsite_, symbols_, &err_str);
+ ReferenceLinker::CompileXmlAttribute(attr_ref, callsite_, context_, symbols_, &err_str);
if (!attr.compiled_attribute) {
DiagMessage error_msg(source);
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index ef99355e5b5f..0ce2e50d6e44 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -47,6 +47,8 @@ class XmlReferenceLinkerTest : public ::testing::Test {
test::AttributeBuilder()
.SetTypeMask(android::ResTable_map::TYPE_STRING)
.Build())
+ .AddPublicSymbol("android:attr/angle", ResourceId(0x01010004),
+ test::AttributeBuilder().Build())
// Add one real symbol that was introduces in v21
.AddPublicSymbol("android:attr/colorAccent", ResourceId(0x01010435),
@@ -75,7 +77,7 @@ class XmlReferenceLinkerTest : public ::testing::Test {
}
protected:
- std::unique_ptr<IAaptContext> context_;
+ std::unique_ptr<test::Context> context_;
};
TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
@@ -254,4 +256,63 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
EXPECT_EQ(make_value(ResourceId(0x7f030000)), ref->id);
}
+
+TEST_F(XmlReferenceLinkerTest, AddAngleOnGradientForAndroidQ) {
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+ <gradient />)");
+
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
+
+ xml::Element* gradient_el = doc->root.get();
+ ASSERT_THAT(gradient_el, NotNull());
+
+ xml::Attribute* xml_attr = gradient_el->FindAttribute(xml::kSchemaAndroid, "angle");
+ ASSERT_THAT(xml_attr, NotNull());
+ ASSERT_TRUE(xml_attr->compiled_attribute);
+ EXPECT_EQ(make_value(ResourceId(0x01010004)), xml_attr->compiled_attribute.value().id);
+
+ BinaryPrimitive* value = ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get());
+ ASSERT_THAT(value, NotNull());
+ EXPECT_EQ(value->value.dataType, android::Res_value::TYPE_INT_DEC);
+ EXPECT_EQ(value->value.data, 0U);
+}
+
+TEST_F(XmlReferenceLinkerTest, DoNotOverwriteAngleOnGradientForAndroidQ) {
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+ <gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ android:angle="90"/>)");
+
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
+
+ xml::Element* gradient_el = doc->root.get();
+ ASSERT_THAT(gradient_el, NotNull());
+
+ xml::Attribute* xml_attr = gradient_el->FindAttribute(xml::kSchemaAndroid, "angle");
+ ASSERT_THAT(xml_attr, NotNull());
+ ASSERT_TRUE(xml_attr->compiled_attribute);
+ EXPECT_EQ(make_value(ResourceId(0x01010004)), xml_attr->compiled_attribute.value().id);
+
+ BinaryPrimitive* value = ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get());
+ ASSERT_THAT(value, NotNull());
+ EXPECT_EQ(value->value.dataType, android::Res_value::TYPE_INT_DEC);
+ EXPECT_EQ(value->value.data, 90U);
+}
+
+TEST_F(XmlReferenceLinkerTest, DoNotOverwriteAngleOnGradientForPostAndroidQ) {
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+ <gradient xmlns:android="http://schemas.android.com/apk/res/android" />)");
+ context_->SetMinSdkVersion(30);
+
+ XmlReferenceLinker linker;
+ ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
+
+ xml::Element* gradient_el = doc->root.get();
+ ASSERT_THAT(gradient_el, NotNull());
+
+ xml::Attribute* xml_attr = gradient_el->FindAttribute(xml::kSchemaAndroid, "angle");
+ ASSERT_THAT(xml_attr, IsNull());
+}
+
} // namespace aapt
diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp
index 8c9c43409569..c686a10a3fa9 100644
--- a/tools/aapt2/optimize/MultiApkGenerator.cpp
+++ b/tools/aapt2/optimize/MultiApkGenerator.cpp
@@ -101,6 +101,10 @@ class ContextWrapper : public IAaptContext {
util::make_unique<SourcePathDiagnostics>(Source{source}, context_->GetDiagnostics());
}
+ const std::set<std::string>& GetSplitNameDependencies() override {
+ return context_->GetSplitNameDependencies();
+ }
+
private:
IAaptContext* context_;
std::unique_ptr<SourcePathDiagnostics> source_diag_;
diff --git a/tools/aapt2/optimize/ResourceDeduper.cpp b/tools/aapt2/optimize/ResourceDeduper.cpp
index 78ebcb97b811..0278b439cfae 100644
--- a/tools/aapt2/optimize/ResourceDeduper.cpp
+++ b/tools/aapt2/optimize/ResourceDeduper.cpp
@@ -63,13 +63,14 @@ class DominatedKeyValueRemover : public DominatorTree::BottomUpVisitor {
// Compare compatible configs for this entry and ensure the values are
// equivalent.
const ConfigDescription& node_configuration = node_value->config;
- for (const auto& sibling : entry_->values) {
- if (!sibling->value) {
+ for (const auto& sibling : parent->children()) {
+ ResourceConfigValue* sibling_value = sibling->value();
+ if (!sibling_value->value) {
// Sibling was already removed.
continue;
}
- if (node_configuration.IsCompatibleWith(sibling->config) &&
- !node_value->value->Equals(sibling->value.get())) {
+ if (node_configuration.IsCompatibleWith(sibling_value->config) &&
+ !node_value->value->Equals(sibling_value->value.get())) {
// The configurations are compatible, but the value is
// different, so we can't remove this value.
return;
diff --git a/tools/aapt2/optimize/ResourceDeduper_test.cpp b/tools/aapt2/optimize/ResourceDeduper_test.cpp
index 2e098aec4f8d..048e318d2802 100644
--- a/tools/aapt2/optimize/ResourceDeduper_test.cpp
+++ b/tools/aapt2/optimize/ResourceDeduper_test.cpp
@@ -80,11 +80,58 @@ TEST(ResourceDeduperTest, DifferentValuesAreKept) {
.Build();
ASSERT_TRUE(ResourceDeduper().Consume(context.get(), table.get()));
+ EXPECT_THAT(table, HasValue("android:string/keep", default_config));
EXPECT_THAT(table, HasValue("android:string/keep", ldrtl_config));
EXPECT_THAT(table, HasValue("android:string/keep", ldrtl_v21_config));
EXPECT_THAT(table, HasValue("android:string/keep", land_config));
}
+TEST(ResourceDeduperTest, SameValuesAreDedupedIncompatibleSiblings) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ const ConfigDescription default_config = {};
+ const ConfigDescription ldrtl_config = test::ParseConfigOrDie("ldrtl");
+ const ConfigDescription ldrtl_night_config = test::ParseConfigOrDie("ldrtl-night");
+ // Chosen because this configuration is not compatible with ldrtl-night.
+ const ConfigDescription ldrtl_notnight_config = test::ParseConfigOrDie("ldrtl-notnight");
+
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddString("android:string/keep", ResourceId{}, default_config, "keep")
+ .AddString("android:string/keep", ResourceId{}, ldrtl_config, "dedupe")
+ .AddString("android:string/keep", ResourceId{}, ldrtl_night_config, "dedupe")
+ .AddString("android:string/keep", ResourceId{}, ldrtl_notnight_config, "keep2")
+ .Build();
+
+ ASSERT_TRUE(ResourceDeduper().Consume(context.get(), table.get()));
+ EXPECT_THAT(table, HasValue("android:string/keep", default_config));
+ EXPECT_THAT(table, HasValue("android:string/keep", ldrtl_config));
+ EXPECT_THAT(table, Not(HasValue("android:string/keep", ldrtl_night_config)));
+ EXPECT_THAT(table, HasValue("android:string/keep", ldrtl_notnight_config));
+}
+
+TEST(ResourceDeduperTest, SameValuesAreDedupedCompatibleNonSiblings) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ const ConfigDescription default_config = {};
+ const ConfigDescription ldrtl_config = test::ParseConfigOrDie("ldrtl");
+ const ConfigDescription ldrtl_night_config = test::ParseConfigOrDie("ldrtl-night");
+ // Chosen because this configuration is compatible with ldrtl.
+ const ConfigDescription land_config = test::ParseConfigOrDie("land");
+
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddString("android:string/keep", ResourceId{}, default_config, "keep")
+ .AddString("android:string/keep", ResourceId{}, ldrtl_config, "dedupe")
+ .AddString("android:string/keep", ResourceId{}, ldrtl_night_config, "dedupe")
+ .AddString("android:string/keep", ResourceId{}, land_config, "keep2")
+ .Build();
+
+ ASSERT_TRUE(ResourceDeduper().Consume(context.get(), table.get()));
+ EXPECT_THAT(table, HasValue("android:string/keep", default_config));
+ EXPECT_THAT(table, HasValue("android:string/keep", ldrtl_config));
+ EXPECT_THAT(table, Not(HasValue("android:string/keep", ldrtl_night_config)));
+ EXPECT_THAT(table, HasValue("android:string/keep", land_config));
+}
+
TEST(ResourceDeduperTest, LocalesValuesAreKept) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
const ConfigDescription default_config = {};
diff --git a/tools/aapt2/optimize/ResourcePathShortener.cpp b/tools/aapt2/optimize/ResourcePathShortener.cpp
index c5df3dd00db9..7ff9bf5aa8df 100644
--- a/tools/aapt2/optimize/ResourcePathShortener.cpp
+++ b/tools/aapt2/optimize/ResourcePathShortener.cpp
@@ -16,13 +16,14 @@
#include "optimize/ResourcePathShortener.h"
-#include <math.h>
+#include <set>
#include <unordered_set>
#include "androidfw/StringPiece.h"
#include "ResourceTable.h"
#include "ValueVisitor.h"
+#include "util/Util.h"
static const std::string base64_chars =
@@ -50,18 +51,15 @@ std::string ShortenFileName(const android::StringPiece& file_path, int output_le
}
-// Calculate the optimal hash length such that an average of 10% of resources
-// collide in their shortened path.
+// Return the optimal hash length such that at most 10% of resources collide in
+// their shortened path.
// Reference: http://matt.might.net/articles/counting-hash-collisions/
int OptimalShortenedLength(int num_resources) {
- int num_chars = 2;
- double N = 64*64; // hash space when hash is 2 chars long
- double max_collisions = num_resources * 0.1;
- while (num_resources - N + N * pow((N - 1) / N, num_resources) > max_collisions) {
- N *= 64;
- num_chars++;
+ if (num_resources > 4000) {
+ return 3;
+ } else {
+ return 2;
}
- return num_chars;
}
std::string GetShortenedPath(const android::StringPiece& shortened_filename,
@@ -74,10 +72,19 @@ std::string GetShortenedPath(const android::StringPiece& shortened_filename,
return shortened_path;
}
+// implement custom comparator of FileReference pointers so as to use the
+// underlying filepath as key rather than the integer address. This is to ensure
+// determinism of output for colliding files.
+struct PathComparator {
+ bool operator() (const FileReference* lhs, const FileReference* rhs) const {
+ return lhs->path->compare(*rhs->path);
+ }
+};
+
bool ResourcePathShortener::Consume(IAaptContext* context, ResourceTable* table) {
// used to detect collisions
std::unordered_set<std::string> shortened_paths;
- std::unordered_set<FileReference*> file_refs;
+ std::set<FileReference*, PathComparator> file_refs;
for (auto& package : table->packages) {
for (auto& type : package->types) {
for (auto& entry : type->entries) {
@@ -95,6 +102,10 @@ bool ResourcePathShortener::Consume(IAaptContext* context, ResourceTable* table)
android::StringPiece res_subdir, actual_filename, extension;
util::ExtractResFilePathParts(*file_ref->path, &res_subdir, &actual_filename, &extension);
+ // Android detects ColorStateLists via pathname, skip res/color*
+ if (util::StartsWith(res_subdir, "res/color"))
+ continue;
+
std::string shortened_filename = ShortenFileName(*file_ref->path, num_chars);
int collision_count = 0;
std::string shortened_path = GetShortenedPath(shortened_filename, extension, collision_count);
diff --git a/tools/aapt2/optimize/ResourcePathShortener_test.cpp b/tools/aapt2/optimize/ResourcePathShortener_test.cpp
index 88cadc76c336..f5a02be0ea5e 100644
--- a/tools/aapt2/optimize/ResourcePathShortener_test.cpp
+++ b/tools/aapt2/optimize/ResourcePathShortener_test.cpp
@@ -24,6 +24,19 @@ using ::testing::Not;
using ::testing::NotNull;
using ::testing::Eq;
+android::StringPiece GetExtension(android::StringPiece path) {
+ auto iter = std::find(path.begin(), path.end(), '.');
+ return android::StringPiece(iter, path.end() - iter);
+}
+
+void FillTable(aapt::test::ResourceTableBuilder& builder, int start, int end) {
+ for (int i=start; i<end; i++) {
+ builder.AddFileReference(
+ "android:drawable/xmlfile" + std::to_string(i),
+ "res/drawable/xmlfile" + std::to_string(i) + ".xml");
+ }
+}
+
namespace aapt {
TEST(ResourcePathShortenerTest, FileRefPathsChangedInResourceTable) {
@@ -64,4 +77,90 @@ TEST(ResourcePathShortenerTest, FileRefPathsChangedInResourceTable) {
EXPECT_THAT(path_map.find("res/should/still/be/the/same.png"), Eq(path_map.end()));
}
+TEST(ResourcePathShortenerTest, SkipColorFileRefPaths) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddFileReference("android:color/colorlist", "res/color/colorlist.xml")
+ .AddFileReference("android:color/colorlist",
+ "res/color-mdp-v21/colorlist.xml",
+ test::ParseConfigOrDie("mdp-v21"))
+ .Build();
+
+ std::map<std::string, std::string> path_map;
+ ASSERT_TRUE(ResourcePathShortener(path_map).Consume(context.get(), table.get()));
+
+ // Expect that the path map to not contain the ColorStateList
+ ASSERT_THAT(path_map.find("res/color/colorlist.xml"), Eq(path_map.end()));
+ ASSERT_THAT(path_map.find("res/color-mdp-v21/colorlist.xml"), Eq(path_map.end()));
+}
+
+TEST(ResourcePathShortenerTest, KeepExtensions) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+
+ std::string original_xml_path = "res/drawable/xmlfile.xml";
+ std::string original_png_path = "res/drawable/pngfile.png";
+
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddFileReference("android:color/xmlfile", original_xml_path)
+ .AddFileReference("android:color/pngfile", original_png_path)
+ .Build();
+
+ std::map<std::string, std::string> path_map;
+ ASSERT_TRUE(ResourcePathShortener(path_map).Consume(context.get(), table.get()));
+
+ // Expect that the path map is populated
+ ASSERT_THAT(path_map.find("res/drawable/xmlfile.xml"), Not(Eq(path_map.end())));
+ ASSERT_THAT(path_map.find("res/drawable/pngfile.png"), Not(Eq(path_map.end())));
+
+ auto shortend_xml_path = path_map[original_xml_path];
+ auto shortend_png_path = path_map[original_png_path];
+
+ EXPECT_THAT(GetExtension(path_map[original_xml_path]), Eq(android::StringPiece(".xml")));
+ EXPECT_THAT(GetExtension(path_map[original_png_path]), Eq(android::StringPiece(".png")));
+}
+
+TEST(ResourcePathShortenerTest, DeterministicallyHandleCollisions) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+
+ // 4000 resources is the limit at which the hash space is expanded to 3
+ // letters to reduce collisions, we want as many collisions as possible thus
+ // N-1.
+ const auto kNumResources = 3999;
+ const auto kNumTries = 5;
+
+ test::ResourceTableBuilder builder1;
+ FillTable(builder1, 0, kNumResources);
+ std::unique_ptr<ResourceTable> table1 = builder1.Build();
+ std::map<std::string, std::string> expected_mapping;
+ ASSERT_TRUE(ResourcePathShortener(expected_mapping).Consume(context.get(), table1.get()));
+
+ // We are trying to ensure lack of non-determinism, it is not simple to prove
+ // a negative, thus we must try the test a few times so that the test itself
+ // is non-flaky. Basically create the pathmap 5 times from the same set of
+ // resources but a different order of addition and then ensure they are always
+ // mapped to the same short path.
+ for (int i=0; i<kNumTries; i++) {
+ test::ResourceTableBuilder builder2;
+ // This loop adds resources to the resource table in the range of
+ // [0:kNumResources). Adding the file references in different order makes
+ // non-determinism more likely to surface. Thus we add resources
+ // [start_index:kNumResources) first then [0:start_index). We also use a
+ // different start_index each run.
+ int start_index = (kNumResources/kNumTries)*i;
+ FillTable(builder2, start_index, kNumResources);
+ FillTable(builder2, 0, start_index);
+ std::unique_ptr<ResourceTable> table2 = builder2.Build();
+
+ std::map<std::string, std::string> actual_mapping;
+ ASSERT_TRUE(ResourcePathShortener(actual_mapping).Consume(context.get(), table2.get()));
+
+ for (auto& item : actual_mapping) {
+ ASSERT_THAT(expected_mapping[item.first], Eq(item.second));
+ }
+ }
+}
+
} // namespace aapt
diff --git a/tools/aapt2/process/IResourceTableConsumer.h b/tools/aapt2/process/IResourceTableConsumer.h
index 30dad8025900..9c4b323db433 100644
--- a/tools/aapt2/process/IResourceTableConsumer.h
+++ b/tools/aapt2/process/IResourceTableConsumer.h
@@ -19,6 +19,7 @@
#include <iostream>
#include <list>
+#include <set>
#include <sstream>
#include "Diagnostics.h"
@@ -50,6 +51,7 @@ struct IAaptContext {
virtual NameMangler* GetNameMangler() = 0;
virtual bool IsVerbose() = 0;
virtual int GetMinSdkVersion() = 0;
+ virtual const std::set<std::string>& GetSplitNameDependencies() = 0;
};
struct IResourceTableConsumer {
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 61a8fbbb7f52..897fa80ffedb 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -245,7 +245,8 @@ std::map<size_t, std::string> AssetManagerSymbolSource::GetAssignedPackageIds()
return package_map;
}
-bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId) const {
+bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId,
+ const std::string& package_name) const {
if (packageId == 0) {
return true;
}
@@ -253,7 +254,7 @@ bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId) const {
for (const std::unique_ptr<const ApkAssets>& assets : apk_assets_) {
for (const std::unique_ptr<const android::LoadedPackage>& loaded_package
: assets->GetLoadedArsc()->GetPackages()) {
- if (packageId == loaded_package->GetPackageId() && loaded_package->IsDynamic()) {
+ if (package_name == loaded_package->GetPackageName() && loaded_package->IsDynamic()) {
return true;
}
}
@@ -313,6 +314,7 @@ static std::unique_ptr<SymbolTable::Symbol> LookupAttributeInTable(
symbol.symbol.name = parsed_name.value();
symbol.symbol.id = ResourceId(map_entry.key);
symbol.value = map_entry.value.data;
+ symbol.type = map_entry.value.dataType;
s->attribute->symbols.push_back(std::move(symbol));
}
}
@@ -327,19 +329,19 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName(
bool found = false;
ResourceId res_id = 0;
uint32_t type_spec_flags;
+ ResourceName real_name;
// There can be mangled resources embedded within other packages. Here we will
// look into each package and look-up the mangled name until we find the resource.
asset_manager_.ForEachPackage([&](const std::string& package_name, uint8_t id) -> bool {
- ResourceName real_name(name.package, name.type, name.entry);
-
+ real_name = ResourceName(name.package, name.type, name.entry);
if (package_name != name.package) {
real_name.entry = mangled_entry;
real_name.package = package_name;
}
res_id = asset_manager_.GetResourceId(real_name.to_string());
- if (res_id.is_valid() && asset_manager_.GetResourceFlags(res_id.id, &type_spec_flags)) {
+ if (res_id.is_valid_static() && asset_manager_.GetResourceFlags(res_id.id, &type_spec_flags)) {
found = true;
return false;
}
@@ -352,12 +354,12 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName(
}
std::unique_ptr<SymbolTable::Symbol> s;
- if (name.type == ResourceType::kAttr) {
+ if (real_name.type == ResourceType::kAttr) {
s = LookupAttributeInTable(asset_manager_, res_id);
} else {
s = util::make_unique<SymbolTable::Symbol>();
s->id = res_id;
- s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id());
+ s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id(), real_name.package);
}
if (s) {
@@ -378,7 +380,7 @@ static Maybe<ResourceName> GetResourceName(android::AssetManager2& am,
std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById(
ResourceId id) {
- if (!id.is_valid()) {
+ if (!id.is_valid_static()) {
// Exit early and avoid the error logs from AssetManager.
return {};
}
@@ -405,7 +407,7 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById(
} else {
s = util::make_unique<SymbolTable::Symbol>();
s->id = id;
- s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id());
+ s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id(), name.package);
}
if (s) {
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 6997cd6714a8..06eaf63ad442 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -194,7 +194,7 @@ class AssetManagerSymbolSource : public ISymbolSource {
bool AddAssetPath(const android::StringPiece& path);
std::map<size_t, std::string> GetAssignedPackageIds() const;
- bool IsPackageDynamic(uint32_t packageId) const;
+ bool IsPackageDynamic(uint32_t packageId, const std::string& package_name) const;
std::unique_ptr<SymbolTable::Symbol> FindByName(
const ResourceName& name) override;
diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h
index 0564db063b9a..553c43e6c469 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -81,6 +81,14 @@ class Context : public IAaptContext {
return min_sdk_version_;
}
+ void SetMinSdkVersion(int min_sdk_version) {
+ min_sdk_version_ = min_sdk_version;
+ }
+
+ const std::set<std::string>& GetSplitNameDependencies() override {
+ return split_name_dependencies_;
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(Context);
@@ -93,6 +101,7 @@ class Context : public IAaptContext {
NameMangler name_mangler_;
SymbolTable symbols_;
int min_sdk_version_;
+ std::set<std::string> split_name_dependencies_;
};
class ContextBuilder {
@@ -127,6 +136,11 @@ class ContextBuilder {
return *this;
}
+ ContextBuilder& SetSplitNameDependencies(const std::set<std::string>& split_name_dependencies) {
+ context_->split_name_dependencies_ = split_name_dependencies;
+ return *this;
+ }
+
std::unique_ptr<Context> Build() { return std::move(context_); }
private:
diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp
index a51b4a4649f1..5386802dbc8e 100644
--- a/tools/aapt2/test/Fixture.cpp
+++ b/tools/aapt2/test/Fixture.cpp
@@ -80,7 +80,7 @@ void TestDirectoryFixture::TearDown() {
ClearDirectory(temp_dir_);
}
-bool TestDirectoryFixture::WriteFile(const std::string& path, const std::string& contents) {
+void TestDirectoryFixture::WriteFile(const std::string& path, const std::string& contents) {
CHECK(util::StartsWith(path, temp_dir_))
<< "Attempting to create a file outside of test temporary directory.";
@@ -91,16 +91,31 @@ bool TestDirectoryFixture::WriteFile(const std::string& path, const std::string&
file::mkdirs(dirs);
}
- return android::base::WriteStringToFile(contents, path);
+ CHECK(android::base::WriteStringToFile(contents, path));
}
bool CommandTestFixture::CompileFile(const std::string& path, const std::string& contents,
const android::StringPiece& out_dir, IDiagnostics* diag) {
- CHECK(WriteFile(path, contents));
+ WriteFile(path, contents);
CHECK(file::mkdirs(out_dir.data()));
return CompileCommand(diag).Execute({path, "-o", out_dir, "-v"}, &std::cerr) == 0;
}
+bool CommandTestFixture::Link(const std::vector<std::string>& args, IDiagnostics* diag) {
+ std::vector<android::StringPiece> link_args;
+ for(const std::string& arg : args) {
+ link_args.emplace_back(arg);
+ }
+
+ // Link against the android SDK
+ std::string android_sdk = file::BuildPath({android::base::GetExecutableDirectory(),
+ "integration-tests", "CommandTests",
+ "android-28.jar"});
+ link_args.insert(link_args.end(), {"-I", android_sdk});
+
+ return LinkCommand(diag).Execute(link_args, &std::cerr) == 0;
+}
+
bool CommandTestFixture::Link(const std::vector<std::string>& args,
const android::StringPiece& flat_dir, IDiagnostics* diag) {
std::vector<android::StringPiece> link_args;
@@ -128,10 +143,10 @@ bool CommandTestFixture::Link(const std::vector<std::string>& args,
std::string CommandTestFixture::GetDefaultManifest(const char* package_name) {
const std::string manifest_file = GetTestPath("AndroidManifest.xml");
- CHECK(WriteFile(manifest_file, android::base::StringPrintf(R"(
+ WriteFile(manifest_file, android::base::StringPrintf(R"(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="%s">
- </manifest>)", package_name)));
+ </manifest>)", package_name));
return manifest_file;
}
diff --git a/tools/aapt2/test/Fixture.h b/tools/aapt2/test/Fixture.h
index fce2aebfecaa..457d65e30b65 100644
--- a/tools/aapt2/test/Fixture.h
+++ b/tools/aapt2/test/Fixture.h
@@ -58,7 +58,7 @@ class TestDirectoryFixture : public ::testing::Test {
// Creates a file with the specified contents, creates any intermediate directories in the
// process. The file path must be an absolute path within the test directory.
- bool WriteFile(const std::string& path, const std::string& contents);
+ void WriteFile(const std::string& path, const std::string& contents);
private:
std::string temp_dir_;
@@ -75,6 +75,9 @@ class CommandTestFixture : public TestDirectoryFixture {
bool CompileFile(const std::string& path, const std::string& contents,
const android::StringPiece& flat_out_dir, IDiagnostics* diag);
+ // Executes the link command with the specified arguments.
+ bool Link(const std::vector<std::string>& args, IDiagnostics* diag);
+
// Executes the link command with the specified arguments. The flattened files residing in the
// flat directory will be added to the link command as file arguments.
bool Link(const std::vector<std::string>& args, const android::StringPiece& flat_dir,