1 /*
2  * Copyright (C) 2015 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 "ConfigDescription.h"
19 #include "NameMangler.h"
20 #include "ResourceValues.h"
21 #include "ValueVisitor.h"
22 #include "util/Util.h"
23 
24 #include <android-base/logging.h>
25 #include <androidfw/ResourceTypes.h>
26 #include <algorithm>
27 #include <memory>
28 #include <string>
29 #include <tuple>
30 
31 using android::StringPiece;
32 
33 namespace aapt {
34 
less_than_type(const std::unique_ptr<ResourceTableType> & lhs,ResourceType rhs)35 static bool less_than_type(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) {
36   return lhs->type < rhs;
37 }
38 
39 template <typename T>
less_than_struct_with_name(const std::unique_ptr<T> & lhs,const StringPiece & rhs)40 static bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, const StringPiece& rhs) {
41   return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
42 }
43 
FindPackage(const StringPiece & name)44 ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) {
45   const auto last = packages.end();
46   auto iter = std::lower_bound(packages.begin(), last, name,
47                                less_than_struct_with_name<ResourceTablePackage>);
48   if (iter != last && name == (*iter)->name) {
49     return iter->get();
50   }
51   return nullptr;
52 }
53 
FindPackageById(uint8_t id)54 ResourceTablePackage* ResourceTable::FindPackageById(uint8_t id) {
55   for (auto& package : packages) {
56     if (package->id && package->id.value() == id) {
57       return package.get();
58     }
59   }
60   return nullptr;
61 }
62 
CreatePackage(const StringPiece & name,Maybe<uint8_t> id)63 ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name, Maybe<uint8_t> id) {
64   ResourceTablePackage* package = FindOrCreatePackage(name);
65   if (id && !package->id) {
66     package->id = id;
67     return package;
68   }
69 
70   if (id && package->id && package->id.value() != id.value()) {
71     return nullptr;
72   }
73   return package;
74 }
75 
FindOrCreatePackage(const StringPiece & name)76 ResourceTablePackage* ResourceTable::FindOrCreatePackage(const StringPiece& name) {
77   const auto last = packages.end();
78   auto iter = std::lower_bound(packages.begin(), last, name,
79                                less_than_struct_with_name<ResourceTablePackage>);
80   if (iter != last && name == (*iter)->name) {
81     return iter->get();
82   }
83 
84   std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
85   new_package->name = name.to_string();
86   return packages.emplace(iter, std::move(new_package))->get();
87 }
88 
FindType(ResourceType type)89 ResourceTableType* ResourceTablePackage::FindType(ResourceType type) {
90   const auto last = types.end();
91   auto iter = std::lower_bound(types.begin(), last, type, less_than_type);
92   if (iter != last && (*iter)->type == type) {
93     return iter->get();
94   }
95   return nullptr;
96 }
97 
FindOrCreateType(ResourceType type)98 ResourceTableType* ResourceTablePackage::FindOrCreateType(ResourceType type) {
99   const auto last = types.end();
100   auto iter = std::lower_bound(types.begin(), last, type, less_than_type);
101   if (iter != last && (*iter)->type == type) {
102     return iter->get();
103   }
104   return types.emplace(iter, new ResourceTableType(type))->get();
105 }
106 
FindEntry(const StringPiece & name)107 ResourceEntry* ResourceTableType::FindEntry(const StringPiece& name) {
108   const auto last = entries.end();
109   auto iter =
110       std::lower_bound(entries.begin(), last, name, less_than_struct_with_name<ResourceEntry>);
111   if (iter != last && name == (*iter)->name) {
112     return iter->get();
113   }
114   return nullptr;
115 }
116 
FindOrCreateEntry(const StringPiece & name)117 ResourceEntry* ResourceTableType::FindOrCreateEntry(const StringPiece& name) {
118   auto last = entries.end();
119   auto iter =
120       std::lower_bound(entries.begin(), last, name, less_than_struct_with_name<ResourceEntry>);
121   if (iter != last && name == (*iter)->name) {
122     return iter->get();
123   }
124   return entries.emplace(iter, new ResourceEntry(name))->get();
125 }
126 
FindValue(const ConfigDescription & config)127 ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config) {
128   return FindValue(config, StringPiece());
129 }
130 
131 struct ConfigKey {
132   const ConfigDescription* config;
133   const StringPiece& product;
134 };
135 
ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue> & lhs,const ConfigKey & rhs)136 bool ltConfigKeyRef(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
137   int cmp = lhs->config.compare(*rhs.config);
138   if (cmp == 0) {
139     cmp = StringPiece(lhs->product).compare(rhs.product);
140   }
141   return cmp < 0;
142 }
143 
FindValue(const ConfigDescription & config,const StringPiece & product)144 ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config,
145                                               const StringPiece& product) {
146   auto iter =
147       std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, ltConfigKeyRef);
148   if (iter != values.end()) {
149     ResourceConfigValue* value = iter->get();
150     if (value->config == config && StringPiece(value->product) == product) {
151       return value;
152     }
153   }
154   return nullptr;
155 }
156 
FindOrCreateValue(const ConfigDescription & config,const StringPiece & product)157 ResourceConfigValue* ResourceEntry::FindOrCreateValue(const ConfigDescription& config,
158                                                       const StringPiece& product) {
159   auto iter =
160       std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product}, ltConfigKeyRef);
161   if (iter != values.end()) {
162     ResourceConfigValue* value = iter->get();
163     if (value->config == config && StringPiece(value->product) == product) {
164       return value;
165     }
166   }
167   ResourceConfigValue* newValue =
168       values.insert(iter, util::make_unique<ResourceConfigValue>(config, product))->get();
169   return newValue;
170 }
171 
FindAllValues(const ConfigDescription & config)172 std::vector<ResourceConfigValue*> ResourceEntry::FindAllValues(const ConfigDescription& config) {
173   std::vector<ResourceConfigValue*> results;
174 
175   auto iter = values.begin();
176   for (; iter != values.end(); ++iter) {
177     ResourceConfigValue* value = iter->get();
178     if (value->config == config) {
179       results.push_back(value);
180       ++iter;
181       break;
182     }
183   }
184 
185   for (; iter != values.end(); ++iter) {
186     ResourceConfigValue* value = iter->get();
187     if (value->config == config) {
188       results.push_back(value);
189     }
190   }
191   return results;
192 }
193 
FindValuesIf(const std::function<bool (ResourceConfigValue *)> & f)194 std::vector<ResourceConfigValue*> ResourceEntry::FindValuesIf(
195     const std::function<bool(ResourceConfigValue*)>& f) {
196   std::vector<ResourceConfigValue*> results;
197   for (auto& configValue : values) {
198     if (f(configValue.get())) {
199       results.push_back(configValue.get());
200     }
201   }
202   return results;
203 }
204 
205 /**
206  * The default handler for collisions.
207  *
208  * Typically, a weak value will be overridden by a strong value. An existing
209  * weak
210  * value will not be overridden by an incoming weak value.
211  *
212  * There are some exceptions:
213  *
214  * Attributes: There are two types of Attribute values: USE and DECL.
215  *
216  * USE is anywhere an Attribute is declared without a format, and in a place
217  * that would
218  * be legal to declare if the Attribute already existed. This is typically in a
219  * <declare-styleable> tag. Attributes defined in a <declare-styleable> are also
220  * weak.
221  *
222  * DECL is an absolute declaration of an Attribute and specifies an explicit
223  * format.
224  *
225  * A DECL will override a USE without error. Two DECLs must match in their
226  * format for there to be
227  * no error.
228  */
ResolveValueCollision(Value * existing,Value * incoming)229 ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* existing,
230                                                                     Value* incoming) {
231   Attribute* existing_attr = ValueCast<Attribute>(existing);
232   Attribute* incoming_attr = ValueCast<Attribute>(incoming);
233   if (!incoming_attr) {
234     if (incoming->IsWeak()) {
235       // We're trying to add a weak resource but a resource
236       // already exists. Keep the existing.
237       return CollisionResult::kKeepOriginal;
238     } else if (existing->IsWeak()) {
239       // Override the weak resource with the new strong resource.
240       return CollisionResult::kTakeNew;
241     }
242     // The existing and incoming values are strong, this is an error
243     // if the values are not both attributes.
244     return CollisionResult::kConflict;
245   }
246 
247   if (!existing_attr) {
248     if (existing->IsWeak()) {
249       // The existing value is not an attribute and it is weak,
250       // so take the incoming attribute value.
251       return CollisionResult::kTakeNew;
252     }
253     // The existing value is not an attribute and it is strong,
254     // so the incoming attribute value is an error.
255     return CollisionResult::kConflict;
256   }
257 
258   CHECK(incoming_attr != nullptr && existing_attr != nullptr);
259 
260   //
261   // Attribute specific handling. At this point we know both
262   // values are attributes. Since we can declare and define
263   // attributes all-over, we do special handling to see
264   // which definition sticks.
265   //
266   if (existing_attr->type_mask == incoming_attr->type_mask) {
267     // The two attributes are both DECLs, but they are plain attributes
268     // with the same formats.
269     // Keep the strongest one.
270     return existing_attr->IsWeak() ? CollisionResult::kTakeNew : CollisionResult::kKeepOriginal;
271   }
272 
273   if (existing_attr->IsWeak() && existing_attr->type_mask == android::ResTable_map::TYPE_ANY) {
274     // Any incoming attribute is better than this.
275     return CollisionResult::kTakeNew;
276   }
277 
278   if (incoming_attr->IsWeak() && incoming_attr->type_mask == android::ResTable_map::TYPE_ANY) {
279     // The incoming attribute may be a USE instead of a DECL.
280     // Keep the existing attribute.
281     return CollisionResult::kKeepOriginal;
282   }
283   return CollisionResult::kConflict;
284 }
285 
286 static constexpr const char* kValidNameChars = "._-";
287 
ValidateName(const StringPiece & name)288 static StringPiece ValidateName(const StringPiece& name) {
289   auto iter = util::FindNonAlphaNumericAndNotInSet(name, kValidNameChars);
290   if (iter != name.end()) {
291     return StringPiece(iter, 1);
292   }
293   return {};
294 }
295 
SkipValidateName(const StringPiece &)296 static StringPiece SkipValidateName(const StringPiece& /*name*/) {
297   return {};
298 }
299 
AddResource(const ResourceNameRef & name,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)300 bool ResourceTable::AddResource(const ResourceNameRef& name,
301                                 const ConfigDescription& config,
302                                 const StringPiece& product,
303                                 std::unique_ptr<Value> value,
304                                 IDiagnostics* diag) {
305   return AddResourceImpl(name, {}, config, product, std::move(value), ValidateName,
306                          ResolveValueCollision, diag);
307 }
308 
AddResource(const ResourceNameRef & name,const ResourceId & res_id,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)309 bool ResourceTable::AddResource(const ResourceNameRef& name,
310                                 const ResourceId& res_id,
311                                 const ConfigDescription& config,
312                                 const StringPiece& product,
313                                 std::unique_ptr<Value> value,
314                                 IDiagnostics* diag) {
315   return AddResourceImpl(name, res_id, config, product, std::move(value), ValidateName,
316                          ResolveValueCollision, diag);
317 }
318 
AddFileReference(const ResourceNameRef & name,const ConfigDescription & config,const Source & source,const StringPiece & path,IDiagnostics * diag)319 bool ResourceTable::AddFileReference(const ResourceNameRef& name,
320                                      const ConfigDescription& config,
321                                      const Source& source,
322                                      const StringPiece& path,
323                                      IDiagnostics* diag) {
324   return AddFileReferenceImpl(name, config, source, path, nullptr, ValidateName, diag);
325 }
326 
AddFileReferenceAllowMangled(const ResourceNameRef & name,const ConfigDescription & config,const Source & source,const StringPiece & path,io::IFile * file,IDiagnostics * diag)327 bool ResourceTable::AddFileReferenceAllowMangled(
328     const ResourceNameRef& name, const ConfigDescription& config,
329     const Source& source, const StringPiece& path, io::IFile* file,
330     IDiagnostics* diag) {
331   return AddFileReferenceImpl(name, config, source, path, file, SkipValidateName, diag);
332 }
333 
AddFileReferenceImpl(const ResourceNameRef & name,const ConfigDescription & config,const Source & source,const StringPiece & path,io::IFile * file,NameValidator name_validator,IDiagnostics * diag)334 bool ResourceTable::AddFileReferenceImpl(const ResourceNameRef& name,
335                                          const ConfigDescription& config, const Source& source,
336                                          const StringPiece& path, io::IFile* file,
337                                          NameValidator name_validator, IDiagnostics* diag) {
338   std::unique_ptr<FileReference> fileRef =
339       util::make_unique<FileReference>(string_pool.MakeRef(path));
340   fileRef->SetSource(source);
341   fileRef->file = file;
342   return AddResourceImpl(name, ResourceId{}, config, StringPiece{}, std::move(fileRef),
343                          name_validator, ResolveValueCollision, diag);
344 }
345 
AddResourceAllowMangled(const ResourceNameRef & name,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)346 bool ResourceTable::AddResourceAllowMangled(const ResourceNameRef& name,
347                                             const ConfigDescription& config,
348                                             const StringPiece& product,
349                                             std::unique_ptr<Value> value,
350                                             IDiagnostics* diag) {
351   return AddResourceImpl(name, ResourceId{}, config, product, std::move(value), SkipValidateName,
352                          ResolveValueCollision, diag);
353 }
354 
AddResourceAllowMangled(const ResourceNameRef & name,const ResourceId & id,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)355 bool ResourceTable::AddResourceAllowMangled(const ResourceNameRef& name,
356                                             const ResourceId& id,
357                                             const ConfigDescription& config,
358                                             const StringPiece& product,
359                                             std::unique_ptr<Value> value,
360                                             IDiagnostics* diag) {
361   return AddResourceImpl(name, id, config, product, std::move(value), SkipValidateName,
362                          ResolveValueCollision, diag);
363 }
364 
AddResourceImpl(const ResourceNameRef & name,const ResourceId & res_id,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,NameValidator name_validator,const CollisionResolverFunc & conflictResolver,IDiagnostics * diag)365 bool ResourceTable::AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
366                                     const ConfigDescription& config, const StringPiece& product,
367                                     std::unique_ptr<Value> value, NameValidator name_validator,
368                                     const CollisionResolverFunc& conflictResolver,
369                                     IDiagnostics* diag) {
370   CHECK(value != nullptr);
371   CHECK(diag != nullptr);
372 
373   const StringPiece bad_char = name_validator(name.entry);
374   if (!bad_char.empty()) {
375     diag->Error(DiagMessage(value->GetSource()) << "resource '" << name
376                                                 << "' has invalid entry name '" << name.entry
377                                                 << "'. Invalid character '" << bad_char << "'");
378 
379     return false;
380   }
381 
382   ResourceTablePackage* package = FindOrCreatePackage(name.package);
383   if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
384     diag->Error(DiagMessage(value->GetSource())
385                 << "trying to add resource '" << name << "' with ID " << res_id
386                 << " but package '" << package->name << "' already has ID "
387                 << std::hex << (int)package->id.value() << std::dec);
388     return false;
389   }
390 
391   ResourceTableType* type = package->FindOrCreateType(name.type);
392   if (res_id.is_valid_dynamic() && type->id && type->id.value() != res_id.type_id()) {
393     diag->Error(DiagMessage(value->GetSource())
394                 << "trying to add resource '" << name << "' with ID " << res_id
395                 << " but type '" << type->type << "' already has ID "
396                 << std::hex << (int)type->id.value() << std::dec);
397     return false;
398   }
399 
400   ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
401   if (res_id.is_valid_dynamic() && entry->id && entry->id.value() != res_id.entry_id()) {
402     diag->Error(DiagMessage(value->GetSource())
403                 << "trying to add resource '" << name << "' with ID " << res_id
404                 << " but resource already has ID "
405                 << ResourceId(package->id.value(), type->id.value(),
406                               entry->id.value()));
407     return false;
408   }
409 
410   ResourceConfigValue* config_value = entry->FindOrCreateValue(config, product);
411   if (!config_value->value) {
412     // Resource does not exist, add it now.
413     config_value->value = std::move(value);
414 
415   } else {
416     switch (conflictResolver(config_value->value.get(), value.get())) {
417       case CollisionResult::kTakeNew:
418         // Take the incoming value.
419         config_value->value = std::move(value);
420         break;
421 
422       case CollisionResult::kConflict:
423         diag->Error(DiagMessage(value->GetSource())
424                     << "duplicate value for resource '" << name << "' "
425                     << "with config '" << config << "'");
426         diag->Error(DiagMessage(config_value->value->GetSource())
427                     << "resource previously defined here");
428         return false;
429 
430       case CollisionResult::kKeepOriginal:
431         break;
432     }
433   }
434 
435   if (res_id.is_valid_dynamic()) {
436     package->id = res_id.package_id();
437     type->id = res_id.type_id();
438     entry->id = res_id.entry_id();
439   }
440   return true;
441 }
442 
SetSymbolState(const ResourceNameRef & name,const ResourceId & res_id,const Symbol & symbol,IDiagnostics * diag)443 bool ResourceTable::SetSymbolState(const ResourceNameRef& name, const ResourceId& res_id,
444                                    const Symbol& symbol, IDiagnostics* diag) {
445   return SetSymbolStateImpl(name, res_id, symbol, ValidateName, diag);
446 }
447 
SetSymbolStateAllowMangled(const ResourceNameRef & name,const ResourceId & res_id,const Symbol & symbol,IDiagnostics * diag)448 bool ResourceTable::SetSymbolStateAllowMangled(const ResourceNameRef& name,
449                                                const ResourceId& res_id,
450                                                const Symbol& symbol,
451                                                IDiagnostics* diag) {
452   return SetSymbolStateImpl(name, res_id, symbol, SkipValidateName, diag);
453 }
454 
SetSymbolStateImpl(const ResourceNameRef & name,const ResourceId & res_id,const Symbol & symbol,NameValidator name_validator,IDiagnostics * diag)455 bool ResourceTable::SetSymbolStateImpl(const ResourceNameRef& name, const ResourceId& res_id,
456                                        const Symbol& symbol, NameValidator name_validator,
457                                        IDiagnostics* diag) {
458   CHECK(diag != nullptr);
459 
460   const StringPiece bad_char = name_validator(name.entry);
461   if (!bad_char.empty()) {
462     diag->Error(DiagMessage(symbol.source) << "resource '" << name << "' has invalid entry name '"
463                                            << name.entry << "'. Invalid character '" << bad_char
464                                            << "'");
465     return false;
466   }
467 
468   ResourceTablePackage* package = FindOrCreatePackage(name.package);
469   if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
470     diag->Error(DiagMessage(symbol.source)
471                 << "trying to add resource '" << name << "' with ID " << res_id
472                 << " but package '" << package->name << "' already has ID "
473                 << std::hex << (int)package->id.value() << std::dec);
474     return false;
475   }
476 
477   ResourceTableType* type = package->FindOrCreateType(name.type);
478   if (res_id.is_valid_dynamic() && type->id && type->id.value() != res_id.type_id()) {
479     diag->Error(DiagMessage(symbol.source)
480                 << "trying to add resource '" << name << "' with ID " << res_id
481                 << " but type '" << type->type << "' already has ID "
482                 << std::hex << (int)type->id.value() << std::dec);
483     return false;
484   }
485 
486   ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
487   if (res_id.is_valid_dynamic() && entry->id && entry->id.value() != res_id.entry_id()) {
488     diag->Error(DiagMessage(symbol.source)
489                 << "trying to add resource '" << name << "' with ID " << res_id
490                 << " but resource already has ID "
491                 << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
492     return false;
493   }
494 
495   if (res_id.is_valid_dynamic()) {
496     package->id = res_id.package_id();
497     type->id = res_id.type_id();
498     entry->id = res_id.entry_id();
499   }
500 
501   // Only mark the type state as public, it doesn't care about being private.
502   if (symbol.state == SymbolState::kPublic) {
503     type->symbol_status.state = SymbolState::kPublic;
504   }
505 
506   if (symbol.allow_new) {
507     // This symbol can be added as a new resource when merging (if it belongs to an overlay).
508     entry->symbol_status.allow_new = true;
509   }
510 
511   if (symbol.state == SymbolState::kUndefined &&
512       entry->symbol_status.state != SymbolState::kUndefined) {
513     // We can't undefine a symbol (remove its visibility). Ignore.
514     return true;
515   }
516 
517   if (symbol.state == SymbolState::kPrivate &&
518       entry->symbol_status.state == SymbolState::kPublic) {
519     // We can't downgrade public to private. Ignore.
520     return true;
521   }
522 
523   // This symbol definition takes precedence, replace.
524   entry->symbol_status.state = symbol.state;
525   entry->symbol_status.source = symbol.source;
526   entry->symbol_status.comment = symbol.comment;
527   return true;
528 }
529 
FindResource(const ResourceNameRef & name)530 Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name) {
531   ResourceTablePackage* package = FindPackage(name.package);
532   if (!package) {
533     return {};
534   }
535 
536   ResourceTableType* type = package->FindType(name.type);
537   if (!type) {
538     return {};
539   }
540 
541   ResourceEntry* entry = type->FindEntry(name.entry);
542   if (!entry) {
543     return {};
544   }
545   return SearchResult{package, type, entry};
546 }
547 
548 }  // namespace aapt
549