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