Loading tools/aapt2/Format.proto +33 −25 Original line number Diff line number Diff line Loading @@ -67,21 +67,12 @@ message CompiledFile { // Top level message representing a resource table. message ResourceTable { // The string pool containing string values (strings that actually end up in the binary ARSC // file) referenced throughout the resource table. optional StringPool string_pool = 1; // The string pool containing source paths referenced throughout the resource table. This does // not end up in the final binary ARSC file. optional StringPool source_pool = 2; // The string pool containing the names of unresolved symbols. This does not end up in the final // binary ARSC file. Unresolved symbols are just resource names that haven't had a resource ID // assigned to them, therefore can't be referenced by resource ID. optional StringPool symbol_pool = 3; optional StringPool source_pool = 1; // Resource definitions corresponding to an Android package. repeated Package packages = 4; repeated Package packages = 2; } // Defines resources for an Android package. Loading Loading @@ -202,9 +193,10 @@ message Item { optional Reference ref = 1; optional String str = 2; optional RawString raw_str = 3; optional FileReference file = 4; optional Id id = 5; optional Primitive prim = 6; optional StyledString styled_str = 4; optional FileReference file = 5; optional Id id = 6; optional Primitive prim = 7; } // A CompoundValue is an abstract type. It represents a value that is a made of other values. Loading Loading @@ -233,10 +225,8 @@ message Reference { // The resource ID (0xPPTTEEEE) of the resource being referred. optional uint32 id = 2; // If the resource ID is not resolved, the index into the symbol string pool where the name of // the reference is stored. The symbol string pool is located at the top level ResourceTable // message. optional uint32 symbol_idx = 3; // The optional resource name. optional string name = 3; // Whether this reference is referencing a private resource (@*package:type/entry). optional bool private = 4; Loading @@ -249,23 +239,41 @@ message Id { // A value that is a string. message String { // The index into the values string pool, located at the top level ResourceTable message. optional uint32 idx = 1; optional string value = 1; } // A value that is a raw string, which is unescaped/uninterpreted. This is typically used to // represent the value of a style attribute before the attribute is compiled and the set of // allowed values is known. message RawString { // The index into the values string pool, located at the top level ResourceTable message. optional uint32 idx = 1; optional string value = 1; } // A string with styling information, like html tags that specify boldness, italics, etc. message StyledString { // The raw text of the string. optional string value = 1; // A Span marks a region of the string text that is styled. message Span { // The name of the tag, and its attributes, encoded as follows: // tag_name;attr1=value1;attr2=value2;[...] optional string tag = 1; // The first character position this span applies to, in UTF-16 offset. optional uint32 first_char = 2; // The last character position this span applies to, in UTF-16 offset. optional uint32 last_char = 3; } repeated Span span = 2; } // A value that is a reference to an external entity, like an XML file or a PNG. message FileReference { // The index into the values string pool, located at the top level ResourceTable message. This // represents the path to the file within an APK (typically res/type-config/entry.ext). optional uint32 path_idx = 1; // Path to a file within the APK (typically res/type-config/entry.ext). optional string path = 1; } // A value that represents a primitive data type (float, int, boolean, etc.). Loading tools/aapt2/proto/TableProtoDeserializer.cpp +33 −86 Original line number Diff line number Diff line Loading @@ -55,15 +55,10 @@ class ReferenceIdToNameVisitor : public ValueVisitor { class PackagePbDeserializer { public: PackagePbDeserializer(const android::ResStringPool* valuePool, const android::ResStringPool* sourcePool, const android::ResStringPool* symbolPool, const Source& source, IDiagnostics* diag) : value_pool_(valuePool), source_pool_(sourcePool), symbol_pool_(symbolPool), source_(source), diag_(diag) {} PackagePbDeserializer(const android::ResStringPool* sourcePool, const Source& source, IDiagnostics* diag) : source_pool_(sourcePool), source_(source), diag_(diag) { } public: bool DeserializeFromPb(const pb::Package& pbPackage, ResourceTable* table) { Loading @@ -87,8 +82,7 @@ class PackagePbDeserializer { for (const pb::Entry& pbEntry : pbType.entries()) { ResourceEntry* entry = type->FindOrCreateEntry(pbEntry.name()); // Deserialize the symbol status (public/private with source and // comments). // Deserialize the symbol status (public/private with source and comments). if (pbEntry.has_symbol_status()) { const pb::SymbolStatus& pbStatus = pbEntry.symbol_status(); if (pbStatus.has_source()) { Loading Loading @@ -161,8 +155,7 @@ class PackagePbDeserializer { private: std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item, const ConfigDescription& config, StringPool* pool) { const ConfigDescription& config, StringPool* pool) { if (pb_item.has_ref()) { const pb::Reference& pb_ref = pb_item.ref(); std::unique_ptr<Reference> ref = util::make_unique<Reference>(); Loading @@ -173,45 +166,32 @@ class PackagePbDeserializer { } else if (pb_item.has_prim()) { const pb::Primitive& pb_prim = pb_item.prim(); android::Res_value prim = {}; prim.dataType = static_cast<uint8_t>(pb_prim.type()); prim.data = pb_prim.data(); return util::make_unique<BinaryPrimitive>(prim); return util::make_unique<BinaryPrimitive>(static_cast<uint8_t>(pb_prim.type()), pb_prim.data()); } else if (pb_item.has_id()) { return util::make_unique<Id>(); } else if (pb_item.has_str()) { const uint32_t idx = pb_item.str().idx(); const std::string str = util::GetString(*value_pool_, idx); const android::ResStringPool_span* spans = value_pool_->styleAt(idx); if (spans && spans->name.index != android::ResStringPool_span::END) { StyleString style_str = {str}; while (spans->name.index != android::ResStringPool_span::END) { style_str.spans.push_back( Span{util::GetString(*value_pool_, spans->name.index), spans->firstChar, spans->lastChar}); spans++; } return util::make_unique<StyledString>(pool->MakeRef( style_str, StringPool::Context(StringPool::Context::kNormalPriority, config))); } return util::make_unique<String>( pool->MakeRef(str, StringPool::Context(config))); pool->MakeRef(pb_item.str().value(), StringPool::Context(config))); } else if (pb_item.has_raw_str()) { const uint32_t idx = pb_item.raw_str().idx(); const std::string str = util::GetString(*value_pool_, idx); return util::make_unique<RawString>( pool->MakeRef(str, StringPool::Context(config))); pool->MakeRef(pb_item.raw_str().value(), StringPool::Context(config))); } else if (pb_item.has_styled_str()) { const pb::StyledString& pb_str = pb_item.styled_str(); StyleString style_str{pb_str.value()}; for (const pb::StyledString::Span& pb_span : pb_str.span()) { style_str.spans.push_back(Span{pb_span.tag(), pb_span.first_char(), pb_span.last_char()}); } return util::make_unique<StyledString>(pool->MakeRef( style_str, StringPool::Context(StringPool::Context::kNormalPriority, config))); } else if (pb_item.has_file()) { const uint32_t idx = pb_item.file().path_idx(); const std::string str = util::GetString(*value_pool_, idx); return util::make_unique<FileReference>(pool->MakeRef( str, StringPool::Context(StringPool::Context::kHighPriority, config))); pb_item.file().path(), StringPool::Context(StringPool::Context::kHighPriority, config))); } else { diag_->Error(DiagMessage(source_) << "unknown item"); Loading Loading @@ -255,15 +235,13 @@ class PackagePbDeserializer { std::unique_ptr<Style> style = util::make_unique<Style>(); if (pb_style.has_parent()) { style->parent = Reference(); if (!DeserializeReferenceFromPb(pb_style.parent(), &style->parent.value())) { if (!DeserializeReferenceFromPb(pb_style.parent(), &style->parent.value())) { return {}; } if (pb_style.has_parent_source()) { Source parent_source; DeserializeSourceFromPb(pb_style.parent_source(), *source_pool_, &parent_source); DeserializeSourceFromPb(pb_style.parent_source(), *source_pool_, &parent_source); style->parent.value().SetSource(std::move(parent_source)); } } Loading Loading @@ -300,8 +278,7 @@ class PackagePbDeserializer { const pb::Array& pb_array = pb_compound_value.array(); std::unique_ptr<Array> array = util::make_unique<Array>(); for (const pb::Array_Entry& pb_entry : pb_array.entries()) { std::unique_ptr<Item> item = DeserializeItemFromPb(pb_entry.item(), config, pool); std::unique_ptr<Item> item = DeserializeItemFromPb(pb_entry.item(), config, pool); if (!item) { return {}; } Loading @@ -316,8 +293,7 @@ class PackagePbDeserializer { std::unique_ptr<Plural> plural = util::make_unique<Plural>(); for (const pb::Plural_Entry& pb_entry : pb_plural.entries()) { size_t pluralIdx = DeserializePluralEnumFromPb(pb_entry.arity()); plural->values[pluralIdx] = DeserializeItemFromPb(pb_entry.item(), config, pool); plural->values[pluralIdx] = DeserializeItemFromPb(pb_entry.item(), config, pool); if (!plural->values[pluralIdx]) { return {}; } Loading Loading @@ -350,11 +326,10 @@ class PackagePbDeserializer { out_ref->id = ResourceId(pb_ref.id()); } if (pb_ref.has_symbol_idx()) { const std::string str_symbol = util::GetString(*symbol_pool_, pb_ref.symbol_idx()); if (pb_ref.has_name()) { ResourceNameRef name_ref; if (!ResourceUtils::ParseResourceName(str_symbol, &name_ref, nullptr)) { diag_->Error(DiagMessage(source_) << "invalid reference name '" << str_symbol << "'"); if (!ResourceUtils::ParseResourceName(pb_ref.name(), &name_ref, nullptr)) { diag_->Error(DiagMessage(source_) << "invalid reference name '" << pb_ref.name() << "'"); return false; } Loading @@ -377,41 +352,24 @@ class PackagePbDeserializer { } private: const android::ResStringPool* value_pool_; const android::ResStringPool* source_pool_; const android::ResStringPool* symbol_pool_; const Source source_; IDiagnostics* diag_; }; } // namespace std::unique_ptr<ResourceTable> DeserializeTableFromPb( const pb::ResourceTable& pb_table, const Source& source, IDiagnostics* diag) { // We import the android namespace because on Windows NO_ERROR is a macro, not // an enum, which std::unique_ptr<ResourceTable> DeserializeTableFromPb(const pb::ResourceTable& pb_table, const Source& source, IDiagnostics* diag) { // We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which // causes errors when qualifying it with android:: using namespace android; std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>(); if (!pb_table.has_string_pool()) { diag->Error(DiagMessage(source) << "no string pool found"); return {}; } ResStringPool value_pool; status_t result = value_pool.setTo(pb_table.string_pool().data().data(), pb_table.string_pool().data().size()); if (result != NO_ERROR) { diag->Error(DiagMessage(source) << "invalid string pool"); return {}; } ResStringPool source_pool; if (pb_table.has_source_pool()) { result = source_pool.setTo(pb_table.source_pool().data().data(), status_t result = source_pool.setTo(pb_table.source_pool().data().data(), pb_table.source_pool().data().size()); if (result != NO_ERROR) { diag->Error(DiagMessage(source) << "invalid source pool"); Loading @@ -419,18 +377,7 @@ std::unique_ptr<ResourceTable> DeserializeTableFromPb( } } ResStringPool symbol_pool; if (pb_table.has_symbol_pool()) { result = symbol_pool.setTo(pb_table.symbol_pool().data().data(), pb_table.symbol_pool().data().size()); if (result != NO_ERROR) { diag->Error(DiagMessage(source) << "invalid symbol pool"); return {}; } } PackagePbDeserializer package_pb_deserializer(&value_pool, &source_pool, &symbol_pool, source, diag); PackagePbDeserializer package_pb_deserializer(&source_pool, source, diag); for (const pb::Package& pb_package : pb_table.packages()) { if (!package_pb_deserializer.DeserializeFromPb(pb_package, table.get())) { return {}; Loading tools/aapt2/proto/TableProtoSerializer.cpp +36 −43 Original line number Diff line number Diff line Loading @@ -24,9 +24,9 @@ #include "android-base/logging.h" using google::protobuf::io::CodedOutputStream; using google::protobuf::io::CodedInputStream; using google::protobuf::io::ZeroCopyOutputStream; using ::google::protobuf::io::CodedInputStream; using ::google::protobuf::io::CodedOutputStream; using ::google::protobuf::io::ZeroCopyOutputStream; namespace aapt { Loading @@ -36,46 +36,46 @@ class PbSerializerVisitor : public RawValueVisitor { public: using RawValueVisitor::Visit; /** * Constructor to use when expecting to serialize any value. */ PbSerializerVisitor(StringPool* source_pool, StringPool* symbol_pool, pb::Value* out_pb_value) : source_pool_(source_pool), symbol_pool_(symbol_pool), out_pb_value_(out_pb_value), out_pb_item_(nullptr) {} /** * Constructor to use when expecting to serialize an Item. */ PbSerializerVisitor(StringPool* sourcePool, StringPool* symbolPool, pb::Item* outPbItem) : source_pool_(sourcePool), symbol_pool_(symbolPool), out_pb_value_(nullptr), out_pb_item_(outPbItem) {} // Constructor to use when expecting to serialize any value. PbSerializerVisitor(StringPool* source_pool, pb::Value* out_pb_value) : source_pool_(source_pool), out_pb_value_(out_pb_value), out_pb_item_(nullptr) { } // Constructor to use when expecting to serialize an Item. PbSerializerVisitor(StringPool* sourcePool, pb::Item* outPbItem) : source_pool_(sourcePool), out_pb_value_(nullptr), out_pb_item_(outPbItem) { } void Visit(Reference* ref) override { SerializeReferenceToPb(*ref, pb_item()->mutable_ref()); } void Visit(String* str) override { pb_item()->mutable_str()->set_idx(str->value.index()); pb_item()->mutable_str()->set_value(*str->value); } void Visit(RawString* str) override { pb_item()->mutable_raw_str()->set_value(*str->value); } void Visit(StyledString* str) override { pb_item()->mutable_str()->set_idx(str->value.index()); pb::StyledString* pb_str = pb_item()->mutable_styled_str(); pb_str->set_value(str->value->value); for (const StringPool::Span& span : str->value->spans) { pb::StyledString::Span* pb_span = pb_str->add_span(); pb_span->set_tag(*span.name); pb_span->set_first_char(span.first_char); pb_span->set_last_char(span.last_char); } } void Visit(FileReference* file) override { pb_item()->mutable_file()->set_path_idx(file->path.index()); pb_item()->mutable_file()->set_path(*file->path); } void Visit(Id* id) override { pb_item()->mutable_id(); } void Visit(RawString* raw_str) override { pb_item()->mutable_raw_str()->set_idx(raw_str->value.index()); void Visit(Id* /*id*/) override { pb_item()->mutable_id(); } void Visit(BinaryPrimitive* prim) override { Loading Loading @@ -119,7 +119,7 @@ class PbSerializerVisitor : public RawValueVisitor { pb::Item* pb_item = pb_entry->mutable_item(); SerializeItemCommonToPb(entry.key, pb_entry); PbSerializerVisitor sub_visitor(source_pool_, symbol_pool_, pb_item); PbSerializerVisitor sub_visitor(source_pool_, pb_item); entry.value->Accept(&sub_visitor); } } Loading @@ -138,8 +138,7 @@ class PbSerializerVisitor : public RawValueVisitor { for (auto& value : array->items) { pb::Array_Entry* pb_entry = pb_array->add_entries(); SerializeItemCommonToPb(*value, pb_entry); PbSerializerVisitor sub_visitor(source_pool_, symbol_pool_, pb_entry->mutable_item()); PbSerializerVisitor sub_visitor(source_pool_, pb_entry->mutable_item()); value->Accept(&sub_visitor); } } Loading @@ -157,7 +156,7 @@ class PbSerializerVisitor : public RawValueVisitor { pb_entry->set_arity(SerializePluralEnumToPb(i)); pb::Item* pb_element = pb_entry->mutable_item(); SerializeItemCommonToPb(*plural->values[i], pb_entry); PbSerializerVisitor sub_visitor(source_pool_, symbol_pool_, pb_element); PbSerializerVisitor sub_visitor(source_pool_, pb_element); plural->values[i]->Accept(&sub_visitor); } } Loading @@ -179,8 +178,7 @@ class PbSerializerVisitor : public RawValueVisitor { template <typename T> void SerializeItemCommonToPb(const Item& item, T* pb_item) { SerializeSourceToPb(item.GetSource(), source_pool_, pb_item->mutable_source()); SerializeSourceToPb(item.GetSource(), source_pool_, pb_item->mutable_source()); if (!item.GetComment().empty()) { pb_item->set_comment(item.GetComment()); } Loading @@ -192,8 +190,7 @@ class PbSerializerVisitor : public RawValueVisitor { } if (ref.name) { StringPool::Ref symbol_ref = symbol_pool_->MakeRef(ref.name.value().ToString()); pb_ref->set_symbol_idx(static_cast<uint32_t>(symbol_ref.index())); pb_ref->set_name(ref.name.value().ToString()); } pb_ref->set_private_(ref.private_reference); Loading @@ -201,7 +198,6 @@ class PbSerializerVisitor : public RawValueVisitor { } StringPool* source_pool_; StringPool* symbol_pool_; pb::Value* out_pb_value_; pb::Item* out_pb_item_; }; Loading @@ -220,9 +216,7 @@ std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) { }); auto pb_table = util::make_unique<pb::ResourceTable>(); SerializeStringPoolToPb(table->string_pool, pb_table->mutable_string_pool()); StringPool source_pool, symbol_pool; StringPool source_pool; for (auto& package : table->packages) { pb::Package* pb_package = pb_table->add_packages(); Loading Loading @@ -270,7 +264,7 @@ std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) { pb_value->set_weak(true); } PbSerializerVisitor visitor(&source_pool, &symbol_pool, pb_value); PbSerializerVisitor visitor(&source_pool, pb_value); config_value->value->Accept(&visitor); } } Loading @@ -278,7 +272,6 @@ std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) { } SerializeStringPoolToPb(source_pool, pb_table->mutable_source_pool()); SerializeStringPoolToPb(symbol_pool, pb_table->mutable_symbol_pool()); return pb_table; } Loading tools/aapt2/proto/TableProtoSerializer_test.cpp +34 −15 Original line number Diff line number Diff line Loading @@ -20,7 +20,9 @@ #include "test/Test.h" using ::google::protobuf::io::StringOutputStream; using ::testing::Eq; using ::testing::NotNull; using ::testing::SizeIs; namespace aapt { Loading @@ -38,12 +40,12 @@ TEST(TableProtoSerializer, SerializeSinglePackage) { Symbol public_symbol; public_symbol.state = SymbolState::kPublic; ASSERT_TRUE(table->SetSymbolState( test::ParseNameOrDie("com.app.a:layout/main"), ResourceId(0x7f020000), public_symbol, context->GetDiagnostics())); ASSERT_TRUE(table->SetSymbolState(test::ParseNameOrDie("com.app.a:layout/main"), ResourceId(0x7f020000), public_symbol, context->GetDiagnostics())); Id* id = test::GetValue<Id>(table.get(), "com.app.a:id/foo"); ASSERT_NE(nullptr, id); ASSERT_THAT(id, NotNull()); // Make a plural. std::unique_ptr<Plural> plural = util::make_unique<Plural>(); Loading @@ -52,6 +54,15 @@ TEST(TableProtoSerializer, SerializeSinglePackage) { ConfigDescription{}, {}, std::move(plural), context->GetDiagnostics())); // Make a styled string. StyleString style_string; style_string.str = "hello"; style_string.spans.push_back(Span{"b", 0u, 4u}); ASSERT_TRUE( table->AddResource(test::ParseNameOrDie("com.app.a:string/styled"), ConfigDescription{}, {}, util::make_unique<StyledString>(table->string_pool.MakeRef(style_string)), context->GetDiagnostics())); // Make a resource with different products. ASSERT_TRUE(table->AddResource( test::ParseNameOrDie("com.app.a:integer/one"), Loading @@ -65,9 +76,8 @@ TEST(TableProtoSerializer, SerializeSinglePackage) { 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 get serialized. // The reference should point to a resource outside of this table to test that both name and id // get serialized. Reference expected_ref; expected_ref.name = test::ParseNameOrDie("android:layout/main"); expected_ref.id = ResourceId(0x01020000); Loading @@ -85,36 +95,45 @@ TEST(TableProtoSerializer, SerializeSinglePackage) { Id* new_id = test::GetValue<Id>(new_table.get(), "com.app.a:id/foo"); ASSERT_THAT(new_id, NotNull()); EXPECT_EQ(id->IsWeak(), new_id->IsWeak()); EXPECT_THAT(new_id->IsWeak(), Eq(id->IsWeak())); Maybe<ResourceTable::SearchResult> result = new_table->FindResource(test::ParseNameOrDie("com.app.a:layout/main")); ASSERT_TRUE(result); EXPECT_EQ(SymbolState::kPublic, result.value().type->symbol_status.state); EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbol_status.state); EXPECT_THAT(result.value().type->symbol_status.state, Eq(SymbolState::kPublic)); EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kPublic)); result = new_table->FindResource(test::ParseNameOrDie("com.app.a:bool/foo")); ASSERT_TRUE(result); EXPECT_EQ(SymbolState::kUndefined, result.value().entry->symbol_status.state); EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kUndefined)); EXPECT_TRUE(result.value().entry->symbol_status.allow_new); // Find the product-dependent values BinaryPrimitive* prim = test::GetValueForConfigAndProduct<BinaryPrimitive>( new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"), ""); ASSERT_THAT(prim, NotNull()); EXPECT_EQ(123u, prim->value.data); EXPECT_THAT(prim->value.data, Eq(123u)); prim = test::GetValueForConfigAndProduct<BinaryPrimitive>( new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"), "tablet"); ASSERT_THAT(prim, NotNull()); EXPECT_EQ(321u, prim->value.data); EXPECT_THAT(prim->value.data, Eq(321u)); Reference* actual_ref = test::GetValue<Reference>(new_table.get(), "com.app.a:layout/abc"); ASSERT_THAT(actual_ref, NotNull()); ASSERT_TRUE(actual_ref->name); ASSERT_TRUE(actual_ref->id); EXPECT_EQ(expected_ref.name.value(), actual_ref->name.value()); EXPECT_EQ(expected_ref.id.value(), actual_ref->id.value()); EXPECT_THAT(*actual_ref, Eq(expected_ref)); StyledString* actual_styled_str = test::GetValue<StyledString>(new_table.get(), "com.app.a:string/styled"); ASSERT_THAT(actual_styled_str, NotNull()); EXPECT_THAT(actual_styled_str->value->value, Eq("hello")); ASSERT_THAT(actual_styled_str->value->spans, SizeIs(1u)); EXPECT_THAT(*actual_styled_str->value->spans[0].name, Eq("b")); EXPECT_THAT(actual_styled_str->value->spans[0].first_char, Eq(0u)); EXPECT_THAT(actual_styled_str->value->spans[0].last_char, Eq(4u)); } TEST(TableProtoSerializer, SerializeFileHeader) { Loading Loading
tools/aapt2/Format.proto +33 −25 Original line number Diff line number Diff line Loading @@ -67,21 +67,12 @@ message CompiledFile { // Top level message representing a resource table. message ResourceTable { // The string pool containing string values (strings that actually end up in the binary ARSC // file) referenced throughout the resource table. optional StringPool string_pool = 1; // The string pool containing source paths referenced throughout the resource table. This does // not end up in the final binary ARSC file. optional StringPool source_pool = 2; // The string pool containing the names of unresolved symbols. This does not end up in the final // binary ARSC file. Unresolved symbols are just resource names that haven't had a resource ID // assigned to them, therefore can't be referenced by resource ID. optional StringPool symbol_pool = 3; optional StringPool source_pool = 1; // Resource definitions corresponding to an Android package. repeated Package packages = 4; repeated Package packages = 2; } // Defines resources for an Android package. Loading Loading @@ -202,9 +193,10 @@ message Item { optional Reference ref = 1; optional String str = 2; optional RawString raw_str = 3; optional FileReference file = 4; optional Id id = 5; optional Primitive prim = 6; optional StyledString styled_str = 4; optional FileReference file = 5; optional Id id = 6; optional Primitive prim = 7; } // A CompoundValue is an abstract type. It represents a value that is a made of other values. Loading Loading @@ -233,10 +225,8 @@ message Reference { // The resource ID (0xPPTTEEEE) of the resource being referred. optional uint32 id = 2; // If the resource ID is not resolved, the index into the symbol string pool where the name of // the reference is stored. The symbol string pool is located at the top level ResourceTable // message. optional uint32 symbol_idx = 3; // The optional resource name. optional string name = 3; // Whether this reference is referencing a private resource (@*package:type/entry). optional bool private = 4; Loading @@ -249,23 +239,41 @@ message Id { // A value that is a string. message String { // The index into the values string pool, located at the top level ResourceTable message. optional uint32 idx = 1; optional string value = 1; } // A value that is a raw string, which is unescaped/uninterpreted. This is typically used to // represent the value of a style attribute before the attribute is compiled and the set of // allowed values is known. message RawString { // The index into the values string pool, located at the top level ResourceTable message. optional uint32 idx = 1; optional string value = 1; } // A string with styling information, like html tags that specify boldness, italics, etc. message StyledString { // The raw text of the string. optional string value = 1; // A Span marks a region of the string text that is styled. message Span { // The name of the tag, and its attributes, encoded as follows: // tag_name;attr1=value1;attr2=value2;[...] optional string tag = 1; // The first character position this span applies to, in UTF-16 offset. optional uint32 first_char = 2; // The last character position this span applies to, in UTF-16 offset. optional uint32 last_char = 3; } repeated Span span = 2; } // A value that is a reference to an external entity, like an XML file or a PNG. message FileReference { // The index into the values string pool, located at the top level ResourceTable message. This // represents the path to the file within an APK (typically res/type-config/entry.ext). optional uint32 path_idx = 1; // Path to a file within the APK (typically res/type-config/entry.ext). optional string path = 1; } // A value that represents a primitive data type (float, int, boolean, etc.). Loading
tools/aapt2/proto/TableProtoDeserializer.cpp +33 −86 Original line number Diff line number Diff line Loading @@ -55,15 +55,10 @@ class ReferenceIdToNameVisitor : public ValueVisitor { class PackagePbDeserializer { public: PackagePbDeserializer(const android::ResStringPool* valuePool, const android::ResStringPool* sourcePool, const android::ResStringPool* symbolPool, const Source& source, IDiagnostics* diag) : value_pool_(valuePool), source_pool_(sourcePool), symbol_pool_(symbolPool), source_(source), diag_(diag) {} PackagePbDeserializer(const android::ResStringPool* sourcePool, const Source& source, IDiagnostics* diag) : source_pool_(sourcePool), source_(source), diag_(diag) { } public: bool DeserializeFromPb(const pb::Package& pbPackage, ResourceTable* table) { Loading @@ -87,8 +82,7 @@ class PackagePbDeserializer { for (const pb::Entry& pbEntry : pbType.entries()) { ResourceEntry* entry = type->FindOrCreateEntry(pbEntry.name()); // Deserialize the symbol status (public/private with source and // comments). // Deserialize the symbol status (public/private with source and comments). if (pbEntry.has_symbol_status()) { const pb::SymbolStatus& pbStatus = pbEntry.symbol_status(); if (pbStatus.has_source()) { Loading Loading @@ -161,8 +155,7 @@ class PackagePbDeserializer { private: std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item, const ConfigDescription& config, StringPool* pool) { const ConfigDescription& config, StringPool* pool) { if (pb_item.has_ref()) { const pb::Reference& pb_ref = pb_item.ref(); std::unique_ptr<Reference> ref = util::make_unique<Reference>(); Loading @@ -173,45 +166,32 @@ class PackagePbDeserializer { } else if (pb_item.has_prim()) { const pb::Primitive& pb_prim = pb_item.prim(); android::Res_value prim = {}; prim.dataType = static_cast<uint8_t>(pb_prim.type()); prim.data = pb_prim.data(); return util::make_unique<BinaryPrimitive>(prim); return util::make_unique<BinaryPrimitive>(static_cast<uint8_t>(pb_prim.type()), pb_prim.data()); } else if (pb_item.has_id()) { return util::make_unique<Id>(); } else if (pb_item.has_str()) { const uint32_t idx = pb_item.str().idx(); const std::string str = util::GetString(*value_pool_, idx); const android::ResStringPool_span* spans = value_pool_->styleAt(idx); if (spans && spans->name.index != android::ResStringPool_span::END) { StyleString style_str = {str}; while (spans->name.index != android::ResStringPool_span::END) { style_str.spans.push_back( Span{util::GetString(*value_pool_, spans->name.index), spans->firstChar, spans->lastChar}); spans++; } return util::make_unique<StyledString>(pool->MakeRef( style_str, StringPool::Context(StringPool::Context::kNormalPriority, config))); } return util::make_unique<String>( pool->MakeRef(str, StringPool::Context(config))); pool->MakeRef(pb_item.str().value(), StringPool::Context(config))); } else if (pb_item.has_raw_str()) { const uint32_t idx = pb_item.raw_str().idx(); const std::string str = util::GetString(*value_pool_, idx); return util::make_unique<RawString>( pool->MakeRef(str, StringPool::Context(config))); pool->MakeRef(pb_item.raw_str().value(), StringPool::Context(config))); } else if (pb_item.has_styled_str()) { const pb::StyledString& pb_str = pb_item.styled_str(); StyleString style_str{pb_str.value()}; for (const pb::StyledString::Span& pb_span : pb_str.span()) { style_str.spans.push_back(Span{pb_span.tag(), pb_span.first_char(), pb_span.last_char()}); } return util::make_unique<StyledString>(pool->MakeRef( style_str, StringPool::Context(StringPool::Context::kNormalPriority, config))); } else if (pb_item.has_file()) { const uint32_t idx = pb_item.file().path_idx(); const std::string str = util::GetString(*value_pool_, idx); return util::make_unique<FileReference>(pool->MakeRef( str, StringPool::Context(StringPool::Context::kHighPriority, config))); pb_item.file().path(), StringPool::Context(StringPool::Context::kHighPriority, config))); } else { diag_->Error(DiagMessage(source_) << "unknown item"); Loading Loading @@ -255,15 +235,13 @@ class PackagePbDeserializer { std::unique_ptr<Style> style = util::make_unique<Style>(); if (pb_style.has_parent()) { style->parent = Reference(); if (!DeserializeReferenceFromPb(pb_style.parent(), &style->parent.value())) { if (!DeserializeReferenceFromPb(pb_style.parent(), &style->parent.value())) { return {}; } if (pb_style.has_parent_source()) { Source parent_source; DeserializeSourceFromPb(pb_style.parent_source(), *source_pool_, &parent_source); DeserializeSourceFromPb(pb_style.parent_source(), *source_pool_, &parent_source); style->parent.value().SetSource(std::move(parent_source)); } } Loading Loading @@ -300,8 +278,7 @@ class PackagePbDeserializer { const pb::Array& pb_array = pb_compound_value.array(); std::unique_ptr<Array> array = util::make_unique<Array>(); for (const pb::Array_Entry& pb_entry : pb_array.entries()) { std::unique_ptr<Item> item = DeserializeItemFromPb(pb_entry.item(), config, pool); std::unique_ptr<Item> item = DeserializeItemFromPb(pb_entry.item(), config, pool); if (!item) { return {}; } Loading @@ -316,8 +293,7 @@ class PackagePbDeserializer { std::unique_ptr<Plural> plural = util::make_unique<Plural>(); for (const pb::Plural_Entry& pb_entry : pb_plural.entries()) { size_t pluralIdx = DeserializePluralEnumFromPb(pb_entry.arity()); plural->values[pluralIdx] = DeserializeItemFromPb(pb_entry.item(), config, pool); plural->values[pluralIdx] = DeserializeItemFromPb(pb_entry.item(), config, pool); if (!plural->values[pluralIdx]) { return {}; } Loading Loading @@ -350,11 +326,10 @@ class PackagePbDeserializer { out_ref->id = ResourceId(pb_ref.id()); } if (pb_ref.has_symbol_idx()) { const std::string str_symbol = util::GetString(*symbol_pool_, pb_ref.symbol_idx()); if (pb_ref.has_name()) { ResourceNameRef name_ref; if (!ResourceUtils::ParseResourceName(str_symbol, &name_ref, nullptr)) { diag_->Error(DiagMessage(source_) << "invalid reference name '" << str_symbol << "'"); if (!ResourceUtils::ParseResourceName(pb_ref.name(), &name_ref, nullptr)) { diag_->Error(DiagMessage(source_) << "invalid reference name '" << pb_ref.name() << "'"); return false; } Loading @@ -377,41 +352,24 @@ class PackagePbDeserializer { } private: const android::ResStringPool* value_pool_; const android::ResStringPool* source_pool_; const android::ResStringPool* symbol_pool_; const Source source_; IDiagnostics* diag_; }; } // namespace std::unique_ptr<ResourceTable> DeserializeTableFromPb( const pb::ResourceTable& pb_table, const Source& source, IDiagnostics* diag) { // We import the android namespace because on Windows NO_ERROR is a macro, not // an enum, which std::unique_ptr<ResourceTable> DeserializeTableFromPb(const pb::ResourceTable& pb_table, const Source& source, IDiagnostics* diag) { // We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which // causes errors when qualifying it with android:: using namespace android; std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>(); if (!pb_table.has_string_pool()) { diag->Error(DiagMessage(source) << "no string pool found"); return {}; } ResStringPool value_pool; status_t result = value_pool.setTo(pb_table.string_pool().data().data(), pb_table.string_pool().data().size()); if (result != NO_ERROR) { diag->Error(DiagMessage(source) << "invalid string pool"); return {}; } ResStringPool source_pool; if (pb_table.has_source_pool()) { result = source_pool.setTo(pb_table.source_pool().data().data(), status_t result = source_pool.setTo(pb_table.source_pool().data().data(), pb_table.source_pool().data().size()); if (result != NO_ERROR) { diag->Error(DiagMessage(source) << "invalid source pool"); Loading @@ -419,18 +377,7 @@ std::unique_ptr<ResourceTable> DeserializeTableFromPb( } } ResStringPool symbol_pool; if (pb_table.has_symbol_pool()) { result = symbol_pool.setTo(pb_table.symbol_pool().data().data(), pb_table.symbol_pool().data().size()); if (result != NO_ERROR) { diag->Error(DiagMessage(source) << "invalid symbol pool"); return {}; } } PackagePbDeserializer package_pb_deserializer(&value_pool, &source_pool, &symbol_pool, source, diag); PackagePbDeserializer package_pb_deserializer(&source_pool, source, diag); for (const pb::Package& pb_package : pb_table.packages()) { if (!package_pb_deserializer.DeserializeFromPb(pb_package, table.get())) { return {}; Loading
tools/aapt2/proto/TableProtoSerializer.cpp +36 −43 Original line number Diff line number Diff line Loading @@ -24,9 +24,9 @@ #include "android-base/logging.h" using google::protobuf::io::CodedOutputStream; using google::protobuf::io::CodedInputStream; using google::protobuf::io::ZeroCopyOutputStream; using ::google::protobuf::io::CodedInputStream; using ::google::protobuf::io::CodedOutputStream; using ::google::protobuf::io::ZeroCopyOutputStream; namespace aapt { Loading @@ -36,46 +36,46 @@ class PbSerializerVisitor : public RawValueVisitor { public: using RawValueVisitor::Visit; /** * Constructor to use when expecting to serialize any value. */ PbSerializerVisitor(StringPool* source_pool, StringPool* symbol_pool, pb::Value* out_pb_value) : source_pool_(source_pool), symbol_pool_(symbol_pool), out_pb_value_(out_pb_value), out_pb_item_(nullptr) {} /** * Constructor to use when expecting to serialize an Item. */ PbSerializerVisitor(StringPool* sourcePool, StringPool* symbolPool, pb::Item* outPbItem) : source_pool_(sourcePool), symbol_pool_(symbolPool), out_pb_value_(nullptr), out_pb_item_(outPbItem) {} // Constructor to use when expecting to serialize any value. PbSerializerVisitor(StringPool* source_pool, pb::Value* out_pb_value) : source_pool_(source_pool), out_pb_value_(out_pb_value), out_pb_item_(nullptr) { } // Constructor to use when expecting to serialize an Item. PbSerializerVisitor(StringPool* sourcePool, pb::Item* outPbItem) : source_pool_(sourcePool), out_pb_value_(nullptr), out_pb_item_(outPbItem) { } void Visit(Reference* ref) override { SerializeReferenceToPb(*ref, pb_item()->mutable_ref()); } void Visit(String* str) override { pb_item()->mutable_str()->set_idx(str->value.index()); pb_item()->mutable_str()->set_value(*str->value); } void Visit(RawString* str) override { pb_item()->mutable_raw_str()->set_value(*str->value); } void Visit(StyledString* str) override { pb_item()->mutable_str()->set_idx(str->value.index()); pb::StyledString* pb_str = pb_item()->mutable_styled_str(); pb_str->set_value(str->value->value); for (const StringPool::Span& span : str->value->spans) { pb::StyledString::Span* pb_span = pb_str->add_span(); pb_span->set_tag(*span.name); pb_span->set_first_char(span.first_char); pb_span->set_last_char(span.last_char); } } void Visit(FileReference* file) override { pb_item()->mutable_file()->set_path_idx(file->path.index()); pb_item()->mutable_file()->set_path(*file->path); } void Visit(Id* id) override { pb_item()->mutable_id(); } void Visit(RawString* raw_str) override { pb_item()->mutable_raw_str()->set_idx(raw_str->value.index()); void Visit(Id* /*id*/) override { pb_item()->mutable_id(); } void Visit(BinaryPrimitive* prim) override { Loading Loading @@ -119,7 +119,7 @@ class PbSerializerVisitor : public RawValueVisitor { pb::Item* pb_item = pb_entry->mutable_item(); SerializeItemCommonToPb(entry.key, pb_entry); PbSerializerVisitor sub_visitor(source_pool_, symbol_pool_, pb_item); PbSerializerVisitor sub_visitor(source_pool_, pb_item); entry.value->Accept(&sub_visitor); } } Loading @@ -138,8 +138,7 @@ class PbSerializerVisitor : public RawValueVisitor { for (auto& value : array->items) { pb::Array_Entry* pb_entry = pb_array->add_entries(); SerializeItemCommonToPb(*value, pb_entry); PbSerializerVisitor sub_visitor(source_pool_, symbol_pool_, pb_entry->mutable_item()); PbSerializerVisitor sub_visitor(source_pool_, pb_entry->mutable_item()); value->Accept(&sub_visitor); } } Loading @@ -157,7 +156,7 @@ class PbSerializerVisitor : public RawValueVisitor { pb_entry->set_arity(SerializePluralEnumToPb(i)); pb::Item* pb_element = pb_entry->mutable_item(); SerializeItemCommonToPb(*plural->values[i], pb_entry); PbSerializerVisitor sub_visitor(source_pool_, symbol_pool_, pb_element); PbSerializerVisitor sub_visitor(source_pool_, pb_element); plural->values[i]->Accept(&sub_visitor); } } Loading @@ -179,8 +178,7 @@ class PbSerializerVisitor : public RawValueVisitor { template <typename T> void SerializeItemCommonToPb(const Item& item, T* pb_item) { SerializeSourceToPb(item.GetSource(), source_pool_, pb_item->mutable_source()); SerializeSourceToPb(item.GetSource(), source_pool_, pb_item->mutable_source()); if (!item.GetComment().empty()) { pb_item->set_comment(item.GetComment()); } Loading @@ -192,8 +190,7 @@ class PbSerializerVisitor : public RawValueVisitor { } if (ref.name) { StringPool::Ref symbol_ref = symbol_pool_->MakeRef(ref.name.value().ToString()); pb_ref->set_symbol_idx(static_cast<uint32_t>(symbol_ref.index())); pb_ref->set_name(ref.name.value().ToString()); } pb_ref->set_private_(ref.private_reference); Loading @@ -201,7 +198,6 @@ class PbSerializerVisitor : public RawValueVisitor { } StringPool* source_pool_; StringPool* symbol_pool_; pb::Value* out_pb_value_; pb::Item* out_pb_item_; }; Loading @@ -220,9 +216,7 @@ std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) { }); auto pb_table = util::make_unique<pb::ResourceTable>(); SerializeStringPoolToPb(table->string_pool, pb_table->mutable_string_pool()); StringPool source_pool, symbol_pool; StringPool source_pool; for (auto& package : table->packages) { pb::Package* pb_package = pb_table->add_packages(); Loading Loading @@ -270,7 +264,7 @@ std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) { pb_value->set_weak(true); } PbSerializerVisitor visitor(&source_pool, &symbol_pool, pb_value); PbSerializerVisitor visitor(&source_pool, pb_value); config_value->value->Accept(&visitor); } } Loading @@ -278,7 +272,6 @@ std::unique_ptr<pb::ResourceTable> SerializeTableToPb(ResourceTable* table) { } SerializeStringPoolToPb(source_pool, pb_table->mutable_source_pool()); SerializeStringPoolToPb(symbol_pool, pb_table->mutable_symbol_pool()); return pb_table; } Loading
tools/aapt2/proto/TableProtoSerializer_test.cpp +34 −15 Original line number Diff line number Diff line Loading @@ -20,7 +20,9 @@ #include "test/Test.h" using ::google::protobuf::io::StringOutputStream; using ::testing::Eq; using ::testing::NotNull; using ::testing::SizeIs; namespace aapt { Loading @@ -38,12 +40,12 @@ TEST(TableProtoSerializer, SerializeSinglePackage) { Symbol public_symbol; public_symbol.state = SymbolState::kPublic; ASSERT_TRUE(table->SetSymbolState( test::ParseNameOrDie("com.app.a:layout/main"), ResourceId(0x7f020000), public_symbol, context->GetDiagnostics())); ASSERT_TRUE(table->SetSymbolState(test::ParseNameOrDie("com.app.a:layout/main"), ResourceId(0x7f020000), public_symbol, context->GetDiagnostics())); Id* id = test::GetValue<Id>(table.get(), "com.app.a:id/foo"); ASSERT_NE(nullptr, id); ASSERT_THAT(id, NotNull()); // Make a plural. std::unique_ptr<Plural> plural = util::make_unique<Plural>(); Loading @@ -52,6 +54,15 @@ TEST(TableProtoSerializer, SerializeSinglePackage) { ConfigDescription{}, {}, std::move(plural), context->GetDiagnostics())); // Make a styled string. StyleString style_string; style_string.str = "hello"; style_string.spans.push_back(Span{"b", 0u, 4u}); ASSERT_TRUE( table->AddResource(test::ParseNameOrDie("com.app.a:string/styled"), ConfigDescription{}, {}, util::make_unique<StyledString>(table->string_pool.MakeRef(style_string)), context->GetDiagnostics())); // Make a resource with different products. ASSERT_TRUE(table->AddResource( test::ParseNameOrDie("com.app.a:integer/one"), Loading @@ -65,9 +76,8 @@ TEST(TableProtoSerializer, SerializeSinglePackage) { 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 get serialized. // The reference should point to a resource outside of this table to test that both name and id // get serialized. Reference expected_ref; expected_ref.name = test::ParseNameOrDie("android:layout/main"); expected_ref.id = ResourceId(0x01020000); Loading @@ -85,36 +95,45 @@ TEST(TableProtoSerializer, SerializeSinglePackage) { Id* new_id = test::GetValue<Id>(new_table.get(), "com.app.a:id/foo"); ASSERT_THAT(new_id, NotNull()); EXPECT_EQ(id->IsWeak(), new_id->IsWeak()); EXPECT_THAT(new_id->IsWeak(), Eq(id->IsWeak())); Maybe<ResourceTable::SearchResult> result = new_table->FindResource(test::ParseNameOrDie("com.app.a:layout/main")); ASSERT_TRUE(result); EXPECT_EQ(SymbolState::kPublic, result.value().type->symbol_status.state); EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbol_status.state); EXPECT_THAT(result.value().type->symbol_status.state, Eq(SymbolState::kPublic)); EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kPublic)); result = new_table->FindResource(test::ParseNameOrDie("com.app.a:bool/foo")); ASSERT_TRUE(result); EXPECT_EQ(SymbolState::kUndefined, result.value().entry->symbol_status.state); EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kUndefined)); EXPECT_TRUE(result.value().entry->symbol_status.allow_new); // Find the product-dependent values BinaryPrimitive* prim = test::GetValueForConfigAndProduct<BinaryPrimitive>( new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"), ""); ASSERT_THAT(prim, NotNull()); EXPECT_EQ(123u, prim->value.data); EXPECT_THAT(prim->value.data, Eq(123u)); prim = test::GetValueForConfigAndProduct<BinaryPrimitive>( new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"), "tablet"); ASSERT_THAT(prim, NotNull()); EXPECT_EQ(321u, prim->value.data); EXPECT_THAT(prim->value.data, Eq(321u)); Reference* actual_ref = test::GetValue<Reference>(new_table.get(), "com.app.a:layout/abc"); ASSERT_THAT(actual_ref, NotNull()); ASSERT_TRUE(actual_ref->name); ASSERT_TRUE(actual_ref->id); EXPECT_EQ(expected_ref.name.value(), actual_ref->name.value()); EXPECT_EQ(expected_ref.id.value(), actual_ref->id.value()); EXPECT_THAT(*actual_ref, Eq(expected_ref)); StyledString* actual_styled_str = test::GetValue<StyledString>(new_table.get(), "com.app.a:string/styled"); ASSERT_THAT(actual_styled_str, NotNull()); EXPECT_THAT(actual_styled_str->value->value, Eq("hello")); ASSERT_THAT(actual_styled_str->value->spans, SizeIs(1u)); EXPECT_THAT(*actual_styled_str->value->spans[0].name, Eq("b")); EXPECT_THAT(actual_styled_str->value->spans[0].first_char, Eq(0u)); EXPECT_THAT(actual_styled_str->value->spans[0].last_char, Eq(4u)); } TEST(TableProtoSerializer, SerializeFileHeader) { Loading