blob: 19923661625c062e5c102e22a71eb6df1190bb4b [file] [log] [blame]
Adam Lesinski59e04c62016-02-04 15:59:23 -08001/*
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"
Adam Lesinski59e04c62016-02-04 15:59:23 -080022
23#include <androidfw/ResourceTypes.h>
24
25namespace aapt {
26
27namespace {
28
29class ReferenceIdToNameVisitor : public ValueVisitor {
30public:
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
50private:
51 const std::map<ResourceId, ResourceNameRef>* mMapping;
52};
53
54class PackagePbDeserializer {
55public:
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
64public:
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
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800136 ResourceConfigValue* configValue = entry->findOrCreateValue(config,
137 pbConfig.product());
138 if (configValue->value) {
Adam Lesinski59e04c62016-02-04 15:59:23 -0800139 // Duplicate config.
140 mDiag->error(DiagMessage(mSource) << "duplicate configuration");
141 return {};
142 }
143
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800144 configValue->value = deserializeValueFromPb(pbConfigValue.value(),
145 config, &table->stringPool);
146 if (!configValue->value) {
Adam Lesinski59e04c62016-02-04 15:59:23 -0800147 return {};
148 }
Adam Lesinski59e04c62016-02-04 15:59:23 -0800149 }
150 }
151 }
152
153 ReferenceIdToNameVisitor visitor(&idIndex);
154 visitAllValuesInPackage(pkg, &visitor);
155 return true;
156 }
157
158private:
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();
Adam Lesinski458b8772016-04-25 14:20:21 -0700237 attr->minInt = pbAttr.min_int();
238 attr->maxInt = pbAttr.max_int();
Adam Lesinski59e04c62016-02-04 15:59:23 -0800239 for (const pb::Attribute_Symbol& pbSymbol : pbAttr.symbols()) {
240 Attribute::Symbol symbol;
241 deserializeItemCommon(pbSymbol, &symbol.symbol);
242 if (!deserializeReferenceFromPb(pbSymbol.name(), &symbol.symbol)) {
243 return {};
244 }
245 symbol.value = pbSymbol.value();
246 attr->symbols.push_back(std::move(symbol));
247 }
248 value = std::move(attr);
249
250 } else if (pbCompoundValue.has_style()) {
251 const pb::Style& pbStyle = pbCompoundValue.style();
252 std::unique_ptr<Style> style = util::make_unique<Style>();
253 if (pbStyle.has_parent()) {
254 style->parent = Reference();
255 if (!deserializeReferenceFromPb(pbStyle.parent(), &style->parent.value())) {
256 return {};
257 }
258
259 if (pbStyle.has_parent_source()) {
260 Source parentSource;
261 deserializeSourceFromPb(pbStyle.parent_source(), *mSourcePool,
262 &parentSource);
263 style->parent.value().setSource(std::move(parentSource));
264 }
265 }
266
267 for (const pb::Style_Entry& pbEntry : pbStyle.entries()) {
268 Style::Entry entry;
269 deserializeItemCommon(pbEntry, &entry.key);
270 if (!deserializeReferenceFromPb(pbEntry.key(), &entry.key)) {
271 return {};
272 }
273
274 entry.value = deserializeItemFromPb(pbEntry.item(), config, pool);
275 if (!entry.value) {
276 return {};
277 }
278
279 deserializeItemCommon(pbEntry, entry.value.get());
280 style->entries.push_back(std::move(entry));
281 }
282 value = std::move(style);
283
284 } else if (pbCompoundValue.has_styleable()) {
285 const pb::Styleable& pbStyleable = pbCompoundValue.styleable();
286 std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
287 for (const pb::Styleable_Entry& pbEntry : pbStyleable.entries()) {
288 Reference attrRef;
289 deserializeItemCommon(pbEntry, &attrRef);
290 deserializeReferenceFromPb(pbEntry.attr(), &attrRef);
291 styleable->entries.push_back(std::move(attrRef));
292 }
293 value = std::move(styleable);
294
295 } else if (pbCompoundValue.has_array()) {
296 const pb::Array& pbArray = pbCompoundValue.array();
297 std::unique_ptr<Array> array = util::make_unique<Array>();
298 for (const pb::Array_Entry& pbEntry : pbArray.entries()) {
299 std::unique_ptr<Item> item = deserializeItemFromPb(pbEntry.item(), config,
300 pool);
301 if (!item) {
302 return {};
303 }
304
305 deserializeItemCommon(pbEntry, item.get());
306 array->items.push_back(std::move(item));
307 }
308 value = std::move(array);
309
310 } else if (pbCompoundValue.has_plural()) {
311 const pb::Plural& pbPlural = pbCompoundValue.plural();
312 std::unique_ptr<Plural> plural = util::make_unique<Plural>();
313 for (const pb::Plural_Entry& pbEntry : pbPlural.entries()) {
314 size_t pluralIdx = deserializePluralEnumFromPb(pbEntry.arity());
315 plural->values[pluralIdx] = deserializeItemFromPb(pbEntry.item(), config,
316 pool);
317 if (!plural->values[pluralIdx]) {
318 return {};
319 }
320
321 deserializeItemCommon(pbEntry, plural->values[pluralIdx].get());
322 }
323 value = std::move(plural);
324
325 } else {
326 mDiag->error(DiagMessage(mSource) << "unknown compound value");
327 return {};
328 }
329 } else {
330 mDiag->error(DiagMessage(mSource) << "unknown value");
331 return {};
332 }
333
334 assert(value && "forgot to set value");
335
336 value->setWeak(isWeak);
337 deserializeItemCommon(pbValue, value.get());
338 return value;
339 }
340
341 bool deserializeReferenceFromPb(const pb::Reference& pbRef, Reference* outRef) {
342 outRef->referenceType = deserializeReferenceTypeFromPb(pbRef.type());
343 outRef->privateReference = pbRef.private_();
344
345 if (!pbRef.has_id() && !pbRef.has_symbol_idx()) {
346 return false;
347 }
348
349 if (pbRef.has_id()) {
350 outRef->id = ResourceId(pbRef.id());
351 }
352
353 if (pbRef.has_symbol_idx()) {
354 StringPiece16 strSymbol = util::getString(*mSymbolPool, pbRef.symbol_idx());
355 ResourceNameRef nameRef;
356 if (!ResourceUtils::parseResourceName(strSymbol, &nameRef, nullptr)) {
357 mDiag->error(DiagMessage(mSource) << "invalid reference name '"
358 << strSymbol << "'");
359 return false;
360 }
361
362 outRef->name = nameRef.toResourceName();
363 }
364 return true;
365 }
366
367 template <typename T>
368 void deserializeItemCommon(const T& pbItem, Value* outValue) {
369 if (pbItem.has_source()) {
370 Source source;
371 deserializeSourceFromPb(pbItem.source(), *mSourcePool, &source);
372 outValue->setSource(std::move(source));
373 }
374
375 if (pbItem.has_comment()) {
376 outValue->setComment(util::utf8ToUtf16(pbItem.comment()));
377 }
378 }
379
380private:
381 const android::ResStringPool* mValuePool;
382 const android::ResStringPool* mSourcePool;
383 const android::ResStringPool* mSymbolPool;
384 const Source mSource;
385 IDiagnostics* mDiag;
386};
387
388} // namespace
389
390std::unique_ptr<ResourceTable> deserializeTableFromPb(const pb::ResourceTable& pbTable,
391 const Source& source,
392 IDiagnostics* diag) {
Adam Lesinski803c7c82016-04-06 16:09:43 -0700393 // We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which
394 // causes errors when qualifying it with android::
395 using namespace android;
396
Adam Lesinski59e04c62016-02-04 15:59:23 -0800397 std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
398
399 if (!pbTable.has_string_pool()) {
400 diag->error(DiagMessage(source) << "no string pool found");
401 return {};
402 }
403
Adam Lesinski803c7c82016-04-06 16:09:43 -0700404 ResStringPool valuePool;
405 status_t result = valuePool.setTo(pbTable.string_pool().data().data(),
406 pbTable.string_pool().data().size());
407 if (result != NO_ERROR) {
Adam Lesinski59e04c62016-02-04 15:59:23 -0800408 diag->error(DiagMessage(source) << "invalid string pool");
409 return {};
410 }
411
Adam Lesinski803c7c82016-04-06 16:09:43 -0700412 ResStringPool sourcePool;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800413 if (pbTable.has_source_pool()) {
414 result = sourcePool.setTo(pbTable.source_pool().data().data(),
415 pbTable.source_pool().data().size());
Adam Lesinski803c7c82016-04-06 16:09:43 -0700416 if (result != NO_ERROR) {
Adam Lesinski59e04c62016-02-04 15:59:23 -0800417 diag->error(DiagMessage(source) << "invalid source pool");
418 return {};
419 }
420 }
421
Adam Lesinski803c7c82016-04-06 16:09:43 -0700422 ResStringPool symbolPool;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800423 if (pbTable.has_symbol_pool()) {
424 result = symbolPool.setTo(pbTable.symbol_pool().data().data(),
425 pbTable.symbol_pool().data().size());
Adam Lesinski803c7c82016-04-06 16:09:43 -0700426 if (result != NO_ERROR) {
Adam Lesinski59e04c62016-02-04 15:59:23 -0800427 diag->error(DiagMessage(source) << "invalid symbol pool");
428 return {};
429 }
430 }
431
432 PackagePbDeserializer packagePbDeserializer(&valuePool, &sourcePool, &symbolPool, source, diag);
433 for (const pb::Package& pbPackage : pbTable.packages()) {
434 if (!packagePbDeserializer.deserializeFromPb(pbPackage, table.get())) {
435 return {};
436 }
437 }
438 return table;
439}
440
441std::unique_ptr<ResourceFile> deserializeCompiledFileFromPb(const pb::CompiledFile& pbFile,
442 const Source& source,
443 IDiagnostics* diag) {
444 std::unique_ptr<ResourceFile> file = util::make_unique<ResourceFile>();
445
446 ResourceNameRef nameRef;
447
448 // Need to create an lvalue here so that nameRef can point to something real.
449 std::u16string utf16Name = util::utf8ToUtf16(pbFile.resource_name());
450 if (!ResourceUtils::parseResourceName(utf16Name, &nameRef)) {
451 diag->error(DiagMessage(source) << "invalid resource name in compiled file header: "
452 << pbFile.resource_name());
453 return {};
454 }
455 file->name = nameRef.toResourceName();
456 file->source.path = pbFile.source_path();
457 deserializeConfigDescriptionFromPb(pbFile.config(), &file->config);
458
459 for (const pb::CompiledFile_Symbol& pbSymbol : pbFile.exported_symbols()) {
460 // Need to create an lvalue here so that nameRef can point to something real.
461 utf16Name = util::utf8ToUtf16(pbSymbol.resource_name());
462 if (!ResourceUtils::parseResourceName(utf16Name, &nameRef)) {
463 diag->error(DiagMessage(source) << "invalid resource name for exported symbol in "
464 "compiled file header: "
465 << pbFile.resource_name());
466 return {};
467 }
468 file->exportedSymbols.push_back(
469 SourcedResourceName{ nameRef.toResourceName(), pbSymbol.line_no() });
470 }
471 return file;
472}
473
474CompiledFileInputStream::CompiledFileInputStream(const void* data, size_t size) :
475 mIn(static_cast<const uint8_t*>(data), size), mPbFile(),
476 mData(static_cast<const uint8_t*>(data)), mSize(size) {
477}
478
479const pb::CompiledFile* CompiledFileInputStream::CompiledFile() {
480 if (!mPbFile) {
481 std::unique_ptr<pb::CompiledFile> pbFile = util::make_unique<pb::CompiledFile>();
Tamas Berghammercbd3f0c2016-06-22 15:21:38 +0100482 google::protobuf::uint64 pbSize = 0u;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800483 if (!mIn.ReadLittleEndian64(&pbSize)) {
484 return nullptr;
485 }
486 mIn.PushLimit(static_cast<int>(pbSize));
487 if (!pbFile->ParsePartialFromCodedStream(&mIn)) {
488 return nullptr;
489 }
490
491 const size_t padding = 4 - (pbSize & 0x03);
Adam Lesinski64587af2016-02-18 18:33:06 -0800492 const size_t offset = sizeof(uint64_t) + pbSize + padding;
493 if (offset > mSize) {
494 return nullptr;
495 }
496
497 mData += offset;
498 mSize -= offset;
Adam Lesinski59e04c62016-02-04 15:59:23 -0800499 mPbFile = std::move(pbFile);
500 }
501 return mPbFile.get();
502}
503
504const void* CompiledFileInputStream::data() {
505 if (!mPbFile) {
506 if (!CompiledFile()) {
507 return nullptr;
508 }
509 }
510 return mData;
511}
512
513size_t CompiledFileInputStream::size() {
514 if (!mPbFile) {
515 if (!CompiledFile()) {
516 return 0;
517 }
518 }
519 return mSize;
520}
521
522} // namespace aapt