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