Home | History | Annotate | Download | only in proto
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "ResourceTable.h"
     18 #include "ResourceUtils.h"
     19 #include "ValueVisitor.h"
     20 #include "proto/ProtoHelpers.h"
     21 #include "proto/ProtoSerialize.h"
     22 
     23 #include <androidfw/ResourceTypes.h>
     24 
     25 namespace aapt {
     26 
     27 namespace {
     28 
     29 class ReferenceIdToNameVisitor : public ValueVisitor {
     30 public:
     31     using ValueVisitor::visit;
     32 
     33     ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceNameRef>* mapping) :
     34             mMapping(mapping) {
     35         assert(mMapping);
     36     }
     37 
     38     void visit(Reference* reference) override {
     39         if (!reference->id || !reference->id.value().isValid()) {
     40             return;
     41         }
     42 
     43         ResourceId id = reference->id.value();
     44         auto cacheIter = mMapping->find(id);
     45         if (cacheIter != mMapping->end()) {
     46             reference->name = cacheIter->second.toResourceName();
     47         }
     48     }
     49 
     50 private:
     51     const std::map<ResourceId, ResourceNameRef>* mMapping;
     52 };
     53 
     54 class PackagePbDeserializer {
     55 public:
     56     PackagePbDeserializer(const android::ResStringPool* valuePool,
     57                           const android::ResStringPool* sourcePool,
     58                           const android::ResStringPool* symbolPool,
     59                           const Source& source, IDiagnostics* diag) :
     60             mValuePool(valuePool), mSourcePool(sourcePool), mSymbolPool(symbolPool),
     61             mSource(source), mDiag(diag) {
     62     }
     63 
     64 public:
     65     bool deserializeFromPb(const pb::Package& pbPackage, ResourceTable* table) {
     66         Maybe<uint8_t> id;
     67         if (pbPackage.has_package_id()) {
     68             id = static_cast<uint8_t>(pbPackage.package_id());
     69         }
     70 
     71         std::map<ResourceId, ResourceNameRef> idIndex;
     72 
     73         ResourceTablePackage* pkg = table->createPackage(
     74                 util::utf8ToUtf16(pbPackage.package_name()), id);
     75         for (const pb::Type& pbType : pbPackage.types()) {
     76             const ResourceType* resType = parseResourceType(util::utf8ToUtf16(pbType.name()));
     77             if (!resType) {
     78                 mDiag->error(DiagMessage(mSource) << "unknown type '" << pbType.name() << "'");
     79                 return {};
     80             }
     81 
     82             ResourceTableType* type = pkg->findOrCreateType(*resType);
     83 
     84             for (const pb::Entry& pbEntry : pbType.entries()) {
     85                 ResourceEntry* entry = type->findOrCreateEntry(util::utf8ToUtf16(pbEntry.name()));
     86 
     87                 // Deserialize the symbol status (public/private with source and comments).
     88                 if (pbEntry.has_symbol_status()) {
     89                     const pb::SymbolStatus& pbStatus = pbEntry.symbol_status();
     90                     if (pbStatus.has_source()) {
     91                         deserializeSourceFromPb(pbStatus.source(), *mSourcePool,
     92                                                 &entry->symbolStatus.source);
     93                     }
     94 
     95                     if (pbStatus.has_comment()) {
     96                         entry->symbolStatus.comment = util::utf8ToUtf16(pbStatus.comment());
     97                     }
     98 
     99                     SymbolState visibility = deserializeVisibilityFromPb(pbStatus.visibility());
    100                     entry->symbolStatus.state = visibility;
    101 
    102                     if (visibility == SymbolState::kPublic) {
    103                         // This is a public symbol, we must encode the ID now if there is one.
    104                         if (pbEntry.has_id()) {
    105                             entry->id = static_cast<uint16_t>(pbEntry.id());
    106                         }
    107 
    108                         if (type->symbolStatus.state != SymbolState::kPublic) {
    109                             // If the type has not been made public, do so now.
    110                             type->symbolStatus.state = SymbolState::kPublic;
    111                             if (pbType.has_id()) {
    112                                 type->id = static_cast<uint8_t>(pbType.id());
    113                             }
    114                         }
    115                     } else if (visibility == SymbolState::kPrivate) {
    116                         if (type->symbolStatus.state == SymbolState::kUndefined) {
    117                             type->symbolStatus.state = SymbolState::kPrivate;
    118                         }
    119                     }
    120                 }
    121 
    122                 ResourceId resId(pbPackage.package_id(), pbType.id(), pbEntry.id());
    123                 if (resId.isValid()) {
    124                     idIndex[resId] = ResourceNameRef(pkg->name, type->type, entry->name);
    125                 }
    126 
    127                 for (const pb::ConfigValue& pbConfigValue : pbEntry.config_values()) {
    128                     const pb::ConfigDescription& pbConfig = pbConfigValue.config();
    129 
    130                     ConfigDescription config;
    131                     if (!deserializeConfigDescriptionFromPb(pbConfig, &config)) {
    132                         mDiag->error(DiagMessage(mSource) << "invalid configuration");
    133                         return {};
    134                     }
    135 
    136                     ResourceConfigValue* configValue = entry->findOrCreateValue(config,
    137                                                                                 pbConfig.product());
    138                     if (configValue->value) {
    139                         // Duplicate config.
    140                         mDiag->error(DiagMessage(mSource) << "duplicate configuration");
    141                         return {};
    142                     }
    143 
    144                     configValue->value = deserializeValueFromPb(pbConfigValue.value(),
    145                                                                 config, &table->stringPool);
    146                     if (!configValue->value) {
    147                         return {};
    148                     }
    149                 }
    150             }
    151         }
    152 
    153         ReferenceIdToNameVisitor visitor(&idIndex);
    154         visitAllValuesInPackage(pkg, &visitor);
    155         return true;
    156     }
    157 
    158 private:
    159     std::unique_ptr<Item> deserializeItemFromPb(const pb::Item& pbItem,
    160                                                 const ConfigDescription& config,
    161                                                 StringPool* pool) {
    162         if (pbItem.has_ref()) {
    163             const pb::Reference& pbRef = pbItem.ref();
    164             std::unique_ptr<Reference> ref = util::make_unique<Reference>();
    165             if (!deserializeReferenceFromPb(pbRef, ref.get())) {
    166                 return {};
    167             }
    168             return std::move(ref);
    169 
    170         } else if (pbItem.has_prim()) {
    171             const pb::Primitive& pbPrim = pbItem.prim();
    172             android::Res_value prim = {};
    173             prim.dataType = static_cast<uint8_t>(pbPrim.type());
    174             prim.data = pbPrim.data();
    175             return util::make_unique<BinaryPrimitive>(prim);
    176 
    177         } else if (pbItem.has_id()) {
    178             return util::make_unique<Id>();
    179 
    180         } else if (pbItem.has_str()) {
    181             const uint32_t idx = pbItem.str().idx();
    182             StringPiece16 str = util::getString(*mValuePool, idx);
    183 
    184             const android::ResStringPool_span* spans = mValuePool->styleAt(idx);
    185             if (spans && spans->name.index != android::ResStringPool_span::END) {
    186                 StyleString styleStr = { str.toString() };
    187                 while (spans->name.index != android::ResStringPool_span::END) {
    188                     styleStr.spans.push_back(Span{
    189                             util::getString(*mValuePool, spans->name.index).toString(),
    190                             spans->firstChar,
    191                             spans->lastChar
    192                     });
    193                     spans++;
    194                 }
    195                 return util::make_unique<StyledString>(
    196                         pool->makeRef(styleStr, StringPool::Context{ 1, config }));
    197             }
    198             return util::make_unique<String>(
    199                     pool->makeRef(str, StringPool::Context{ 1, config }));
    200 
    201         } else if (pbItem.has_raw_str()) {
    202             const uint32_t idx = pbItem.raw_str().idx();
    203             StringPiece16 str = util::getString(*mValuePool, idx);
    204             return util::make_unique<RawString>(
    205                     pool->makeRef(str, StringPool::Context{ 1, config }));
    206 
    207         } else if (pbItem.has_file()) {
    208             const uint32_t idx = pbItem.file().path_idx();
    209             StringPiece16 str = util::getString(*mValuePool, idx);
    210             return util::make_unique<FileReference>(
    211                     pool->makeRef(str, StringPool::Context{ 0, config }));
    212 
    213         } else {
    214             mDiag->error(DiagMessage(mSource) << "unknown item");
    215         }
    216         return {};
    217     }
    218 
    219     std::unique_ptr<Value> deserializeValueFromPb(const pb::Value& pbValue,
    220                                                   const ConfigDescription& config,
    221                                                   StringPool* pool) {
    222         const bool isWeak = pbValue.has_weak() ? pbValue.weak() : false;
    223 
    224         std::unique_ptr<Value> value;
    225         if (pbValue.has_item()) {
    226             value = deserializeItemFromPb(pbValue.item(), config, pool);
    227             if (!value) {
    228                 return {};
    229             }
    230 
    231         } else if (pbValue.has_compound_value()) {
    232             const pb::CompoundValue pbCompoundValue = pbValue.compound_value();
    233             if (pbCompoundValue.has_attr()) {
    234                 const pb::Attribute& pbAttr = pbCompoundValue.attr();
    235                 std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
    236                 attr->typeMask = pbAttr.format_flags();
    237                 for (const pb::Attribute_Symbol& pbSymbol : pbAttr.symbols()) {
    238                     Attribute::Symbol symbol;
    239                     deserializeItemCommon(pbSymbol, &symbol.symbol);
    240                     if (!deserializeReferenceFromPb(pbSymbol.name(), &symbol.symbol)) {
    241                         return {};
    242                     }
    243                     symbol.value = pbSymbol.value();
    244                     attr->symbols.push_back(std::move(symbol));
    245                 }
    246                 value = std::move(attr);
    247 
    248             } else if (pbCompoundValue.has_style()) {
    249                 const pb::Style& pbStyle = pbCompoundValue.style();
    250                 std::unique_ptr<Style> style = util::make_unique<Style>();
    251                 if (pbStyle.has_parent()) {
    252                     style->parent = Reference();
    253                     if (!deserializeReferenceFromPb(pbStyle.parent(), &style->parent.value())) {
    254                         return {};
    255                     }
    256 
    257                     if (pbStyle.has_parent_source()) {
    258                         Source parentSource;
    259                         deserializeSourceFromPb(pbStyle.parent_source(), *mSourcePool,
    260                                                 &parentSource);
    261                         style->parent.value().setSource(std::move(parentSource));
    262                     }
    263                 }
    264 
    265                 for (const pb::Style_Entry& pbEntry : pbStyle.entries()) {
    266                     Style::Entry entry;
    267                     deserializeItemCommon(pbEntry, &entry.key);
    268                     if (!deserializeReferenceFromPb(pbEntry.key(), &entry.key)) {
    269                         return {};
    270                     }
    271 
    272                     entry.value = deserializeItemFromPb(pbEntry.item(), config, pool);
    273                     if (!entry.value) {
    274                         return {};
    275                     }
    276 
    277                     deserializeItemCommon(pbEntry, entry.value.get());
    278                     style->entries.push_back(std::move(entry));
    279                 }
    280                 value = std::move(style);
    281 
    282             } else if (pbCompoundValue.has_styleable()) {
    283                 const pb::Styleable& pbStyleable = pbCompoundValue.styleable();
    284                 std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
    285                 for (const pb::Styleable_Entry& pbEntry : pbStyleable.entries()) {
    286                     Reference attrRef;
    287                     deserializeItemCommon(pbEntry, &attrRef);
    288                     deserializeReferenceFromPb(pbEntry.attr(), &attrRef);
    289                     styleable->entries.push_back(std::move(attrRef));
    290                 }
    291                 value = std::move(styleable);
    292 
    293             } else if (pbCompoundValue.has_array()) {
    294                 const pb::Array& pbArray = pbCompoundValue.array();
    295                 std::unique_ptr<Array> array = util::make_unique<Array>();
    296                 for (const pb::Array_Entry& pbEntry : pbArray.entries()) {
    297                     std::unique_ptr<Item> item = deserializeItemFromPb(pbEntry.item(), config,
    298                                                                        pool);
    299                     if (!item) {
    300                         return {};
    301                     }
    302 
    303                     deserializeItemCommon(pbEntry, item.get());
    304                     array->items.push_back(std::move(item));
    305                 }
    306                 value = std::move(array);
    307 
    308             } else if (pbCompoundValue.has_plural()) {
    309                 const pb::Plural& pbPlural = pbCompoundValue.plural();
    310                 std::unique_ptr<Plural> plural = util::make_unique<Plural>();
    311                 for (const pb::Plural_Entry& pbEntry : pbPlural.entries()) {
    312                     size_t pluralIdx = deserializePluralEnumFromPb(pbEntry.arity());
    313                     plural->values[pluralIdx] = deserializeItemFromPb(pbEntry.item(), config,
    314                                                                       pool);
    315                     if (!plural->values[pluralIdx]) {
    316                         return {};
    317                     }
    318 
    319                     deserializeItemCommon(pbEntry, plural->values[pluralIdx].get());
    320                 }
    321                 value = std::move(plural);
    322 
    323             } else {
    324                 mDiag->error(DiagMessage(mSource) << "unknown compound value");
    325                 return {};
    326             }
    327         } else {
    328             mDiag->error(DiagMessage(mSource) << "unknown value");
    329             return {};
    330         }
    331 
    332         assert(value && "forgot to set value");
    333 
    334         value->setWeak(isWeak);
    335         deserializeItemCommon(pbValue, value.get());
    336         return value;
    337     }
    338 
    339     bool deserializeReferenceFromPb(const pb::Reference& pbRef, Reference* outRef) {
    340         outRef->referenceType = deserializeReferenceTypeFromPb(pbRef.type());
    341         outRef->privateReference = pbRef.private_();
    342 
    343         if (!pbRef.has_id() && !pbRef.has_symbol_idx()) {
    344             return false;
    345         }
    346 
    347         if (pbRef.has_id()) {
    348             outRef->id = ResourceId(pbRef.id());
    349         }
    350 
    351         if (pbRef.has_symbol_idx()) {
    352             StringPiece16 strSymbol = util::getString(*mSymbolPool, pbRef.symbol_idx());
    353             ResourceNameRef nameRef;
    354             if (!ResourceUtils::parseResourceName(strSymbol, &nameRef, nullptr)) {
    355                 mDiag->error(DiagMessage(mSource) << "invalid reference name '"
    356                              << strSymbol << "'");
    357                 return false;
    358             }
    359 
    360             outRef->name = nameRef.toResourceName();
    361         }
    362         return true;
    363     }
    364 
    365     template <typename T>
    366     void deserializeItemCommon(const T& pbItem, Value* outValue) {
    367         if (pbItem.has_source()) {
    368             Source source;
    369             deserializeSourceFromPb(pbItem.source(), *mSourcePool, &source);
    370             outValue->setSource(std::move(source));
    371         }
    372 
    373         if (pbItem.has_comment()) {
    374             outValue->setComment(util::utf8ToUtf16(pbItem.comment()));
    375         }
    376     }
    377 
    378 private:
    379     const android::ResStringPool* mValuePool;
    380     const android::ResStringPool* mSourcePool;
    381     const android::ResStringPool* mSymbolPool;
    382     const Source mSource;
    383     IDiagnostics* mDiag;
    384 };
    385 
    386 } // namespace
    387 
    388 std::unique_ptr<ResourceTable> deserializeTableFromPb(const pb::ResourceTable& pbTable,
    389                                                       const Source& source,
    390                                                       IDiagnostics* diag) {
    391     // We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which
    392     // causes errors when qualifying it with android::
    393     using namespace android;
    394 
    395     std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
    396 
    397     if (!pbTable.has_string_pool()) {
    398         diag->error(DiagMessage(source) << "no string pool found");
    399         return {};
    400     }
    401 
    402     ResStringPool valuePool;
    403     status_t result = valuePool.setTo(pbTable.string_pool().data().data(),
    404                                       pbTable.string_pool().data().size());
    405     if (result != NO_ERROR) {
    406         diag->error(DiagMessage(source) << "invalid string pool");
    407         return {};
    408     }
    409 
    410     ResStringPool sourcePool;
    411     if (pbTable.has_source_pool()) {
    412         result = sourcePool.setTo(pbTable.source_pool().data().data(),
    413                                   pbTable.source_pool().data().size());
    414         if (result != NO_ERROR) {
    415             diag->error(DiagMessage(source) << "invalid source pool");
    416             return {};
    417         }
    418     }
    419 
    420     ResStringPool symbolPool;
    421     if (pbTable.has_symbol_pool()) {
    422         result = symbolPool.setTo(pbTable.symbol_pool().data().data(),
    423                                   pbTable.symbol_pool().data().size());
    424         if (result != NO_ERROR) {
    425             diag->error(DiagMessage(source) << "invalid symbol pool");
    426             return {};
    427         }
    428     }
    429 
    430     PackagePbDeserializer packagePbDeserializer(&valuePool, &sourcePool, &symbolPool, source, diag);
    431     for (const pb::Package& pbPackage : pbTable.packages()) {
    432         if (!packagePbDeserializer.deserializeFromPb(pbPackage, table.get())) {
    433             return {};
    434         }
    435     }
    436     return table;
    437 }
    438 
    439 std::unique_ptr<ResourceFile> deserializeCompiledFileFromPb(const pb::CompiledFile& pbFile,
    440                                                             const Source& source,
    441                                                             IDiagnostics* diag) {
    442     std::unique_ptr<ResourceFile> file = util::make_unique<ResourceFile>();
    443 
    444     ResourceNameRef nameRef;
    445 
    446     // Need to create an lvalue here so that nameRef can point to something real.
    447     std::u16string utf16Name = util::utf8ToUtf16(pbFile.resource_name());
    448     if (!ResourceUtils::parseResourceName(utf16Name, &nameRef)) {
    449         diag->error(DiagMessage(source) << "invalid resource name in compiled file header: "
    450                     << pbFile.resource_name());
    451         return {};
    452     }
    453     file->name = nameRef.toResourceName();
    454     file->source.path = pbFile.source_path();
    455     deserializeConfigDescriptionFromPb(pbFile.config(), &file->config);
    456 
    457     for (const pb::CompiledFile_Symbol& pbSymbol : pbFile.exported_symbols()) {
    458         // Need to create an lvalue here so that nameRef can point to something real.
    459         utf16Name = util::utf8ToUtf16(pbSymbol.resource_name());
    460         if (!ResourceUtils::parseResourceName(utf16Name, &nameRef)) {
    461             diag->error(DiagMessage(source) << "invalid resource name for exported symbol in "
    462                                                "compiled file header: "
    463                                             << pbFile.resource_name());
    464             return {};
    465         }
    466         file->exportedSymbols.push_back(
    467                 SourcedResourceName{ nameRef.toResourceName(), pbSymbol.line_no() });
    468     }
    469     return file;
    470 }
    471 
    472 CompiledFileInputStream::CompiledFileInputStream(const void* data, size_t size) :
    473         mIn(static_cast<const uint8_t*>(data), size), mPbFile(),
    474         mData(static_cast<const uint8_t*>(data)), mSize(size) {
    475 }
    476 
    477 const pb::CompiledFile* CompiledFileInputStream::CompiledFile() {
    478     if (!mPbFile) {
    479         std::unique_ptr<pb::CompiledFile> pbFile = util::make_unique<pb::CompiledFile>();
    480         uint64_t pbSize = 0u;
    481         if (!mIn.ReadLittleEndian64(&pbSize)) {
    482             return nullptr;
    483         }
    484         mIn.PushLimit(static_cast<int>(pbSize));
    485         if (!pbFile->ParsePartialFromCodedStream(&mIn)) {
    486             return nullptr;
    487         }
    488 
    489         const size_t padding = 4 - (pbSize & 0x03);
    490         const size_t offset = sizeof(uint64_t) + pbSize + padding;
    491         if (offset > mSize) {
    492             return nullptr;
    493         }
    494 
    495         mData += offset;
    496         mSize -= offset;
    497         mPbFile = std::move(pbFile);
    498     }
    499     return mPbFile.get();
    500 }
    501 
    502 const void* CompiledFileInputStream::data() {
    503     if (!mPbFile) {
    504         if (!CompiledFile()) {
    505             return nullptr;
    506         }
    507     }
    508     return mData;
    509 }
    510 
    511 size_t CompiledFileInputStream::size() {
    512     if (!mPbFile) {
    513         if (!CompiledFile()) {
    514             return 0;
    515         }
    516     }
    517     return mSize;
    518 }
    519 
    520 } // namespace aapt
    521