/* * Copyright (C) 2016 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 "ResourceTable.h" #include "ResourceUtils.h" #include "ValueVisitor.h" #include "proto/ProtoHelpers.h" #include "proto/ProtoSerialize.h" #include <androidfw/ResourceTypes.h> namespace aapt { namespace { class ReferenceIdToNameVisitor : public ValueVisitor { public: using ValueVisitor::visit; ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceNameRef>* mapping) : mMapping(mapping) { assert(mMapping); } void visit(Reference* reference) override { if (!reference->id || !reference->id.value().isValid()) { return; } ResourceId id = reference->id.value(); auto cacheIter = mMapping->find(id); if (cacheIter != mMapping->end()) { reference->name = cacheIter->second.toResourceName(); } } private: const std::map<ResourceId, ResourceNameRef>* mMapping; }; class PackagePbDeserializer { public: PackagePbDeserializer(const android::ResStringPool* valuePool, const android::ResStringPool* sourcePool, const android::ResStringPool* symbolPool, const Source& source, IDiagnostics* diag) : mValuePool(valuePool), mSourcePool(sourcePool), mSymbolPool(symbolPool), mSource(source), mDiag(diag) { } public: bool deserializeFromPb(const pb::Package& pbPackage, ResourceTable* table) { Maybe<uint8_t> id; if (pbPackage.has_package_id()) { id = static_cast<uint8_t>(pbPackage.package_id()); } std::map<ResourceId, ResourceNameRef> idIndex; ResourceTablePackage* pkg = table->createPackage( util::utf8ToUtf16(pbPackage.package_name()), id); for (const pb::Type& pbType : pbPackage.types()) { const ResourceType* resType = parseResourceType(util::utf8ToUtf16(pbType.name())); if (!resType) { mDiag->error(DiagMessage(mSource) << "unknown type '" << pbType.name() << "'"); return {}; } ResourceTableType* type = pkg->findOrCreateType(*resType); for (const pb::Entry& pbEntry : pbType.entries()) { ResourceEntry* entry = type->findOrCreateEntry(util::utf8ToUtf16(pbEntry.name())); // Deserialize the symbol status (public/private with source and comments). if (pbEntry.has_symbol_status()) { const pb::SymbolStatus& pbStatus = pbEntry.symbol_status(); if (pbStatus.has_source()) { deserializeSourceFromPb(pbStatus.source(), *mSourcePool, &entry->symbolStatus.source); } if (pbStatus.has_comment()) { entry->symbolStatus.comment = util::utf8ToUtf16(pbStatus.comment()); } SymbolState visibility = deserializeVisibilityFromPb(pbStatus.visibility()); entry->symbolStatus.state = visibility; if (visibility == SymbolState::kPublic) { // This is a public symbol, we must encode the ID now if there is one. if (pbEntry.has_id()) { entry->id = static_cast<uint16_t>(pbEntry.id()); } if (type->symbolStatus.state != SymbolState::kPublic) { // If the type has not been made public, do so now. type->symbolStatus.state = SymbolState::kPublic; if (pbType.has_id()) { type->id = static_cast<uint8_t>(pbType.id()); } } } else if (visibility == SymbolState::kPrivate) { if (type->symbolStatus.state == SymbolState::kUndefined) { type->symbolStatus.state = SymbolState::kPrivate; } } } ResourceId resId(pbPackage.package_id(), pbType.id(), pbEntry.id()); if (resId.isValid()) { idIndex[resId] = ResourceNameRef(pkg->name, type->type, entry->name); } for (const pb::ConfigValue& pbConfigValue : pbEntry.config_values()) { const pb::ConfigDescription& pbConfig = pbConfigValue.config(); ConfigDescription config; if (!deserializeConfigDescriptionFromPb(pbConfig, &config)) { mDiag->error(DiagMessage(mSource) << "invalid configuration"); return {}; } ResourceConfigValue* configValue = entry->findOrCreateValue(config, pbConfig.product()); if (configValue->value) { // Duplicate config. mDiag->error(DiagMessage(mSource) << "duplicate configuration"); return {}; } configValue->value = deserializeValueFromPb(pbConfigValue.value(), config, &table->stringPool); if (!configValue->value) { return {}; } } } } ReferenceIdToNameVisitor visitor(&idIndex); visitAllValuesInPackage(pkg, &visitor); return true; } private: std::unique_ptr<Item> deserializeItemFromPb(const pb::Item& pbItem, const ConfigDescription& config, StringPool* pool) { if (pbItem.has_ref()) { const pb::Reference& pbRef = pbItem.ref(); std::unique_ptr<Reference> ref = util::make_unique<Reference>(); if (!deserializeReferenceFromPb(pbRef, ref.get())) { return {}; } return std::move(ref); } else if (pbItem.has_prim()) { const pb::Primitive& pbPrim = pbItem.prim(); android::Res_value prim = {}; prim.dataType = static_cast<uint8_t>(pbPrim.type()); prim.data = pbPrim.data(); return util::make_unique<BinaryPrimitive>(prim); } else if (pbItem.has_id()) { return util::make_unique<Id>(); } else if (pbItem.has_str()) { const uint32_t idx = pbItem.str().idx(); StringPiece16 str = util::getString(*mValuePool, idx); const android::ResStringPool_span* spans = mValuePool->styleAt(idx); if (spans && spans->name.index != android::ResStringPool_span::END) { StyleString styleStr = { str.toString() }; while (spans->name.index != android::ResStringPool_span::END) { styleStr.spans.push_back(Span{ util::getString(*mValuePool, spans->name.index).toString(), spans->firstChar, spans->lastChar }); spans++; } return util::make_unique<StyledString>( pool->makeRef(styleStr, StringPool::Context{ 1, config })); } return util::make_unique<String>( pool->makeRef(str, StringPool::Context{ 1, config })); } else if (pbItem.has_raw_str()) { const uint32_t idx = pbItem.raw_str().idx(); StringPiece16 str = util::getString(*mValuePool, idx); return util::make_unique<RawString>( pool->makeRef(str, StringPool::Context{ 1, config })); } else if (pbItem.has_file()) { const uint32_t idx = pbItem.file().path_idx(); StringPiece16 str = util::getString(*mValuePool, idx); return util::make_unique<FileReference>( pool->makeRef(str, StringPool::Context{ 0, config })); } else { mDiag->error(DiagMessage(mSource) << "unknown item"); } return {}; } std::unique_ptr<Value> deserializeValueFromPb(const pb::Value& pbValue, const ConfigDescription& config, StringPool* pool) { const bool isWeak = pbValue.has_weak() ? pbValue.weak() : false; std::unique_ptr<Value> value; if (pbValue.has_item()) { value = deserializeItemFromPb(pbValue.item(), config, pool); if (!value) { return {}; } } else if (pbValue.has_compound_value()) { const pb::CompoundValue pbCompoundValue = pbValue.compound_value(); if (pbCompoundValue.has_attr()) { const pb::Attribute& pbAttr = pbCompoundValue.attr(); std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak); attr->typeMask = pbAttr.format_flags(); attr->minInt = pbAttr.min_int(); attr->maxInt = pbAttr.max_int(); for (const pb::Attribute_Symbol& pbSymbol : pbAttr.symbols()) { Attribute::Symbol symbol; deserializeItemCommon(pbSymbol, &symbol.symbol); if (!deserializeReferenceFromPb(pbSymbol.name(), &symbol.symbol)) { return {}; } symbol.value = pbSymbol.value(); attr->symbols.push_back(std::move(symbol)); } value = std::move(attr); } else if (pbCompoundValue.has_style()) { const pb::Style& pbStyle = pbCompoundValue.style(); std::unique_ptr<Style> style = util::make_unique<Style>(); if (pbStyle.has_parent()) { style->parent = Reference(); if (!deserializeReferenceFromPb(pbStyle.parent(), &style->parent.value())) { return {}; } if (pbStyle.has_parent_source()) { Source parentSource; deserializeSourceFromPb(pbStyle.parent_source(), *mSourcePool, &parentSource); style->parent.value().setSource(std::move(parentSource)); } } for (const pb::Style_Entry& pbEntry : pbStyle.entries()) { Style::Entry entry; deserializeItemCommon(pbEntry, &entry.key); if (!deserializeReferenceFromPb(pbEntry.key(), &entry.key)) { return {}; } entry.value = deserializeItemFromPb(pbEntry.item(), config, pool); if (!entry.value) { return {}; } deserializeItemCommon(pbEntry, entry.value.get()); style->entries.push_back(std::move(entry)); } value = std::move(style); } else if (pbCompoundValue.has_styleable()) { const pb::Styleable& pbStyleable = pbCompoundValue.styleable(); std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>(); for (const pb::Styleable_Entry& pbEntry : pbStyleable.entries()) { Reference attrRef; deserializeItemCommon(pbEntry, &attrRef); deserializeReferenceFromPb(pbEntry.attr(), &attrRef); styleable->entries.push_back(std::move(attrRef)); } value = std::move(styleable); } else if (pbCompoundValue.has_array()) { const pb::Array& pbArray = pbCompoundValue.array(); std::unique_ptr<Array> array = util::make_unique<Array>(); for (const pb::Array_Entry& pbEntry : pbArray.entries()) { std::unique_ptr<Item> item = deserializeItemFromPb(pbEntry.item(), config, pool); if (!item) { return {}; } deserializeItemCommon(pbEntry, item.get()); array->items.push_back(std::move(item)); } value = std::move(array); } else if (pbCompoundValue.has_plural()) { const pb::Plural& pbPlural = pbCompoundValue.plural(); std::unique_ptr<Plural> plural = util::make_unique<Plural>(); for (const pb::Plural_Entry& pbEntry : pbPlural.entries()) { size_t pluralIdx = deserializePluralEnumFromPb(pbEntry.arity()); plural->values[pluralIdx] = deserializeItemFromPb(pbEntry.item(), config, pool); if (!plural->values[pluralIdx]) { return {}; } deserializeItemCommon(pbEntry, plural->values[pluralIdx].get()); } value = std::move(plural); } else { mDiag->error(DiagMessage(mSource) << "unknown compound value"); return {}; } } else { mDiag->error(DiagMessage(mSource) << "unknown value"); return {}; } assert(value && "forgot to set value"); value->setWeak(isWeak); deserializeItemCommon(pbValue, value.get()); return value; } bool deserializeReferenceFromPb(const pb::Reference& pbRef, Reference* outRef) { outRef->referenceType = deserializeReferenceTypeFromPb(pbRef.type()); outRef->privateReference = pbRef.private_(); if (!pbRef.has_id() && !pbRef.has_symbol_idx()) { return false; } if (pbRef.has_id()) { outRef->id = ResourceId(pbRef.id()); } if (pbRef.has_symbol_idx()) { StringPiece16 strSymbol = util::getString(*mSymbolPool, pbRef.symbol_idx()); ResourceNameRef nameRef; if (!ResourceUtils::parseResourceName(strSymbol, &nameRef, nullptr)) { mDiag->error(DiagMessage(mSource) << "invalid reference name '" << strSymbol << "'"); return false; } outRef->name = nameRef.toResourceName(); } return true; } template <typename T> void deserializeItemCommon(const T& pbItem, Value* outValue) { if (pbItem.has_source()) { Source source; deserializeSourceFromPb(pbItem.source(), *mSourcePool, &source); outValue->setSource(std::move(source)); } if (pbItem.has_comment()) { outValue->setComment(util::utf8ToUtf16(pbItem.comment())); } } private: const android::ResStringPool* mValuePool; const android::ResStringPool* mSourcePool; const android::ResStringPool* mSymbolPool; const Source mSource; IDiagnostics* mDiag; }; } // namespace std::unique_ptr<ResourceTable> deserializeTableFromPb(const pb::ResourceTable& pbTable, const Source& source, IDiagnostics* diag) { // We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which // causes errors when qualifying it with android:: using namespace android; std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>(); if (!pbTable.has_string_pool()) { diag->error(DiagMessage(source) << "no string pool found"); return {}; } ResStringPool valuePool; status_t result = valuePool.setTo(pbTable.string_pool().data().data(), pbTable.string_pool().data().size()); if (result != NO_ERROR) { diag->error(DiagMessage(source) << "invalid string pool"); return {}; } ResStringPool sourcePool; if (pbTable.has_source_pool()) { result = sourcePool.setTo(pbTable.source_pool().data().data(), pbTable.source_pool().data().size()); if (result != NO_ERROR) { diag->error(DiagMessage(source) << "invalid source pool"); return {}; } } ResStringPool symbolPool; if (pbTable.has_symbol_pool()) { result = symbolPool.setTo(pbTable.symbol_pool().data().data(), pbTable.symbol_pool().data().size()); if (result != NO_ERROR) { diag->error(DiagMessage(source) << "invalid symbol pool"); return {}; } } PackagePbDeserializer packagePbDeserializer(&valuePool, &sourcePool, &symbolPool, source, diag); for (const pb::Package& pbPackage : pbTable.packages()) { if (!packagePbDeserializer.deserializeFromPb(pbPackage, table.get())) { return {}; } } return table; } std::unique_ptr<ResourceFile> deserializeCompiledFileFromPb(const pb::CompiledFile& pbFile, const Source& source, IDiagnostics* diag) { std::unique_ptr<ResourceFile> file = util::make_unique<ResourceFile>(); ResourceNameRef nameRef; // Need to create an lvalue here so that nameRef can point to something real. std::u16string utf16Name = util::utf8ToUtf16(pbFile.resource_name()); if (!ResourceUtils::parseResourceName(utf16Name, &nameRef)) { diag->error(DiagMessage(source) << "invalid resource name in compiled file header: " << pbFile.resource_name()); return {}; } file->name = nameRef.toResourceName(); file->source.path = pbFile.source_path(); deserializeConfigDescriptionFromPb(pbFile.config(), &file->config); for (const pb::CompiledFile_Symbol& pbSymbol : pbFile.exported_symbols()) { // Need to create an lvalue here so that nameRef can point to something real. utf16Name = util::utf8ToUtf16(pbSymbol.resource_name()); if (!ResourceUtils::parseResourceName(utf16Name, &nameRef)) { diag->error(DiagMessage(source) << "invalid resource name for exported symbol in " "compiled file header: " << pbFile.resource_name()); return {}; } file->exportedSymbols.push_back( SourcedResourceName{ nameRef.toResourceName(), pbSymbol.line_no() }); } return file; } CompiledFileInputStream::CompiledFileInputStream(const void* data, size_t size) : mIn(static_cast<const uint8_t*>(data), size), mPbFile(), mData(static_cast<const uint8_t*>(data)), mSize(size) { } const pb::CompiledFile* CompiledFileInputStream::CompiledFile() { if (!mPbFile) { std::unique_ptr<pb::CompiledFile> pbFile = util::make_unique<pb::CompiledFile>(); uint64_t pbSize = 0u; if (!mIn.ReadLittleEndian64(&pbSize)) { return nullptr; } mIn.PushLimit(static_cast<int>(pbSize)); if (!pbFile->ParsePartialFromCodedStream(&mIn)) { return nullptr; } const size_t padding = 4 - (pbSize & 0x03); const size_t offset = sizeof(uint64_t) + pbSize + padding; if (offset > mSize) { return nullptr; } mData += offset; mSize -= offset; mPbFile = std::move(pbFile); } return mPbFile.get(); } const void* CompiledFileInputStream::data() { if (!mPbFile) { if (!CompiledFile()) { return nullptr; } } return mData; } size_t CompiledFileInputStream::size() { if (!mPbFile) { if (!CompiledFile()) { return 0; } } return mSize; } } // namespace aapt