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, ResourceType rhs) {
36  return lhs->type < rhs;
37}
38
39template <typename T>
40static 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
44ResourceTablePackage* 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
54ResourceTablePackage* 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
63ResourceTablePackage* 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
76ResourceTablePackage* 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
89ResourceTableType* 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
98ResourceTableType* 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
107ResourceEntry* 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
117ResourceEntry* 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
127ResourceConfigValue* ResourceEntry::FindValue(const ConfigDescription& config) {
128  return FindValue(config, StringPiece());
129}
130
131struct ConfigKey {
132  const ConfigDescription* config;
133  const StringPiece& product;
134};
135
136bool 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
144ResourceConfigValue* 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
157ResourceConfigValue* 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
172std::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
194std::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 */
229ResourceTable::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
286static constexpr const char* kValidNameChars = "._-";
287
288static 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
296static StringPiece SkipValidateName(const StringPiece& /*name*/) {
297  return {};
298}
299
300bool 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
309bool 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
319bool 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
327bool 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
334bool 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
346bool 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
355bool 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
365bool 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
443bool 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
448bool 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
455bool 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
530Maybe<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