• 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 "android-base/stringprintf.h"
26 #include "androidfw/ConfigDescription.h"
27 #include "androidfw/ResourceTypes.h"
28 
29 #include "Debug.h"
30 #include "NameMangler.h"
31 #include "ResourceValues.h"
32 #include "ValueVisitor.h"
33 #include "trace/TraceBuffer.h"
34 #include "text/Unicode.h"
35 #include "util/Util.h"
36 
37 using ::aapt::text::IsValidResourceEntryName;
38 using ::android::ConfigDescription;
39 using ::android::StringPiece;
40 using ::android::base::StringPrintf;
41 
42 namespace aapt {
43 
44 const char* Overlayable::kActorScheme = "overlay";
45 
less_than_type_and_id(const std::unique_ptr<ResourceTableType> & lhs,const std::pair<ResourceType,Maybe<uint8_t>> & rhs)46 static bool less_than_type_and_id(const std::unique_ptr<ResourceTableType>& lhs,
47                                   const std::pair<ResourceType, Maybe<uint8_t>>& rhs) {
48   return lhs->type < rhs.first || (lhs->type == rhs.first && rhs.second && lhs->id < rhs.second);
49 }
50 
51 template <typename T>
less_than_struct_with_name(const std::unique_ptr<T> & lhs,const StringPiece & rhs)52 static bool less_than_struct_with_name(const std::unique_ptr<T>& lhs, const StringPiece& rhs) {
53   return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0;
54 }
55 
56 template <typename T>
less_than_struct_with_name_and_id(const std::unique_ptr<T> & lhs,const std::pair<StringPiece,Maybe<uint16_t>> & rhs)57 static bool less_than_struct_with_name_and_id(const std::unique_ptr<T>& lhs,
58                                               const std::pair<StringPiece, Maybe<uint16_t>>& rhs) {
59   int name_cmp = lhs->name.compare(0, lhs->name.size(), rhs.first.data(), rhs.first.size());
60   return name_cmp < 0 || (name_cmp == 0 && rhs.second && lhs->id < rhs.second);
61 }
62 
FindPackage(const StringPiece & name) const63 ResourceTablePackage* ResourceTable::FindPackage(const StringPiece& name) const {
64   const auto last = packages.end();
65   auto iter = std::lower_bound(packages.begin(), last, name,
66                                less_than_struct_with_name<ResourceTablePackage>);
67   if (iter != last && name == (*iter)->name) {
68     return iter->get();
69   }
70   return nullptr;
71 }
72 
FindPackageById(uint8_t id) const73 ResourceTablePackage* ResourceTable::FindPackageById(uint8_t id) const {
74   for (auto& package : packages) {
75     if (package->id && package->id.value() == id) {
76       return package.get();
77     }
78   }
79   return nullptr;
80 }
81 
CreatePackage(const StringPiece & name,Maybe<uint8_t> id)82 ResourceTablePackage* ResourceTable::CreatePackage(const StringPiece& name, Maybe<uint8_t> id) {
83   TRACE_CALL();
84   ResourceTablePackage* package = FindOrCreatePackage(name);
85   if (id && !package->id) {
86     package->id = id;
87     return package;
88   }
89 
90   if (id && package->id && package->id.value() != id.value()) {
91     return nullptr;
92   }
93   return package;
94 }
95 
CreatePackageAllowingDuplicateNames(const StringPiece & name,const Maybe<uint8_t> id)96 ResourceTablePackage* ResourceTable::CreatePackageAllowingDuplicateNames(const StringPiece& name,
97                                                                          const Maybe<uint8_t> id) {
98   const auto last = packages.end();
99   auto iter = std::lower_bound(packages.begin(), last, std::make_pair(name, id),
100                                less_than_struct_with_name_and_id<ResourceTablePackage>);
101 
102   if (iter != last && name == (*iter)->name && id == (*iter)->id) {
103     return iter->get();
104   }
105 
106   std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
107   new_package->name = name.to_string();
108   new_package->id = id;
109   return packages.emplace(iter, std::move(new_package))->get();
110 }
111 
FindOrCreatePackage(const StringPiece & name)112 ResourceTablePackage* ResourceTable::FindOrCreatePackage(const StringPiece& name) {
113   const auto last = packages.end();
114   auto iter = std::lower_bound(packages.begin(), last, name,
115                                less_than_struct_with_name<ResourceTablePackage>);
116   if (iter != last && name == (*iter)->name) {
117     return iter->get();
118   }
119 
120   std::unique_ptr<ResourceTablePackage> new_package = util::make_unique<ResourceTablePackage>();
121   new_package->name = name.to_string();
122   return packages.emplace(iter, std::move(new_package))->get();
123 }
124 
FindType(ResourceType type,const Maybe<uint8_t> id)125 ResourceTableType* ResourceTablePackage::FindType(ResourceType type, const Maybe<uint8_t> id) {
126   const auto last = types.end();
127   auto iter = std::lower_bound(types.begin(), last, std::make_pair(type, id),
128                                less_than_type_and_id);
129   if (iter != last && (*iter)->type == type && (!id || id == (*iter)->id)) {
130     return iter->get();
131   }
132   return nullptr;
133 }
134 
FindOrCreateType(ResourceType type,const Maybe<uint8_t> id)135 ResourceTableType* ResourceTablePackage::FindOrCreateType(ResourceType type,
136                                                           const Maybe<uint8_t> id) {
137   const auto last = types.end();
138   auto iter = std::lower_bound(types.begin(), last, std::make_pair(type, id),
139                                less_than_type_and_id);
140   if (iter != last && (*iter)->type == type && (!id || id == (*iter)->id)) {
141     return iter->get();
142   }
143 
144   auto new_type = new ResourceTableType(type);
145   new_type->id = id;
146   return types.emplace(iter, std::move(new_type))->get();
147 }
148 
FindEntry(const StringPiece & name,const Maybe<uint16_t> id)149 ResourceEntry* ResourceTableType::FindEntry(const StringPiece& name, const Maybe<uint16_t> id) {
150   const auto last = entries.end();
151   auto iter = std::lower_bound(entries.begin(), last, std::make_pair(name, id),
152       less_than_struct_with_name_and_id<ResourceEntry>);
153   if (iter != last && name == (*iter)->name && (!id || id == (*iter)->id)) {
154     return iter->get();
155   }
156   return nullptr;
157 }
158 
FindOrCreateEntry(const StringPiece & name,const Maybe<uint16_t> id)159 ResourceEntry* ResourceTableType::FindOrCreateEntry(const StringPiece& name,
160                                                     const Maybe<uint16_t > id) {
161   auto last = entries.end();
162   auto iter = std::lower_bound(entries.begin(), last, std::make_pair(name, id),
163                                less_than_struct_with_name_and_id<ResourceEntry>);
164   if (iter != last && name == (*iter)->name && (!id || id == (*iter)->id)) {
165     return iter->get();
166   }
167 
168   auto new_entry = new ResourceEntry(name);
169   new_entry->id = id;
170   return entries.emplace(iter, std::move(new_entry))->get();
171 }
172 
FindValue(const ConfigDescription & config)173 ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config) {
174   return FindValue(config, StringPiece());
175 }
176 
177 struct ConfigKey {
178   const ConfigDescription* config;
179   const StringPiece& product;
180 };
181 
lt_config_key_ref(const std::unique_ptr<ResourceConfigValue> & lhs,const ConfigKey & rhs)182 bool lt_config_key_ref(const std::unique_ptr<ResourceConfigValue>& lhs, const ConfigKey& rhs) {
183   int cmp = lhs->config.compare(*rhs.config);
184   if (cmp == 0) {
185     cmp = StringPiece(lhs->product).compare(rhs.product);
186   }
187   return cmp < 0;
188 }
189 
FindValue(const ConfigDescription & config,const StringPiece & product)190 ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config,
191                                               const StringPiece& product) {
192   auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
193                                lt_config_key_ref);
194   if (iter != values.end()) {
195     ResourceConfigValue* value = iter->get();
196     if (value->config == config && StringPiece(value->product) == product) {
197       return value;
198     }
199   }
200   return nullptr;
201 }
202 
FindOrCreateValue(const ConfigDescription & config,const StringPiece & product)203 ResourceConfigValue* ResourceEntry::FindOrCreateValue(const ConfigDescription& config,
204                                                       const StringPiece& product) {
205   auto iter = std::lower_bound(values.begin(), values.end(), ConfigKey{&config, product},
206                                lt_config_key_ref);
207   if (iter != values.end()) {
208     ResourceConfigValue* value = iter->get();
209     if (value->config == config && StringPiece(value->product) == product) {
210       return value;
211     }
212   }
213   ResourceConfigValue* newValue =
214       values.insert(iter, util::make_unique<ResourceConfigValue>(config, product))->get();
215   return newValue;
216 }
217 
FindAllValues(const ConfigDescription & config)218 std::vector<ResourceConfigValue*> ResourceEntry::FindAllValues(const ConfigDescription& config) {
219   std::vector<ResourceConfigValue*> results;
220 
221   auto iter = values.begin();
222   for (; iter != values.end(); ++iter) {
223     ResourceConfigValue* value = iter->get();
224     if (value->config == config) {
225       results.push_back(value);
226       ++iter;
227       break;
228     }
229   }
230 
231   for (; iter != values.end(); ++iter) {
232     ResourceConfigValue* value = iter->get();
233     if (value->config == config) {
234       results.push_back(value);
235     }
236   }
237   return results;
238 }
239 
HasDefaultValue() const240 bool ResourceEntry::HasDefaultValue() const {
241   const ConfigDescription& default_config = ConfigDescription::DefaultConfig();
242 
243   // The default config should be at the top of the list, since the list is sorted.
244   for (auto& config_value : values) {
245     if (config_value->config == default_config) {
246       return true;
247     }
248   }
249   return false;
250 }
251 
252 // The default handler for collisions.
253 //
254 // Typically, a weak value will be overridden by a strong value. An existing weak
255 // value will not be overridden by an incoming weak value.
256 //
257 // There are some exceptions:
258 //
259 // Attributes: There are two types of Attribute values: USE and DECL.
260 //
261 // USE is anywhere an Attribute is declared without a format, and in a place that would
262 // be legal to declare if the Attribute already existed. This is typically in a
263 // <declare-styleable> tag. Attributes defined in a <declare-styleable> are also weak.
264 //
265 // DECL is an absolute declaration of an Attribute and specifies an explicit format.
266 //
267 // A DECL will override a USE without error. Two DECLs must match in their format for there to be
268 // no error.
ResolveValueCollision(Value * existing,Value * incoming,bool overlay)269 ResourceTable::CollisionResult ResourceTable::ResolveValueCollision(Value* existing,
270                                                                     Value* incoming,
271                                                                     bool overlay) {
272   Attribute* existing_attr = ValueCast<Attribute>(existing);
273   Attribute* incoming_attr = ValueCast<Attribute>(incoming);
274   if (!incoming_attr) {
275     if (incoming->IsWeak()) {
276       // We're trying to add a weak resource but a resource
277       // already exists. Keep the existing.
278       return CollisionResult::kKeepOriginal;
279     } else if (existing->IsWeak()) {
280       // Override the weak resource with the new strong resource.
281       return CollisionResult::kTakeNew;
282     }
283     // The existing and incoming values are strong, this is an error
284     // if the values are not both attributes.
285     return overlay ? CollisionResult::kTakeNew : CollisionResult::kConflict;
286   }
287 
288   if (!existing_attr) {
289     if (existing->IsWeak()) {
290       // The existing value is not an attribute and it is weak,
291       // so take the incoming attribute value.
292       return CollisionResult::kTakeNew;
293     }
294     // The existing value is not an attribute and it is strong,
295     // so the incoming attribute value is an error.
296     return overlay ? CollisionResult::kTakeNew : CollisionResult::kConflict;
297   }
298 
299   CHECK(incoming_attr != nullptr && existing_attr != nullptr);
300 
301   //
302   // Attribute specific handling. At this point we know both
303   // values are attributes. Since we can declare and define
304   // attributes all-over, we do special handling to see
305   // which definition sticks.
306   //
307   if (existing_attr->IsCompatibleWith(*incoming_attr)) {
308     // The two attributes are both DECLs, but they are plain attributes with compatible formats.
309     // Keep the strongest one.
310     return existing_attr->IsWeak() ? CollisionResult::kTakeNew : CollisionResult::kKeepOriginal;
311   }
312 
313   if (existing_attr->IsWeak() && existing_attr->type_mask == android::ResTable_map::TYPE_ANY) {
314     // Any incoming attribute is better than this.
315     return CollisionResult::kTakeNew;
316   }
317 
318   if (incoming_attr->IsWeak() && incoming_attr->type_mask == android::ResTable_map::TYPE_ANY) {
319     // The incoming attribute may be a USE instead of a DECL.
320     // Keep the existing attribute.
321     return CollisionResult::kKeepOriginal;
322   }
323 
324   return CollisionResult::kConflict;
325 }
326 
IgnoreCollision(Value *,Value *,bool)327 ResourceTable::CollisionResult ResourceTable::IgnoreCollision(Value* /* existing */,
328                                                               Value* /* incoming */,
329                                                               bool /* overlay */) {
330   return CollisionResult::kKeepBoth;
331 }
332 
ResourceNameValidator(const StringPiece & name)333 static StringPiece ResourceNameValidator(const StringPiece& name) {
334   if (!IsValidResourceEntryName(name)) {
335     return name;
336   }
337   return {};
338 }
339 
SkipNameValidator(const StringPiece &)340 static StringPiece SkipNameValidator(const StringPiece& /*name*/) {
341   return {};
342 }
343 
AddResource(const ResourceNameRef & name,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)344 bool ResourceTable::AddResource(const ResourceNameRef& name,
345                                 const ConfigDescription& config,
346                                 const StringPiece& product,
347                                 std::unique_ptr<Value> value,
348                                 IDiagnostics* diag) {
349   return AddResourceImpl(name, ResourceId{}, config, product, std::move(value),
350                          (validate_resources_ ? ResourceNameValidator : SkipNameValidator),
351                          (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
352 }
353 
AddResourceWithId(const ResourceNameRef & name,const ResourceId & res_id,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)354 bool ResourceTable::AddResourceWithId(const ResourceNameRef& name, const ResourceId& res_id,
355                                       const ConfigDescription& config, const StringPiece& product,
356                                       std::unique_ptr<Value> value, IDiagnostics* diag) {
357   return AddResourceImpl(name, res_id, config, product, std::move(value),
358                          (validate_resources_ ? ResourceNameValidator : SkipNameValidator),
359                          (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
360 }
361 
AddResourceMangled(const ResourceNameRef & name,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)362 bool ResourceTable::AddResourceMangled(const ResourceNameRef& name, const ConfigDescription& config,
363                                        const StringPiece& product, std::unique_ptr<Value> value,
364                                        IDiagnostics* diag) {
365   return AddResourceImpl(name, ResourceId{}, config, product, std::move(value), SkipNameValidator,
366                          (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
367 }
368 
AddResourceWithIdMangled(const ResourceNameRef & name,const ResourceId & id,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,IDiagnostics * diag)369 bool ResourceTable::AddResourceWithIdMangled(const ResourceNameRef& name, const ResourceId& id,
370                                              const ConfigDescription& config,
371                                              const StringPiece& product,
372                                              std::unique_ptr<Value> value, IDiagnostics* diag) {
373   return AddResourceImpl(name, id, config, product, std::move(value), SkipNameValidator,
374                          (validate_resources_ ? ResolveValueCollision : IgnoreCollision), diag);
375 }
376 
ValidateName(NameValidator name_validator,const ResourceNameRef & name,const Source & source,IDiagnostics * diag)377 bool ResourceTable::ValidateName(NameValidator name_validator, const ResourceNameRef& name,
378                                  const Source& source, IDiagnostics* diag) {
379   const StringPiece bad_char = name_validator(name.entry);
380   if (!bad_char.empty()) {
381     diag->Error(DiagMessage(source) << "resource '" << name << "' has invalid entry name '"
382                                     << name.entry << "'. Invalid character '" << bad_char << "'");
383     return false;
384   }
385   return true;
386 }
387 
AddResourceImpl(const ResourceNameRef & name,const ResourceId & res_id,const ConfigDescription & config,const StringPiece & product,std::unique_ptr<Value> value,NameValidator name_validator,const CollisionResolverFunc & conflict_resolver,IDiagnostics * diag)388 bool ResourceTable::AddResourceImpl(const ResourceNameRef& name, const ResourceId& res_id,
389                                     const ConfigDescription& config, const StringPiece& product,
390                                     std::unique_ptr<Value> value, NameValidator name_validator,
391                                     const CollisionResolverFunc& conflict_resolver,
392                                     IDiagnostics* diag) {
393   CHECK(value != nullptr);
394   CHECK(diag != nullptr);
395 
396   const Source& source = value->GetSource();
397   if (!ValidateName(name_validator, name, source, diag)) {
398     return false;
399   }
400 
401   // Check for package names appearing twice with two different package ids
402   ResourceTablePackage* package = FindOrCreatePackage(name.package);
403   if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
404     diag->Error(DiagMessage(source)
405                     << "trying to add resource '" << name << "' with ID " << res_id
406                     << " but package '" << package->name << "' already has ID "
407                     << StringPrintf("%02x", package->id.value()));
408     return false;
409   }
410 
411   // Whether or not to error on duplicate resources
412   bool check_id = validate_resources_ && res_id.is_valid_dynamic();
413   // Whether or not to create a duplicate resource if the id does not match
414   bool use_id = !validate_resources_ && res_id.is_valid_dynamic();
415 
416   ResourceTableType* type = package->FindOrCreateType(name.type, use_id ? res_id.type_id()
417                                                                         : Maybe<uint8_t>());
418 
419   // Check for types appearing twice with two different type ids
420   if (check_id && type->id && type->id.value() != res_id.type_id()) {
421     diag->Error(DiagMessage(source)
422                     << "trying to add resource '" << name << "' with ID " << res_id
423                     << " but type '" << type->type << "' already has ID "
424                     << StringPrintf("%02x", type->id.value()));
425     return false;
426   }
427 
428   ResourceEntry* entry = type->FindOrCreateEntry(name.entry, use_id ? res_id.entry_id()
429                                                                     : Maybe<uint16_t>());
430 
431   // Check for entries appearing twice with two different entry ids
432   if (check_id && entry->id && entry->id.value() != res_id.entry_id()) {
433     diag->Error(DiagMessage(source)
434                     << "trying to add resource '" << name << "' with ID " << res_id
435                     << " but resource already has ID "
436                     << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
437     return false;
438   }
439 
440   ResourceConfigValue* config_value = entry->FindOrCreateValue(config, product);
441   if (!config_value->value) {
442     // Resource does not exist, add it now.
443     config_value->value = std::move(value);
444   } else {
445     switch (conflict_resolver(config_value->value.get(), value.get(), false /* overlay */)) {
446       case CollisionResult::kKeepBoth:
447         // Insert the value ignoring for duplicate configurations
448         entry->values.push_back(util::make_unique<ResourceConfigValue>(config, product));
449         entry->values.back()->value = std::move(value);
450         break;
451 
452       case CollisionResult::kTakeNew:
453         // Take the incoming value.
454         config_value->value = std::move(value);
455         break;
456 
457       case CollisionResult::kConflict:
458         diag->Error(DiagMessage(source) << "duplicate value for resource '" << name << "' "
459                                         << "with config '" << config << "'");
460         diag->Error(DiagMessage(source) << "resource previously defined here");
461         return false;
462 
463       case CollisionResult::kKeepOriginal:
464         break;
465     }
466   }
467 
468   if (res_id.is_valid_dynamic()) {
469     package->id = res_id.package_id();
470     type->id = res_id.type_id();
471     entry->id = res_id.entry_id();
472   }
473 
474   return true;
475 }
476 
GetValidateResources()477 bool ResourceTable::GetValidateResources() {
478   return validate_resources_;
479 }
480 
SetVisibility(const ResourceNameRef & name,const Visibility & visibility,IDiagnostics * diag)481 bool ResourceTable::SetVisibility(const ResourceNameRef& name, const Visibility& visibility,
482                                   IDiagnostics* diag) {
483   return SetVisibilityImpl(name, visibility, {}, ResourceNameValidator, diag);
484 }
485 
SetVisibilityWithId(const ResourceNameRef & name,const Visibility & visibility,const ResourceId & res_id,IDiagnostics * diag)486 bool ResourceTable::SetVisibilityWithId(const ResourceNameRef& name, const Visibility& visibility,
487                                         const ResourceId& res_id, IDiagnostics* diag) {
488   return SetVisibilityImpl(name, visibility, res_id, ResourceNameValidator, diag);
489 }
490 
SetVisibilityWithIdMangled(const ResourceNameRef & name,const Visibility & visibility,const ResourceId & res_id,IDiagnostics * diag)491 bool ResourceTable::SetVisibilityWithIdMangled(const ResourceNameRef& name,
492                                                const Visibility& visibility,
493                                                const ResourceId& res_id, IDiagnostics* diag) {
494   return SetVisibilityImpl(name, visibility, res_id, SkipNameValidator, diag);
495 }
496 
SetVisibilityImpl(const ResourceNameRef & name,const Visibility & visibility,const ResourceId & res_id,NameValidator name_validator,IDiagnostics * diag)497 bool ResourceTable::SetVisibilityImpl(const ResourceNameRef& name, const Visibility& visibility,
498                                       const ResourceId& res_id, NameValidator name_validator,
499                                       IDiagnostics* diag) {
500   CHECK(diag != nullptr);
501 
502   const Source& source = visibility.source;
503   if (!ValidateName(name_validator, name, source, diag)) {
504     return false;
505   }
506 
507   // Check for package names appearing twice with two different package ids
508   ResourceTablePackage* package = FindOrCreatePackage(name.package);
509   if (res_id.is_valid_dynamic() && package->id && package->id.value() != res_id.package_id()) {
510     diag->Error(DiagMessage(source)
511                     << "trying to add resource '" << name << "' with ID " << res_id
512                     << " but package '" << package->name << "' already has ID "
513                     << StringPrintf("%02x", package->id.value()));
514     return false;
515   }
516 
517   // Whether or not to error on duplicate resources
518   bool check_id = validate_resources_ && res_id.is_valid_dynamic();
519   // Whether or not to create a duplicate resource if the id does not match
520   bool use_id = !validate_resources_ && res_id.is_valid_dynamic();
521 
522   ResourceTableType* type = package->FindOrCreateType(name.type, use_id ? res_id.type_id()
523                                                                         : Maybe<uint8_t>());
524 
525   // Check for types appearing twice with two different type ids
526   if (check_id && type->id && type->id.value() != res_id.type_id()) {
527     diag->Error(DiagMessage(source)
528                     << "trying to add resource '" << name << "' with ID " << res_id
529                     << " but type '" << type->type << "' already has ID "
530                     << StringPrintf("%02x", type->id.value()));
531     return false;
532   }
533 
534   ResourceEntry* entry = type->FindOrCreateEntry(name.entry, use_id ? res_id.entry_id()
535                                                                     : Maybe<uint16_t>());
536 
537   // Check for entries appearing twice with two different entry ids
538   if (check_id && entry->id && entry->id.value() != res_id.entry_id()) {
539     diag->Error(DiagMessage(source)
540                     << "trying to add resource '" << name << "' with ID " << res_id
541                     << " but resource already has ID "
542                     << ResourceId(package->id.value(), type->id.value(), entry->id.value()));
543     return false;
544   }
545 
546   if (res_id.is_valid_dynamic()) {
547     package->id = res_id.package_id();
548     type->id = res_id.type_id();
549     entry->id = res_id.entry_id();
550   }
551 
552   // Only mark the type visibility level as public, it doesn't care about being private.
553   if (visibility.level == Visibility::Level::kPublic) {
554     type->visibility_level = Visibility::Level::kPublic;
555   }
556 
557   if (visibility.level == Visibility::Level::kUndefined &&
558       entry->visibility.level != Visibility::Level::kUndefined) {
559     // We can't undefine a symbol (remove its visibility). Ignore.
560     return true;
561   }
562 
563   if (visibility.level < entry->visibility.level) {
564     // We can't downgrade public to private. Ignore.
565     return true;
566   }
567 
568   // This symbol definition takes precedence, replace.
569   entry->visibility = visibility;
570   return true;
571 }
572 
SetAllowNew(const ResourceNameRef & name,const AllowNew & allow_new,IDiagnostics * diag)573 bool ResourceTable::SetAllowNew(const ResourceNameRef& name, const AllowNew& allow_new,
574                                 IDiagnostics* diag) {
575   return SetAllowNewImpl(name, allow_new, ResourceNameValidator, diag);
576 }
577 
SetAllowNewMangled(const ResourceNameRef & name,const AllowNew & allow_new,IDiagnostics * diag)578 bool ResourceTable::SetAllowNewMangled(const ResourceNameRef& name, const AllowNew& allow_new,
579                                        IDiagnostics* diag) {
580   return SetAllowNewImpl(name, allow_new, SkipNameValidator, diag);
581 }
582 
SetAllowNewImpl(const ResourceNameRef & name,const AllowNew & allow_new,NameValidator name_validator,IDiagnostics * diag)583 bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew& allow_new,
584                                     NameValidator name_validator, IDiagnostics* diag) {
585   CHECK(diag != nullptr);
586 
587   if (!ValidateName(name_validator, name, allow_new.source, diag)) {
588     return false;
589   }
590 
591   ResourceTablePackage* package = FindOrCreatePackage(name.package);
592   ResourceTableType* type = package->FindOrCreateType(name.type);
593   ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
594   entry->allow_new = allow_new;
595   return true;
596 }
597 
SetOverlayable(const ResourceNameRef & name,const OverlayableItem & overlayable,IDiagnostics * diag)598 bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const OverlayableItem& overlayable,
599                                    IDiagnostics* diag) {
600   return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
601 }
602 
SetOverlayableImpl(const ResourceNameRef & name,const OverlayableItem & overlayable,NameValidator name_validator,IDiagnostics * diag)603 bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name,
604                                        const OverlayableItem& overlayable,
605                                        NameValidator name_validator, IDiagnostics *diag) {
606   CHECK(diag != nullptr);
607 
608   if (!ValidateName(name_validator, name, overlayable.source, diag)) {
609     return false;
610   }
611 
612   ResourceTablePackage* package = FindOrCreatePackage(name.package);
613   ResourceTableType* type = package->FindOrCreateType(name.type);
614   ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
615 
616   if (entry->overlayable_item) {
617     diag->Error(DiagMessage(overlayable.source)
618                 << "duplicate overlayable declaration for resource '" << name << "'");
619     diag->Error(DiagMessage(entry->overlayable_item.value().source)
620                 << "previous declaration here");
621     return false;
622   }
623 
624   entry->overlayable_item = overlayable;
625   return true;
626 }
627 
FindResource(const ResourceNameRef & name) const628 Maybe<ResourceTable::SearchResult> ResourceTable::FindResource(const ResourceNameRef& name) const {
629   ResourceTablePackage* package = FindPackage(name.package);
630   if (package == nullptr) {
631     return {};
632   }
633 
634   ResourceTableType* type = package->FindType(name.type);
635   if (type == nullptr) {
636     return {};
637   }
638 
639   ResourceEntry* entry = type->FindEntry(name.entry);
640   if (entry == nullptr) {
641     return {};
642   }
643   return SearchResult{package, type, entry};
644 }
645 
Clone() const646 std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
647   std::unique_ptr<ResourceTable> new_table = util::make_unique<ResourceTable>();
648   for (const auto& pkg : packages) {
649     ResourceTablePackage* new_pkg = new_table->CreatePackage(pkg->name, pkg->id);
650     for (const auto& type : pkg->types) {
651       ResourceTableType* new_type = new_pkg->FindOrCreateType(type->type);
652       new_type->id = type->id;
653       new_type->visibility_level = type->visibility_level;
654 
655       for (const auto& entry : type->entries) {
656         ResourceEntry* new_entry = new_type->FindOrCreateEntry(entry->name);
657         new_entry->id = entry->id;
658         new_entry->visibility = entry->visibility;
659         new_entry->allow_new = entry->allow_new;
660         new_entry->overlayable_item = entry->overlayable_item;
661 
662         for (const auto& config_value : entry->values) {
663           ResourceConfigValue* new_value =
664               new_entry->FindOrCreateValue(config_value->config, config_value->product);
665           new_value->value.reset(config_value->value->Clone(&new_table->string_pool));
666         }
667       }
668     }
669   }
670   return new_table;
671 }
672 
673 }  // namespace aapt
674