/* * 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 "proto/ProtoSerialize.h" #include "android-base/logging.h" #include "androidfw/ResourceTypes.h" #include "ResourceTable.h" #include "ResourceUtils.h" #include "ValueVisitor.h" #include "proto/ProtoHelpers.h" namespace aapt { namespace { class ReferenceIdToNameVisitor : public ValueVisitor { public: using ValueVisitor::Visit; explicit ReferenceIdToNameVisitor( const std::map* mapping) : mapping_(mapping) { CHECK(mapping_ != nullptr); } void Visit(Reference* reference) override { if (!reference->id || !reference->id.value().is_valid()) { return; } ResourceId id = reference->id.value(); auto cache_iter = mapping_->find(id); if (cache_iter != mapping_->end()) { reference->name = cache_iter->second.ToResourceName(); } } private: const std::map* mapping_; }; class PackagePbDeserializer { public: PackagePbDeserializer(const android::ResStringPool* valuePool, const android::ResStringPool* sourcePool, const android::ResStringPool* symbolPool, const Source& source, IDiagnostics* diag) : value_pool_(valuePool), source_pool_(sourcePool), symbol_pool_(symbolPool), source_(source), diag_(diag) {} public: bool DeserializeFromPb(const pb::Package& pbPackage, ResourceTable* table) { Maybe id; if (pbPackage.has_package_id()) { id = static_cast(pbPackage.package_id()); } std::map idIndex; ResourceTablePackage* pkg = table->CreatePackage(pbPackage.package_name(), id); for (const pb::Type& pbType : pbPackage.types()) { const ResourceType* resType = ParseResourceType(pbType.name()); if (!resType) { diag_->Error(DiagMessage(source_) << "unknown type '" << pbType.name() << "'"); return {}; } ResourceTableType* type = pkg->FindOrCreateType(*resType); for (const pb::Entry& pbEntry : pbType.entries()) { ResourceEntry* entry = type->FindOrCreateEntry(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(), *source_pool_, &entry->symbol_status.source); } if (pbStatus.has_comment()) { entry->symbol_status.comment = pbStatus.comment(); } SymbolState visibility = DeserializeVisibilityFromPb(pbStatus.visibility()); entry->symbol_status.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(pbEntry.id()); } if (type->symbol_status.state != SymbolState::kPublic) { // If the type has not been made public, do so now. type->symbol_status.state = SymbolState::kPublic; if (pbType.has_id()) { type->id = static_cast(pbType.id()); } } } else if (visibility == SymbolState::kPrivate) { if (type->symbol_status.state == SymbolState::kUndefined) { type->symbol_status.state = SymbolState::kPrivate; } } } ResourceId resId(pbPackage.package_id(), pbType.id(), pbEntry.id()); if (resId.is_valid()) { 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)) { diag_->Error(DiagMessage(source_) << "invalid configuration"); return {}; } ResourceConfigValue* configValue = entry->FindOrCreateValue(config, pbConfig.product()); if (configValue->value) { // Duplicate config. diag_->Error(DiagMessage(source_) << "duplicate configuration"); return {}; } configValue->value = DeserializeValueFromPb( pbConfigValue.value(), config, &table->string_pool); if (!configValue->value) { return {}; } } } } ReferenceIdToNameVisitor visitor(&idIndex); VisitAllValuesInPackage(pkg, &visitor); return true; } private: std::unique_ptr DeserializeItemFromPb(const pb::Item& pb_item, const ConfigDescription& config, StringPool* pool) { if (pb_item.has_ref()) { const pb::Reference& pb_ref = pb_item.ref(); std::unique_ptr ref = util::make_unique(); if (!DeserializeReferenceFromPb(pb_ref, ref.get())) { return {}; } return std::move(ref); } else if (pb_item.has_prim()) { const pb::Primitive& pb_prim = pb_item.prim(); android::Res_value prim = {}; prim.dataType = static_cast(pb_prim.type()); prim.data = pb_prim.data(); return util::make_unique(prim); } else if (pb_item.has_id()) { return util::make_unique(); } else if (pb_item.has_str()) { const uint32_t idx = pb_item.str().idx(); const std::string str = util::GetString(*value_pool_, idx); const android::ResStringPool_span* spans = value_pool_->styleAt(idx); if (spans && spans->name.index != android::ResStringPool_span::END) { StyleString style_str = {str}; while (spans->name.index != android::ResStringPool_span::END) { style_str.spans.push_back( Span{util::GetString(*value_pool_, spans->name.index), spans->firstChar, spans->lastChar}); spans++; } return util::make_unique(pool->MakeRef( style_str, StringPool::Context(StringPool::Context::kStylePriority, config))); } return util::make_unique( pool->MakeRef(str, StringPool::Context(config))); } else if (pb_item.has_raw_str()) { const uint32_t idx = pb_item.raw_str().idx(); const std::string str = util::GetString(*value_pool_, idx); return util::make_unique( pool->MakeRef(str, StringPool::Context(config))); } else if (pb_item.has_file()) { const uint32_t idx = pb_item.file().path_idx(); const std::string str = util::GetString(*value_pool_, idx); return util::make_unique(pool->MakeRef( str, StringPool::Context(StringPool::Context::kHighPriority, config))); } else { diag_->Error(DiagMessage(source_) << "unknown item"); } return {}; } std::unique_ptr DeserializeValueFromPb(const pb::Value& pb_value, const ConfigDescription& config, StringPool* pool) { const bool is_weak = pb_value.has_weak() ? pb_value.weak() : false; std::unique_ptr value; if (pb_value.has_item()) { value = DeserializeItemFromPb(pb_value.item(), config, pool); if (!value) { return {}; } } else if (pb_value.has_compound_value()) { const pb::CompoundValue& pb_compound_value = pb_value.compound_value(); if (pb_compound_value.has_attr()) { const pb::Attribute& pb_attr = pb_compound_value.attr(); std::unique_ptr attr = util::make_unique(is_weak); attr->type_mask = pb_attr.format_flags(); attr->min_int = pb_attr.min_int(); attr->max_int = pb_attr.max_int(); for (const pb::Attribute_Symbol& pb_symbol : pb_attr.symbols()) { Attribute::Symbol symbol; DeserializeItemCommon(pb_symbol, &symbol.symbol); if (!DeserializeReferenceFromPb(pb_symbol.name(), &symbol.symbol)) { return {}; } symbol.value = pb_symbol.value(); attr->symbols.push_back(std::move(symbol)); } value = std::move(attr); } else if (pb_compound_value.has_style()) { const pb::Style& pb_style = pb_compound_value.style(); std::unique_ptr