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