From f0c5ff46edfe99c5038558f7b89186e476eb9780 Mon Sep 17 00:00:00 2001 From: Iurii Makhno Date: Tue, 22 Feb 2022 13:31:02 +0000 Subject: Switch ResourceTable to use ResourceNamedType instead of ResourceType. DD: go/custom-resource-types-in-aapt2 Bug: b/215108200 Test: existing Change-Id: If9ddcd529dff66c8ec4238f56ab64391c2f44c28 --- tools/aapt2/Debug.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tools/aapt2/Debug.cpp') diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp index f47d66ea5e87..41896f622228 100644 --- a/tools/aapt2/Debug.cpp +++ b/tools/aapt2/Debug.cpp @@ -273,7 +273,7 @@ void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions& printer->Indent(); for (const auto& type : package.types) { printer->Print("type "); - printer->Print(to_string(type.type)); + printer->Print(type.named_type.to_string()); if (type.id) { printer->Print(StringPrintf(" id=%02x", type.id.value())); } @@ -287,7 +287,7 @@ void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions& printer->Print(" "); // Write the name without the package (this is obvious and too verbose). - printer->Print(to_string(type.type)); + printer->Print(type.named_type.to_string()); printer->Print("/"); printer->Print(entry.name); @@ -547,7 +547,7 @@ void Debug::DumpOverlayable(const ResourceTable& table, text::Printer* printer) 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()); + StringPrintf("%s/%s", type->named_type.to_string().data(), entry->name.c_str()); items.push_back(DumpOverlayableEntry{overlayable_section, policy_subsection, value}); } } -- cgit v1.2.3-59-g8ed1b From 56f36e8f2fa808128bc68d4ae9adad9e65b90367 Mon Sep 17 00:00:00 2001 From: Jeremy Meyer Date: Fri, 20 May 2022 20:35:42 +0000 Subject: Move StringPool to libandroidfw Test: verified affected tests pass Bug: 232940948 Change-Id: I22089893d7e5013f759c39ce190bec07fa6435db --- libs/androidfw/Android.bp | 4 + libs/androidfw/AssetManager2.cpp | 4 + libs/androidfw/BigBuffer.cpp | 87 ++++ libs/androidfw/StringPool.cpp | 508 ++++++++++++++++++++ libs/androidfw/Util.cpp | 124 +++++ libs/androidfw/include/androidfw/BigBuffer.h | 192 ++++++++ libs/androidfw/include/androidfw/IDiagnostics.h | 130 ++++++ libs/androidfw/include/androidfw/Source.h | 91 ++++ libs/androidfw/include/androidfw/StringPool.h | 228 +++++++++ libs/androidfw/include/androidfw/Util.h | 43 +- libs/androidfw/tests/BigBuffer_test.cpp | 101 ++++ libs/androidfw/tests/StringPool_test.cpp | 388 ++++++++++++++++ tools/aapt2/Android.bp | 2 - tools/aapt2/Debug.cpp | 105 +++-- tools/aapt2/Debug.h | 3 +- tools/aapt2/Diagnostics.h | 100 +--- tools/aapt2/LoadedApk.cpp | 62 +-- tools/aapt2/LoadedApk.h | 17 +- tools/aapt2/Main.cpp | 13 +- tools/aapt2/Resource.h | 4 +- tools/aapt2/ResourceParser.cpp | 303 ++++++------ tools/aapt2/ResourceParser.h | 23 +- tools/aapt2/ResourceParser_test.cpp | 6 +- tools/aapt2/ResourceTable.cpp | 24 +- tools/aapt2/ResourceTable.h | 40 +- tools/aapt2/ResourceTable_test.cpp | 15 +- tools/aapt2/ResourceUtils.cpp | 48 +- tools/aapt2/ResourceUtils.h | 13 +- tools/aapt2/ResourceUtils_test.cpp | 2 +- tools/aapt2/ResourceValues.cpp | 37 +- tools/aapt2/ResourceValues.h | 41 +- tools/aapt2/ResourceValues_test.cpp | 22 +- tools/aapt2/Resources.proto | 2 +- tools/aapt2/Source.h | 89 ---- tools/aapt2/StringPool.cpp | 511 --------------------- tools/aapt2/StringPool.h | 227 --------- tools/aapt2/StringPool_test.cpp | 388 ---------------- tools/aapt2/ValueTransformer.h | 8 +- tools/aapt2/ValueTransformer_inline.h | 4 +- tools/aapt2/cmd/ApkInfo.cpp | 8 +- tools/aapt2/cmd/ApkInfo.h | 6 +- tools/aapt2/cmd/ApkInfo_test.cpp | 9 +- tools/aapt2/cmd/Compile.cpp | 134 +++--- tools/aapt2/cmd/Compile.h | 16 +- tools/aapt2/cmd/Compile_test.cpp | 4 +- tools/aapt2/cmd/Convert.cpp | 69 +-- tools/aapt2/cmd/Convert_test.cpp | 3 +- tools/aapt2/cmd/Diff.cpp | 10 +- tools/aapt2/cmd/Dump.cpp | 52 +-- tools/aapt2/cmd/Dump.h | 50 +- tools/aapt2/cmd/Dump_test.cpp | 9 +- tools/aapt2/cmd/Link.cpp | 351 +++++++------- tools/aapt2/cmd/Link.h | 9 +- tools/aapt2/cmd/Link_test.cpp | 14 +- tools/aapt2/cmd/Optimize.cpp | 61 +-- tools/aapt2/cmd/Optimize_test.cpp | 2 +- tools/aapt2/cmd/Util.cpp | 50 +- tools/aapt2/cmd/Util.h | 15 +- tools/aapt2/cmd/Util_test.cpp | 4 +- tools/aapt2/compile/IdAssigner.cpp | 33 +- tools/aapt2/compile/InlineXmlFormatParser.cpp | 18 +- tools/aapt2/compile/Png.cpp | 84 ++-- tools/aapt2/compile/Png.h | 17 +- tools/aapt2/compile/PngCrunch.cpp | 36 +- tools/aapt2/compile/PseudolocaleGenerator.cpp | 30 +- tools/aapt2/compile/PseudolocaleGenerator.h | 7 +- tools/aapt2/compile/PseudolocaleGenerator_test.cpp | 40 +- tools/aapt2/compile/Pseudolocalizer.h | 5 +- tools/aapt2/compile/XmlIdCollector.cpp | 13 +- tools/aapt2/configuration/ConfigurationParser.cpp | 150 +++--- tools/aapt2/configuration/ConfigurationParser.h | 12 +- .../configuration/ConfigurationParser.internal.h | 32 +- tools/aapt2/dump/DumpManifest.cpp | 12 +- tools/aapt2/dump/DumpManifest.h | 8 +- tools/aapt2/format/Archive.cpp | 12 +- tools/aapt2/format/Archive.h | 9 +- tools/aapt2/format/Container.h | 7 +- tools/aapt2/format/binary/BinaryResourceParser.cpp | 197 ++++---- tools/aapt2/format/binary/BinaryResourceParser.h | 21 +- tools/aapt2/format/binary/ChunkWriter.h | 19 +- tools/aapt2/format/binary/ResChunkPullParser.cpp | 18 +- tools/aapt2/format/binary/ResChunkPullParser.h | 9 +- tools/aapt2/format/binary/TableFlattener.cpp | 165 +++---- tools/aapt2/format/binary/TableFlattener.h | 9 +- tools/aapt2/format/binary/TableFlattener_test.cpp | 10 +- tools/aapt2/format/binary/XmlFlattener.cpp | 66 +-- tools/aapt2/format/binary/XmlFlattener.h | 7 +- tools/aapt2/format/binary/XmlFlattener_test.cpp | 7 +- tools/aapt2/format/proto/ProtoDeserialize.cpp | 48 +- tools/aapt2/format/proto/ProtoDeserialize.h | 21 +- tools/aapt2/format/proto/ProtoSerialize.cpp | 32 +- tools/aapt2/format/proto/ProtoSerialize.h | 15 +- tools/aapt2/format/proto/ProtoSerialize_test.cpp | 17 +- tools/aapt2/io/BigBufferStream.h | 12 +- tools/aapt2/io/File.h | 7 +- tools/aapt2/io/FileSystem.cpp | 12 +- tools/aapt2/io/FileSystem.h | 6 +- tools/aapt2/io/Util.cpp | 22 +- tools/aapt2/io/ZipArchive.cpp | 19 +- tools/aapt2/io/ZipArchive.h | 6 +- tools/aapt2/java/ManifestClassGenerator.cpp | 24 +- tools/aapt2/java/ManifestClassGenerator.h | 5 +- tools/aapt2/java/ProguardRules.h | 7 +- tools/aapt2/link/Linkers.h | 7 +- tools/aapt2/link/ManifestFixer.cpp | 91 ++-- tools/aapt2/link/ManifestFixer.h | 2 +- tools/aapt2/link/NoDefaultResourceRemover.cpp | 10 +- tools/aapt2/link/ProductFilter.cpp | 28 +- tools/aapt2/link/ProductFilter_test.cpp | 40 +- tools/aapt2/link/ReferenceLinker.cpp | 34 +- tools/aapt2/link/ReferenceLinker.h | 5 +- tools/aapt2/link/ResourceExcluder.cpp | 9 +- tools/aapt2/link/TableMerger.cpp | 74 +-- tools/aapt2/link/TableMerger.h | 10 +- tools/aapt2/link/TableMerger_test.cpp | 2 +- tools/aapt2/link/XmlCompatVersioner.cpp | 10 +- tools/aapt2/link/XmlCompatVersioner.h | 6 +- tools/aapt2/link/XmlReferenceLinker.cpp | 18 +- tools/aapt2/optimize/MultiApkGenerator.cpp | 61 +-- tools/aapt2/optimize/MultiApkGenerator.h | 10 +- tools/aapt2/optimize/ResourceDeduper.cpp | 11 +- tools/aapt2/process/IResourceTableConsumer.h | 6 +- tools/aapt2/process/SymbolTable_test.cpp | 2 +- tools/aapt2/split/TableSplitter.cpp | 12 +- tools/aapt2/test/Builders.cpp | 6 +- tools/aapt2/test/Builders.h | 4 +- tools/aapt2/test/Common.cpp | 6 +- tools/aapt2/test/Common.h | 6 +- tools/aapt2/test/Context.h | 6 +- tools/aapt2/test/Fixture.cpp | 16 +- tools/aapt2/test/Fixture.h | 8 +- tools/aapt2/util/BigBuffer.cpp | 87 ---- tools/aapt2/util/BigBuffer.h | 189 -------- tools/aapt2/util/BigBuffer_test.cpp | 100 ---- tools/aapt2/util/Files.cpp | 10 +- tools/aapt2/util/Files.h | 12 +- tools/aapt2/util/Util.cpp | 131 +----- tools/aapt2/util/Util.h | 47 +- tools/aapt2/xml/XmlActionExecutor.cpp | 22 +- tools/aapt2/xml/XmlActionExecutor.h | 11 +- tools/aapt2/xml/XmlDom.cpp | 30 +- tools/aapt2/xml/XmlDom.h | 10 +- tools/aapt2/xml/XmlDom_test.cpp | 6 +- 143 files changed, 3739 insertions(+), 3607 deletions(-) create mode 100644 libs/androidfw/BigBuffer.cpp create mode 100644 libs/androidfw/StringPool.cpp create mode 100644 libs/androidfw/include/androidfw/BigBuffer.h create mode 100644 libs/androidfw/include/androidfw/IDiagnostics.h create mode 100644 libs/androidfw/include/androidfw/Source.h create mode 100644 libs/androidfw/include/androidfw/StringPool.h create mode 100644 libs/androidfw/tests/BigBuffer_test.cpp create mode 100644 libs/androidfw/tests/StringPool_test.cpp delete mode 100644 tools/aapt2/Source.h delete mode 100644 tools/aapt2/StringPool.cpp delete mode 100644 tools/aapt2/StringPool.h delete mode 100644 tools/aapt2/StringPool_test.cpp delete mode 100644 tools/aapt2/util/BigBuffer.cpp delete mode 100644 tools/aapt2/util/BigBuffer.h delete mode 100644 tools/aapt2/util/BigBuffer_test.cpp (limited to 'tools/aapt2/Debug.cpp') diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 8a379d581532..779c4b75efc4 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -61,6 +61,7 @@ cc_library { "AssetManager2.cpp", "AssetsProvider.cpp", "AttributeResolution.cpp", + "BigBuffer.cpp", "ChunkIterator.cpp", "ConfigDescription.cpp", "Idmap.cpp", @@ -73,6 +74,7 @@ cc_library { "ResourceTypes.cpp", "ResourceUtils.cpp", "StreamingZipInflater.cpp", + "StringPool.cpp", "TypeWrappers.cpp", "Util.cpp", "ZipFileRO.cpp", @@ -162,6 +164,7 @@ cc_test { "tests/AssetManager2_test.cpp", "tests/AttributeFinder_test.cpp", "tests/AttributeResolution_test.cpp", + "tests/BigBuffer_test.cpp", "tests/ByteBucketArray_test.cpp", "tests/Config_test.cpp", "tests/ConfigDescription_test.cpp", @@ -174,6 +177,7 @@ cc_test { "tests/ResTable_test.cpp", "tests/Split_test.cpp", "tests/StringPiece_test.cpp", + "tests/StringPool_test.cpp", "tests/Theme_test.cpp", "tests/TypeWrappers_test.cpp", "tests/ZipUtils_test.cpp", diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 136fc6ca4e2a..39c7d198fe5b 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -601,6 +601,10 @@ base::expected AssetManager2::FindEntry( return base::unexpected(result.error()); } + if (type_idx == 0x1c) { + LOG(ERROR) << base::StringPrintf("foobar first result %s", result->package_name->c_str()); + } + bool overlaid = false; if (!stop_at_first_match && !ignore_configuration && !apk_assets_[result->cookie]->IsLoader()) { for (const auto& id_map : package_group.overlays_) { diff --git a/libs/androidfw/BigBuffer.cpp b/libs/androidfw/BigBuffer.cpp new file mode 100644 index 000000000000..bedfc49a1b0d --- /dev/null +++ b/libs/androidfw/BigBuffer.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2015 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 + +#include +#include +#include + +#include "android-base/logging.h" + +namespace android { + +void* BigBuffer::NextBlockImpl(size_t size) { + if (!blocks_.empty()) { + Block& block = blocks_.back(); + if (block.block_size_ - block.size >= size) { + void* out_buffer = block.buffer.get() + block.size; + block.size += size; + size_ += size; + return out_buffer; + } + } + + const size_t actual_size = std::max(block_size_, size); + + Block block = {}; + + // Zero-allocate the block's buffer. + block.buffer = std::unique_ptr(new uint8_t[actual_size]()); + CHECK(block.buffer); + + block.size = size; + block.block_size_ = actual_size; + + blocks_.push_back(std::move(block)); + size_ += size; + return blocks_.back().buffer.get(); +} + +void* BigBuffer::NextBlock(size_t* out_size) { + if (!blocks_.empty()) { + Block& block = blocks_.back(); + if (block.size != block.block_size_) { + void* out_buffer = block.buffer.get() + block.size; + size_t size = block.block_size_ - block.size; + block.size = block.block_size_; + size_ += size; + *out_size = size; + return out_buffer; + } + } + + // Zero-allocate the block's buffer. + Block block = {}; + block.buffer = std::unique_ptr(new uint8_t[block_size_]()); + CHECK(block.buffer); + block.size = block_size_; + block.block_size_ = block_size_; + blocks_.push_back(std::move(block)); + size_ += block_size_; + *out_size = block_size_; + return blocks_.back().buffer.get(); +} + +std::string BigBuffer::to_string() const { + std::string result; + for (const Block& block : blocks_) { + result.append(block.buffer.get(), block.buffer.get() + block.size); + } + return result; +} + +} // namespace android diff --git a/libs/androidfw/StringPool.cpp b/libs/androidfw/StringPool.cpp new file mode 100644 index 000000000000..b59e906f6281 --- /dev/null +++ b/libs/androidfw/StringPool.cpp @@ -0,0 +1,508 @@ +/* + * Copyright (C) 2015 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 +#include + +#include +#include +#include + +#include "android-base/logging.h" +#include "androidfw/ResourceTypes.h" +#include "androidfw/StringPiece.h" +#include "androidfw/Util.h" + +using ::android::StringPiece; + +namespace android { + +StringPool::Ref::Ref() : entry_(nullptr) { +} + +StringPool::Ref::Ref(const StringPool::Ref& rhs) : entry_(rhs.entry_) { + if (entry_ != nullptr) { + entry_->ref_++; + } +} + +StringPool::Ref::Ref(StringPool::Entry* entry) : entry_(entry) { + if (entry_ != nullptr) { + entry_->ref_++; + } +} + +StringPool::Ref::~Ref() { + if (entry_ != nullptr) { + entry_->ref_--; + } +} + +StringPool::Ref& StringPool::Ref::operator=(const StringPool::Ref& rhs) { + if (rhs.entry_ != nullptr) { + rhs.entry_->ref_++; + } + + if (entry_ != nullptr) { + entry_->ref_--; + } + entry_ = rhs.entry_; + return *this; +} + +bool StringPool::Ref::operator==(const Ref& rhs) const { + return entry_->value == rhs.entry_->value; +} + +bool StringPool::Ref::operator!=(const Ref& rhs) const { + return entry_->value != rhs.entry_->value; +} + +const std::string* StringPool::Ref::operator->() const { + return &entry_->value; +} + +const std::string& StringPool::Ref::operator*() const { + return entry_->value; +} + +size_t StringPool::Ref::index() const { + // Account for the styles, which *always* come first. + return entry_->pool_->styles_.size() + entry_->index_; +} + +const StringPool::Context& StringPool::Ref::GetContext() const { + return entry_->context; +} + +StringPool::StyleRef::StyleRef() : entry_(nullptr) { +} + +StringPool::StyleRef::StyleRef(const StringPool::StyleRef& rhs) : entry_(rhs.entry_) { + if (entry_ != nullptr) { + entry_->ref_++; + } +} + +StringPool::StyleRef::StyleRef(StringPool::StyleEntry* entry) : entry_(entry) { + if (entry_ != nullptr) { + entry_->ref_++; + } +} + +StringPool::StyleRef::~StyleRef() { + if (entry_ != nullptr) { + entry_->ref_--; + } +} + +StringPool::StyleRef& StringPool::StyleRef::operator=(const StringPool::StyleRef& rhs) { + if (rhs.entry_ != nullptr) { + rhs.entry_->ref_++; + } + + if (entry_ != nullptr) { + entry_->ref_--; + } + entry_ = rhs.entry_; + return *this; +} + +bool StringPool::StyleRef::operator==(const StyleRef& rhs) const { + if (entry_->value != rhs.entry_->value) { + return false; + } + + if (entry_->spans.size() != rhs.entry_->spans.size()) { + return false; + } + + auto rhs_iter = rhs.entry_->spans.begin(); + for (const Span& span : entry_->spans) { + const Span& rhs_span = *rhs_iter; + if (span.first_char != rhs_span.first_char || span.last_char != rhs_span.last_char || + span.name != rhs_span.name) { + return false; + } + } + return true; +} + +bool StringPool::StyleRef::operator!=(const StyleRef& rhs) const { + return !operator==(rhs); +} + +const StringPool::StyleEntry* StringPool::StyleRef::operator->() const { + return entry_; +} + +const StringPool::StyleEntry& StringPool::StyleRef::operator*() const { + return *entry_; +} + +size_t StringPool::StyleRef::index() const { + return entry_->index_; +} + +const StringPool::Context& StringPool::StyleRef::GetContext() const { + return entry_->context; +} + +StringPool::Ref StringPool::MakeRef(const StringPiece& str) { + return MakeRefImpl(str, Context{}, true); +} + +StringPool::Ref StringPool::MakeRef(const StringPiece& str, const Context& context) { + return MakeRefImpl(str, context, true); +} + +StringPool::Ref StringPool::MakeRefImpl(const StringPiece& str, const Context& context, + bool unique) { + if (unique) { + auto range = indexed_strings_.equal_range(str); + for (auto iter = range.first; iter != range.second; ++iter) { + if (context.priority == iter->second->context.priority) { + return Ref(iter->second); + } + } + } + + std::unique_ptr entry(new Entry()); + entry->value = str.to_string(); + entry->context = context; + entry->index_ = strings_.size(); + entry->ref_ = 0; + entry->pool_ = this; + + Entry* borrow = entry.get(); + strings_.emplace_back(std::move(entry)); + indexed_strings_.insert(std::make_pair(StringPiece(borrow->value), borrow)); + return Ref(borrow); +} + +StringPool::Ref StringPool::MakeRef(const Ref& ref) { + if (ref.entry_->pool_ == this) { + return ref; + } + return MakeRef(ref.entry_->value, ref.entry_->context); +} + +StringPool::StyleRef StringPool::MakeRef(const StyleString& str) { + return MakeRef(str, Context{}); +} + +StringPool::StyleRef StringPool::MakeRef(const StyleString& str, const Context& context) { + std::unique_ptr entry(new StyleEntry()); + entry->value = str.str; + entry->context = context; + entry->index_ = styles_.size(); + entry->ref_ = 0; + for (const android::Span& span : str.spans) { + entry->spans.emplace_back(Span{MakeRef(span.name), span.first_char, span.last_char}); + } + + StyleEntry* borrow = entry.get(); + styles_.emplace_back(std::move(entry)); + return StyleRef(borrow); +} + +StringPool::StyleRef StringPool::MakeRef(const StyleRef& ref) { + std::unique_ptr entry(new StyleEntry()); + entry->value = ref.entry_->value; + entry->context = ref.entry_->context; + entry->index_ = styles_.size(); + entry->ref_ = 0; + for (const Span& span : ref.entry_->spans) { + entry->spans.emplace_back(Span{MakeRef(*span.name), span.first_char, span.last_char}); + } + + StyleEntry* borrow = entry.get(); + styles_.emplace_back(std::move(entry)); + return StyleRef(borrow); +} + +void StringPool::ReAssignIndices() { + // Assign the style indices. + const size_t style_len = styles_.size(); + for (size_t index = 0; index < style_len; index++) { + styles_[index]->index_ = index; + } + + // Assign the string indices. + const size_t string_len = strings_.size(); + for (size_t index = 0; index < string_len; index++) { + strings_[index]->index_ = index; + } +} + +void StringPool::Merge(StringPool&& pool) { + // First, change the owning pool for the incoming strings. + for (std::unique_ptr& entry : pool.strings_) { + entry->pool_ = this; + } + + // Now move the styles, strings, and indices over. + std::move(pool.styles_.begin(), pool.styles_.end(), std::back_inserter(styles_)); + pool.styles_.clear(); + std::move(pool.strings_.begin(), pool.strings_.end(), std::back_inserter(strings_)); + pool.strings_.clear(); + indexed_strings_.insert(pool.indexed_strings_.begin(), pool.indexed_strings_.end()); + pool.indexed_strings_.clear(); + + ReAssignIndices(); +} + +void StringPool::HintWillAdd(size_t string_count, size_t style_count) { + strings_.reserve(strings_.size() + string_count); + styles_.reserve(styles_.size() + style_count); +} + +void StringPool::Prune() { + const auto iter_end = indexed_strings_.end(); + auto index_iter = indexed_strings_.begin(); + while (index_iter != iter_end) { + if (index_iter->second->ref_ <= 0) { + index_iter = indexed_strings_.erase(index_iter); + } else { + ++index_iter; + } + } + + auto end_iter2 = + std::remove_if(strings_.begin(), strings_.end(), + [](const std::unique_ptr& entry) -> bool { return entry->ref_ <= 0; }); + auto end_iter3 = std::remove_if( + styles_.begin(), styles_.end(), + [](const std::unique_ptr& entry) -> bool { return entry->ref_ <= 0; }); + + // Remove the entries at the end or else we'll be accessing a deleted string from the StyleEntry. + strings_.erase(end_iter2, strings_.end()); + styles_.erase(end_iter3, styles_.end()); + + ReAssignIndices(); +} + +template +static void SortEntries( + std::vector>& entries, + const std::function& cmp) { + using UEntry = std::unique_ptr; + + if (cmp != nullptr) { + std::sort(entries.begin(), entries.end(), [&cmp](const UEntry& a, const UEntry& b) -> bool { + int r = cmp(a->context, b->context); + if (r == 0) { + r = a->value.compare(b->value); + } + return r < 0; + }); + } else { + std::sort(entries.begin(), entries.end(), + [](const UEntry& a, const UEntry& b) -> bool { return a->value < b->value; }); + } +} + +void StringPool::Sort(const std::function& cmp) { + SortEntries(styles_, cmp); + SortEntries(strings_, cmp); + ReAssignIndices(); +} + +template +static T* EncodeLength(T* data, size_t length) { + static_assert(std::is_integral::value, "wat."); + + constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1); + constexpr size_t kMaxSize = kMask - 1; + if (length > kMaxSize) { + *data++ = kMask | (kMaxSize & (length >> (sizeof(T) * 8))); + } + *data++ = length; + return data; +} + +/** + * Returns the maximum possible string length that can be successfully encoded + * using 2 units of the specified T. + * EncodeLengthMax -> maximum unit length of 0x7FFF + * EncodeLengthMax -> maximum unit length of 0x7FFFFFFF + **/ +template +static size_t EncodeLengthMax() { + static_assert(std::is_integral::value, "wat."); + + constexpr size_t kMask = 1 << ((sizeof(T) * 8 * 2) - 1); + constexpr size_t max = kMask - 1; + return max; +} + +/** + * Returns the number of units (1 or 2) needed to encode the string length + * before writing the string. + */ +template +static size_t EncodedLengthUnits(size_t length) { + static_assert(std::is_integral::value, "wat."); + + constexpr size_t kMask = 1 << ((sizeof(T) * 8) - 1); + constexpr size_t kMaxSize = kMask - 1; + return length > kMaxSize ? 2 : 1; +} + +const std::string kStringTooLarge = "STRING_TOO_LARGE"; + +static bool EncodeString(const std::string& str, const bool utf8, BigBuffer* out, + IDiagnostics* diag) { + if (utf8) { + const std::string& encoded = util::Utf8ToModifiedUtf8(str); + const ssize_t utf16_length = + utf8_to_utf16_length(reinterpret_cast(encoded.data()), encoded.size()); + CHECK(utf16_length >= 0); + + // Make sure the lengths to be encoded do not exceed the maximum length that + // can be encoded using chars + if ((((size_t)encoded.size()) > EncodeLengthMax()) || + (((size_t)utf16_length) > EncodeLengthMax())) { + diag->Error(DiagMessage() << "string too large to encode using UTF-8 " + << "written instead as '" << kStringTooLarge << "'"); + + EncodeString(kStringTooLarge, utf8, out, diag); + return false; + } + + const size_t total_size = EncodedLengthUnits(utf16_length) + + EncodedLengthUnits(encoded.size()) + encoded.size() + 1; + + char* data = out->NextBlock(total_size); + + // First encode the UTF16 string length. + data = EncodeLength(data, utf16_length); + + // Now encode the size of the real UTF8 string. + data = EncodeLength(data, encoded.size()); + strncpy(data, encoded.data(), encoded.size()); + + } else { + const std::u16string encoded = util::Utf8ToUtf16(str); + const ssize_t utf16_length = encoded.size(); + + // Make sure the length to be encoded does not exceed the maximum possible + // length that can be encoded + if (((size_t)utf16_length) > EncodeLengthMax()) { + diag->Error(DiagMessage() << "string too large to encode using UTF-16 " + << "written instead as '" << kStringTooLarge << "'"); + + EncodeString(kStringTooLarge, utf8, out, diag); + return false; + } + + // Total number of 16-bit words to write. + const size_t total_size = EncodedLengthUnits(utf16_length) + encoded.size() + 1; + + char16_t* data = out->NextBlock(total_size); + + // Encode the actual UTF16 string length. + data = EncodeLength(data, utf16_length); + const size_t byte_length = encoded.size() * sizeof(char16_t); + + // NOTE: For some reason, strncpy16(data, entry->value.data(), + // entry->value.size()) truncates the string. + memcpy(data, encoded.data(), byte_length); + + // The null-terminating character is already here due to the block of data + // being set to 0s on allocation. + } + + return true; +} + +bool StringPool::Flatten(BigBuffer* out, const StringPool& pool, bool utf8, IDiagnostics* diag) { + bool no_error = true; + const size_t start_index = out->size(); + android::ResStringPool_header* header = out->NextBlock(); + header->header.type = util::HostToDevice16(android::RES_STRING_POOL_TYPE); + header->header.headerSize = util::HostToDevice16(sizeof(*header)); + header->stringCount = util::HostToDevice32(pool.size()); + header->styleCount = util::HostToDevice32(pool.styles_.size()); + if (utf8) { + header->flags |= android::ResStringPool_header::UTF8_FLAG; + } + + uint32_t* indices = pool.size() != 0 ? out->NextBlock(pool.size()) : nullptr; + uint32_t* style_indices = + pool.styles_.size() != 0 ? out->NextBlock(pool.styles_.size()) : nullptr; + + const size_t before_strings_index = out->size(); + header->stringsStart = before_strings_index - start_index; + + // Styles always come first. + for (const std::unique_ptr& entry : pool.styles_) { + *indices++ = out->size() - before_strings_index; + no_error = EncodeString(entry->value, utf8, out, diag) && no_error; + } + + for (const std::unique_ptr& entry : pool.strings_) { + *indices++ = out->size() - before_strings_index; + no_error = EncodeString(entry->value, utf8, out, diag) && no_error; + } + + out->Align4(); + + if (style_indices != nullptr) { + const size_t before_styles_index = out->size(); + header->stylesStart = util::HostToDevice32(before_styles_index - start_index); + + for (const std::unique_ptr& entry : pool.styles_) { + *style_indices++ = out->size() - before_styles_index; + + if (!entry->spans.empty()) { + android::ResStringPool_span* span = + out->NextBlock(entry->spans.size()); + for (const Span& s : entry->spans) { + span->name.index = util::HostToDevice32(s.name.index()); + span->firstChar = util::HostToDevice32(s.first_char); + span->lastChar = util::HostToDevice32(s.last_char); + span++; + } + } + + uint32_t* spanEnd = out->NextBlock(); + *spanEnd = android::ResStringPool_span::END; + } + + // The error checking code in the platform looks for an entire + // ResStringPool_span structure worth of 0xFFFFFFFF at the end + // of the style block, so fill in the remaining 2 32bit words + // with 0xFFFFFFFF. + const size_t padding_length = + sizeof(android::ResStringPool_span) - sizeof(android::ResStringPool_span::name); + uint8_t* padding = out->NextBlock(padding_length); + memset(padding, 0xff, padding_length); + out->Align4(); + } + header->header.size = util::HostToDevice32(out->size() - start_index); + return no_error; +} + +bool StringPool::FlattenUtf8(BigBuffer* out, const StringPool& pool, IDiagnostics* diag) { + return Flatten(out, pool, true, diag); +} + +bool StringPool::FlattenUtf16(BigBuffer* out, const StringPool& pool, IDiagnostics* diag) { + return Flatten(out, pool, false, diag); +} + +} // namespace android diff --git a/libs/androidfw/Util.cpp b/libs/androidfw/Util.cpp index 59c9d640bb91..be9edc430871 100644 --- a/libs/androidfw/Util.cpp +++ b/libs/androidfw/Util.cpp @@ -68,6 +68,107 @@ std::string Utf16ToUtf8(const StringPiece16& utf16) { return utf8; } +std::string Utf8ToModifiedUtf8(const std::string& utf8) { + // Java uses Modified UTF-8 which only supports the 1, 2, and 3 byte formats of UTF-8. To encode + // 4 byte UTF-8 codepoints, Modified UTF-8 allows the use of surrogate pairs in the same format + // of CESU-8 surrogate pairs. Calculate the size of the utf8 string with all 4 byte UTF-8 + // codepoints replaced with 2 3 byte surrogate pairs + size_t modified_size = 0; + const size_t size = utf8.size(); + for (size_t i = 0; i < size; i++) { + if (((uint8_t)utf8[i] >> 4) == 0xF) { + modified_size += 6; + i += 3; + } else { + modified_size++; + } + } + + // Early out if no 4 byte codepoints are found + if (size == modified_size) { + return utf8; + } + + std::string output; + output.reserve(modified_size); + for (size_t i = 0; i < size; i++) { + if (((uint8_t)utf8[i] >> 4) == 0xF) { + int32_t codepoint = utf32_from_utf8_at(utf8.data(), size, i, nullptr); + + // Calculate the high and low surrogates as UTF-16 would + int32_t high = ((codepoint - 0x10000) / 0x400) + 0xD800; + int32_t low = ((codepoint - 0x10000) % 0x400) + 0xDC00; + + // Encode each surrogate in UTF-8 + output.push_back((char)(0xE4 | ((high >> 12) & 0xF))); + output.push_back((char)(0x80 | ((high >> 6) & 0x3F))); + output.push_back((char)(0x80 | (high & 0x3F))); + output.push_back((char)(0xE4 | ((low >> 12) & 0xF))); + output.push_back((char)(0x80 | ((low >> 6) & 0x3F))); + output.push_back((char)(0x80 | (low & 0x3F))); + i += 3; + } else { + output.push_back(utf8[i]); + } + } + + return output; +} + +std::string ModifiedUtf8ToUtf8(const std::string& modified_utf8) { + // The UTF-8 representation will have a byte length less than or equal to the Modified UTF-8 + // representation. + std::string output; + output.reserve(modified_utf8.size()); + + size_t index = 0; + const size_t modified_size = modified_utf8.size(); + while (index < modified_size) { + size_t next_index; + int32_t high_surrogate = + utf32_from_utf8_at(modified_utf8.data(), modified_size, index, &next_index); + if (high_surrogate < 0) { + return {}; + } + + // Check that the first codepoint is within the high surrogate range + if (high_surrogate >= 0xD800 && high_surrogate <= 0xDB7F) { + int32_t low_surrogate = + utf32_from_utf8_at(modified_utf8.data(), modified_size, next_index, &next_index); + if (low_surrogate < 0) { + return {}; + } + + // Check that the second codepoint is within the low surrogate range + if (low_surrogate >= 0xDC00 && low_surrogate <= 0xDFFF) { + const char32_t codepoint = + (char32_t)(((high_surrogate - 0xD800) * 0x400) + (low_surrogate - 0xDC00) + 0x10000); + + // The decoded codepoint should represent a 4 byte, UTF-8 character + const size_t utf8_length = (size_t)utf32_to_utf8_length(&codepoint, 1); + if (utf8_length != 4) { + return {}; + } + + // Encode the UTF-8 representation of the codepoint into the string + char* start = &output[output.size()]; + output.resize(output.size() + utf8_length); + utf32_to_utf8((char32_t*)&codepoint, 1, start, utf8_length + 1); + + index = next_index; + continue; + } + } + + // Append non-surrogate pairs to the output string + for (size_t i = index; i < next_index; i++) { + output.push_back(modified_utf8[i]); + } + index = next_index; + } + return output; +} + static std::vector SplitAndTransform( const StringPiece& str, char sep, const std::function& f) { std::vector parts; @@ -90,6 +191,29 @@ std::vector SplitAndLowercase(const StringPiece& str, char sep) { return SplitAndTransform(str, sep, ::tolower); } +std::unique_ptr Copy(const BigBuffer& buffer) { + std::unique_ptr data = std::unique_ptr(new uint8_t[buffer.size()]); + uint8_t* p = data.get(); + for (const auto& block : buffer) { + memcpy(p, block.buffer.get(), block.size); + p += block.size; + } + return data; +} + +StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx) { + if (auto str = pool.stringAt(idx); str.ok()) { + return *str; + } + return StringPiece16(); +} + +std::string GetString(const android::ResStringPool& pool, size_t idx) { + if (auto str = pool.string8At(idx); str.ok()) { + return ModifiedUtf8ToUtf8(str->to_string()); + } + return Utf16ToUtf8(GetString16(pool, idx)); +} } // namespace util } // namespace android diff --git a/libs/androidfw/include/androidfw/BigBuffer.h b/libs/androidfw/include/androidfw/BigBuffer.h new file mode 100644 index 000000000000..b99a4edf9d88 --- /dev/null +++ b/libs/androidfw/include/androidfw/BigBuffer.h @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef _ANDROID_BIG_BUFFER_H +#define _ANDROID_BIG_BUFFER_H + +#include +#include +#include +#include +#include + +#include "android-base/logging.h" +#include "android-base/macros.h" + +namespace android { + +/** + * Inspired by protobuf's ZeroCopyOutputStream, offers blocks of memory + * in which to write without knowing the full size of the entire payload. + * This is essentially a list of memory blocks. As one fills up, another + * block is allocated and appended to the end of the list. + */ +class BigBuffer { + public: + /** + * A contiguous block of allocated memory. + */ + struct Block { + /** + * Pointer to the memory. + */ + std::unique_ptr buffer; + + /** + * Size of memory that is currently occupied. The actual + * allocation may be larger. + */ + size_t size; + + private: + friend class BigBuffer; + + /** + * The size of the memory block allocation. + */ + size_t block_size_; + }; + + typedef std::vector::const_iterator const_iterator; + + /** + * Create a BigBuffer with block allocation sizes + * of block_size. + */ + explicit BigBuffer(size_t block_size); + + BigBuffer(BigBuffer&& rhs) noexcept; + + /** + * Number of occupied bytes in all the allocated blocks. + */ + size_t size() const; + + /** + * Returns a pointer to an array of T, where T is + * a POD type. The elements are zero-initialized. + */ + template + T* NextBlock(size_t count = 1); + + /** + * Returns the next block available and puts the size in out_count. + * This is useful for grabbing blocks where the size doesn't matter. + * Use BackUp() to give back any bytes that were not used. + */ + void* NextBlock(size_t* out_count); + + /** + * Backs up count bytes. This must only be called after NextBlock() + * and can not be larger than sizeof(T) * count of the last NextBlock() + * call. + */ + void BackUp(size_t count); + + /** + * Moves the specified BigBuffer into this one. When this method + * returns, buffer is empty. + */ + void AppendBuffer(BigBuffer&& buffer); + + /** + * Pads the block with 'bytes' bytes of zero values. + */ + void Pad(size_t bytes); + + /** + * Pads the block so that it aligns on a 4 byte boundary. + */ + void Align4(); + + size_t block_size() const; + + const_iterator begin() const; + const_iterator end() const; + + std::string to_string() const; + + private: + DISALLOW_COPY_AND_ASSIGN(BigBuffer); + + /** + * Returns a pointer to a buffer of the requested size. + * The buffer is zero-initialized. + */ + void* NextBlockImpl(size_t size); + + size_t block_size_; + size_t size_; + std::vector blocks_; +}; + +inline BigBuffer::BigBuffer(size_t block_size) : block_size_(block_size), size_(0) { +} + +inline BigBuffer::BigBuffer(BigBuffer&& rhs) noexcept + : block_size_(rhs.block_size_), size_(rhs.size_), blocks_(std::move(rhs.blocks_)) { +} + +inline size_t BigBuffer::size() const { + return size_; +} + +inline size_t BigBuffer::block_size() const { + return block_size_; +} + +template +inline T* BigBuffer::NextBlock(size_t count) { + static_assert(std::is_standard_layout::value, "T must be standard_layout type"); + CHECK(count != 0); + return reinterpret_cast(NextBlockImpl(sizeof(T) * count)); +} + +inline void BigBuffer::BackUp(size_t count) { + Block& block = blocks_.back(); + block.size -= count; + size_ -= count; +} + +inline void BigBuffer::AppendBuffer(BigBuffer&& buffer) { + std::move(buffer.blocks_.begin(), buffer.blocks_.end(), std::back_inserter(blocks_)); + size_ += buffer.size_; + buffer.blocks_.clear(); + buffer.size_ = 0; +} + +inline void BigBuffer::Pad(size_t bytes) { + NextBlock(bytes); +} + +inline void BigBuffer::Align4() { + const size_t unaligned = size_ % 4; + if (unaligned != 0) { + Pad(4 - unaligned); + } +} + +inline BigBuffer::const_iterator BigBuffer::begin() const { + return blocks_.begin(); +} + +inline BigBuffer::const_iterator BigBuffer::end() const { + return blocks_.end(); +} + +} // namespace android + +#endif // _ANDROID_BIG_BUFFER_H diff --git a/libs/androidfw/include/androidfw/IDiagnostics.h b/libs/androidfw/include/androidfw/IDiagnostics.h new file mode 100644 index 000000000000..273b05ac7689 --- /dev/null +++ b/libs/androidfw/include/androidfw/IDiagnostics.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef _ANDROID_DIAGNOSTICS_H +#define _ANDROID_DIAGNOSTICS_H + +#include +#include + +#include "Source.h" +#include "android-base/macros.h" +#include "androidfw/StringPiece.h" + +namespace android { + +struct DiagMessageActual { + Source source; + std::string message; +}; + +struct DiagMessage { + public: + DiagMessage() = default; + + explicit DiagMessage(const android::StringPiece& src) : source_(src) { + } + + explicit DiagMessage(const Source& src) : source_(src) { + } + + explicit DiagMessage(size_t line) : source_(Source().WithLine(line)) { + } + + template + DiagMessage& operator<<(const T& value) { + message_ << value; + return *this; + } + + DiagMessageActual Build() const { + return DiagMessageActual{source_, message_.str()}; + } + + private: + Source source_; + std::stringstream message_; +}; + +template <> +inline DiagMessage& DiagMessage::operator<<(const ::std::u16string& value) { + message_ << android::StringPiece16(value); + return *this; +} + +struct IDiagnostics { + virtual ~IDiagnostics() = default; + + enum class Level { Note, Warn, Error }; + + virtual void Log(Level level, DiagMessageActual& actualMsg) = 0; + + virtual void Error(const DiagMessage& message) { + DiagMessageActual actual = message.Build(); + Log(Level::Error, actual); + } + + virtual void Warn(const DiagMessage& message) { + DiagMessageActual actual = message.Build(); + Log(Level::Warn, actual); + } + + virtual void Note(const DiagMessage& message) { + DiagMessageActual actual = message.Build(); + Log(Level::Note, actual); + } +}; + +class SourcePathDiagnostics : public IDiagnostics { + public: + SourcePathDiagnostics(const Source& src, IDiagnostics* diag) : source_(src), diag_(diag) { + } + + void Log(Level level, DiagMessageActual& actual_msg) override { + actual_msg.source.path = source_.path; + diag_->Log(level, actual_msg); + if (level == Level::Error) { + error = true; + } + } + + bool HadError() { + return error; + } + + private: + Source source_; + IDiagnostics* diag_; + bool error = false; + + DISALLOW_COPY_AND_ASSIGN(SourcePathDiagnostics); +}; + +class NoOpDiagnostics : public IDiagnostics { + public: + NoOpDiagnostics() = default; + + void Log(Level level, DiagMessageActual& actual_msg) override { + (void)level; + (void)actual_msg; + } + + DISALLOW_COPY_AND_ASSIGN(NoOpDiagnostics); +}; + +} // namespace android + +#endif /* _ANDROID_DIAGNOSTICS_H */ diff --git a/libs/androidfw/include/androidfw/Source.h b/libs/androidfw/include/androidfw/Source.h new file mode 100644 index 000000000000..0421a91d3eba --- /dev/null +++ b/libs/androidfw/include/androidfw/Source.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef _ANDROID_SOURCE_H +#define _ANDROID_SOURCE_H + +#include +#include +#include + +#include "android-base/stringprintf.h" +#include "androidfw/StringPiece.h" + +namespace android { + +// Represents a file on disk. Used for logging and showing errors. +struct Source { + std::string path; + std::optional line; + std::optional archive; + + Source() = default; + + inline Source(const android::StringPiece& path) : path(path.to_string()) { // NOLINT(implicit) + } + + inline Source(const android::StringPiece& path, const android::StringPiece& archive) + : path(path.to_string()), archive(archive.to_string()) { + } + + inline Source(const android::StringPiece& path, size_t line) + : path(path.to_string()), line(line) { + } + + inline Source WithLine(size_t line) const { + return Source(path, line); + } + + std::string to_string() const { + std::string s = path; + if (archive) { + s = ::android::base::StringPrintf("%s@%s", archive.value().c_str(), s.c_str()); + } + if (line) { + s = ::android::base::StringPrintf("%s:%zd", s.c_str(), line.value()); + } + return s; + } +}; + +// +// Implementations +// + +inline ::std::ostream& operator<<(::std::ostream& out, const Source& source) { + return out << source.to_string(); +} + +inline bool operator==(const Source& lhs, const Source& rhs) { + return lhs.path == rhs.path && lhs.line == rhs.line; +} + +inline bool operator<(const Source& lhs, const Source& rhs) { + int cmp = lhs.path.compare(rhs.path); + if (cmp < 0) return true; + if (cmp > 0) return false; + if (lhs.line) { + if (rhs.line) { + return lhs.line.value() < rhs.line.value(); + } + return false; + } + return bool(rhs.line); +} + +} // namespace android + +#endif // _ANDROID_SOURCE_H diff --git a/libs/androidfw/include/androidfw/StringPool.h b/libs/androidfw/include/androidfw/StringPool.h new file mode 100644 index 000000000000..25174d8fe3c8 --- /dev/null +++ b/libs/androidfw/include/androidfw/StringPool.h @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2015 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. + */ + +#ifndef _ANDROID_STRING_POOL_H +#define _ANDROID_STRING_POOL_H + +#include +#include +#include +#include +#include + +#include "BigBuffer.h" +#include "IDiagnostics.h" +#include "android-base/macros.h" +#include "androidfw/ConfigDescription.h" +#include "androidfw/StringPiece.h" + +namespace android { + +struct Span { + std::string name; + uint32_t first_char; + uint32_t last_char; + + bool operator==(const Span& right) const { + return name == right.name && first_char == right.first_char && last_char == right.last_char; + } +}; + +struct StyleString { + std::string str; + std::vector spans; +}; + +// A StringPool for storing the value of String and StyledString resources. +// Styles and Strings are stored separately, since the runtime variant of this +// class -- ResStringPool -- requires that styled strings *always* appear first, since their +// style data is stored as an array indexed by the same indices as the main string pool array. +// Otherwise, the style data array would have to be sparse and take up more space. +class StringPool { + public: + using size_type = size_t; + + class Context { + public: + enum : uint32_t { + kHighPriority = 1u, + kNormalPriority = 0x7fffffffu, + kLowPriority = 0xffffffffu, + }; + uint32_t priority = kNormalPriority; + android::ConfigDescription config; + + Context() = default; + Context(uint32_t p, const android::ConfigDescription& c) : priority(p), config(c) { + } + explicit Context(uint32_t p) : priority(p) { + } + explicit Context(const android::ConfigDescription& c) : priority(kNormalPriority), config(c) { + } + }; + + class Entry; + + class Ref { + public: + Ref(); + Ref(const Ref&); + ~Ref(); + + Ref& operator=(const Ref& rhs); + bool operator==(const Ref& rhs) const; + bool operator!=(const Ref& rhs) const; + const std::string* operator->() const; + const std::string& operator*() const; + + size_t index() const; + const Context& GetContext() const; + + private: + friend class StringPool; + + explicit Ref(Entry* entry); + + Entry* entry_; + }; + + class StyleEntry; + + class StyleRef { + public: + StyleRef(); + StyleRef(const StyleRef&); + ~StyleRef(); + + StyleRef& operator=(const StyleRef& rhs); + bool operator==(const StyleRef& rhs) const; + bool operator!=(const StyleRef& rhs) const; + const StyleEntry* operator->() const; + const StyleEntry& operator*() const; + + size_t index() const; + const Context& GetContext() const; + + private: + friend class StringPool; + + explicit StyleRef(StyleEntry* entry); + + StyleEntry* entry_; + }; + + class Entry { + public: + std::string value; + Context context; + + private: + friend class StringPool; + friend class Ref; + + size_t index_; + int ref_; + const StringPool* pool_; + }; + + struct Span { + Ref name; + uint32_t first_char; + uint32_t last_char; + }; + + class StyleEntry { + public: + std::string value; + Context context; + std::vector spans; + + private: + friend class StringPool; + friend class StyleRef; + + size_t index_; + int ref_; + }; + + static bool FlattenUtf8(BigBuffer* out, const StringPool& pool, IDiagnostics* diag); + static bool FlattenUtf16(BigBuffer* out, const StringPool& pool, IDiagnostics* diag); + + StringPool() = default; + StringPool(StringPool&&) = default; + StringPool& operator=(StringPool&&) = default; + + // Adds a string to the pool, unless it already exists. Returns a reference to the string in the + // pool. + Ref MakeRef(const android::StringPiece& str); + + // Adds a string to the pool, unless it already exists, with a context object that can be used + // when sorting the string pool. Returns a reference to the string in the pool. + Ref MakeRef(const android::StringPiece& str, const Context& context); + + // Adds a string from another string pool. Returns a reference to the string in the string pool. + Ref MakeRef(const Ref& ref); + + // Adds a style to the string pool and returns a reference to it. + StyleRef MakeRef(const StyleString& str); + + // Adds a style to the string pool with a context object that can be used when sorting the string + // pool. Returns a reference to the style in the string pool. + StyleRef MakeRef(const StyleString& str, const Context& context); + + // Adds a style from another string pool. Returns a reference to the style in the string pool. + StyleRef MakeRef(const StyleRef& ref); + + // Moves pool into this one without coalescing strings. When this function returns, pool will be + // empty. + void Merge(StringPool&& pool); + + inline const std::vector>& strings() const { + return strings_; + } + + // Returns the number of strings in the table. + inline size_t size() const { + return styles_.size() + strings_.size(); + } + + // Reserves space for strings and styles as an optimization. + void HintWillAdd(size_t string_count, size_t style_count); + + // Sorts the strings according to their Context using some comparison function. + // Equal Contexts are further sorted by string value, lexicographically. + // If no comparison function is provided, values are only sorted lexicographically. + void Sort(const std::function& cmp = nullptr); + + // Removes any strings that have no references. + void Prune(); + + private: + DISALLOW_COPY_AND_ASSIGN(StringPool); + + static bool Flatten(BigBuffer* out, const StringPool& pool, bool utf8, IDiagnostics* diag); + + Ref MakeRefImpl(const android::StringPiece& str, const Context& context, bool unique); + void ReAssignIndices(); + + std::vector> strings_; + std::vector> styles_; + std::unordered_multimap indexed_strings_; +}; + +} // namespace android + +#endif // _ANDROID_STRING_POOL_H diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h index c59b5b6c51a2..1bbc7f5716bc 100644 --- a/libs/androidfw/include/androidfw/Util.h +++ b/libs/androidfw/include/androidfw/Util.h @@ -17,15 +17,18 @@ #ifndef UTIL_H_ #define UTIL_H_ +#include +#include + #include #include #include #include -#include -#include - +#include "androidfw/BigBuffer.h" +#include "androidfw/ResourceTypes.h" #include "androidfw/StringPiece.h" +#include "utils/ByteOrder.h" #ifdef __ANDROID__ #define ANDROID_LOG(x) LOG(x) @@ -125,6 +128,28 @@ std::u16string Utf8ToUtf16(const StringPiece& utf8); // Converts a UTF-16 string to a UTF-8 string. std::string Utf16ToUtf8(const StringPiece16& utf16); +// Converts a UTF8 string into Modified UTF8 +std::string Utf8ToModifiedUtf8(const std::string& utf8); + +// Converts a Modified UTF8 string into a UTF8 string +std::string ModifiedUtf8ToUtf8(const std::string& modified_utf8); + +inline uint16_t HostToDevice16(uint16_t value) { + return htods(value); +} + +inline uint32_t HostToDevice32(uint32_t value) { + return htodl(value); +} + +inline uint16_t DeviceToHost16(uint16_t value) { + return dtohs(value); +} + +inline uint32_t DeviceToHost32(uint32_t value) { + return dtohl(value); +} + std::vector SplitAndLowercase(const android::StringPiece& str, char sep); template @@ -136,6 +161,18 @@ inline bool IsFourByteAligned(const void* data) { return ((size_t)data & 0x3U) == 0; } +// Helper method to extract a UTF-16 string from a StringPool. If the string is stored as UTF-8, +// the conversion to UTF-16 happens within ResStringPool. +android::StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx); + +// Helper method to extract a UTF-8 string from a StringPool. If the string is stored as UTF-16, +// the conversion from UTF-16 to UTF-8 does not happen in ResStringPool and is done by this method, +// which maintains no state or cache. This means we must return an std::string copy. +std::string GetString(const android::ResStringPool& pool, size_t idx); + +// Copies the entire BigBuffer into a single buffer. +std::unique_ptr Copy(const android::BigBuffer& buffer); + } // namespace util } // namespace android diff --git a/libs/androidfw/tests/BigBuffer_test.cpp b/libs/androidfw/tests/BigBuffer_test.cpp new file mode 100644 index 000000000000..382d21e20846 --- /dev/null +++ b/libs/androidfw/tests/BigBuffer_test.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2015 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 "androidfw/BigBuffer.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using ::testing::NotNull; + +namespace android { + +TEST(BigBufferTest, AllocateSingleBlock) { + BigBuffer buffer(4); + + EXPECT_THAT(buffer.NextBlock(2), NotNull()); + EXPECT_EQ(2u, buffer.size()); +} + +TEST(BigBufferTest, ReturnSameBlockIfNextAllocationFits) { + BigBuffer buffer(16); + + char* b1 = buffer.NextBlock(8); + EXPECT_THAT(b1, NotNull()); + + char* b2 = buffer.NextBlock(4); + EXPECT_THAT(b2, NotNull()); + + EXPECT_EQ(b1 + 8, b2); +} + +TEST(BigBufferTest, AllocateExactSizeBlockIfLargerThanBlockSize) { + BigBuffer buffer(16); + + EXPECT_THAT(buffer.NextBlock(32), NotNull()); + EXPECT_EQ(32u, buffer.size()); +} + +TEST(BigBufferTest, AppendAndMoveBlock) { + BigBuffer buffer(16); + + uint32_t* b1 = buffer.NextBlock(); + ASSERT_THAT(b1, NotNull()); + *b1 = 33; + + { + BigBuffer buffer2(16); + b1 = buffer2.NextBlock(); + ASSERT_THAT(b1, NotNull()); + *b1 = 44; + + buffer.AppendBuffer(std::move(buffer2)); + EXPECT_EQ(0u, buffer2.size()); // NOLINT + EXPECT_EQ(buffer2.begin(), buffer2.end()); + } + + EXPECT_EQ(2 * sizeof(uint32_t), buffer.size()); + + auto b = buffer.begin(); + ASSERT_NE(b, buffer.end()); + ASSERT_EQ(sizeof(uint32_t), b->size); + ASSERT_EQ(33u, *reinterpret_cast(b->buffer.get())); + ++b; + + ASSERT_NE(b, buffer.end()); + ASSERT_EQ(sizeof(uint32_t), b->size); + ASSERT_EQ(44u, *reinterpret_cast(b->buffer.get())); + ++b; + + ASSERT_EQ(b, buffer.end()); +} + +TEST(BigBufferTest, PadAndAlignProperly) { + BigBuffer buffer(16); + + ASSERT_THAT(buffer.NextBlock(2), NotNull()); + ASSERT_EQ(2u, buffer.size()); + buffer.Pad(2); + ASSERT_EQ(4u, buffer.size()); + buffer.Align4(); + ASSERT_EQ(4u, buffer.size()); + buffer.Pad(2); + ASSERT_EQ(6u, buffer.size()); + buffer.Align4(); + ASSERT_EQ(8u, buffer.size()); +} + +} // namespace android diff --git a/libs/androidfw/tests/StringPool_test.cpp b/libs/androidfw/tests/StringPool_test.cpp new file mode 100644 index 000000000000..047d45785409 --- /dev/null +++ b/libs/androidfw/tests/StringPool_test.cpp @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2015 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 "androidfw/StringPool.h" + +#include + +#include "androidfw/IDiagnostics.h" +#include "androidfw/StringPiece.h" +#include "androidfw/Util.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using ::android::StringPiece; +using ::android::StringPiece16; +using ::testing::Eq; +using ::testing::Ne; +using ::testing::NotNull; +using ::testing::Pointee; + +namespace android { + +TEST(StringPoolTest, InsertOneString) { + StringPool pool; + + StringPool::Ref ref = pool.MakeRef("wut"); + EXPECT_THAT(*ref, Eq("wut")); +} + +TEST(StringPoolTest, InsertTwoUniqueStrings) { + StringPool pool; + + StringPool::Ref ref_a = pool.MakeRef("wut"); + StringPool::Ref ref_b = pool.MakeRef("hey"); + + EXPECT_THAT(*ref_a, Eq("wut")); + EXPECT_THAT(*ref_b, Eq("hey")); +} + +TEST(StringPoolTest, DoNotInsertNewDuplicateString) { + StringPool pool; + + StringPool::Ref ref_a = pool.MakeRef("wut"); + StringPool::Ref ref_b = pool.MakeRef("wut"); + + EXPECT_THAT(*ref_a, Eq("wut")); + EXPECT_THAT(*ref_b, Eq("wut")); + EXPECT_THAT(pool.size(), Eq(1u)); +} + +TEST(StringPoolTest, DoNotDedupeSameStringDifferentPriority) { + StringPool pool; + + StringPool::Ref ref_a = pool.MakeRef("wut", StringPool::Context(0x81010001)); + StringPool::Ref ref_b = pool.MakeRef("wut", StringPool::Context(0x81010002)); + + EXPECT_THAT(*ref_a, Eq("wut")); + EXPECT_THAT(*ref_b, Eq("wut")); + EXPECT_THAT(pool.size(), Eq(2u)); +} + +TEST(StringPoolTest, MaintainInsertionOrderIndex) { + StringPool pool; + + StringPool::Ref ref_a = pool.MakeRef("z"); + StringPool::Ref ref_b = pool.MakeRef("a"); + StringPool::Ref ref_c = pool.MakeRef("m"); + + EXPECT_THAT(ref_a.index(), Eq(0u)); + EXPECT_THAT(ref_b.index(), Eq(1u)); + EXPECT_THAT(ref_c.index(), Eq(2u)); +} + +TEST(StringPoolTest, PruneStringsWithNoReferences) { + StringPool pool; + + StringPool::Ref ref_a = pool.MakeRef("foo"); + + { + StringPool::Ref ref_b = pool.MakeRef("wut"); + EXPECT_THAT(*ref_b, Eq("wut")); + EXPECT_THAT(pool.size(), Eq(2u)); + pool.Prune(); + EXPECT_THAT(pool.size(), Eq(2u)); + } + EXPECT_THAT(pool.size(), Eq(2u)); + + { + StringPool::Ref ref_c = pool.MakeRef("bar"); + EXPECT_THAT(pool.size(), Eq(3u)); + + pool.Prune(); + EXPECT_THAT(pool.size(), Eq(2u)); + } + EXPECT_THAT(pool.size(), Eq(2u)); + + pool.Prune(); + EXPECT_THAT(pool.size(), Eq(1u)); +} + +TEST(StringPoolTest, SortAndMaintainIndexesInStringReferences) { + StringPool pool; + + StringPool::Ref ref_a = pool.MakeRef("z"); + StringPool::Ref ref_b = pool.MakeRef("a"); + StringPool::Ref ref_c = pool.MakeRef("m"); + + EXPECT_THAT(*ref_a, Eq("z")); + EXPECT_THAT(ref_a.index(), Eq(0u)); + + EXPECT_THAT(*ref_b, Eq("a")); + EXPECT_THAT(ref_b.index(), Eq(1u)); + + EXPECT_THAT(*ref_c, Eq("m")); + EXPECT_THAT(ref_c.index(), Eq(2u)); + + pool.Sort(); + + EXPECT_THAT(*ref_a, Eq("z")); + EXPECT_THAT(ref_a.index(), Eq(2u)); + + EXPECT_THAT(*ref_b, Eq("a")); + EXPECT_THAT(ref_b.index(), Eq(0u)); + + EXPECT_THAT(*ref_c, Eq("m")); + EXPECT_THAT(ref_c.index(), Eq(1u)); +} + +TEST(StringPoolTest, SortAndStillDedupe) { + StringPool pool; + + StringPool::Ref ref_a = pool.MakeRef("z"); + StringPool::Ref ref_b = pool.MakeRef("a"); + StringPool::Ref ref_c = pool.MakeRef("m"); + + pool.Sort(); + + StringPool::Ref ref_d = pool.MakeRef("z"); + StringPool::Ref ref_e = pool.MakeRef("a"); + StringPool::Ref ref_f = pool.MakeRef("m"); + + EXPECT_THAT(ref_d.index(), Eq(ref_a.index())); + EXPECT_THAT(ref_e.index(), Eq(ref_b.index())); + EXPECT_THAT(ref_f.index(), Eq(ref_c.index())); +} + +TEST(StringPoolTest, AddStyles) { + StringPool pool; + + StringPool::StyleRef ref = pool.MakeRef(StyleString{{"android"}, {Span{{"b"}, 2, 6}}}); + EXPECT_THAT(ref.index(), Eq(0u)); + EXPECT_THAT(ref->value, Eq("android")); + ASSERT_THAT(ref->spans.size(), Eq(1u)); + + const StringPool::Span& span = ref->spans.front(); + EXPECT_THAT(*span.name, Eq("b")); + EXPECT_THAT(span.first_char, Eq(2u)); + EXPECT_THAT(span.last_char, Eq(6u)); +} + +TEST(StringPoolTest, DoNotDedupeStyleWithSameStringAsNonStyle) { + StringPool pool; + + StringPool::Ref ref = pool.MakeRef("android"); + + StyleString str{{"android"}, {}}; + StringPool::StyleRef style_ref = pool.MakeRef(StyleString{{"android"}, {}}); + + EXPECT_THAT(ref.index(), Ne(style_ref.index())); +} + +TEST(StringPoolTest, StylesAndStringsAreSeparateAfterSorting) { + StringPool pool; + + StringPool::StyleRef ref_a = pool.MakeRef(StyleString{{"beta"}, {}}); + StringPool::Ref ref_b = pool.MakeRef("alpha"); + StringPool::StyleRef ref_c = pool.MakeRef(StyleString{{"alpha"}, {}}); + + EXPECT_THAT(ref_b.index(), Ne(ref_c.index())); + + pool.Sort(); + + EXPECT_THAT(ref_c.index(), Eq(0u)); + EXPECT_THAT(ref_a.index(), Eq(1u)); + EXPECT_THAT(ref_b.index(), Eq(2u)); +} + +TEST(StringPoolTest, FlattenEmptyStringPoolUtf8) { + using namespace android; // For NO_ERROR on Windows. + NoOpDiagnostics diag; + + StringPool pool; + BigBuffer buffer(1024); + StringPool::FlattenUtf8(&buffer, pool, &diag); + + std::unique_ptr data = android::util::Copy(buffer); + ResStringPool test; + ASSERT_THAT(test.setTo(data.get(), buffer.size()), Eq(NO_ERROR)); +} + +TEST(StringPoolTest, FlattenOddCharactersUtf16) { + using namespace android; // For NO_ERROR on Windows. + NoOpDiagnostics diag; + + StringPool pool; + pool.MakeRef("\u093f"); + BigBuffer buffer(1024); + StringPool::FlattenUtf16(&buffer, pool, &diag); + + std::unique_ptr data = android::util::Copy(buffer); + ResStringPool test; + ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR); + auto str = test.stringAt(0); + ASSERT_TRUE(str.has_value()); + EXPECT_THAT(str->size(), Eq(1u)); + EXPECT_THAT(str->data(), Pointee(Eq(u'\u093f'))); + EXPECT_THAT(str->data()[1], Eq(0u)); +} + +constexpr const char* sLongString = + "バッテリーを長持ちさせるため、バッテリーセーバーは端末のパフォーマンスを抑" + "え、バイブレーション、位置情報サービス、大半のバックグラウンドデータを制限" + "します。メール、SMSや、同期を使 " + "用するその他のアプリは、起動しても更新されないことがあります。バッテリーセ" + "ーバーは端末の充電中は自動的にOFFになります。"; + +TEST(StringPoolTest, Flatten) { + using namespace android; // For NO_ERROR on Windows. + NoOpDiagnostics diag; + + StringPool pool; + + StringPool::Ref ref_a = pool.MakeRef("hello"); + StringPool::Ref ref_b = pool.MakeRef("goodbye"); + StringPool::Ref ref_c = pool.MakeRef(sLongString); + StringPool::Ref ref_d = pool.MakeRef(""); + StringPool::StyleRef ref_e = + pool.MakeRef(StyleString{{"style"}, {Span{{"b"}, 0, 1}, Span{{"i"}, 2, 3}}}); + + // Styles are always first. + EXPECT_THAT(ref_e.index(), Eq(0u)); + + EXPECT_THAT(ref_a.index(), Eq(1u)); + EXPECT_THAT(ref_b.index(), Eq(2u)); + EXPECT_THAT(ref_c.index(), Eq(3u)); + EXPECT_THAT(ref_d.index(), Eq(4u)); + + BigBuffer buffers[2] = {BigBuffer(1024), BigBuffer(1024)}; + StringPool::FlattenUtf8(&buffers[0], pool, &diag); + StringPool::FlattenUtf16(&buffers[1], pool, &diag); + + // Test both UTF-8 and UTF-16 buffers. + for (const BigBuffer& buffer : buffers) { + std::unique_ptr data = android::util::Copy(buffer); + + ResStringPool test; + ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR); + + EXPECT_THAT(android::util::GetString(test, 1), Eq("hello")); + EXPECT_THAT(android::util::GetString16(test, 1), Eq(u"hello")); + + EXPECT_THAT(android::util::GetString(test, 2), Eq("goodbye")); + EXPECT_THAT(android::util::GetString16(test, 2), Eq(u"goodbye")); + + EXPECT_THAT(android::util::GetString(test, 3), Eq(sLongString)); + EXPECT_THAT(android::util::GetString16(test, 3), Eq(util::Utf8ToUtf16(sLongString))); + + EXPECT_TRUE(test.stringAt(4).has_value() || test.string8At(4).has_value()); + + EXPECT_THAT(android::util::GetString(test, 0), Eq("style")); + EXPECT_THAT(android::util::GetString16(test, 0), Eq(u"style")); + + auto span_result = test.styleAt(0); + ASSERT_TRUE(span_result.has_value()); + + const ResStringPool_span* span = span_result->unsafe_ptr(); + EXPECT_THAT(android::util::GetString(test, span->name.index), Eq("b")); + EXPECT_THAT(android::util::GetString16(test, span->name.index), Eq(u"b")); + EXPECT_THAT(span->firstChar, Eq(0u)); + EXPECT_THAT(span->lastChar, Eq(1u)); + span++; + + ASSERT_THAT(span->name.index, Ne(ResStringPool_span::END)); + EXPECT_THAT(android::util::GetString(test, span->name.index), Eq("i")); + EXPECT_THAT(android::util::GetString16(test, span->name.index), Eq(u"i")); + EXPECT_THAT(span->firstChar, Eq(2u)); + EXPECT_THAT(span->lastChar, Eq(3u)); + span++; + + EXPECT_THAT(span->name.index, Eq(ResStringPool_span::END)); + } +} + +TEST(StringPoolTest, ModifiedUTF8) { + using namespace android; // For NO_ERROR on Windows. + NoOpDiagnostics diag; + StringPool pool; + StringPool::Ref ref_a = pool.MakeRef("\xF0\x90\x90\x80"); // 𐐀 (U+10400) + StringPool::Ref ref_b = pool.MakeRef("foo \xF0\x90\x90\xB7 bar"); // 𐐷 (U+10437) + StringPool::Ref ref_c = pool.MakeRef("\xF0\x90\x90\x80\xF0\x90\x90\xB7"); + + BigBuffer buffer(1024); + StringPool::FlattenUtf8(&buffer, pool, &diag); + std::unique_ptr data = android::util::Copy(buffer); + + // Check that the codepoints are encoded using two three-byte surrogate pairs + ResStringPool test; + ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR); + auto str = test.string8At(0); + ASSERT_TRUE(str.has_value()); + EXPECT_THAT(str->to_string(), Eq("\xED\xA0\x81\xED\xB0\x80")); + + str = test.string8At(1); + ASSERT_TRUE(str.has_value()); + EXPECT_THAT(str->to_string(), Eq("foo \xED\xA0\x81\xED\xB0\xB7 bar")); + + str = test.string8At(2); + ASSERT_TRUE(str.has_value()); + EXPECT_THAT(str->to_string(), Eq("\xED\xA0\x81\xED\xB0\x80\xED\xA0\x81\xED\xB0\xB7")); + + // Check that retrieving the strings returns the original UTF-8 character bytes + EXPECT_THAT(android::util::GetString(test, 0), Eq("\xF0\x90\x90\x80")); + EXPECT_THAT(android::util::GetString(test, 1), Eq("foo \xF0\x90\x90\xB7 bar")); + EXPECT_THAT(android::util::GetString(test, 2), Eq("\xF0\x90\x90\x80\xF0\x90\x90\xB7")); +} + +TEST(StringPoolTest, MaxEncodingLength) { + NoOpDiagnostics diag; + using namespace android; // For NO_ERROR on Windows. + ResStringPool test; + + StringPool pool; + pool.MakeRef("aaaaaaaaaa"); + BigBuffer buffers[2] = {BigBuffer(1024), BigBuffer(1024)}; + + // Make sure a UTF-8 string under the maximum length does not produce an error + EXPECT_THAT(StringPool::FlattenUtf8(&buffers[0], pool, &diag), Eq(true)); + std::unique_ptr data = android::util::Copy(buffers[0]); + test.setTo(data.get(), buffers[0].size()); + EXPECT_THAT(android::util::GetString(test, 0), Eq("aaaaaaaaaa")); + + // Make sure a UTF-16 string under the maximum length does not produce an error + EXPECT_THAT(StringPool::FlattenUtf16(&buffers[1], pool, &diag), Eq(true)); + data = android::util::Copy(buffers[1]); + test.setTo(data.get(), buffers[1].size()); + EXPECT_THAT(android::util::GetString16(test, 0), Eq(u"aaaaaaaaaa")); + + StringPool pool2; + std::string longStr(50000, 'a'); + pool2.MakeRef("this fits1"); + pool2.MakeRef(longStr); + pool2.MakeRef("this fits2"); + BigBuffer buffers2[2] = {BigBuffer(1024), BigBuffer(1024)}; + + // Make sure a string that exceeds the maximum length of UTF-8 produces an + // error and writes a shorter error string instead + EXPECT_THAT(StringPool::FlattenUtf8(&buffers2[0], pool2, &diag), Eq(false)); + data = android::util::Copy(buffers2[0]); + test.setTo(data.get(), buffers2[0].size()); + EXPECT_THAT(android::util::GetString(test, 0), "this fits1"); + EXPECT_THAT(android::util::GetString(test, 1), "STRING_TOO_LARGE"); + EXPECT_THAT(android::util::GetString(test, 2), "this fits2"); + + // Make sure a string that a string that exceeds the maximum length of UTF-8 + // but not UTF-16 does not error for UTF-16 + StringPool pool3; + std::u16string longStr16(50000, 'a'); + pool3.MakeRef(longStr); + EXPECT_THAT(StringPool::FlattenUtf16(&buffers2[1], pool3, &diag), Eq(true)); + data = android::util::Copy(buffers2[1]); + test.setTo(data.get(), buffers2[1].size()); + EXPECT_THAT(android::util::GetString16(test, 0), Eq(longStr16)); +} + +} // namespace android diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index cee3e5ca5da7..7efe3c3472fa 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -137,7 +137,6 @@ cc_library_host_static { "text/Printer.cpp", "text/Unicode.cpp", "text/Utf8Iterator.cpp", - "util/BigBuffer.cpp", "util/Files.cpp", "util/Util.cpp", "Debug.cpp", @@ -154,7 +153,6 @@ cc_library_host_static { "ResourceUtils.cpp", "ResourceValues.cpp", "SdkConstants.cpp", - "StringPool.cpp", "trace/TraceBuffer.cpp", "xml/XmlActionExecutor.cpp", "xml/XmlDom.cpp", diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp index 41896f622228..dfa229173373 100644 --- a/tools/aapt2/Debug.cpp +++ b/tools/aapt2/Debug.cpp @@ -17,6 +17,7 @@ #include "Debug.h" #include +#include #include #include @@ -592,12 +593,12 @@ using namespace android; class ChunkPrinter { public: - ChunkPrinter(const void* data, size_t len, Printer* printer, IDiagnostics* diag) + ChunkPrinter(const void* data, size_t len, Printer* printer, android::IDiagnostics* diag) : data_(data), data_len_(len), printer_(printer), diag_(diag) { } void PrintChunkHeader(const ResChunk_header* chunk) { - switch (util::DeviceToHost16(chunk->type)) { + switch (android::util::DeviceToHost16(chunk->type)) { case RES_STRING_POOL_TYPE: printer_->Print("[RES_STRING_POOL_TYPE]"); break; @@ -620,13 +621,14 @@ class ChunkPrinter { break; } - printer_->Print(StringPrintf(" chunkSize: %u", util::DeviceToHost32(chunk->size))); - printer_->Print(StringPrintf(" headerSize: %u", util::DeviceToHost32(chunk->headerSize))); + printer_->Print(StringPrintf(" chunkSize: %u", android::util::DeviceToHost32(chunk->size))); + printer_->Print( + StringPrintf(" headerSize: %u", android::util::DeviceToHost32(chunk->headerSize))); } bool PrintTable(const ResTable_header* chunk) { printer_->Print( - StringPrintf(" Package count: %u\n", util::DeviceToHost32(chunk->packageCount))); + StringPrintf(" Package count: %u\n", android::util::DeviceToHost32(chunk->packageCount))); // Print the chunks contained within the table printer_->Indent(); @@ -639,9 +641,10 @@ class ChunkPrinter { void PrintResValue(const Res_value* value, const ConfigDescription& config, const ResourceType* type) { printer_->Print("[Res_value]"); - printer_->Print(StringPrintf(" size: %u", util::DeviceToHost32(value->size))); - printer_->Print(StringPrintf(" dataType: 0x%02x", util::DeviceToHost32(value->dataType))); - printer_->Print(StringPrintf(" data: 0x%08x", util::DeviceToHost32(value->data))); + printer_->Print(StringPrintf(" size: %u", android::util::DeviceToHost32(value->size))); + printer_->Print( + StringPrintf(" dataType: 0x%02x", android::util::DeviceToHost32(value->dataType))); + printer_->Print(StringPrintf(" data: 0x%08x", android::util::DeviceToHost32(value->data))); if (type) { auto item = @@ -655,19 +658,23 @@ class ChunkPrinter { } bool PrintTableType(const ResTable_type* chunk) { - printer_->Print(StringPrintf(" id: 0x%02x", util::DeviceToHost32(chunk->id))); + printer_->Print(StringPrintf(" id: 0x%02x", android::util::DeviceToHost32(chunk->id))); printer_->Print(StringPrintf( - " name: %s", util::GetString(type_pool_, util::DeviceToHost32(chunk->id) - 1).c_str())); - printer_->Print(StringPrintf(" flags: 0x%02x", util::DeviceToHost32(chunk->flags))); - printer_->Print(StringPrintf(" entryCount: %u", util::DeviceToHost32(chunk->entryCount))); - printer_->Print(StringPrintf(" entryStart: %u", util::DeviceToHost32(chunk->entriesStart))); + " name: %s", + android::util::GetString(type_pool_, android::util::DeviceToHost32(chunk->id) - 1) + .c_str())); + printer_->Print(StringPrintf(" flags: 0x%02x", android::util::DeviceToHost32(chunk->flags))); + printer_->Print( + StringPrintf(" entryCount: %u", android::util::DeviceToHost32(chunk->entryCount))); + printer_->Print( + StringPrintf(" entryStart: %u", android::util::DeviceToHost32(chunk->entriesStart))); ConfigDescription config; config.copyFromDtoH(chunk->config); printer_->Print(StringPrintf(" config: %s\n", config.to_string().c_str())); - const ResourceType* type = - ParseResourceType(util::GetString(type_pool_, util::DeviceToHost32(chunk->id) - 1)); + const ResourceType* type = ParseResourceType( + android::util::GetString(type_pool_, android::util::DeviceToHost32(chunk->id) - 1)); printer_->Indent(); @@ -682,35 +689,42 @@ class ChunkPrinter { : "[ResTable_entry]"); printer_->Print(StringPrintf(" id: 0x%04x", it.index())); printer_->Print(StringPrintf( - " name: %s", util::GetString(key_pool_, util::DeviceToHost32(entry->key.index)).c_str())); - printer_->Print(StringPrintf(" keyIndex: %u", util::DeviceToHost32(entry->key.index))); - printer_->Print(StringPrintf(" size: %u", util::DeviceToHost32(entry->size))); - printer_->Print(StringPrintf(" flags: 0x%04x", util::DeviceToHost32(entry->flags))); + " name: %s", + android::util::GetString(key_pool_, android::util::DeviceToHost32(entry->key.index)) + .c_str())); + printer_->Print( + StringPrintf(" keyIndex: %u", android::util::DeviceToHost32(entry->key.index))); + printer_->Print(StringPrintf(" size: %u", android::util::DeviceToHost32(entry->size))); + printer_->Print(StringPrintf(" flags: 0x%04x", android::util::DeviceToHost32(entry->flags))); printer_->Indent(); if (entry->flags & ResTable_entry::FLAG_COMPLEX) { auto map_entry = (const ResTable_map_entry*)entry; - printer_->Print(StringPrintf(" count: 0x%04x", util::DeviceToHost32(map_entry->count))); printer_->Print( - StringPrintf(" parent: 0x%08x\n", util::DeviceToHost32(map_entry->parent.ident))); + StringPrintf(" count: 0x%04x", android::util::DeviceToHost32(map_entry->count))); + printer_->Print(StringPrintf(" parent: 0x%08x\n", + android::util::DeviceToHost32(map_entry->parent.ident))); // Print the name and value mappings - auto maps = - (const ResTable_map*)((const uint8_t*)entry + util::DeviceToHost32(entry->size)); - for (size_t i = 0, count = util::DeviceToHost32(map_entry->count); i < count; i++) { + auto maps = (const ResTable_map*)((const uint8_t*)entry + + android::util::DeviceToHost32(entry->size)); + for (size_t i = 0, count = android::util::DeviceToHost32(map_entry->count); i < count; + i++) { PrintResValue(&(maps[i].value), config, type); printer_->Print(StringPrintf( " name: %s name-id:%d\n", - util::GetString(key_pool_, util::DeviceToHost32(maps[i].name.ident)).c_str(), - util::DeviceToHost32(maps[i].name.ident))); + android::util::GetString(key_pool_, android::util::DeviceToHost32(maps[i].name.ident)) + .c_str(), + android::util::DeviceToHost32(maps[i].name.ident))); } } else { printer_->Print("\n"); // Print the value of the entry - auto value = (const Res_value*)((const uint8_t*)entry + util::DeviceToHost32(entry->size)); + auto value = + (const Res_value*)((const uint8_t*)entry + android::util::DeviceToHost32(entry->size)); PrintResValue(value, config, type); } @@ -735,33 +749,37 @@ class ChunkPrinter { return; } - pool->setTo(chunk, - util::DeviceToHost32((reinterpret_cast(chunk))->size)); + pool->setTo(chunk, android::util::DeviceToHost32( + (reinterpret_cast(chunk))->size)); printer_->Print("\n"); for (size_t i = 0; i < pool->size(); i++) { - printer_->Print(StringPrintf("#%zd : %s\n", i, util::GetString(*pool, i).c_str())); + printer_->Print(StringPrintf("#%zd : %s\n", i, android::util::GetString(*pool, i).c_str())); } } bool PrintPackage(const ResTable_package* chunk) { - printer_->Print(StringPrintf(" id: 0x%02x", util::DeviceToHost32(chunk->id))); + printer_->Print(StringPrintf(" id: 0x%02x", android::util::DeviceToHost32(chunk->id))); size_t len = strnlen16((const char16_t*)chunk->name, std::size(chunk->name)); std::u16string package_name(len, u'\0'); package_name.resize(len); for (size_t i = 0; i < len; i++) { - package_name[i] = util::DeviceToHost16(chunk->name[i]); + package_name[i] = android::util::DeviceToHost16(chunk->name[i]); } printer_->Print(StringPrintf("name: %s", String8(package_name.c_str()).c_str())); - printer_->Print(StringPrintf(" typeStrings: %u", util::DeviceToHost32(chunk->typeStrings))); printer_->Print( - StringPrintf(" lastPublicType: %u", util::DeviceToHost32(chunk->lastPublicType))); - printer_->Print(StringPrintf(" keyStrings: %u", util::DeviceToHost32(chunk->keyStrings))); - printer_->Print(StringPrintf(" lastPublicKey: %u", util::DeviceToHost32(chunk->lastPublicKey))); - printer_->Print(StringPrintf(" typeIdOffset: %u\n", util::DeviceToHost32(chunk->typeIdOffset))); + StringPrintf(" typeStrings: %u", android::util::DeviceToHost32(chunk->typeStrings))); + printer_->Print( + StringPrintf(" lastPublicType: %u", android::util::DeviceToHost32(chunk->lastPublicType))); + printer_->Print( + StringPrintf(" keyStrings: %u", android::util::DeviceToHost32(chunk->keyStrings))); + printer_->Print( + StringPrintf(" lastPublicKey: %u", android::util::DeviceToHost32(chunk->lastPublicKey))); + printer_->Print( + StringPrintf(" typeIdOffset: %u\n", android::util::DeviceToHost32(chunk->typeIdOffset))); // Print the chunks contained within the table printer_->Indent(); @@ -776,7 +794,7 @@ class ChunkPrinter { auto chunk = parser.chunk(); PrintChunkHeader(chunk); - switch (util::DeviceToHost16(chunk->type)) { + switch (android::util::DeviceToHost16(chunk->type)) { case RES_STRING_POOL_TYPE: PrintStringPool(reinterpret_cast(chunk)); break; @@ -802,7 +820,7 @@ class ChunkPrinter { } if (parser.event() == ResChunkPullParser::Event::kBadDocument) { - diag_->Error(DiagMessage(source_) << "corrupt resource table: " << parser.error()); + diag_->Error(android::DiagMessage(source_) << "corrupt resource table: " << parser.error()); return false; } @@ -815,11 +833,11 @@ class ChunkPrinter { } private: - const Source source_; + const android::Source source_; const void* data_; const size_t data_len_; Printer* printer_; - IDiagnostics* diag_; + android::IDiagnostics* diag_; // The standard value string pool for resource values. ResStringPool value_pool_; @@ -832,12 +850,13 @@ class ChunkPrinter { // in this table. ResStringPool key_pool_; - StringPool out_pool_; + android::StringPool out_pool_; }; } // namespace -void Debug::DumpChunks(const void* data, size_t len, Printer* printer, IDiagnostics* diag) { +void Debug::DumpChunks(const void* data, size_t len, Printer* printer, + android::IDiagnostics* diag) { ChunkPrinter chunk_printer(data, len, printer, diag); chunk_printer.Print(); } diff --git a/tools/aapt2/Debug.h b/tools/aapt2/Debug.h index 4da92044cf2a..8015249e7d36 100644 --- a/tools/aapt2/Debug.h +++ b/tools/aapt2/Debug.h @@ -40,7 +40,8 @@ struct Debug { 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); - static void DumpChunks(const void* data, size_t len, text::Printer* printer, IDiagnostics* diag); + static void DumpChunks(const void* data, size_t len, text::Printer* printer, + android::IDiagnostics* diag); }; } // namespace aapt diff --git a/tools/aapt2/Diagnostics.h b/tools/aapt2/Diagnostics.h index 30deb5555b21..c89db725e6f2 100644 --- a/tools/aapt2/Diagnostics.h +++ b/tools/aapt2/Diagnostics.h @@ -13,86 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -#ifndef AAPT_DIAGNOSTICS_H -#define AAPT_DIAGNOSTICS_H +#ifndef AAPT_DIAGNOSTICS_H_ +#define AAPT_DIAGNOSTICS_H_ #include #include #include #include "android-base/macros.h" +#include "androidfw/IDiagnostics.h" +#include "androidfw/Source.h" #include "androidfw/StringPiece.h" - -#include "Source.h" #include "util/Util.h" namespace aapt { - -struct DiagMessageActual { - Source source; - std::string message; -}; - -struct DiagMessage { - public: - DiagMessage() = default; - - explicit DiagMessage(const android::StringPiece& src) : source_(src) {} - - explicit DiagMessage(const Source& src) : source_(src) {} - - explicit DiagMessage(size_t line) : source_(Source().WithLine(line)) {} - - template - DiagMessage& operator<<(const T& value) { - message_ << value; - return *this; - } - - DiagMessageActual Build() const { - return DiagMessageActual{source_, message_.str()}; - } - - private: - Source source_; - std::stringstream message_; -}; - -template <> -inline DiagMessage& DiagMessage::operator<<(const ::std::u16string& value) { - message_ << android::StringPiece16(value); - return *this; -} - -struct IDiagnostics { - virtual ~IDiagnostics() = default; - - enum class Level { Note, Warn, Error }; - - virtual void Log(Level level, DiagMessageActual& actualMsg) = 0; - - virtual void Error(const DiagMessage& message) { - DiagMessageActual actual = message.Build(); - Log(Level::Error, actual); - } - - virtual void Warn(const DiagMessage& message) { - DiagMessageActual actual = message.Build(); - Log(Level::Warn, actual); - } - - virtual void Note(const DiagMessage& message) { - DiagMessageActual actual = message.Build(); - Log(Level::Note, actual); - } -}; - -class StdErrDiagnostics : public IDiagnostics { +class StdErrDiagnostics : public android::IDiagnostics { public: StdErrDiagnostics() = default; - void Log(Level level, DiagMessageActual& actual_msg) override { + void Log(Level level, android::DiagMessageActual& actual_msg) override { const char* tag; switch (level) { @@ -125,31 +64,6 @@ class StdErrDiagnostics : public IDiagnostics { DISALLOW_COPY_AND_ASSIGN(StdErrDiagnostics); }; -class SourcePathDiagnostics : public IDiagnostics { - public: - SourcePathDiagnostics(const Source& src, IDiagnostics* diag) - : source_(src), diag_(diag) {} - - void Log(Level level, DiagMessageActual& actual_msg) override { - actual_msg.source.path = source_.path; - diag_->Log(level, actual_msg); - if (level == Level::Error) { - error = true; - } - } - - bool HadError() { - return error; - } - - private: - Source source_; - IDiagnostics* diag_; - bool error = false; - - DISALLOW_COPY_AND_ASSIGN(SourcePathDiagnostics); -}; - } // namespace aapt -#endif /* AAPT_DIAGNOSTICS_H */ +#endif /* AAPT_DIAGNOSTICS_H_ */ diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp index 830bc5fa36aa..9b9cde2f37da 100644 --- a/tools/aapt2/LoadedApk.cpp +++ b/tools/aapt2/LoadedApk.cpp @@ -72,12 +72,13 @@ static ApkFormat DetermineApkFormat(io::IFileCollection* apk) { } } -std::unique_ptr LoadedApk::LoadApkFromPath(const StringPiece& path, IDiagnostics* diag) { - Source source(path); +std::unique_ptr LoadedApk::LoadApkFromPath(const StringPiece& path, + android::IDiagnostics* diag) { + android::Source source(path); std::string error; std::unique_ptr apk = io::ZipFileCollection::Create(path, &error); if (apk == nullptr) { - diag->Error(DiagMessage(path) << "failed opening zip: " << error); + diag->Error(android::DiagMessage(path) << "failed opening zip: " << error); return {}; } @@ -88,13 +89,14 @@ std::unique_ptr LoadedApk::LoadApkFromPath(const StringPiece& path, I case ApkFormat::kProto: return LoadProtoApkFromFileCollection(source, std::move(apk), diag); default: - diag->Error(DiagMessage(path) << "could not identify format of APK"); + diag->Error(android::DiagMessage(path) << "could not identify format of APK"); return {}; } } std::unique_ptr LoadedApk::LoadProtoApkFromFileCollection( - const Source& source, unique_ptr collection, IDiagnostics* diag) { + const android::Source& source, unique_ptr collection, + android::IDiagnostics* diag) { std::unique_ptr table; io::IFile* table_file = collection->FindFile(kProtoResourceTablePath); @@ -102,20 +104,20 @@ std::unique_ptr LoadedApk::LoadProtoApkFromFileCollection( pb::ResourceTable pb_table; std::unique_ptr in = table_file->OpenInputStream(); if (in == nullptr) { - diag->Error(DiagMessage(source) << "failed to open " << kProtoResourceTablePath); + diag->Error(android::DiagMessage(source) << "failed to open " << kProtoResourceTablePath); return {}; } io::ProtoInputStreamReader proto_reader(in.get()); if (!proto_reader.ReadMessage(&pb_table)) { - diag->Error(DiagMessage(source) << "failed to read " << kProtoResourceTablePath); + diag->Error(android::DiagMessage(source) << "failed to read " << kProtoResourceTablePath); return {}; } std::string error; table = util::make_unique(ResourceTable::Validation::kDisabled); if (!DeserializeTableFromPb(pb_table, collection.get(), table.get(), &error)) { - diag->Error(DiagMessage(source) + diag->Error(android::DiagMessage(source) << "failed to deserialize " << kProtoResourceTablePath << ": " << error); return {}; } @@ -123,27 +125,27 @@ std::unique_ptr LoadedApk::LoadProtoApkFromFileCollection( io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath); if (manifest_file == nullptr) { - diag->Error(DiagMessage(source) << "failed to find " << kAndroidManifestPath); + diag->Error(android::DiagMessage(source) << "failed to find " << kAndroidManifestPath); return {}; } std::unique_ptr manifest_in = manifest_file->OpenInputStream(); if (manifest_in == nullptr) { - diag->Error(DiagMessage(source) << "failed to open " << kAndroidManifestPath); + diag->Error(android::DiagMessage(source) << "failed to open " << kAndroidManifestPath); return {}; } pb::XmlNode pb_node; io::ProtoInputStreamReader proto_reader(manifest_in.get()); if (!proto_reader.ReadMessage(&pb_node)) { - diag->Error(DiagMessage(source) << "failed to read proto " << kAndroidManifestPath); + diag->Error(android::DiagMessage(source) << "failed to read proto " << kAndroidManifestPath); return {}; } std::string error; std::unique_ptr manifest = DeserializeXmlResourceFromPb(pb_node, &error); if (manifest == nullptr) { - diag->Error(DiagMessage(source) + diag->Error(android::DiagMessage(source) << "failed to deserialize proto " << kAndroidManifestPath << ": " << error); return {}; } @@ -152,7 +154,8 @@ std::unique_ptr LoadedApk::LoadProtoApkFromFileCollection( } std::unique_ptr LoadedApk::LoadBinaryApkFromFileCollection( - const Source& source, unique_ptr collection, IDiagnostics* diag) { + const android::Source& source, unique_ptr collection, + android::IDiagnostics* diag) { std::unique_ptr table; io::IFile* table_file = collection->FindFile(kApkResourceTablePath); @@ -160,7 +163,7 @@ std::unique_ptr LoadedApk::LoadBinaryApkFromFileCollection( table = util::make_unique(ResourceTable::Validation::kDisabled); std::unique_ptr data = table_file->OpenAsData(); if (data == nullptr) { - diag->Error(DiagMessage(source) << "failed to open " << kApkResourceTablePath); + diag->Error(android::DiagMessage(source) << "failed to open " << kApkResourceTablePath); return {}; } BinaryResourceParser parser(diag, table.get(), source, data->data(), data->size(), @@ -172,13 +175,13 @@ std::unique_ptr LoadedApk::LoadBinaryApkFromFileCollection( io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath); if (manifest_file == nullptr) { - diag->Error(DiagMessage(source) << "failed to find " << kAndroidManifestPath); + diag->Error(android::DiagMessage(source) << "failed to find " << kAndroidManifestPath); return {}; } std::unique_ptr manifest_data = manifest_file->OpenAsData(); if (manifest_data == nullptr) { - diag->Error(DiagMessage(source) << "failed to open " << kAndroidManifestPath); + diag->Error(android::DiagMessage(source) << "failed to open " << kAndroidManifestPath); return {}; } @@ -186,7 +189,7 @@ std::unique_ptr LoadedApk::LoadBinaryApkFromFileCollection( std::unique_ptr manifest = xml::Inflate(manifest_data->data(), manifest_data->size(), &error); if (manifest == nullptr) { - diag->Error(DiagMessage(source) + diag->Error(android::DiagMessage(source) << "failed to parse binary " << kAndroidManifestPath << ": " << error); return {}; } @@ -235,7 +238,7 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table // Skip resources that are not referenced if requested. if (is_resource && referenced_resources.find(output_path) == referenced_resources.end()) { if (context->IsVerbose()) { - context->GetDiagnostics()->Note(DiagMessage() + context->GetDiagnostics()->Note(android::DiagMessage() << "Removing resource '" << path << "' from APK."); } continue; @@ -243,14 +246,15 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table if (!filters->Keep(path)) { if (context->IsVerbose()) { - context->GetDiagnostics()->Note(DiagMessage() << "Filtered '" << path << "' from APK."); + context->GetDiagnostics()->Note(android::DiagMessage() + << "Filtered '" << path << "' from APK."); } continue; } // The resource table needs to be re-serialized since it might have changed. if (format_ == ApkFormat::kBinary && path == kApkResourceTablePath) { - BigBuffer buffer(4096); + android::BigBuffer buffer(4096); // TODO(adamlesinski): How to determine if there were sparse entries (and if to encode // with sparse entries) b/35389232. TableFlattener flattener(options, &buffer); @@ -282,12 +286,12 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table return false; } } else if (manifest != nullptr && path == "AndroidManifest.xml") { - BigBuffer buffer(8192); + android::BigBuffer buffer(8192); XmlFlattenerOptions xml_flattener_options; xml_flattener_options.use_utf16 = true; XmlFlattener xml_flattener(&buffer, xml_flattener_options); if (!xml_flattener.Consume(context, manifest)) { - context->GetDiagnostics()->Error(DiagMessage(path) << "flattening failed"); + context->GetDiagnostics()->Error(android::DiagMessage(path) << "flattening failed"); return false; } @@ -308,10 +312,10 @@ bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table } std::unique_ptr LoadedApk::LoadXml(const std::string& file_path, - IDiagnostics* diag) const { + android::IDiagnostics* diag) const { io::IFile* file = apk_->FindFile(file_path); if (file == nullptr) { - diag->Error(DiagMessage() << "failed to find file"); + diag->Error(android::DiagMessage() << "failed to find file"); return nullptr; } @@ -319,34 +323,34 @@ std::unique_ptr LoadedApk::LoadXml(const std::string& file_pat if (format_ == ApkFormat::kProto) { std::unique_ptr in = file->OpenInputStream(); if (!in) { - diag->Error(DiagMessage() << "failed to open file"); + diag->Error(android::DiagMessage() << "failed to open file"); return nullptr; } pb::XmlNode pb_node; io::ProtoInputStreamReader proto_reader(in.get()); if (!proto_reader.ReadMessage(&pb_node)) { - diag->Error(DiagMessage() << "failed to parse file as proto XML"); + diag->Error(android::DiagMessage() << "failed to parse file as proto XML"); return nullptr; } std::string err; doc = DeserializeXmlResourceFromPb(pb_node, &err); if (!doc) { - diag->Error(DiagMessage() << "failed to deserialize proto XML: " << err); + diag->Error(android::DiagMessage() << "failed to deserialize proto XML: " << err); return nullptr; } } else if (format_ == ApkFormat::kBinary) { std::unique_ptr data = file->OpenAsData(); if (!data) { - diag->Error(DiagMessage() << "failed to open file"); + diag->Error(android::DiagMessage() << "failed to open file"); return nullptr; } std::string err; doc = xml::Inflate(data->data(), data->size(), &err); if (!doc) { - diag->Error(DiagMessage() << "failed to parse file as binary XML: " << err); + diag->Error(android::DiagMessage() << "failed to parse file as binary XML: " << err); return nullptr; } } diff --git a/tools/aapt2/LoadedApk.h b/tools/aapt2/LoadedApk.h index 5b6f45ebb38d..a4aff3f8376a 100644 --- a/tools/aapt2/LoadedApk.h +++ b/tools/aapt2/LoadedApk.h @@ -46,17 +46,19 @@ class LoadedApk { // Loads both binary and proto APKs from disk. static std::unique_ptr LoadApkFromPath(const ::android::StringPiece& path, - IDiagnostics* diag); + android::IDiagnostics* diag); // Loads a proto APK from the given file collection. static std::unique_ptr LoadProtoApkFromFileCollection( - const Source& source, std::unique_ptr collection, IDiagnostics* diag); + const android::Source& source, std::unique_ptr collection, + android::IDiagnostics* diag); // Loads a binary APK from the given file collection. static std::unique_ptr LoadBinaryApkFromFileCollection( - const Source& source, std::unique_ptr collection, IDiagnostics* diag); + const android::Source& source, std::unique_ptr collection, + android::IDiagnostics* diag); - LoadedApk(const Source& source, std::unique_ptr apk, + LoadedApk(const android::Source& source, std::unique_ptr apk, std::unique_ptr table, std::unique_ptr manifest, const ApkFormat& format) : source_(source), @@ -82,7 +84,7 @@ class LoadedApk { return table_.get(); } - const Source& GetSource() { + const android::Source& GetSource() { return source_; } @@ -111,12 +113,13 @@ class LoadedApk { IArchiveWriter* writer, xml::XmlResource* manifest = nullptr); /** Loads the file as an xml document. */ - std::unique_ptr LoadXml(const std::string& file_path, IDiagnostics* diag) const; + std::unique_ptr LoadXml(const std::string& file_path, + android::IDiagnostics* diag) const; private: DISALLOW_COPY_AND_ASSIGN(LoadedApk); - Source source_; + android::Source source_; std::unique_ptr apk_; std::unique_ptr table_; std::unique_ptr manifest_; diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index e47bd67c4b8d..a0b4dab9b8e5 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -27,6 +27,7 @@ #include "Diagnostics.h" #include "android-base/stringprintf.h" #include "android-base/utf8.h" +#include "androidfw/IDiagnostics.h" #include "androidfw/StringPiece.h" #include "cmd/ApkInfo.h" #include "cmd/Command.h" @@ -63,7 +64,7 @@ class VersionCommand : public Command { /** The main entry point of AAPT. */ class MainCommand : public Command { public: - explicit MainCommand(text::Printer* printer, IDiagnostics* diagnostics) + explicit MainCommand(text::Printer* printer, android::IDiagnostics* diagnostics) : Command("aapt2"), diagnostics_(diagnostics) { AddOptionalSubcommand(util::make_unique(diagnostics)); AddOptionalSubcommand(util::make_unique(diagnostics)); @@ -77,9 +78,9 @@ class MainCommand : public Command { int Action(const std::vector& args) override { if (args.size() == 0) { - diagnostics_->Error(DiagMessage() << "no subcommand specified"); + diagnostics_->Error(android::DiagMessage() << "no subcommand specified"); } else { - diagnostics_->Error(DiagMessage() << "unknown subcommand '" << args[0] << "'"); + diagnostics_->Error(android::DiagMessage() << "unknown subcommand '" << args[0] << "'"); } Usage(&std::cerr); @@ -87,7 +88,7 @@ class MainCommand : public Command { } private: - IDiagnostics* diagnostics_; + android::IDiagnostics* diagnostics_; }; /* @@ -98,7 +99,7 @@ class MainCommand : public Command { */ class DaemonCommand : public Command { public: - explicit DaemonCommand(io::FileOutputStream* out, IDiagnostics* diagnostics) + explicit DaemonCommand(io::FileOutputStream* out, android::IDiagnostics* diagnostics) : Command("daemon", "m"), out_(out), diagnostics_(diagnostics) { SetDescription("Runs aapt in daemon mode. Each subsequent line is a single parameter to the\n" "command. The end of an invocation is signaled by providing an empty line."); @@ -147,7 +148,7 @@ class DaemonCommand : public Command { private: io::FileOutputStream* out_; - IDiagnostics* diagnostics_; + android::IDiagnostics* diagnostics_; std::optional trace_folder_; }; diff --git a/tools/aapt2/Resource.h b/tools/aapt2/Resource.h index b41d8514230b..9cfaf4742ca5 100644 --- a/tools/aapt2/Resource.h +++ b/tools/aapt2/Resource.h @@ -25,8 +25,8 @@ #include #include -#include "Source.h" #include "androidfw/ConfigDescription.h" +#include "androidfw/Source.h" #include "androidfw/StringPiece.h" #include "utils/JenkinsHash.h" @@ -228,7 +228,7 @@ struct ResourceFile { Type type; // Source - Source source; + android::Source source; // Exported symbols std::vector exported_symbols; diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index a99e4b234c6b..19fd306d5a42 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -102,7 +102,7 @@ struct ParsedResource { ResourceName name; ConfigDescription config; std::string product; - Source source; + android::Source source; ResourceId id; Visibility::Level visibility_level = Visibility::Level::kUndefined; @@ -117,7 +117,8 @@ struct ParsedResource { }; // Recursively adds resources to the ResourceTable. -static bool AddResourcesToTable(ResourceTable* table, IDiagnostics* diag, ParsedResource* res) { +static bool AddResourcesToTable(ResourceTable* table, android::IDiagnostics* diag, + ParsedResource* res) { StringPiece trimmed_comment = util::TrimWhitespace(res->comment); if (trimmed_comment.size() != res->comment.size()) { // Only if there was a change do we re-assign. @@ -175,15 +176,11 @@ static bool AddResourcesToTable(ResourceTable* table, IDiagnostics* diag, Parsed // Convenient aliases for more readable function calls. enum { kAllowRawString = true, kNoRawString = false }; -ResourceParser::ResourceParser(IDiagnostics* diag, ResourceTable* table, - const Source& source, - const ConfigDescription& config, +ResourceParser::ResourceParser(android::IDiagnostics* diag, ResourceTable* table, + const android::Source& source, const ConfigDescription& config, const ResourceParserOptions& options) - : diag_(diag), - table_(table), - source_(source), - config_(config), - options_(options) {} + : diag_(diag), table_(table), source_(source), config_(config), options_(options) { +} // Base class Node for representing the various Spans and UntranslatableSections of an XML string. // This will be used to traverse and flatten the XML string into a single std::string, with all @@ -245,7 +242,7 @@ class UntranslatableNode : public Node { // Build a string from XML that converts nested elements into Span objects. bool ResourceParser::FlattenXmlSubtree( - xml::XmlPullParser* parser, std::string* out_raw_string, StyleString* out_style_string, + xml::XmlPullParser* parser, std::string* out_raw_string, android::StyleString* out_style_string, std::vector* out_untranslatable_sections) { std::string raw_string; std::string current_text; @@ -308,7 +305,7 @@ bool ResourceParser::FlattenXmlSubtree( // Check that an 'untranslatable' tag is not already being processed. Nested // tags are illegal. if (untranslatable_start_depth) { - diag_->Error(DiagMessage(source_.WithLine(parser->line_number())) + diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << "illegal nested XLIFF 'g' tag"); return false; } else { @@ -323,7 +320,7 @@ bool ResourceParser::FlattenXmlSubtree( } } else { // Besides XLIFF, any other namespaced tag is unsupported and ignored. - diag_->Warn(DiagMessage(source_.WithLine(parser->line_number())) + diag_->Warn(android::DiagMessage(source_.WithLine(parser->line_number())) << "ignoring element '" << parser->element_name() << "' with unknown namespace '" << parser->element_namespace() << "'"); node_stack.push_back(node_stack.back()->AddChild(util::make_unique())); @@ -383,7 +380,8 @@ bool ResourceParser::FlattenXmlSubtree( StringBuilder builder; root.Build(&builder); if (!builder) { - diag_->Error(DiagMessage(source_.WithLine(parser->line_number())) << builder.GetError()); + diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) + << builder.GetError()); return false; } @@ -405,7 +403,7 @@ bool ResourceParser::Parse(xml::XmlPullParser* parser) { } if (!parser->element_namespace().empty() || parser->element_name() != "resources") { - diag_->Error(DiagMessage(source_.WithLine(parser->line_number())) + diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << "root element must be "); return false; } @@ -415,7 +413,7 @@ bool ResourceParser::Parse(xml::XmlPullParser* parser) { }; if (parser->event() == xml::XmlPullParser::Event::kBadDocument) { - diag_->Error(DiagMessage(source_.WithLine(parser->line_number())) + diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << "xml parser error: " << parser->error()); return false; } @@ -437,7 +435,7 @@ bool ResourceParser::ParseResources(xml::XmlPullParser* parser) { if (event == xml::XmlPullParser::Event::kText) { if (!util::TrimWhitespace(parser->text()).empty()) { - diag_->Error(DiagMessage(source_.WithLine(parser->line_number())) + diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << "plain text not allowed here"); error = true; } @@ -486,8 +484,9 @@ bool ResourceParser::ParseResources(xml::XmlPullParser* parser) { for (const ResourceName& stripped_resource : stripped_resources) { if (!table_->FindResource(stripped_resource)) { // Failed to find the resource. - diag_->Error(DiagMessage(source_) << "resource '" << stripped_resource - << "' was filtered out but no product variant remains"); + diag_->Error(android::DiagMessage(source_) + << "resource '" << stripped_resource + << "' was filtered out but no product variant remains"); error = true; } } @@ -562,7 +561,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, if (std::optional maybe_type = xml::FindNonEmptyAttribute(parser, "type")) { resource_type = maybe_type.value().to_string(); } else { - diag_->Error(DiagMessage(source_.WithLine(parser->line_number())) + diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << " must have a 'type' attribute"); return false; } @@ -573,9 +572,8 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, // overridden. resource_format = ParseFormatTypeNoEnumsOrFlags(maybe_format.value()); if (!resource_format) { - diag_->Error(DiagMessage(out_resource->source) - << "'" << maybe_format.value() - << "' is an invalid format"); + diag_->Error(android::DiagMessage(out_resource->source) + << "'" << maybe_format.value() << "' is an invalid format"); return false; } } @@ -586,7 +584,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, if (std::optional maybe_type = xml::FindNonEmptyAttribute(parser, "type")) { resource_type = maybe_type.value().to_string(); } else { - diag_->Error(DiagMessage(source_.WithLine(parser->line_number())) + diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << " must have a 'type' attribute"); return false; } @@ -598,9 +596,8 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, if (resource_type == "id") { if (!maybe_name) { - diag_->Error(DiagMessage(out_resource->source) - << "<" << parser->element_name() - << "> missing 'name' attribute"); + diag_->Error(android::DiagMessage(out_resource->source) + << "<" << parser->element_name() << "> missing 'name' attribute"); return false; } @@ -626,9 +623,9 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, out_resource->value = util::make_unique(); } else if (!ref || ref->name.value().type.type != ResourceType::kId) { // If an inner element exists, the inner element must be a reference to another resource id - diag_->Error(DiagMessage(out_resource->source) - << "<" << parser->element_name() - << "> inner element must either be a resource reference or empty"); + diag_->Error(android::DiagMessage(out_resource->source) + << "<" << parser->element_name() + << "> inner element must either be a resource reference or empty"); return false; } } @@ -636,7 +633,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, return true; } else if (resource_type == "macro") { if (!maybe_name) { - diag_->Error(DiagMessage(out_resource->source) + diag_->Error(android::DiagMessage(out_resource->source) << "<" << parser->element_name() << "> missing 'name' attribute"); return false; } @@ -653,7 +650,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, // This is an item, record its type and format and start parsing. if (!maybe_name) { - diag_->Error(DiagMessage(out_resource->source) + diag_->Error(android::DiagMessage(out_resource->source) << "<" << parser->element_name() << "> missing 'name' attribute"); return false; } @@ -682,7 +679,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, if (resource_type != kPublicGroupTag && resource_type != kStagingPublicGroupTag && resource_type != kStagingPublicGroupFinalTag && resource_type != "overlayable") { if (!maybe_name) { - diag_->Error(DiagMessage(out_resource->source) + diag_->Error(android::DiagMessage(out_resource->source) << "<" << parser->element_name() << "> missing 'name' attribute"); return false; } @@ -705,9 +702,8 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, std::optional parsed_type = ParseResourceNamedType(resource_type); if (parsed_type) { if (!maybe_name) { - diag_->Error(DiagMessage(out_resource->source) - << "<" << parser->element_name() - << "> missing 'name' attribute"); + diag_->Error(android::DiagMessage(out_resource->source) + << "<" << parser->element_name() << "> missing 'name' attribute"); return false; } @@ -715,7 +711,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, out_resource->name.entry = maybe_name.value().to_string(); out_resource->value = ParseXml(parser, android::ResTable_map::TYPE_REFERENCE, kNoRawString); if (!out_resource->value) { - diag_->Error(DiagMessage(out_resource->source) + diag_->Error(android::DiagMessage(out_resource->source) << "invalid value for type '" << *parsed_type << "'. Expected a reference"); return false; } @@ -724,8 +720,8 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser, } // If the resource type was not recognized, write the error and return false. - diag_->Error(DiagMessage(out_resource->source) - << "unknown resource type '" << resource_type << "'"); + diag_->Error(android::DiagMessage(out_resource->source) + << "unknown resource type '" << resource_type << "'"); return false; } @@ -738,8 +734,8 @@ bool ResourceParser::ParseItem(xml::XmlPullParser* parser, out_resource->value = ParseXml(parser, format, kNoRawString); if (!out_resource->value) { - diag_->Error(DiagMessage(out_resource->source) << "invalid " - << out_resource->name.type); + diag_->Error(android::DiagMessage(out_resource->source) + << "invalid " << out_resource->name.type); return false; } return true; @@ -750,7 +746,7 @@ std::optional ResourceParser::CreateFlattenSubTree( const size_t begin_xml_line = parser->line_number(); std::string raw_value; - StyleString style_string; + android::StyleString style_string; std::vector untranslatable_sections; if (!FlattenXmlSubtree(parser, &raw_value, &style_string, &untranslatable_sections)) { return {}; @@ -783,13 +779,13 @@ std::unique_ptr ResourceParser::ParseXml(const FlattenedXmlSubTree& xmlsub const uint32_t type_mask, const bool allow_raw_value, ResourceTable& table, const android::ConfigDescription& config, - IDiagnostics& diag) { + android::IDiagnostics& diag) { if (!xmlsub_tree.style_string.spans.empty()) { // This can only be a StyledString. std::unique_ptr styled_string = util::make_unique(table.string_pool.MakeRef( xmlsub_tree.style_string, - StringPool::Context(StringPool::Context::kNormalPriority, config))); + android::StringPool::Context(android::StringPool::Context::kNormalPriority, config))); styled_string->untranslatable_sections = xmlsub_tree.untranslatable_sections; return std::move(styled_string); } @@ -817,8 +813,8 @@ std::unique_ptr ResourceParser::ParseXml(const FlattenedXmlSubTree& xmlsub // Try making a regular string. if (type_mask & android::ResTable_map::TYPE_STRING) { // Use the trimmed, escaped string. - std::unique_ptr string = util::make_unique( - table.string_pool.MakeRef(xmlsub_tree.style_string.str, StringPool::Context(config))); + std::unique_ptr string = util::make_unique(table.string_pool.MakeRef( + xmlsub_tree.style_string.str, android::StringPool::Context(config))); string->untranslatable_sections = xmlsub_tree.untranslatable_sections; return std::move(string); } @@ -826,7 +822,7 @@ std::unique_ptr ResourceParser::ParseXml(const FlattenedXmlSubTree& xmlsub if (allow_raw_value) { // We can't parse this so return a RawString if we are allowed. return util::make_unique(table.string_pool.MakeRef( - util::TrimWhitespace(xmlsub_tree.raw_value), StringPool::Context(config))); + util::TrimWhitespace(xmlsub_tree.raw_value), android::StringPool::Context(config))); } else if (util::TrimWhitespace(xmlsub_tree.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(); @@ -840,7 +836,7 @@ bool ResourceParser::ParseString(xml::XmlPullParser* parser, if (std::optional formatted_attr = xml::FindAttribute(parser, "formatted")) { std::optional maybe_formatted = ResourceUtils::ParseBool(formatted_attr.value()); if (!maybe_formatted) { - diag_->Error(DiagMessage(out_resource->source) + diag_->Error(android::DiagMessage(out_resource->source) << "invalid value for 'formatted'. Must be a boolean"); return false; } @@ -851,7 +847,7 @@ bool ResourceParser::ParseString(xml::XmlPullParser* parser, if (std::optional translatable_attr = xml::FindAttribute(parser, "translatable")) { std::optional maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value()); if (!maybe_translatable) { - diag_->Error(DiagMessage(out_resource->source) + diag_->Error(android::DiagMessage(out_resource->source) << "invalid value for 'translatable'. Must be a boolean"); return false; } @@ -861,7 +857,7 @@ bool ResourceParser::ParseString(xml::XmlPullParser* parser, out_resource->value = ParseXml(parser, android::ResTable_map::TYPE_STRING, kNoRawString); if (!out_resource->value) { - diag_->Error(DiagMessage(out_resource->source) << "not a valid string"); + diag_->Error(android::DiagMessage(out_resource->source) << "not a valid string"); return false; } @@ -870,7 +866,7 @@ bool ResourceParser::ParseString(xml::XmlPullParser* parser, if (formatted && translatable) { if (!util::VerifyJavaStringFormat(*string_value->value)) { - DiagMessage msg(out_resource->source); + android::DiagMessage msg(out_resource->source); msg << "multiple substitutions specified in non-positional format; " "did you mean to add the formatted=\"false\" attribute?"; if (options_.error_on_positional_arguments) { @@ -895,7 +891,7 @@ bool ResourceParser::ParseMacro(xml::XmlPullParser* parser, ParsedResource* out_ } if (out_resource->config != ConfigDescription::DefaultConfig()) { - diag_->Error(DiagMessage(out_resource->source) + diag_->Error(android::DiagMessage(out_resource->source) << " tags cannot be declared in configurations other than the default " "configuration'"); return false; @@ -919,28 +915,27 @@ bool ResourceParser::ParseMacro(xml::XmlPullParser* parser, ParsedResource* out_ bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource) { if (options_.visibility) { - diag_->Error(DiagMessage(out_resource->source) + diag_->Error(android::DiagMessage(out_resource->source) << " tag not allowed with --visibility flag"); return false; } if (out_resource->config != ConfigDescription::DefaultConfig()) { - diag_->Warn(DiagMessage(out_resource->source) + diag_->Warn(android::DiagMessage(out_resource->source) << "ignoring configuration '" << out_resource->config << "' for tag"); } std::optional maybe_type = xml::FindNonEmptyAttribute(parser, "type"); if (!maybe_type) { - diag_->Error(DiagMessage(out_resource->source) + diag_->Error(android::DiagMessage(out_resource->source) << " must have a 'type' attribute"); return false; } std::optional parsed_type = ParseResourceNamedType(maybe_type.value()); if (!parsed_type) { - diag_->Error(DiagMessage(out_resource->source) << "invalid resource type '" - << maybe_type.value() - << "' in "); + diag_->Error(android::DiagMessage(out_resource->source) + << "invalid resource type '" << maybe_type.value() << "' in "); return false; } @@ -949,7 +944,7 @@ bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out if (std::optional maybe_id_str = xml::FindNonEmptyAttribute(parser, "id")) { std::optional maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value()); if (!maybe_id) { - diag_->Error(DiagMessage(out_resource->source) + diag_->Error(android::DiagMessage(out_resource->source) << "invalid resource ID '" << maybe_id_str.value() << "' in "); return false; } @@ -967,16 +962,16 @@ bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out template bool static ParseGroupImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, - const char* tag_name, IDiagnostics* diag, Func&& func) { + const char* tag_name, android::IDiagnostics* diag, Func&& func) { if (out_resource->config != ConfigDescription::DefaultConfig()) { - diag->Warn(DiagMessage(out_resource->source) + diag->Warn(android::DiagMessage(out_resource->source) << "ignoring configuration '" << out_resource->config << "' for <" << tag_name << "> tag"); } std::optional maybe_type = xml::FindNonEmptyAttribute(parser, "type"); if (!maybe_type) { - diag->Error(DiagMessage(out_resource->source) + diag->Error(android::DiagMessage(out_resource->source) << "<" << tag_name << "> must have a 'type' attribute"); return false; } @@ -984,7 +979,7 @@ bool static ParseGroupImpl(xml::XmlPullParser* parser, ParsedResource* out_resou std::optional maybe_parsed_type = ParseResourceNamedType(maybe_type.value()); if (!maybe_parsed_type) { - diag->Error(DiagMessage(out_resource->source) + diag->Error(android::DiagMessage(out_resource->source) << "invalid resource type '" << maybe_type.value() << "' in <" << tag_name << ">"); return false; } @@ -992,14 +987,14 @@ bool static ParseGroupImpl(xml::XmlPullParser* parser, ParsedResource* out_resou std::optional maybe_id_str = xml::FindNonEmptyAttribute(parser, "first-id"); if (!maybe_id_str) { - diag->Error(DiagMessage(out_resource->source) + diag->Error(android::DiagMessage(out_resource->source) << "<" << tag_name << "> must have a 'first-id' attribute"); return false; } std::optional maybe_id = ResourceUtils::ParseResourceId(maybe_id_str.value()); if (!maybe_id) { - diag->Error(DiagMessage(out_resource->source) + diag->Error(android::DiagMessage(out_resource->source) << "invalid resource ID '" << maybe_id_str.value() << "' in <" << tag_name << ">"); return false; } @@ -1017,25 +1012,27 @@ bool static ParseGroupImpl(xml::XmlPullParser* parser, ParsedResource* out_resou continue; } - const Source item_source = out_resource->source.WithLine(parser->line_number()); + const android::Source item_source = out_resource->source.WithLine(parser->line_number()); const std::string& element_namespace = parser->element_namespace(); const std::string& element_name = parser->element_name(); if (element_namespace.empty() && element_name == "public") { auto maybe_name = xml::FindNonEmptyAttribute(parser, "name"); if (!maybe_name) { - diag->Error(DiagMessage(item_source) << " must have a 'name' attribute"); + diag->Error(android::DiagMessage(item_source) << " must have a 'name' attribute"); error = true; continue; } if (xml::FindNonEmptyAttribute(parser, "id")) { - diag->Error(DiagMessage(item_source) << "'id' is ignored within <" << tag_name << ">"); + diag->Error(android::DiagMessage(item_source) + << "'id' is ignored within <" << tag_name << ">"); error = true; continue; } if (xml::FindNonEmptyAttribute(parser, "type")) { - diag->Error(DiagMessage(item_source) << "'type' is ignored within <" << tag_name << ">"); + diag->Error(android::DiagMessage(item_source) + << "'type' is ignored within <" << tag_name << ">"); error = true; continue; } @@ -1059,7 +1056,7 @@ bool static ParseGroupImpl(xml::XmlPullParser* parser, ParsedResource* out_resou next_id.id++; } else if (!ShouldIgnoreElement(element_namespace, element_name)) { - diag->Error(DiagMessage(item_source) << ":" << element_name << ">"); + diag->Error(android::DiagMessage(item_source) << ":" << element_name << ">"); error = true; } } @@ -1086,7 +1083,7 @@ bool ResourceParser::ParseStagingPublicGroupFinal(xml::XmlPullParser* parser, bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource) { if (options_.visibility) { - diag_->Error(DiagMessage(out_resource->source) + diag_->Error(android::DiagMessage(out_resource->source) << "<" << kPublicGroupTag << "> tag not allowed with --visibility flag"); return false; } @@ -1102,15 +1099,14 @@ bool ResourceParser::ParseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* out_resource) { std::optional maybe_type = xml::FindNonEmptyAttribute(parser, "type"); if (!maybe_type) { - diag_->Error(DiagMessage(out_resource->source) - << "<" << parser->element_name() - << "> must have a 'type' attribute"); + diag_->Error(android::DiagMessage(out_resource->source) + << "<" << parser->element_name() << "> must have a 'type' attribute"); return false; } std::optional parsed_type = ParseResourceNamedType(maybe_type.value()); if (!parsed_type) { - diag_->Error(DiagMessage(out_resource->source) + diag_->Error(android::DiagMessage(out_resource->source) << "invalid resource type '" << maybe_type.value() << "' in <" << parser->element_name() << ">"); return false; @@ -1122,12 +1118,12 @@ bool ResourceParser::ParseSymbolImpl(xml::XmlPullParser* parser, bool ResourceParser::ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource) { if (options_.visibility) { - diag_->Error(DiagMessage(out_resource->source) + diag_->Error(android::DiagMessage(out_resource->source) << " and tags not allowed with --visibility flag"); return false; } if (out_resource->config != ConfigDescription::DefaultConfig()) { - diag_->Warn(DiagMessage(out_resource->source) + diag_->Warn(android::DiagMessage(out_resource->source) << "ignoring configuration '" << out_resource->config << "' for <" << parser->element_name() << "> tag"); } @@ -1142,15 +1138,14 @@ bool ResourceParser::ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource) { if (out_resource->config != ConfigDescription::DefaultConfig()) { - diag_->Warn(DiagMessage(out_resource->source) - << "ignoring configuration '" << out_resource->config - << "' for tag"); + diag_->Warn(android::DiagMessage(out_resource->source) + << "ignoring configuration '" << out_resource->config << "' for tag"); } std::optional overlayable_name = xml::FindNonEmptyAttribute(parser, "name"); if (!overlayable_name) { - diag_->Error(DiagMessage(out_resource->source) - << " tag must have a 'name' attribute"); + diag_->Error(android::DiagMessage(out_resource->source) + << " tag must have a 'name' attribute"); return false; } @@ -1158,7 +1153,7 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource android::base::StringPrintf("%s://", Overlayable::kActorScheme); std::optional overlayable_actor = xml::FindNonEmptyAttribute(parser, "actor"); if (overlayable_actor && !util::StartsWith(overlayable_actor.value(), kActorUriScheme)) { - diag_->Error(DiagMessage(out_resource->source) + diag_->Error(android::DiagMessage(out_resource->source) << "specified tag 'actor' attribute must use the scheme '" << Overlayable::kActorScheme << "'"); return false; @@ -1192,13 +1187,13 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource continue; } - const Source element_source = source_.WithLine(parser->line_number()); + const android::Source element_source = source_.WithLine(parser->line_number()); 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 == PolicyFlags::NONE) { - diag_->Error(DiagMessage(element_source) - << " within an must be inside a block"); + diag_->Error(android::DiagMessage(element_source) + << " within an must be inside a block"); error = true; continue; } @@ -1206,7 +1201,7 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource // Items specify the name and type of resource that should be overlayable std::optional item_name = xml::FindNonEmptyAttribute(parser, "name"); if (!item_name) { - diag_->Error(DiagMessage(element_source) + diag_->Error(android::DiagMessage(element_source) << " within an must have a 'name' attribute"); error = true; continue; @@ -1214,7 +1209,7 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource std::optional item_type = xml::FindNonEmptyAttribute(parser, "type"); if (!item_type) { - diag_->Error(DiagMessage(element_source) + diag_->Error(android::DiagMessage(element_source) << " within an must have a 'type' attribute"); error = true; continue; @@ -1222,7 +1217,7 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource std::optional type = ParseResourceNamedType(item_type.value()); if (!type) { - diag_->Error(DiagMessage(element_source) + diag_->Error(android::DiagMessage(element_source) << "invalid resource type '" << item_type.value() << "' in within an "); error = true; @@ -1243,7 +1238,8 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource } else if (element_namespace.empty() && element_name == "policy") { 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) << " blocks cannot be recursively nested"); + diag_->Error(android::DiagMessage(element_source) + << " blocks cannot be recursively nested"); error = true; break; } else if (std::optional maybe_type = @@ -1258,7 +1254,7 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource return trimmed_part == it.first; }); if (policy == kPolicyStringToFlag.end()) { - diag_->Error(DiagMessage(element_source) + diag_->Error(android::DiagMessage(element_source) << " has unsupported type '" << trimmed_part << "'"); error = true; continue; @@ -1267,14 +1263,15 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource current_policies |= policy->second; } } else { - diag_->Error(DiagMessage(element_source) + diag_->Error(android::DiagMessage(element_source) << " must have a 'type' attribute"); error = true; continue; } } else if (!ShouldIgnoreElement(element_namespace, element_name)) { - diag_->Error(DiagMessage(element_source) << "invalid element <" << element_name << "> " - << " in "); + diag_->Error(android::DiagMessage(element_source) + << "invalid element <" << element_name << "> " + << " in "); error = true; break; } @@ -1306,9 +1303,9 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser, // Attributes only end up in default configuration. if (out_resource->config != ConfigDescription::DefaultConfig()) { - diag_->Warn(DiagMessage(out_resource->source) - << "ignoring configuration '" << out_resource->config - << "' for attribute " << out_resource->name); + diag_->Warn(android::DiagMessage(out_resource->source) + << "ignoring configuration '" << out_resource->config << "' for attribute " + << out_resource->name); out_resource->config = ConfigDescription::DefaultConfig(); } @@ -1318,7 +1315,7 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser, if (maybe_format) { type_mask = ParseFormatAttribute(maybe_format.value()); if (type_mask == 0) { - diag_->Error(DiagMessage(source_.WithLine(parser->line_number())) + diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << "invalid attribute format '" << maybe_format.value() << "'"); return false; } @@ -1329,7 +1326,7 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser, if (std::optional maybe_min_str = xml::FindAttribute(parser, "min")) { StringPiece min_str = util::TrimWhitespace(maybe_min_str.value()); if (!min_str.empty()) { - std::u16string min_str16 = util::Utf8ToUtf16(min_str); + std::u16string min_str16 = android::util::Utf8ToUtf16(min_str); android::Res_value value; if (android::ResTable::stringToInt(min_str16.data(), min_str16.size(), &value)) { maybe_min = static_cast(value.data); @@ -1337,7 +1334,7 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser, } if (!maybe_min) { - diag_->Error(DiagMessage(source_.WithLine(parser->line_number())) + diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << "invalid 'min' value '" << min_str << "'"); return false; } @@ -1346,7 +1343,7 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser, if (std::optional maybe_max_str = xml::FindAttribute(parser, "max")) { StringPiece max_str = util::TrimWhitespace(maybe_max_str.value()); if (!max_str.empty()) { - std::u16string max_str16 = util::Utf8ToUtf16(max_str); + std::u16string max_str16 = android::util::Utf8ToUtf16(max_str); android::Res_value value; if (android::ResTable::stringToInt(max_str16.data(), max_str16.size(), &value)) { maybe_max = static_cast(value.data); @@ -1354,7 +1351,7 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser, } if (!maybe_max) { - diag_->Error(DiagMessage(source_.WithLine(parser->line_number())) + diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << "invalid 'max' value '" << max_str << "'"); return false; } @@ -1362,7 +1359,7 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser, if ((maybe_min || maybe_max) && (type_mask & android::ResTable_map::TYPE_INTEGER) == 0) { - diag_->Error(DiagMessage(source_.WithLine(parser->line_number())) + diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << "'min' and 'max' can only be used when format='integer'"); return false; } @@ -1387,13 +1384,13 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser, continue; } - const Source item_source = source_.WithLine(parser->line_number()); + const android::Source item_source = source_.WithLine(parser->line_number()); const std::string& element_namespace = parser->element_namespace(); const std::string& element_name = parser->element_name(); if (element_namespace.empty() && (element_name == "flag" || element_name == "enum")) { if (element_name == "enum") { if (type_mask & android::ResTable_map::TYPE_FLAGS) { - diag_->Error(DiagMessage(item_source) + diag_->Error(android::DiagMessage(item_source) << "can not define an ; already defined a "); error = true; continue; @@ -1402,7 +1399,7 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser, } else if (element_name == "flag") { if (type_mask & android::ResTable_map::TYPE_ENUM) { - diag_->Error(DiagMessage(item_source) + diag_->Error(android::DiagMessage(item_source) << "can not define a ; already defined an "); error = true; continue; @@ -1427,11 +1424,10 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser, auto insert_result = items.insert(std::move(symbol)); if (!insert_result.second) { const Attribute::Symbol& existing_symbol = *insert_result.first; - diag_->Error(DiagMessage(item_source) - << "duplicate symbol '" - << existing_symbol.symbol.name.value().entry << "'"); + diag_->Error(android::DiagMessage(item_source) + << "duplicate symbol '" << existing_symbol.symbol.name.value().entry << "'"); - diag_->Note(DiagMessage(existing_symbol.symbol.GetSource()) + diag_->Note(android::DiagMessage(existing_symbol.symbol.GetSource()) << "first defined here"); error = true; } @@ -1439,7 +1435,7 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser, error = true; } } else if (!ShouldIgnoreElement(element_namespace, element_name)) { - diag_->Error(DiagMessage(item_source) << ":" << element_name << ">"); + diag_->Error(android::DiagMessage(item_source) << ":" << element_name << ">"); error = true; } @@ -1462,28 +1458,27 @@ bool ResourceParser::ParseAttrImpl(xml::XmlPullParser* parser, std::optional ResourceParser::ParseEnumOrFlagItem(xml::XmlPullParser* parser, const StringPiece& tag) { - const Source source = source_.WithLine(parser->line_number()); + const android::Source source = source_.WithLine(parser->line_number()); std::optional maybe_name = xml::FindNonEmptyAttribute(parser, "name"); if (!maybe_name) { - diag_->Error(DiagMessage(source) << "no attribute 'name' found for tag <" - << tag << ">"); + diag_->Error(android::DiagMessage(source) + << "no attribute 'name' found for tag <" << tag << ">"); return {}; } std::optional maybe_value = xml::FindNonEmptyAttribute(parser, "value"); if (!maybe_value) { - diag_->Error(DiagMessage(source) << "no attribute 'value' found for tag <" - << tag << ">"); + diag_->Error(android::DiagMessage(source) + << "no attribute 'value' found for tag <" << tag << ">"); return {}; } - std::u16string value16 = util::Utf8ToUtf16(maybe_value.value()); + std::u16string value16 = android::util::Utf8ToUtf16(maybe_value.value()); android::Res_value val; if (!android::ResTable::stringToInt(value16.data(), value16.size(), &val)) { - diag_->Error(DiagMessage(source) << "invalid value '" << maybe_value.value() - << "' for <" << tag - << ">; must be an integer"); + diag_->Error(android::DiagMessage(source) << "invalid value '" << maybe_value.value() + << "' for <" << tag << ">; must be an integer"); return {}; } @@ -1494,17 +1489,18 @@ std::optional ResourceParser::ParseEnumOrFlagItem(xml::XmlPul } bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) { - const Source source = source_.WithLine(parser->line_number()); + const android::Source source = source_.WithLine(parser->line_number()); std::optional maybe_name = xml::FindNonEmptyAttribute(parser, "name"); if (!maybe_name) { - diag_->Error(DiagMessage(source) << " must have a 'name' attribute"); + diag_->Error(android::DiagMessage(source) << " must have a 'name' attribute"); return false; } std::optional maybe_key = ResourceUtils::ParseXmlAttributeName(maybe_name.value()); if (!maybe_key) { - diag_->Error(DiagMessage(source) << "invalid attribute name '" << maybe_name.value() << "'"); + diag_->Error(android::DiagMessage(source) + << "invalid attribute name '" << maybe_name.value() << "'"); return false; } @@ -1513,7 +1509,7 @@ bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) { std::unique_ptr value = ParseXml(parser, 0, kAllowRawString); if (!value) { - diag_->Error(DiagMessage(source) << "could not parse style item"); + diag_->Error(android::DiagMessage(source) << "could not parse style item"); return false; } @@ -1534,7 +1530,7 @@ bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* par std::string err_str; style->parent = ResourceUtils::ParseStyleParentReference(maybe_parent.value(), &err_str); if (!style->parent) { - diag_->Error(DiagMessage(out_resource->source) << err_str); + diag_->Error(android::DiagMessage(out_resource->source) << err_str); return false; } @@ -1568,7 +1564,7 @@ bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* par error |= !ParseStyleItem(parser, style.get()); } else if (!ShouldIgnoreElement(element_namespace, element_name)) { - diag_->Error(DiagMessage(source_.WithLine(parser->line_number())) + diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << ":" << element_name << ">"); error = true; } @@ -1587,7 +1583,7 @@ bool ResourceParser::ParseArray(xml::XmlPullParser* parser, ParsedResource* out_ if (std::optional format_attr = xml::FindNonEmptyAttribute(parser, "format")) { resource_format = ParseFormatTypeNoEnumsOrFlags(format_attr.value()); if (resource_format == 0u) { - diag_->Error(DiagMessage(source_.WithLine(parser->line_number())) + diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << "'" << format_attr.value() << "' is an invalid format"); return false; } @@ -1615,7 +1611,7 @@ bool ResourceParser::ParseArrayImpl(xml::XmlPullParser* parser, if (std::optional translatable_attr = xml::FindAttribute(parser, "translatable")) { std::optional maybe_translatable = ResourceUtils::ParseBool(translatable_attr.value()); if (!maybe_translatable) { - diag_->Error(DiagMessage(out_resource->source) + diag_->Error(android::DiagMessage(out_resource->source) << "invalid value for 'translatable'. Must be a boolean"); return false; } @@ -1631,13 +1627,13 @@ bool ResourceParser::ParseArrayImpl(xml::XmlPullParser* parser, continue; } - const Source item_source = source_.WithLine(parser->line_number()); + const android::Source item_source = source_.WithLine(parser->line_number()); const std::string& element_namespace = parser->element_namespace(); const std::string& element_name = parser->element_name(); if (element_namespace.empty() && element_name == "item") { std::unique_ptr item = ParseXml(parser, typeMask, kNoRawString); if (!item) { - diag_->Error(DiagMessage(item_source) << "could not parse array item"); + diag_->Error(android::DiagMessage(item_source) << "could not parse array item"); error = true; continue; } @@ -1645,9 +1641,8 @@ bool ResourceParser::ParseArrayImpl(xml::XmlPullParser* parser, array->elements.emplace_back(std::move(item)); } else if (!ShouldIgnoreElement(element_namespace, element_name)) { - diag_->Error(DiagMessage(source_.WithLine(parser->line_number())) - << "unknown tag <" << element_namespace << ":" - << element_name << ">"); + diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) + << "unknown tag <" << element_namespace << ":" << element_name << ">"); error = true; } } @@ -1675,15 +1670,14 @@ bool ResourceParser::ParsePlural(xml::XmlPullParser* parser, continue; } - const Source item_source = source_.WithLine(parser->line_number()); + const android::Source item_source = source_.WithLine(parser->line_number()); const std::string& element_namespace = parser->element_namespace(); const std::string& element_name = parser->element_name(); if (element_namespace.empty() && element_name == "item") { std::optional maybe_quantity = xml::FindNonEmptyAttribute(parser, "quantity"); if (!maybe_quantity) { - diag_->Error(DiagMessage(item_source) - << " in requires attribute " - << "'quantity'"); + diag_->Error(android::DiagMessage(item_source) << " in requires attribute " + << "'quantity'"); error = true; continue; } @@ -1704,16 +1698,16 @@ bool ResourceParser::ParsePlural(xml::XmlPullParser* parser, } else if (trimmed_quantity == "other") { index = Plural::Other; } else { - diag_->Error(DiagMessage(item_source) - << " in has invalid value '" - << trimmed_quantity << "' for attribute 'quantity'"); + diag_->Error(android::DiagMessage(item_source) + << " in has invalid value '" << trimmed_quantity + << "' for attribute 'quantity'"); error = true; continue; } if (plural->values[index]) { - diag_->Error(DiagMessage(item_source) << "duplicate quantity '" - << trimmed_quantity << "'"); + diag_->Error(android::DiagMessage(item_source) + << "duplicate quantity '" << trimmed_quantity << "'"); error = true; continue; } @@ -1727,9 +1721,8 @@ bool ResourceParser::ParsePlural(xml::XmlPullParser* parser, plural->values[index]->SetSource(item_source); } else if (!ShouldIgnoreElement(element_namespace, element_name)) { - diag_->Error(DiagMessage(item_source) << "unknown tag <" - << element_namespace << ":" - << element_name << ">"); + diag_->Error(android::DiagMessage(item_source) + << "unknown tag <" << element_namespace << ":" << element_name << ">"); error = true; } } @@ -1758,9 +1751,9 @@ bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser, // Declare-styleable only ends up in default config; if (out_resource->config != ConfigDescription::DefaultConfig()) { - diag_->Warn(DiagMessage(out_resource->source) - << "ignoring configuration '" << out_resource->config - << "' for styleable " << out_resource->name.entry); + diag_->Warn(android::DiagMessage(out_resource->source) + << "ignoring configuration '" << out_resource->config << "' for styleable " + << out_resource->name.entry); out_resource->config = ConfigDescription::DefaultConfig(); } @@ -1778,13 +1771,14 @@ bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser, continue; } - const Source item_source = source_.WithLine(parser->line_number()); + const android::Source item_source = source_.WithLine(parser->line_number()); const std::string& element_namespace = parser->element_namespace(); const std::string& element_name = parser->element_name(); if (element_namespace.empty() && element_name == "attr") { std::optional maybe_name = xml::FindNonEmptyAttribute(parser, "name"); if (!maybe_name) { - diag_->Error(DiagMessage(item_source) << " tag must have a 'name' attribute"); + diag_->Error(android::DiagMessage(item_source) + << " tag must have a 'name' attribute"); error = true; continue; } @@ -1794,8 +1788,8 @@ bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser, // Eg. std::optional maybe_ref = ResourceUtils::ParseXmlAttributeName(maybe_name.value()); if (!maybe_ref) { - diag_->Error(DiagMessage(item_source) << " tag has invalid name '" - << maybe_name.value() << "'"); + diag_->Error(android::DiagMessage(item_source) + << " tag has invalid name '" << maybe_name.value() << "'"); error = true; continue; } @@ -1833,9 +1827,8 @@ bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser, } } else if (!ShouldIgnoreElement(element_namespace, element_name)) { - diag_->Error(DiagMessage(item_source) << "unknown tag <" - << element_namespace << ":" - << element_name << ">"); + diag_->Error(android::DiagMessage(item_source) + << "unknown tag <" << element_namespace << ":" << element_name << ">"); error = true; } diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h index 548f5f9531fd..396ce9767fe9 100644 --- a/tools/aapt2/ResourceParser.h +++ b/tools/aapt2/ResourceParser.h @@ -20,14 +20,13 @@ #include #include +#include "ResourceTable.h" +#include "ResourceValues.h" #include "android-base/macros.h" #include "androidfw/ConfigDescription.h" +#include "androidfw/IDiagnostics.h" #include "androidfw/StringPiece.h" - -#include "Diagnostics.h" -#include "ResourceTable.h" -#include "ResourceValues.h" -#include "StringPool.h" +#include "androidfw/StringPool.h" #include "xml/XmlPullParser.h" namespace aapt { @@ -59,10 +58,10 @@ struct ResourceParserOptions { struct FlattenedXmlSubTree { std::string raw_value; - StyleString style_string; + android::StyleString style_string; std::vector untranslatable_sections; xml::IPackageDeclStack* namespace_resolver; - Source source; + android::Source source; }; /* @@ -70,7 +69,7 @@ struct FlattenedXmlSubTree { */ class ResourceParser { public: - ResourceParser(IDiagnostics* diag, ResourceTable* table, const Source& source, + ResourceParser(android::IDiagnostics* diag, ResourceTable* table, const android::Source& source, const android::ConfigDescription& config, const ResourceParserOptions& options = {}); bool Parse(xml::XmlPullParser* parser); @@ -78,7 +77,7 @@ class ResourceParser { static std::unique_ptr ParseXml(const FlattenedXmlSubTree& xmlsub_tree, uint32_t type_mask, bool allow_raw_value, ResourceTable& table, const android::ConfigDescription& config, - IDiagnostics& diag); + android::IDiagnostics& diag); private: DISALLOW_COPY_AND_ASSIGN(ResourceParser); @@ -93,7 +92,7 @@ class ResourceParser { // `out_untranslatable_sections` contains the sections of the string that should not be // translated. bool FlattenXmlSubtree(xml::XmlPullParser* parser, std::string* out_raw_string, - StyleString* out_style_string, + android::StyleString* out_style_string, std::vector* out_untranslatable_sections); /* @@ -133,9 +132,9 @@ class ResourceParser { bool ParseArrayImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, uint32_t typeMask); bool ParsePlural(xml::XmlPullParser* parser, ParsedResource* out_resource); - IDiagnostics* diag_; + android::IDiagnostics* diag_; ResourceTable* table_; - Source source_; + android::Source source_; android::ConfigDescription config_; ResourceParserOptions options_; }; diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp index 556ffa221db5..fe7eb96ffe16 100644 --- a/tools/aapt2/ResourceParser_test.cpp +++ b/tools/aapt2/ResourceParser_test.cpp @@ -50,7 +50,7 @@ constexpr const char* kXmlPreamble = " TEST(ResourceParserSingleTest, FailToParseWithNoRootResourcesElement) { std::unique_ptr context = test::ContextBuilder().Build(); ResourceTable table; - ResourceParser parser(context->GetDiagnostics(), &table, Source{"test"}, {}); + ResourceParser parser(context->GetDiagnostics(), &table, android::Source{"test"}, {}); std::string input = kXmlPreamble; input += R"()"; @@ -71,7 +71,7 @@ class ResourceParserTest : public ::testing::Test { ::testing::AssertionResult TestParse(const StringPiece& str, const ConfigDescription& config) { ResourceParserOptions parserOptions; - ResourceParser parser(context_->GetDiagnostics(), &table_, Source{"test"}, config, + ResourceParser parser(context_->GetDiagnostics(), &table_, android::Source{"test"}, config, parserOptions); std::string input = kXmlPreamble; @@ -711,7 +711,7 @@ TEST_F(ResourceParserTest, ParseDeclareStyleablePreservingVisibility) { )"); - ResourceParser parser(context_->GetDiagnostics(), &table_, Source{"test"}, + ResourceParser parser(context_->GetDiagnostics(), &table_, android::Source{"test"}, ConfigDescription::DefaultConfig(), ResourceParserOptions{.preserve_visibility_of_styleables = true}); diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp index 0f5118da9408..cb4811445ed1 100644 --- a/tools/aapt2/ResourceTable.cpp +++ b/tools/aapt2/ResourceTable.cpp @@ -464,20 +464,21 @@ ResourceTableView ResourceTable::GetPartitionedView(const ResourceTableViewOptio return view; } -bool ResourceTable::AddResource(NewResource&& res, IDiagnostics* diag) { +bool ResourceTable::AddResource(NewResource&& res, android::IDiagnostics* diag) { CHECK(diag != nullptr) << "Diagnostic pointer is null"; const bool validate = validation_ == Validation::kEnabled; - const Source source = res.value ? res.value->GetSource() : Source{}; + const android::Source source = res.value ? res.value->GetSource() : android::Source{}; if (validate && !res.allow_mangled && !IsValidResourceEntryName(res.name.entry)) { - diag->Error(DiagMessage(source) + diag->Error(android::DiagMessage(source) << "resource '" << res.name << "' has invalid entry name '" << res.name.entry); return false; } if (res.id.has_value() && !res.id->first.is_valid()) { - diag->Error(DiagMessage(source) << "trying to add resource '" << res.name << "' with ID " - << res.id->first << " but that ID is invalid"); + diag->Error(android::DiagMessage(source) + << "trying to add resource '" << res.name << "' with ID " << res.id->first + << " but that ID is invalid"); return false; } @@ -513,7 +514,7 @@ bool ResourceTable::AddResource(NewResource&& res, IDiagnostics* diag) { if (res.id.has_value()) { if (entry->id && entry->id.value() != res.id->first) { if (res.id->second != OnIdConflict::CREATE_ENTRY) { - diag->Error(DiagMessage(source) + diag->Error(android::DiagMessage(source) << "trying to add resource '" << res.name << "' with ID " << res.id->first << " but resource already has ID " << entry->id.value()); return false; @@ -541,9 +542,9 @@ bool ResourceTable::AddResource(NewResource&& res, IDiagnostics* diag) { if (res.overlayable.has_value()) { if (entry->overlayable_item) { - diag->Error(DiagMessage(res.overlayable->source) + diag->Error(android::DiagMessage(res.overlayable->source) << "duplicate overlayable declaration for resource '" << res.name << "'"); - diag->Error(DiagMessage(entry->overlayable_item.value().source) + diag->Error(android::DiagMessage(entry->overlayable_item.value().source) << "previous declaration here"); return false; } @@ -581,9 +582,10 @@ bool ResourceTable::AddResource(NewResource&& res, IDiagnostics* diag) { break; case CollisionResult::kConflict: - diag->Error(DiagMessage(source) << "duplicate value for resource '" << res.name << "' " - << "with config '" << res.config << "'"); - diag->Error(DiagMessage(source) << "resource previously defined here"); + diag->Error(android::DiagMessage(source) + << "duplicate value for resource '" << res.name << "' " + << "with config '" << res.config << "'"); + diag->Error(android::DiagMessage(source) << "resource previously defined here"); return false; case CollisionResult::kKeepOriginal: diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h index 7aa8b0f0c8ef..f49ce8147f71 100644 --- a/tools/aapt2/ResourceTable.h +++ b/tools/aapt2/ResourceTable.h @@ -17,17 +17,6 @@ #ifndef AAPT_RESOURCE_TABLE_H #define AAPT_RESOURCE_TABLE_H -#include "Diagnostics.h" -#include "Resource.h" -#include "ResourceValues.h" -#include "Source.h" -#include "StringPool.h" -#include "io/File.h" - -#include "android-base/macros.h" -#include "androidfw/ConfigDescription.h" -#include "androidfw/StringPiece.h" - #include #include #include @@ -36,6 +25,16 @@ #include #include +#include "Resource.h" +#include "ResourceValues.h" +#include "android-base/macros.h" +#include "androidfw/ConfigDescription.h" +#include "androidfw/IDiagnostics.h" +#include "androidfw/Source.h" +#include "androidfw/StringPiece.h" +#include "androidfw/StringPool.h" +#include "io/File.h" + using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; namespace aapt { @@ -49,7 +48,7 @@ struct Visibility { }; Level level = Level::kUndefined; - Source source; + android::Source source; std::string comment; // Indicates that the resource id may change across builds and that the public R.java identifier @@ -60,14 +59,14 @@ struct Visibility { // Represents in an overlay. struct AllowNew { - Source source; + android::Source source; std::string comment; }; // Represents the staged resource id of a finalized resource. struct StagedId { ResourceId id; - Source source; + android::Source source; }; struct Overlayable { @@ -75,13 +74,14 @@ struct Overlayable { Overlayable(const android::StringPiece& name, const android::StringPiece& actor) : name(name.to_string()), actor(actor.to_string()) {} Overlayable(const android::StringPiece& name, const android::StringPiece& actor, - const Source& source) - : name(name.to_string()), actor(actor.to_string()), source(source ){} + const android::Source& source) + : name(name.to_string()), actor(actor.to_string()), source(source) { + } static const char* kActorScheme; std::string name; std::string actor; - Source source; + android::Source source; }; // Represents a declaration that a resource is overlayable at runtime. @@ -91,7 +91,7 @@ struct OverlayableItem { std::shared_ptr overlayable; PolicyFlags policies = PolicyFlags::NONE; std::string comment; - Source source; + android::Source source; }; class ResourceConfigValue { @@ -300,7 +300,7 @@ class ResourceTable { ResourceTable() = default; explicit ResourceTable(Validation validation); - bool AddResource(NewResource&& res, IDiagnostics* diag); + bool AddResource(NewResource&& res, android::IDiagnostics* diag); // Retrieves a sorted a view of the packages, types, and entries sorted in ascending resource id // order. @@ -333,7 +333,7 @@ class ResourceTable { // When `string_pool` references are destroyed (as they will be when `packages` is destroyed), // they decrement a refCount, which would cause invalid memory access if the pool was already // destroyed. - StringPool string_pool; + android::StringPool string_pool; // The list of packages in this table, sorted alphabetically by package name and increasing // package ID (missing ID being the lowest). diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp index de73d2c203e4..0cf84736a081 100644 --- a/tools/aapt2/ResourceTable_test.cpp +++ b/tools/aapt2/ResourceTable_test.cpp @@ -15,15 +15,16 @@ */ #include "ResourceTable.h" -#include "Diagnostics.h" -#include "ResourceValues.h" -#include "test/Test.h" -#include "util/Util.h" #include #include #include +#include "ResourceValues.h" +#include "androidfw/IDiagnostics.h" +#include "test/Test.h" +#include "util/Util.h" + using ::android::ConfigDescription; using ::android::StringPiece; using ::testing::Eq; @@ -263,13 +264,13 @@ TEST(ResourceTableTest, SetAllowNew) { TEST(ResourceTableTest, SetOverlayable) { ResourceTable table; - auto overlayable = std::make_shared("Name", "overlay://theme", - Source("res/values/overlayable.xml", 40)); + auto overlayable = std::make_shared( + "Name", "overlay://theme", android::Source("res/values/overlayable.xml", 40)); OverlayableItem overlayable_item(overlayable); 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); + overlayable_item.source = android::Source("res/values/overlayable.xml", 42); const ResourceName name = test::ParseNameOrDie("android:string/foo"); ASSERT_TRUE(table.AddResource(NewResourceBuilder(name).SetOverlayable(overlayable_item).Build(), diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index b4e79ca8ca08..945f45b8a337 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -44,7 +44,7 @@ static std::optional ToResourceNamedType(const char16_t* type const char* type, size_t type_len) { std::optional parsed_type; if (type16) { - auto converted = util::Utf16ToUtf8(StringPiece16(type16, type_len)); + auto converted = android::util::Utf16ToUtf8(StringPiece16(type16, type_len)); parsed_type = ParseResourceNamedType(converted); } else if (type) { parsed_type = ParseResourceNamedType(StringPiece(type, type_len)); @@ -64,8 +64,7 @@ std::optional ToResourceName(const android::ResTable::resource_nam return {}; } - name_out.package = - util::Utf16ToUtf8(StringPiece16(name_in.package, name_in.packageLen)); + name_out.package = android::util::Utf16ToUtf8(StringPiece16(name_in.package, name_in.packageLen)); std::optional type = ToResourceNamedType(name_in.type, name_in.name8, name_in.typeLen); @@ -75,8 +74,7 @@ std::optional ToResourceName(const android::ResTable::resource_nam name_out.type = *type; if (name_in.name) { - name_out.entry = - util::Utf16ToUtf8(StringPiece16(name_in.name, name_in.nameLen)); + name_out.entry = android::util::Utf16ToUtf8(StringPiece16(name_in.name, name_in.nameLen)); } else if (name_in.name8) { name_out.entry.assign(name_in.name8, name_in.nameLen); } else { @@ -101,8 +99,7 @@ std::optional ToResourceName(const android::AssetManager2::Resourc name_out.type = *type; if (name_in.entry16) { - name_out.entry = - util::Utf16ToUtf8(StringPiece16(name_in.entry16, name_in.entry_len)); + name_out.entry = android::util::Utf16ToUtf8(StringPiece16(name_in.entry16, name_in.entry_len)); } else if (name_in.entry) { name_out.entry = std::string(name_in.entry, name_in.entry_len); } else { @@ -498,7 +495,7 @@ std::optional ParseBool(const StringPiece& str) { } std::optional ParseInt(const StringPiece& str) { - std::u16string str16 = util::Utf8ToUtf16(str); + std::u16string str16 = android::util::Utf8ToUtf16(str); android::Res_value value; if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) { return value.data; @@ -509,7 +506,7 @@ std::optional ParseInt(const StringPiece& str) { std::optional ParseResourceId(const StringPiece& str) { StringPiece trimmed_str(util::TrimWhitespace(str)); - std::u16string str16 = util::Utf8ToUtf16(trimmed_str); + std::u16string str16 = android::util::Utf8ToUtf16(trimmed_str); android::Res_value value; if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) { if (value.dataType == android::Res_value::TYPE_INT_HEX) { @@ -525,7 +522,7 @@ std::optional ParseResourceId(const StringPiece& str) { std::optional ParseSdkVersion(const StringPiece& str) { StringPiece trimmed_str(util::TrimWhitespace(str)); - std::u16string str16 = util::Utf8ToUtf16(trimmed_str); + std::u16string str16 = android::util::Utf8ToUtf16(trimmed_str); android::Res_value value; if (android::ResTable::stringToInt(str16.data(), str16.size(), &value)) { return static_cast(value.data); @@ -562,7 +559,7 @@ std::unique_ptr MakeBool(bool val) { } std::unique_ptr TryParseInt(const StringPiece& str) { - std::u16string str16 = util::Utf8ToUtf16(util::TrimWhitespace(str)); + std::u16string str16 = android::util::Utf8ToUtf16(util::TrimWhitespace(str)); android::Res_value value; if (!android::ResTable::stringToInt(str16.data(), str16.size(), &value)) { return {}; @@ -575,7 +572,7 @@ std::unique_ptr MakeInt(uint32_t val) { } std::unique_ptr TryParseFloat(const StringPiece& str) { - std::u16string str16 = util::Utf8ToUtf16(util::TrimWhitespace(str)); + std::u16string str16 = android::util::Utf8ToUtf16(util::TrimWhitespace(str)); android::Res_value value; if (!android::ResTable::stringToFloat(str16.data(), str16.size(), &value)) { return {}; @@ -737,7 +734,7 @@ std::string BuildResourceFileName(const ResourceFile& res_file, const NameMangle std::unique_ptr ParseBinaryResValue(const ResourceType& type, const ConfigDescription& config, const android::ResStringPool& src_pool, const android::Res_value& res_value, - StringPool* dst_pool) { + android::StringPool* dst_pool) { if (type == ResourceType::kId) { if (res_value.dataType != android::Res_value::TYPE_REFERENCE && res_value.dataType != android::Res_value::TYPE_DYNAMIC_REFERENCE) { @@ -748,30 +745,32 @@ std::unique_ptr ParseBinaryResValue(const ResourceType& type, const Config // fall through to regular reference deserialization logic } - const uint32_t data = util::DeviceToHost32(res_value.data); + const uint32_t data = android::util::DeviceToHost32(res_value.data); switch (res_value.dataType) { case android::Res_value::TYPE_STRING: { - const std::string str = util::GetString(src_pool, data); + const std::string str = android::util::GetString(src_pool, data); auto spans_result = src_pool.styleAt(data); // Check if the string has a valid style associated with it. if (spans_result.has_value() && (*spans_result)->name.index != android::ResStringPool_span::END) { const android::ResStringPool_span* spans = spans_result->unsafe_ptr(); - StyleString style_str = {str}; + android::StyleString style_str = {str}; while (spans->name.index != android::ResStringPool_span::END) { - style_str.spans.push_back(Span{util::GetString(src_pool, spans->name.index), - spans->firstChar, spans->lastChar}); + style_str.spans.push_back( + android::Span{android::util::GetString(src_pool, spans->name.index), spans->firstChar, + spans->lastChar}); spans++; } return util::make_unique(dst_pool->MakeRef( - style_str, StringPool::Context(StringPool::Context::kNormalPriority, config))); + style_str, + android::StringPool::Context(android::StringPool::Context::kNormalPriority, config))); } else { if (type != ResourceType::kString && util::StartsWith(str, "res/")) { // This must be a FileReference. - std::unique_ptr file_ref = - util::make_unique(dst_pool->MakeRef( - str, StringPool::Context(StringPool::Context::kHighPriority, config))); + std::unique_ptr file_ref = util::make_unique( + dst_pool->MakeRef(str, android::StringPool::Context( + android::StringPool::Context::kHighPriority, config))); if (type == ResourceType::kRaw) { file_ref->type = ResourceFile::Type::kUnknown; } else if (util::EndsWith(*file_ref->path, ".xml")) { @@ -783,7 +782,8 @@ std::unique_ptr ParseBinaryResValue(const ResourceType& type, const Config } // There are no styles associated with this string, so treat it as a simple string. - return util::make_unique(dst_pool->MakeRef(str, StringPool::Context(config))); + return util::make_unique( + dst_pool->MakeRef(str, android::StringPool::Context(config))); } } break; @@ -950,7 +950,7 @@ StringBuilder::SpanHandle StringBuilder::StartSpan(const std::string& name) { // When we start a span, all state associated with whitespace truncation and quotation is ended. ResetTextState(); - Span span; + android::Span span; span.name = name; span.first_char = span.last_char = utf16_len_; xml_string_.spans.push_back(std::move(span)); diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h index fe450a834dfa..22cf3459809d 100644 --- a/tools/aapt2/ResourceUtils.h +++ b/tools/aapt2/ResourceUtils.h @@ -20,15 +20,14 @@ #include #include +#include "NameMangler.h" +#include "Resource.h" +#include "ResourceValues.h" #include "androidfw/AssetManager2.h" #include "androidfw/ConfigDescription.h" #include "androidfw/ResourceTypes.h" #include "androidfw/StringPiece.h" - -#include "NameMangler.h" -#include "Resource.h" -#include "ResourceValues.h" -#include "StringPool.h" +#include "androidfw/StringPool.h" namespace aapt { namespace ResourceUtils { @@ -230,14 +229,14 @@ std::unique_ptr ParseBinaryResValue(const ResourceType& type, const android::ConfigDescription& config, const android::ResStringPool& src_pool, const android::Res_value& res_value, - StringPool* dst_pool); + android::StringPool* dst_pool); // A string flattened from an XML hierarchy, which maintains tags and untranslatable sections // in parallel data structures. struct FlattenedXmlString { std::string text; std::vector untranslatable_sections; - std::vector spans; + std::vector spans; }; // Flattens an XML hierarchy into a FlattenedXmlString, formatting the text, escaping characters, diff --git a/tools/aapt2/ResourceUtils_test.cpp b/tools/aapt2/ResourceUtils_test.cpp index 1aaa34deee79..568871a4d66e 100644 --- a/tools/aapt2/ResourceUtils_test.cpp +++ b/tools/aapt2/ResourceUtils_test.cpp @@ -111,7 +111,7 @@ TEST(ResourceUtilsTest, ParsePrivateReference) { TEST(ResourceUtilsTest, ParseBinaryDynamicReference) { android::Res_value value = {}; - value.data = util::HostToDevice32(0x01); + value.data = android::util::HostToDevice32(0x01); value.dataType = android::Res_value::TYPE_DYNAMIC_REFERENCE; std::unique_ptr item = ResourceUtils::ParseBinaryResValue(ResourceType::kId, android::ConfigDescription(), diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp index b796eb07f076..c4d54be01efe 100644 --- a/tools/aapt2/ResourceValues.cpp +++ b/tools/aapt2/ResourceValues.cpp @@ -75,7 +75,8 @@ void BaseItem::Accept(ConstValueVisitor* visitor) const { visitor->Visit(static_cast(this)); } -RawString::RawString(const StringPool::Ref& ref) : value(ref) {} +RawString::RawString(const android::StringPool::Ref& ref) : value(ref) { +} bool RawString::Equals(const Value* value) const { const RawString* other = ValueCast(value); @@ -87,7 +88,7 @@ bool RawString::Equals(const Value* value) const { bool RawString::Flatten(android::Res_value* out_value) const { out_value->dataType = android::Res_value::TYPE_STRING; - out_value->data = util::HostToDevice32(static_cast(value.index())); + out_value->data = android::util::HostToDevice32(static_cast(value.index())); return true; } @@ -136,7 +137,7 @@ bool Reference::Flatten(android::Res_value* out_value) const { out_value->dataType = android::Res_value::TYPE_ATTRIBUTE; } } - out_value->data = util::HostToDevice32(resid.id); + out_value->data = android::util::HostToDevice32(resid.id); return true; } @@ -216,7 +217,7 @@ bool Id::Equals(const Value* value) const { bool Id::Flatten(android::Res_value* out) const { out->dataType = android::Res_value::TYPE_INT_BOOLEAN; - out->data = util::HostToDevice32(0); + out->data = android::util::HostToDevice32(0); return true; } @@ -224,7 +225,7 @@ void Id::Print(std::ostream* out) const { *out << "(id)"; } -String::String(const StringPool::Ref& ref) : value(ref) { +String::String(const android::StringPool::Ref& ref) : value(ref) { } bool String::Equals(const Value* value) const { @@ -258,7 +259,7 @@ bool String::Flatten(android::Res_value* out_value) const { } out_value->dataType = android::Res_value::TYPE_STRING; - out_value->data = util::HostToDevice32(static_cast(value.index())); + out_value->data = android::util::HostToDevice32(static_cast(value.index())); return true; } @@ -272,7 +273,7 @@ void String::PrettyPrint(Printer* printer) const { printer->Print("\""); } -StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref) { +StyledString::StyledString(const android::StringPool::StyleRef& ref) : value(ref) { } bool StyledString::Equals(const Value* value) const { @@ -305,18 +306,18 @@ bool StyledString::Flatten(android::Res_value* out_value) const { } out_value->dataType = android::Res_value::TYPE_STRING; - out_value->data = util::HostToDevice32(static_cast(value.index())); + out_value->data = android::util::HostToDevice32(static_cast(value.index())); return true; } void StyledString::Print(std::ostream* out) const { *out << "(styled string) \"" << value->value << "\""; - for (const StringPool::Span& span : value->spans) { + for (const android::StringPool::Span& span : value->spans) { *out << " " << *span.name << ":" << span.first_char << "," << span.last_char; } } -FileReference::FileReference(const StringPool::Ref& _path) : path(_path) { +FileReference::FileReference(const android::StringPool::Ref& _path) : path(_path) { } bool FileReference::Equals(const Value* value) const { @@ -333,7 +334,7 @@ bool FileReference::Flatten(android::Res_value* out_value) const { } out_value->dataType = android::Res_value::TYPE_STRING; - out_value->data = util::HostToDevice32(static_cast(path.index())); + out_value->data = android::util::HostToDevice32(static_cast(path.index())); return true; } @@ -373,7 +374,7 @@ bool BinaryPrimitive::Equals(const Value* value) const { bool BinaryPrimitive::Flatten(::android::Res_value* out_value) const { out_value->dataType = value.dataType; - out_value->data = util::HostToDevice32(value.data); + out_value->data = android::util::HostToDevice32(value.data); return true; } @@ -678,7 +679,7 @@ void Attribute::Print(std::ostream* out) const { } static void BuildAttributeMismatchMessage(const Attribute& attr, const Item& value, - DiagMessage* out_msg) { + android::DiagMessage* out_msg) { *out_msg << "expected"; if (attr.type_mask & android::ResTable_map::TYPE_BOOLEAN) { *out_msg << " boolean"; @@ -723,7 +724,7 @@ static void BuildAttributeMismatchMessage(const Attribute& attr, const Item& val *out_msg << " but got " << value; } -bool Attribute::Matches(const Item& item, DiagMessage* out_msg) const { +bool Attribute::Matches(const Item& item, android::DiagMessage* out_msg) const { constexpr const uint32_t TYPE_ENUM = android::ResTable_map::TYPE_ENUM; constexpr const uint32_t TYPE_FLAGS = android::ResTable_map::TYPE_FLAGS; constexpr const uint32_t TYPE_INTEGER = android::ResTable_map::TYPE_INTEGER; @@ -732,7 +733,7 @@ bool Attribute::Matches(const Item& item, DiagMessage* out_msg) const { android::Res_value val = {}; item.Flatten(&val); - const uint32_t flattened_data = util::DeviceToHost32(val.data); + const uint32_t flattened_data = android::util::DeviceToHost32(val.data); // Always allow references. const uint32_t actual_type = ResourceUtils::AndroidTypeToAttributeTypeMask(val.dataType); @@ -872,7 +873,7 @@ void Style::Print(std::ostream* out) const { *out << " [" << util::Joiner(entries, ", ") << "]"; } -Style::Entry CloneEntry(const Style::Entry& entry, StringPool* pool) { +Style::Entry CloneEntry(const Style::Entry& entry, android::StringPool* pool) { Style::Entry cloned_entry{entry.key}; if (entry.value != nullptr) { CloningValueTransformer cloner(pool); @@ -881,7 +882,7 @@ Style::Entry CloneEntry(const Style::Entry& entry, StringPool* pool) { return cloned_entry; } -void Style::MergeWith(Style* other, StringPool* pool) { +void Style::MergeWith(Style* other, android::StringPool* pool) { if (other->parent) { parent = other->parent; } @@ -1077,7 +1078,7 @@ std::unique_ptr CopyValueFields(std::unique_ptr new_value, const T* value) return new_value; } -CloningValueTransformer::CloningValueTransformer(StringPool* new_pool) +CloningValueTransformer::CloningValueTransformer(android::StringPool* new_pool) : ValueTransformer(new_pool) { } diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h index 1694d6b6fe4a..f5167a1ac8e6 100644 --- a/tools/aapt2/ResourceValues.h +++ b/tools/aapt2/ResourceValues.h @@ -22,13 +22,12 @@ #include #include -#include "androidfw/ResourceTypes.h" -#include "androidfw/StringPiece.h" - -#include "Diagnostics.h" #include "Resource.h" -#include "StringPool.h" #include "ValueTransformer.h" +#include "androidfw/IDiagnostics.h" +#include "androidfw/ResourceTypes.h" +#include "androidfw/StringPiece.h" +#include "androidfw/StringPool.h" #include "io/File.h" #include "text/Printer.h" @@ -67,15 +66,15 @@ class Value { } // Returns the source where this value was defined. - const Source& GetSource() const { + const android::Source& GetSource() const { return source_; } - void SetSource(const Source& source) { + void SetSource(const android::Source& source) { source_ = source; } - void SetSource(Source&& source) { + void SetSource(android::Source&& source) { source_ = std::move(source); } @@ -113,7 +112,7 @@ class Value { friend std::ostream& operator<<(std::ostream& out, const Value& value); protected: - Source source_; + android::Source source_; std::string comment_; bool weak_ = false; bool translatable_ = true; @@ -197,9 +196,9 @@ struct Id : public TransformableItem> { // A raw, unprocessed string. This may contain quotations, escape sequences, and whitespace. // This shall *NOT* end up in the final resource table. struct RawString : public TransformableItem> { - StringPool::Ref value; + android::StringPool::Ref value; - explicit RawString(const StringPool::Ref& ref); + explicit RawString(const android::StringPool::Ref& ref); bool Equals(const Value* value) const override; bool Flatten(android::Res_value* out_value) const override; @@ -225,14 +224,14 @@ inline bool operator!=(const UntranslatableSection& a, const UntranslatableSecti } struct String : public TransformableItem> { - StringPool::Ref value; + android::StringPool::Ref value; // Sections of the string to NOT translate. Mainly used // for pseudolocalization. This data is NOT persisted // in any format. std::vector untranslatable_sections; - explicit String(const StringPool::Ref& ref); + explicit String(const android::StringPool::Ref& ref); bool Equals(const Value* value) const override; bool Flatten(android::Res_value* out_value) const override; @@ -241,14 +240,14 @@ struct String : public TransformableItem> { }; struct StyledString : public TransformableItem> { - StringPool::StyleRef value; + android::StringPool::StyleRef value; // Sections of the string to NOT translate. Mainly used // for pseudolocalization. This data is NOT persisted // in any format. std::vector untranslatable_sections; - explicit StyledString(const StringPool::StyleRef& ref); + explicit StyledString(const android::StringPool::StyleRef& ref); bool Equals(const Value* value) const override; bool Flatten(android::Res_value* out_value) const override; @@ -256,7 +255,7 @@ struct StyledString : public TransformableItem> { - StringPool::Ref path; + android::StringPool::Ref path; // A handle to the file object from which this file can be read. // This field is NOT persisted in any format. It is transient. @@ -267,7 +266,7 @@ struct FileReference : public TransformableItem> { static std::string MaskString(uint32_t type_mask); void Print(std::ostream* out) const override; - bool Matches(const Item& item, DiagMessage* out_msg = nullptr) const; + bool Matches(const Item& item, android::DiagMessage* out_msg = nullptr) const; }; struct Style : public TransformableValue> { @@ -338,7 +337,7 @@ struct Style : public TransformableValue> { // Merges `style` into this Style. All identical attributes of `style` take precedence, including // the parent, if there is one. - void MergeWith(Style* style, StringPool* pool); + void MergeWith(Style* style, android::StringPool* pool); }; struct Array : public TransformableValue> { @@ -367,7 +366,7 @@ struct Styleable : public TransformableValue> { struct Macro : public TransformableValue> { std::string raw_value; - StyleString style_string; + android::StyleString style_string; std::vector untranslatable_sections; struct Namespace { @@ -399,7 +398,7 @@ typename std::enable_if::value, std::ostream&>::type o } struct CloningValueTransformer : public ValueTransformer { - explicit CloningValueTransformer(StringPool* new_pool); + explicit CloningValueTransformer(android::StringPool* new_pool); std::unique_ptr TransformDerived(const Reference* value) override; std::unique_ptr TransformDerived(const Id* value) override; diff --git a/tools/aapt2/ResourceValues_test.cpp b/tools/aapt2/ResourceValues_test.cpp index c75a4b99e138..d788e3fd5fc7 100644 --- a/tools/aapt2/ResourceValues_test.cpp +++ b/tools/aapt2/ResourceValues_test.cpp @@ -37,7 +37,7 @@ constexpr const uint32_t TYPE_STRING = android::ResTable_map::TYPE_STRING; } // namespace TEST(ResourceValuesTest, PluralEquals) { - StringPool pool; + android::StringPool pool; Plural a; a.values[Plural::One] = util::make_unique(pool.MakeRef("one")); @@ -56,7 +56,7 @@ TEST(ResourceValuesTest, PluralEquals) { } TEST(ResourceValuesTest, PluralClone) { - StringPool pool; + android::StringPool pool; Plural a; a.values[Plural::One] = util::make_unique(pool.MakeRef("one")); @@ -68,7 +68,7 @@ TEST(ResourceValuesTest, PluralClone) { } TEST(ResourceValuesTest, ArrayEquals) { - StringPool pool; + android::StringPool pool; Array a; a.elements.push_back(util::make_unique(pool.MakeRef("one"))); @@ -92,7 +92,7 @@ TEST(ResourceValuesTest, ArrayEquals) { } TEST(ResourceValuesTest, ArrayClone) { - StringPool pool; + android::StringPool pool; Array a; a.elements.push_back(util::make_unique(pool.MakeRef("one"))); @@ -104,7 +104,7 @@ TEST(ResourceValuesTest, ArrayClone) { } TEST(ResourceValuesTest, StyleEquals) { - StringPool pool; + android::StringPool pool; std::unique_ptr