Merge "Enable --collapse-resource-names on bundles"

This commit is contained in:
Mohamed Heikal
2020-04-02 21:21:04 +00:00
committed by Android (Google) Code Review
5 changed files with 202 additions and 4 deletions

View File

@@ -267,8 +267,14 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table
return false;
}
} else if (format_ == ApkFormat::kProto && path == kProtoResourceTablePath) {
SerializeTableOptions proto_serialize_options;
proto_serialize_options.collapse_key_stringpool =
options.collapse_key_stringpool;
proto_serialize_options.name_collapse_exemptions =
options.name_collapse_exemptions;
pb::ResourceTable pb_table;
SerializeTableToPb(*split_table, &pb_table, context->GetDiagnostics());
SerializeTableToPb(*split_table, &pb_table, context->GetDiagnostics(),
proto_serialize_options);
if (!io::CopyProtoToArchive(context,
&pb_table,
path,

View File

@@ -446,9 +446,12 @@ static bool DeserializePackageFromPb(const pb::Package& pb_package, const ResStr
}
for (const pb::Entry& pb_entry : pb_type.entry()) {
ResourceEntry* entry = type->FindOrCreateEntry(pb_entry.name());
ResourceEntry* entry;
if (pb_entry.has_entry_id()) {
entry->id = static_cast<uint16_t>(pb_entry.entry_id().id());
auto entry_id = static_cast<uint16_t>(pb_entry.entry_id().id());
entry = type->FindOrCreateEntry(pb_entry.name(), entry_id);
} else {
entry = type->FindOrCreateEntry(pb_entry.name());
}
// Deserialize the symbol status (public/private with source and comments).

View File

@@ -356,12 +356,21 @@ void SerializeTableToPb(const ResourceTable& table, pb::ResourceTable* out_table
}
pb_type->set_name(to_string(type->type).to_string());
// hardcoded string uses characters which make it an invalid resource name
static const char* obfuscated_resource_name = "0_resource_name_obfuscated";
for (const std::unique_ptr<ResourceEntry>& entry : type->entries) {
pb::Entry* pb_entry = pb_type->add_entry();
if (entry->id) {
pb_entry->mutable_entry_id()->set_id(entry->id.value());
}
pb_entry->set_name(entry->name);
ResourceName resource_name({}, type->type, entry->name);
if (options.collapse_key_stringpool &&
options.name_collapse_exemptions.find(resource_name) ==
options.name_collapse_exemptions.end()) {
pb_entry->set_name(obfuscated_resource_name);
} else {
pb_entry->set_name(entry->name);
}
// Write the Visibility struct.
pb::Visibility* pb_visibility = pb_entry->mutable_visibility();

View File

@@ -38,6 +38,15 @@ struct SerializeXmlOptions {
struct SerializeTableOptions {
/** Prevent serializing the source pool and source protos. */
bool exclude_sources = false;
// When true, all the entry names in pb:ResourceTable are collapsed to a
// single entry name. When the proto table is converted to binary
// resources.arsc, the key string pool is collapsed to a single entry. All
// resource entries have name indices that point to this single value.
bool collapse_key_stringpool = false;
// Set of resources to avoid collapsing to a single entry in key stringpool.
std::set<ResourceName> name_collapse_exemptions;
};
// Serializes a Value to its protobuf representation. An optional StringPool will hold the

View File

@@ -24,6 +24,7 @@ using ::android::ConfigDescription;
using ::android::StringPiece;
using ::testing::Eq;
using ::testing::IsEmpty;
using ::testing::IsNull;
using ::testing::NotNull;
using ::testing::SizeIs;
using ::testing::StrEq;
@@ -39,6 +40,13 @@ class MockFileCollection : public io::IFileCollection {
MOCK_METHOD0(GetDirSeparator, char());
};
ResourceEntry* GetEntry(ResourceTable* table, const ResourceNameRef& res_name,
uint32_t id) {
ResourceTablePackage* package = table->FindPackage(res_name.package);
ResourceTableType* type = package->FindType(res_name.type);
return type->FindEntry(res_name.entry, id);
}
TEST(ProtoSerializeTest, SerializeSinglePackage) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<ResourceTable> table =
@@ -662,4 +670,167 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeNonDynamicReference) {
EXPECT_FALSE(actual_ref->is_dynamic);
}
TEST(ProtoSerializeTest, CollapsingResourceNamesNoNameCollapseExemptionsSucceeds) {
const uint32_t id_one_id = 0x7f020000;
const uint32_t id_two_id = 0x7f020001;
const uint32_t id_three_id = 0x7f020002;
const uint32_t integer_three_id = 0x7f030000;
const uint32_t string_test_id = 0x7f040000;
const uint32_t layout_bar_id = 0x7f050000;
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("com.app.test", 0x7f)
.AddSimple("com.app.test:id/one", ResourceId(id_one_id))
.AddSimple("com.app.test:id/two", ResourceId(id_two_id))
.AddValue("com.app.test:id/three", ResourceId(id_three_id),
test::BuildReference("com.app.test:id/one", ResourceId(id_one_id)))
.AddValue("com.app.test:integer/one", ResourceId(integer_three_id),
util::make_unique<BinaryPrimitive>(
uint8_t(android::Res_value::TYPE_INT_DEC), 1u))
.AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"),
ResourceId(integer_three_id),
util::make_unique<BinaryPrimitive>(
uint8_t(android::Res_value::TYPE_INT_DEC), 2u))
.AddString("com.app.test:string/test", ResourceId(string_test_id), "foo")
.AddFileReference("com.app.test:layout/bar", ResourceId(layout_bar_id),
"res/layout/bar.xml")
.Build();
SerializeTableOptions options;
options.collapse_key_stringpool = true;
pb::ResourceTable pb_table;
SerializeTableToPb(*table, &pb_table, context->GetDiagnostics(), options);
test::TestFile file_a("res/layout/bar.xml");
MockFileCollection files;
EXPECT_CALL(files, FindFile(Eq("res/layout/bar.xml")))
.WillRepeatedly(::testing::Return(&file_a));
ResourceTable new_table;
std::string error;
ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error)) << error;
EXPECT_THAT(error, IsEmpty());
ResourceName real_id_resource(
"com.app.test", ResourceType::kId, "one");
EXPECT_THAT(GetEntry(&new_table, real_id_resource, id_one_id), IsNull());
ResourceName obfuscated_id_resource(
"com.app.test", ResourceType::kId, "0_resource_name_obfuscated");
EXPECT_THAT(GetEntry(&new_table, obfuscated_id_resource,
id_one_id), NotNull());
EXPECT_THAT(GetEntry(&new_table, obfuscated_id_resource,
id_two_id), NotNull());
ResourceEntry* entry = GetEntry(&new_table, obfuscated_id_resource, id_three_id);
EXPECT_THAT(entry, NotNull());
ResourceConfigValue* config_value = entry->FindValue({});
Reference* ref = ValueCast<Reference>(config_value->value.get());
EXPECT_THAT(ref->id.value(), Eq(id_one_id));
ResourceName obfuscated_integer_resource(
"com.app.test", ResourceType::kInteger, "0_resource_name_obfuscated");
entry = GetEntry(&new_table, obfuscated_integer_resource, integer_three_id);
EXPECT_THAT(entry, NotNull());
config_value = entry->FindValue({});
BinaryPrimitive* bp = ValueCast<BinaryPrimitive>(config_value->value.get());
EXPECT_THAT(bp->value.data, Eq(1u));
config_value = entry->FindValue(test::ParseConfigOrDie("v1"));
bp = ValueCast<BinaryPrimitive>(config_value->value.get());
EXPECT_THAT(bp->value.data, Eq(2u));
ResourceName obfuscated_string_resource(
"com.app.test", ResourceType::kString, "0_resource_name_obfuscated");
entry = GetEntry(&new_table, obfuscated_string_resource, string_test_id);
EXPECT_THAT(entry, NotNull());
config_value = entry->FindValue({});
String* s = ValueCast<String>(config_value->value.get());
EXPECT_THAT(*(s->value), Eq("foo"));
ResourceName obfuscated_layout_resource(
"com.app.test", ResourceType::kLayout, "0_resource_name_obfuscated");
entry = GetEntry(&new_table, obfuscated_layout_resource, layout_bar_id);
EXPECT_THAT(entry, NotNull());
config_value = entry->FindValue({});
FileReference* f = ValueCast<FileReference>(config_value->value.get());
EXPECT_THAT(*(f->path), Eq("res/layout/bar.xml"));
}
TEST(ProtoSerializeTest, ObfuscatingResourceNamesWithNameCollapseExemptionsSucceeds) {
const uint32_t id_one_id = 0x7f020000;
const uint32_t id_two_id = 0x7f020001;
const uint32_t id_three_id = 0x7f020002;
const uint32_t integer_three_id = 0x7f030000;
const uint32_t string_test_id = 0x7f040000;
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.SetPackageId("com.app.test", 0x7f)
.AddSimple("com.app.test:id/one", ResourceId(id_one_id))
.AddSimple("com.app.test:id/two", ResourceId(id_two_id))
.AddValue("com.app.test:id/three", ResourceId(id_three_id),
test::BuildReference("com.app.test:id/one", ResourceId(id_one_id)))
.AddValue("com.app.test:integer/one", ResourceId(integer_three_id),
util::make_unique<BinaryPrimitive>(
uint8_t(android::Res_value::TYPE_INT_DEC), 1u))
.AddValue("com.app.test:integer/one", test::ParseConfigOrDie("v1"),
ResourceId(integer_three_id),
util::make_unique<BinaryPrimitive>(
uint8_t(android::Res_value::TYPE_INT_DEC), 2u))
.AddString("com.app.test:string/test", ResourceId(string_test_id), "foo")
.Build();
SerializeTableOptions options;
options.collapse_key_stringpool = true;
options.name_collapse_exemptions.insert(ResourceName({}, ResourceType::kId, "one"));
options.name_collapse_exemptions.insert(ResourceName({}, ResourceType::kString, "test"));
pb::ResourceTable pb_table;
SerializeTableToPb(*table, &pb_table, context->GetDiagnostics(), options);
MockFileCollection files;
ResourceTable new_table;
std::string error;
ASSERT_TRUE(DeserializeTableFromPb(pb_table, &files, &new_table, &error)) << error;
EXPECT_THAT(error, IsEmpty());
EXPECT_THAT(GetEntry(&new_table, ResourceName("com.app.test", ResourceType::kId, "one"),
id_one_id), NotNull());
ResourceName obfuscated_id_resource(
"com.app.test", ResourceType::kId, "0_resource_name_obfuscated");
EXPECT_THAT(GetEntry(&new_table, obfuscated_id_resource, id_one_id), IsNull());
ResourceName real_id_resource(
"com.app.test", ResourceType::kId, "two");
EXPECT_THAT(GetEntry(&new_table, real_id_resource, id_two_id), IsNull());
EXPECT_THAT(GetEntry(&new_table, obfuscated_id_resource, id_two_id), NotNull());
ResourceEntry* entry = GetEntry(&new_table, obfuscated_id_resource, id_three_id);
EXPECT_THAT(entry, NotNull());
ResourceConfigValue* config_value = entry->FindValue({});
Reference* ref = ValueCast<Reference>(config_value->value.get());
EXPECT_THAT(ref->id.value(), Eq(id_one_id));
// Note that this resource is also named "one", but it's a different type, so gets obfuscated.
ResourceName obfuscated_integer_resource(
"com.app.test", ResourceType::kInteger, "0_resource_name_obfuscated");
entry = GetEntry(&new_table, obfuscated_integer_resource, integer_three_id);
EXPECT_THAT(entry, NotNull());
config_value = entry->FindValue({});
BinaryPrimitive* bp = ValueCast<BinaryPrimitive>(config_value->value.get());
EXPECT_THAT(bp->value.data, Eq(1u));
config_value = entry->FindValue(test::ParseConfigOrDie("v1"));
bp = ValueCast<BinaryPrimitive>(config_value->value.get());
EXPECT_THAT(bp->value.data, Eq(2u));
entry = GetEntry(&new_table, ResourceName("com.app.test", ResourceType::kString, "test"),
string_test_id);
EXPECT_THAT(entry, NotNull());
config_value = entry->FindValue({});
String* s = ValueCast<String>(config_value->value.get());
EXPECT_THAT(*(s->value), Eq("foo"));
}
} // namespace aapt