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:
Iurii Makhno
2022-02-22 15:54:49 +00:00
parent 9d4a4d6021
commit ce8fc1489c
6 changed files with 125 additions and 13 deletions

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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());

View File

@@ -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";

View File

@@ -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