Support custom resource types in proto/binary formats.
DD: go/custom-resource-types-in-aapt2 Bug: b/215108200 Test: TableFlattener_test, ProtoSerialize_test Change-Id: I9e8a7f946df3899eabeb2f82d5fa4f1df692f8b6
This commit is contained in:
@@ -18,19 +18,19 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "android-base/logging.h"
|
||||
#include "android-base/macros.h"
|
||||
#include "android-base/stringprintf.h"
|
||||
#include "androidfw/ResourceTypes.h"
|
||||
#include "androidfw/TypeWrappers.h"
|
||||
|
||||
#include "ResourceTable.h"
|
||||
#include "ResourceUtils.h"
|
||||
#include "ResourceValues.h"
|
||||
#include "Source.h"
|
||||
#include "ValueVisitor.h"
|
||||
#include "android-base/logging.h"
|
||||
#include "android-base/macros.h"
|
||||
#include "android-base/stringprintf.h"
|
||||
#include "androidfw/ResourceTypes.h"
|
||||
#include "androidfw/TypeWrappers.h"
|
||||
#include "format/binary/ResChunkPullParser.h"
|
||||
#include "util/Util.h"
|
||||
|
||||
@@ -364,7 +364,7 @@ bool BinaryResourceParser::ParseType(const ResourceTablePackage* package,
|
||||
config.copyFromDtoH(type->config);
|
||||
|
||||
const std::string type_str = util::GetString(type_pool_, type->id - 1);
|
||||
const ResourceType* parsed_type = ParseResourceType(type_str);
|
||||
std::optional<ResourceNamedTypeRef> parsed_type = ParseResourceNamedType(type_str);
|
||||
if (!parsed_type) {
|
||||
diag_->Warn(DiagMessage(source_)
|
||||
<< "invalid type name '" << type_str << "' for type with ID " << type->id);
|
||||
|
||||
@@ -607,7 +607,7 @@ class PackageFlattener {
|
||||
expected_type_id++;
|
||||
}
|
||||
expected_type_id++;
|
||||
type_pool_.MakeRef(to_string(type.named_type.type));
|
||||
type_pool_.MakeRef(type.named_type.to_string());
|
||||
|
||||
if (!FlattenTypeSpec(type, type.entries, buffer)) {
|
||||
return false;
|
||||
|
||||
@@ -837,4 +837,45 @@ TEST_F(TableFlattenerTest, FlattenOverlayableNoPolicyFails) {
|
||||
ASSERT_FALSE(Flatten(context_.get(), {}, table.get(), &output_table));
|
||||
}
|
||||
|
||||
TEST_F(TableFlattenerTest, FlattenCustomResourceTypes) {
|
||||
std::unique_ptr<ResourceTable> table =
|
||||
test::ResourceTableBuilder()
|
||||
.AddSimple("com.app.test:id/one", ResourceId(0x7f010000))
|
||||
.AddSimple("com.app.test:id.2/two", ResourceId(0x7f020000))
|
||||
.AddValue("com.app.test:integer/one", ResourceId(0x7f030000),
|
||||
util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 10u))
|
||||
.AddValue("com.app.test:integer.1/one", ResourceId(0x7f040000),
|
||||
util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 1u))
|
||||
.AddValue("com.app.test:integer.1/one", test::ParseConfigOrDie("v1"),
|
||||
ResourceId(0x7f040000),
|
||||
util::make_unique<BinaryPrimitive>(uint8_t(Res_value::TYPE_INT_DEC), 2u))
|
||||
.AddString("com.app.test:layout.custom/bar", ResourceId(0x7f050000), "res/layout/bar.xml")
|
||||
.Build();
|
||||
|
||||
ResTable res_table;
|
||||
ASSERT_TRUE(Flatten(context_.get(), {}, table.get(), &res_table));
|
||||
|
||||
EXPECT_TRUE(Exists(&res_table, "com.app.test:id/one", ResourceId(0x7f010000), {},
|
||||
Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
|
||||
|
||||
EXPECT_TRUE(Exists(&res_table, "com.app.test:id.2/two", ResourceId(0x7f020000), {},
|
||||
Res_value::TYPE_INT_BOOLEAN, 0u, 0u));
|
||||
|
||||
EXPECT_TRUE(Exists(&res_table, "com.app.test:integer/one", ResourceId(0x7f030000), {},
|
||||
Res_value::TYPE_INT_DEC, 10u, 0u));
|
||||
|
||||
EXPECT_TRUE(Exists(&res_table, "com.app.test:integer.1/one", ResourceId(0x7f040000), {},
|
||||
Res_value::TYPE_INT_DEC, 1u, ResTable_config::CONFIG_VERSION));
|
||||
|
||||
EXPECT_TRUE(Exists(&res_table, "com.app.test:integer.1/one", ResourceId(0x7f040000),
|
||||
test::ParseConfigOrDie("v1"), Res_value::TYPE_INT_DEC, 2u,
|
||||
ResTable_config::CONFIG_VERSION));
|
||||
|
||||
std::u16string bar_path = u"res/layout/bar.xml";
|
||||
auto idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size());
|
||||
ASSERT_TRUE(idx.has_value());
|
||||
EXPECT_TRUE(Exists(&res_table, "com.app.test:layout.custom/bar", ResourceId(0x7f050000), {},
|
||||
Res_value::TYPE_STRING, (uint32_t)*idx, 0u));
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -429,16 +429,15 @@ static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStr
|
||||
|
||||
ResourceTablePackage* pkg = out_table->FindOrCreatePackage(pb_package.package_name());
|
||||
for (const pb::Type& pb_type : pb_package.type()) {
|
||||
const ResourceType* res_type = ParseResourceType(pb_type.name());
|
||||
if (res_type == nullptr) {
|
||||
auto res_type = ParseResourceNamedType(pb_type.name());
|
||||
if (!res_type) {
|
||||
std::ostringstream error;
|
||||
error << "unknown type '" << pb_type.name() << "'";
|
||||
*out_error = error.str();
|
||||
return false;
|
||||
}
|
||||
|
||||
auto named_type = ResourceNamedTypeWithDefaultName(*res_type);
|
||||
ResourceTableType* type = pkg->FindOrCreateType(named_type);
|
||||
ResourceTableType* type = pkg->FindOrCreateType(*res_type);
|
||||
|
||||
for (const pb::Entry& pb_entry : pb_type.entry()) {
|
||||
ResourceEntry* entry = type->CreateEntry(pb_entry.name());
|
||||
|
||||
@@ -358,7 +358,7 @@ void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table
|
||||
if (type.id) {
|
||||
pb_type->mutable_type_id()->set_id(type.id.value());
|
||||
}
|
||||
pb_type->set_name(to_string(type.named_type.type).to_string());
|
||||
pb_type->set_name(type.named_type.to_string());
|
||||
|
||||
// hardcoded string uses characters which make it an invalid resource name
|
||||
static const char* obfuscated_resource_name = "0_resource_name_obfuscated";
|
||||
|
||||
@@ -951,4 +951,76 @@ TEST(ProtoSerializeTest, StagedId) {
|
||||
EXPECT_THAT(result.value().entry->staged_id.value().id, Eq(ResourceId(0x01ff0001)));
|
||||
}
|
||||
|
||||
TEST(ProtoSerializeTest, CustomResourceTypes) {
|
||||
const uint32_t id_one_id = 0x7f020000;
|
||||
const uint32_t id_2_two_id = 0x7f030000;
|
||||
const uint32_t integer_three_id = 0x7f030000;
|
||||
const uint32_t integer_1_four_id = 0x7f030000;
|
||||
const uint32_t layout_bar_id = 0x7f050000;
|
||||
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
|
||||
std::unique_ptr<ResourceTable> table =
|
||||
test::ResourceTableBuilder()
|
||||
.AddSimple("com.app.test:id/one", ResourceId(id_one_id))
|
||||
.AddSimple("com.app.test:id.2/two", ResourceId(id_2_two_id))
|
||||
.AddValue(
|
||||
"com.app.test:integer/one", ResourceId(integer_three_id),
|
||||
util::make_unique<BinaryPrimitive>(uint8_t(android::Res_value::TYPE_INT_DEC), 10u))
|
||||
.AddValue(
|
||||
"com.app.test:integer.1/one", ResourceId(integer_1_four_id),
|
||||
util::make_unique<BinaryPrimitive>(uint8_t(android::Res_value::TYPE_INT_DEC), 1u))
|
||||
.AddValue(
|
||||
"com.app.test:integer.1/one", test::ParseConfigOrDie("v1"),
|
||||
ResourceId(integer_1_four_id),
|
||||
util::make_unique<BinaryPrimitive>(uint8_t(android::Res_value::TYPE_INT_DEC), 2u))
|
||||
.AddFileReference("com.app.test:layout.custom/bar", ResourceId(layout_bar_id),
|
||||
"res/layout/bar.xml")
|
||||
.Build();
|
||||
|
||||
test::TestFile file_a("res/layout/bar.xml");
|
||||
MockFileCollection files;
|
||||
EXPECT_CALL(files, FindFile(Eq("res/layout/bar.xml"))).WillRepeatedly(::testing::Return(&file_a));
|
||||
|
||||
ResourceTable new_table;
|
||||
pb::ResourceTable pb_table;
|
||||
std::string error;
|
||||
SerializeTableToPb(*table, &pb_table, context->GetDiagnostics());
|
||||
DeserializeTableFromPb(pb_table, &files, &new_table, &error);
|
||||
ASSERT_THAT(error, IsEmpty());
|
||||
|
||||
auto bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(
|
||||
&new_table, "com.app.test:integer.1/one", ConfigDescription::DefaultConfig(), "");
|
||||
ASSERT_THAT(bp, NotNull());
|
||||
EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_INT_DEC));
|
||||
EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseInt("1")->value.data));
|
||||
|
||||
bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "com.app.test:integer.1/one",
|
||||
test::ParseConfigOrDie("v1"), "");
|
||||
ASSERT_THAT(bp, NotNull());
|
||||
EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_INT_DEC));
|
||||
EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseInt("2")->value.data));
|
||||
|
||||
bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "com.app.test:integer/one",
|
||||
ConfigDescription::DefaultConfig(), "");
|
||||
ASSERT_THAT(bp, NotNull());
|
||||
EXPECT_THAT(bp->value.dataType, Eq(android::Res_value::TYPE_INT_DEC));
|
||||
EXPECT_THAT(bp->value.data, Eq(ResourceUtils::TryParseInt("10")->value.data));
|
||||
|
||||
bp = test::GetValueForConfigAndProduct<BinaryPrimitive>(&new_table, "com.app.test:integer/one",
|
||||
test::ParseConfigOrDie("v1"), "");
|
||||
ASSERT_THAT(bp, IsNull());
|
||||
|
||||
auto id = test::GetValueForConfigAndProduct<Id>(&new_table, "com.app.test:id/one",
|
||||
ConfigDescription::DefaultConfig(), "");
|
||||
ASSERT_THAT(id, NotNull());
|
||||
|
||||
id = test::GetValueForConfigAndProduct<Id>(&new_table, "com.app.test:id.2/two",
|
||||
ConfigDescription::DefaultConfig(), "");
|
||||
ASSERT_THAT(id, NotNull());
|
||||
|
||||
auto custom_layout = test::GetValueForConfigAndProduct<FileReference>(
|
||||
&new_table, "com.app.test:layout.custom/bar", ConfigDescription::DefaultConfig(), "");
|
||||
ASSERT_THAT(custom_layout, NotNull());
|
||||
EXPECT_THAT(*(custom_layout->path), Eq("res/layout/bar.xml"));
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
Reference in New Issue
Block a user