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 "proto/ProtoSerialize.h"
18
19 #include "android-base/logging.h"
20 #include "androidfw/ResourceTypes.h"
21
22 #include "ResourceTable.h"
23 #include "ResourceUtils.h"
24 #include "ValueVisitor.h"
25 #include "proto/ProtoHelpers.h"
26
27 namespace aapt {
28
29 namespace {
30
31 class ReferenceIdToNameVisitor : public ValueVisitor {
32 public:
33 using ValueVisitor::Visit;
34
ReferenceIdToNameVisitor(const std::map<ResourceId,ResourceNameRef> * mapping)35 explicit ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceNameRef>* mapping)
36 : mapping_(mapping) {
37 CHECK(mapping_ != nullptr);
38 }
39
Visit(Reference * reference)40 void Visit(Reference* reference) override {
41 if (!reference->id || !reference->id.value().is_valid()) {
42 return;
43 }
44
45 ResourceId id = reference->id.value();
46 auto cache_iter = mapping_->find(id);
47 if (cache_iter != mapping_->end()) {
48 reference->name = cache_iter->second.ToResourceName();
49 }
50 }
51
52 private:
53 const std::map<ResourceId, ResourceNameRef>* mapping_;
54 };
55
56 class PackagePbDeserializer {
57 public:
PackagePbDeserializer(const android::ResStringPool * valuePool,const android::ResStringPool * sourcePool,const android::ResStringPool * symbolPool,const Source & source,IDiagnostics * diag)58 PackagePbDeserializer(const android::ResStringPool* valuePool,
59 const android::ResStringPool* sourcePool,
60 const android::ResStringPool* symbolPool,
61 const Source& source, IDiagnostics* diag)
62 : value_pool_(valuePool),
63 source_pool_(sourcePool),
64 symbol_pool_(symbolPool),
65 source_(source),
66 diag_(diag) {}
67
68 public:
DeserializeFromPb(const pb::Package & pbPackage,ResourceTable * table)69 bool DeserializeFromPb(const pb::Package& pbPackage, ResourceTable* table) {
70 Maybe<uint8_t> id;
71 if (pbPackage.has_package_id()) {
72 id = static_cast<uint8_t>(pbPackage.package_id());
73 }
74
75 std::map<ResourceId, ResourceNameRef> idIndex;
76
77 ResourceTablePackage* pkg = table->CreatePackage(pbPackage.package_name(), id);
78 for (const pb::Type& pbType : pbPackage.types()) {
79 const ResourceType* resType = ParseResourceType(pbType.name());
80 if (!resType) {
81 diag_->Error(DiagMessage(source_) << "unknown type '" << pbType.name() << "'");
82 return {};
83 }
84
85 ResourceTableType* type = pkg->FindOrCreateType(*resType);
86
87 for (const pb::Entry& pbEntry : pbType.entries()) {
88 ResourceEntry* entry = type->FindOrCreateEntry(pbEntry.name());
89
90 // Deserialize the symbol status (public/private with source and
91 // comments).
92 if (pbEntry.has_symbol_status()) {
93 const pb::SymbolStatus& pbStatus = pbEntry.symbol_status();
94 if (pbStatus.has_source()) {
95 DeserializeSourceFromPb(pbStatus.source(), *source_pool_, &entry->symbol_status.source);
96 }
97
98 if (pbStatus.has_comment()) {
99 entry->symbol_status.comment = pbStatus.comment();
100 }
101
102 entry->symbol_status.allow_new = pbStatus.allow_new();
103
104 SymbolState visibility = DeserializeVisibilityFromPb(pbStatus.visibility());
105 entry->symbol_status.state = visibility;
106
107 if (visibility == SymbolState::kPublic) {
108 // This is a public symbol, we must encode the ID now if there is one.
109 if (pbEntry.has_id()) {
110 entry->id = static_cast<uint16_t>(pbEntry.id());
111 }
112
113 if (type->symbol_status.state != SymbolState::kPublic) {
114 // If the type has not been made public, do so now.
115 type->symbol_status.state = SymbolState::kPublic;
116 if (pbType.has_id()) {
117 type->id = static_cast<uint8_t>(pbType.id());
118 }
119 }
120 } else if (visibility == SymbolState::kPrivate) {
121 if (type->symbol_status.state == SymbolState::kUndefined) {
122 type->symbol_status.state = SymbolState::kPrivate;
123 }
124 }
125 }
126
127 ResourceId resId(pbPackage.package_id(), pbType.id(), pbEntry.id());
128 if (resId.is_valid()) {
129 idIndex[resId] = ResourceNameRef(pkg->name, type->type, entry->name);
130 }
131
132 for (const pb::ConfigValue& pbConfigValue : pbEntry.config_values()) {
133 const pb::ConfigDescription& pbConfig = pbConfigValue.config();
134
135 ConfigDescription config;
136 if (!DeserializeConfigDescriptionFromPb(pbConfig, &config)) {
137 diag_->Error(DiagMessage(source_) << "invalid configuration");
138 return {};
139 }
140
141 ResourceConfigValue* configValue = entry->FindOrCreateValue(config, pbConfig.product());
142 if (configValue->value) {
143 // Duplicate config.
144 diag_->Error(DiagMessage(source_) << "duplicate configuration");
145 return {};
146 }
147
148 configValue->value =
149 DeserializeValueFromPb(pbConfigValue.value(), config, &table->string_pool);
150 if (!configValue->value) {
151 return {};
152 }
153 }
154 }
155 }
156
157 ReferenceIdToNameVisitor visitor(&idIndex);
158 VisitAllValuesInPackage(pkg, &visitor);
159 return true;
160 }
161
162 private:
DeserializeItemFromPb(const pb::Item & pb_item,const ConfigDescription & config,StringPool * pool)163 std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
164 const ConfigDescription& config,
165 StringPool* pool) {
166 if (pb_item.has_ref()) {
167 const pb::Reference& pb_ref = pb_item.ref();
168 std::unique_ptr<Reference> ref = util::make_unique<Reference>();
169 if (!DeserializeReferenceFromPb(pb_ref, ref.get())) {
170 return {};
171 }
172 return std::move(ref);
173
174 } else if (pb_item.has_prim()) {
175 const pb::Primitive& pb_prim = pb_item.prim();
176 android::Res_value prim = {};
177 prim.dataType = static_cast<uint8_t>(pb_prim.type());
178 prim.data = pb_prim.data();
179 return util::make_unique<BinaryPrimitive>(prim);
180
181 } else if (pb_item.has_id()) {
182 return util::make_unique<Id>();
183
184 } else if (pb_item.has_str()) {
185 const uint32_t idx = pb_item.str().idx();
186 const std::string str = util::GetString(*value_pool_, idx);
187
188 const android::ResStringPool_span* spans = value_pool_->styleAt(idx);
189 if (spans && spans->name.index != android::ResStringPool_span::END) {
190 StyleString style_str = {str};
191 while (spans->name.index != android::ResStringPool_span::END) {
192 style_str.spans.push_back(
193 Span{util::GetString(*value_pool_, spans->name.index),
194 spans->firstChar, spans->lastChar});
195 spans++;
196 }
197 return util::make_unique<StyledString>(pool->MakeRef(
198 style_str,
199 StringPool::Context(StringPool::Context::kStylePriority, config)));
200 }
201 return util::make_unique<String>(
202 pool->MakeRef(str, StringPool::Context(config)));
203
204 } else if (pb_item.has_raw_str()) {
205 const uint32_t idx = pb_item.raw_str().idx();
206 const std::string str = util::GetString(*value_pool_, idx);
207 return util::make_unique<RawString>(
208 pool->MakeRef(str, StringPool::Context(config)));
209
210 } else if (pb_item.has_file()) {
211 const uint32_t idx = pb_item.file().path_idx();
212 const std::string str = util::GetString(*value_pool_, idx);
213 return util::make_unique<FileReference>(pool->MakeRef(
214 str,
215 StringPool::Context(StringPool::Context::kHighPriority, config)));
216
217 } else {
218 diag_->Error(DiagMessage(source_) << "unknown item");
219 }
220 return {};
221 }
222
DeserializeValueFromPb(const pb::Value & pb_value,const ConfigDescription & config,StringPool * pool)223 std::unique_ptr<Value> DeserializeValueFromPb(const pb::Value& pb_value,
224 const ConfigDescription& config,
225 StringPool* pool) {
226 const bool is_weak = pb_value.has_weak() ? pb_value.weak() : false;
227
228 std::unique_ptr<Value> value;
229 if (pb_value.has_item()) {
230 value = DeserializeItemFromPb(pb_value.item(), config, pool);
231 if (!value) {
232 return {};
233 }
234
235 } else if (pb_value.has_compound_value()) {
236 const pb::CompoundValue& pb_compound_value = pb_value.compound_value();
237 if (pb_compound_value.has_attr()) {
238 const pb::Attribute& pb_attr = pb_compound_value.attr();
239 std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(is_weak);
240 attr->type_mask = pb_attr.format_flags();
241 attr->min_int = pb_attr.min_int();
242 attr->max_int = pb_attr.max_int();
243 for (const pb::Attribute_Symbol& pb_symbol : pb_attr.symbols()) {
244 Attribute::Symbol symbol;
245 DeserializeItemCommon(pb_symbol, &symbol.symbol);
246 if (!DeserializeReferenceFromPb(pb_symbol.name(), &symbol.symbol)) {
247 return {};
248 }
249 symbol.value = pb_symbol.value();
250 attr->symbols.push_back(std::move(symbol));
251 }
252 value = std::move(attr);
253
254 } else if (pb_compound_value.has_style()) {
255 const pb::Style& pb_style = pb_compound_value.style();
256 std::unique_ptr<Style> style = util::make_unique<Style>();
257 if (pb_style.has_parent()) {
258 style->parent = Reference();
259 if (!DeserializeReferenceFromPb(pb_style.parent(),
260 &style->parent.value())) {
261 return {};
262 }
263
264 if (pb_style.has_parent_source()) {
265 Source parent_source;
266 DeserializeSourceFromPb(pb_style.parent_source(), *source_pool_,
267 &parent_source);
268 style->parent.value().SetSource(std::move(parent_source));
269 }
270 }
271
272 for (const pb::Style_Entry& pb_entry : pb_style.entries()) {
273 Style::Entry entry;
274 DeserializeItemCommon(pb_entry, &entry.key);
275 if (!DeserializeReferenceFromPb(pb_entry.key(), &entry.key)) {
276 return {};
277 }
278
279 entry.value = DeserializeItemFromPb(pb_entry.item(), config, pool);
280 if (!entry.value) {
281 return {};
282 }
283
284 DeserializeItemCommon(pb_entry, entry.value.get());
285 style->entries.push_back(std::move(entry));
286 }
287 value = std::move(style);
288
289 } else if (pb_compound_value.has_styleable()) {
290 const pb::Styleable& pb_styleable = pb_compound_value.styleable();
291 std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
292 for (const pb::Styleable_Entry& pb_entry : pb_styleable.entries()) {
293 Reference attr_ref;
294 DeserializeItemCommon(pb_entry, &attr_ref);
295 DeserializeReferenceFromPb(pb_entry.attr(), &attr_ref);
296 styleable->entries.push_back(std::move(attr_ref));
297 }
298 value = std::move(styleable);
299
300 } else if (pb_compound_value.has_array()) {
301 const pb::Array& pb_array = pb_compound_value.array();
302 std::unique_ptr<Array> array = util::make_unique<Array>();
303 for (const pb::Array_Entry& pb_entry : pb_array.entries()) {
304 std::unique_ptr<Item> item =
305 DeserializeItemFromPb(pb_entry.item(), config, pool);
306 if (!item) {
307 return {};
308 }
309
310 DeserializeItemCommon(pb_entry, item.get());
311 array->items.push_back(std::move(item));
312 }
313 value = std::move(array);
314
315 } else if (pb_compound_value.has_plural()) {
316 const pb::Plural& pb_plural = pb_compound_value.plural();
317 std::unique_ptr<Plural> plural = util::make_unique<Plural>();
318 for (const pb::Plural_Entry& pb_entry : pb_plural.entries()) {
319 size_t pluralIdx = DeserializePluralEnumFromPb(pb_entry.arity());
320 plural->values[pluralIdx] =
321 DeserializeItemFromPb(pb_entry.item(), config, pool);
322 if (!plural->values[pluralIdx]) {
323 return {};
324 }
325
326 DeserializeItemCommon(pb_entry, plural->values[pluralIdx].get());
327 }
328 value = std::move(plural);
329
330 } else {
331 diag_->Error(DiagMessage(source_) << "unknown compound value");
332 return {};
333 }
334 } else {
335 diag_->Error(DiagMessage(source_) << "unknown value");
336 return {};
337 }
338
339 CHECK(value) << "forgot to set value";
340
341 value->SetWeak(is_weak);
342 DeserializeItemCommon(pb_value, value.get());
343 return value;
344 }
345
DeserializeReferenceFromPb(const pb::Reference & pb_ref,Reference * out_ref)346 bool DeserializeReferenceFromPb(const pb::Reference& pb_ref, Reference* out_ref) {
347 out_ref->reference_type = DeserializeReferenceTypeFromPb(pb_ref.type());
348 out_ref->private_reference = pb_ref.private_();
349
350 if (pb_ref.has_id()) {
351 out_ref->id = ResourceId(pb_ref.id());
352 }
353
354 if (pb_ref.has_symbol_idx()) {
355 const std::string str_symbol = util::GetString(*symbol_pool_, pb_ref.symbol_idx());
356 ResourceNameRef name_ref;
357 if (!ResourceUtils::ParseResourceName(str_symbol, &name_ref, nullptr)) {
358 diag_->Error(DiagMessage(source_) << "invalid reference name '" << str_symbol << "'");
359 return false;
360 }
361
362 out_ref->name = name_ref.ToResourceName();
363 }
364 return true;
365 }
366
367 template <typename T>
DeserializeItemCommon(const T & pb_item,Value * out_value)368 void DeserializeItemCommon(const T& pb_item, Value* out_value) {
369 if (pb_item.has_source()) {
370 Source source;
371 DeserializeSourceFromPb(pb_item.source(), *source_pool_, &source);
372 out_value->SetSource(std::move(source));
373 }
374
375 if (pb_item.has_comment()) {
376 out_value->SetComment(pb_item.comment());
377 }
378 }
379
380 private:
381 const android::ResStringPool* value_pool_;
382 const android::ResStringPool* source_pool_;
383 const android::ResStringPool* symbol_pool_;
384 const Source source_;
385 IDiagnostics* diag_;
386 };
387
388 } // namespace
389
DeserializeTableFromPb(const pb::ResourceTable & pb_table,const Source & source,IDiagnostics * diag)390 std::unique_ptr<ResourceTable> DeserializeTableFromPb(
391 const pb::ResourceTable& pb_table, const Source& source,
392 IDiagnostics* diag) {
393 // We import the android namespace because on Windows NO_ERROR is a macro, not
394 // an enum, which
395 // causes errors when qualifying it with android::
396 using namespace android;
397
398 std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
399
400 if (!pb_table.has_string_pool()) {
401 diag->Error(DiagMessage(source) << "no string pool found");
402 return {};
403 }
404
405 ResStringPool value_pool;
406 status_t result = value_pool.setTo(pb_table.string_pool().data().data(),
407 pb_table.string_pool().data().size());
408 if (result != NO_ERROR) {
409 diag->Error(DiagMessage(source) << "invalid string pool");
410 return {};
411 }
412
413 ResStringPool source_pool;
414 if (pb_table.has_source_pool()) {
415 result = source_pool.setTo(pb_table.source_pool().data().data(),
416 pb_table.source_pool().data().size());
417 if (result != NO_ERROR) {
418 diag->Error(DiagMessage(source) << "invalid source pool");
419 return {};
420 }
421 }
422
423 ResStringPool symbol_pool;
424 if (pb_table.has_symbol_pool()) {
425 result = symbol_pool.setTo(pb_table.symbol_pool().data().data(),
426 pb_table.symbol_pool().data().size());
427 if (result != NO_ERROR) {
428 diag->Error(DiagMessage(source) << "invalid symbol pool");
429 return {};
430 }
431 }
432
433 PackagePbDeserializer package_pb_deserializer(&value_pool, &source_pool,
434 &symbol_pool, source, diag);
435 for (const pb::Package& pb_package : pb_table.packages()) {
436 if (!package_pb_deserializer.DeserializeFromPb(pb_package, table.get())) {
437 return {};
438 }
439 }
440 return table;
441 }
442
DeserializeCompiledFileFromPb(const pb::CompiledFile & pb_file,const Source & source,IDiagnostics * diag)443 std::unique_ptr<ResourceFile> DeserializeCompiledFileFromPb(
444 const pb::CompiledFile& pb_file, const Source& source, IDiagnostics* diag) {
445 std::unique_ptr<ResourceFile> file = util::make_unique<ResourceFile>();
446
447 ResourceNameRef name_ref;
448
449 // Need to create an lvalue here so that nameRef can point to something real.
450 if (!ResourceUtils::ParseResourceName(pb_file.resource_name(), &name_ref)) {
451 diag->Error(DiagMessage(source)
452 << "invalid resource name in compiled file header: "
453 << pb_file.resource_name());
454 return {};
455 }
456 file->name = name_ref.ToResourceName();
457 file->source.path = pb_file.source_path();
458 DeserializeConfigDescriptionFromPb(pb_file.config(), &file->config);
459
460 for (const pb::CompiledFile_Symbol& pb_symbol : pb_file.exported_symbols()) {
461 // Need to create an lvalue here so that nameRef can point to something
462 // real.
463 if (!ResourceUtils::ParseResourceName(pb_symbol.resource_name(),
464 &name_ref)) {
465 diag->Error(DiagMessage(source)
466 << "invalid resource name for exported symbol in "
467 "compiled file header: "
468 << pb_file.resource_name());
469 return {};
470 }
471 file->exported_symbols.push_back(
472 SourcedResourceName{name_ref.ToResourceName(), pb_symbol.line_no()});
473 }
474 return file;
475 }
476
477 } // namespace aapt
478