1c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski/*
2c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski * Copyright (C) 2017 The Android Open Source Project
3c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski *
4c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
5c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski * you may not use this file except in compliance with the License.
6c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski * You may obtain a copy of the License at
7c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski *
8c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
9c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski *
10c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski * Unless required by applicable law or agreed to in writing, software
11c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
12c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski * See the License for the specific language governing permissions and
14c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski * limitations under the License.
15c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski */
16c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
17c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski#include "link/XmlCompatVersioner.h"
18c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
19c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski#include <algorithm>
20c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
21c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski#include "util/Util.h"
22c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
23c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinskinamespace aapt {
24c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
25c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinskistatic xml::Attribute CopyAttr(const xml::Attribute& src, StringPool* out_string_pool) {
26c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  xml::Attribute dst{src.namespace_uri, src.name, src.value, src.compiled_attribute};
27c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  if (src.compiled_value != nullptr) {
28c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    dst.compiled_value.reset(src.compiled_value->Clone(out_string_pool));
29c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  }
30c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  return dst;
31c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski}
32c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
33c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski// Returns false if the attribute is not copied because an existing attribute takes precedence
34c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski// (came from a rule).
35c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinskistatic bool CopyAttribute(const xml::Attribute& src_attr, bool generated, xml::Element* dst_el,
36c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                          StringPool* out_string_pool) {
37c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  xml::Attribute* dst_attr = dst_el->FindAttribute(src_attr.namespace_uri, src_attr.name);
38c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  if (dst_attr != nullptr) {
39c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    if (generated) {
40c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      // Generated attributes always take precedence.
41c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      dst_attr->value = src_attr.value;
42c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      dst_attr->compiled_attribute = src_attr.compiled_attribute;
43c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      if (src_attr.compiled_value != nullptr) {
44c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski        dst_attr->compiled_value.reset(src_attr.compiled_value->Clone(out_string_pool));
45c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      }
46c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      return true;
47c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    }
48c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    return false;
49c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  }
50c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  dst_el->attributes.push_back(CopyAttr(src_attr, out_string_pool));
51c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  return true;
52c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski}
53c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
54c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinskivoid XmlCompatVersioner::ProcessRule(const xml::Element& src_el, const xml::Attribute& src_attr,
55c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                                     const ApiVersion& src_attr_version, const IDegradeRule* rule,
56c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                                     const util::Range<ApiVersion>& api_range, bool generated,
57c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                                     xml::Element* dst_el,
58c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                                     std::set<ApiVersion>* out_apis_referenced,
59c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                                     StringPool* out_string_pool) {
60c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  if (src_attr_version <= api_range.start) {
61c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    // The API is compatible, so don't check the rule and just copy.
62c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    if (!CopyAttribute(src_attr, generated, dst_el, out_string_pool)) {
63c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      // TODO(adamlesinski): Log a warning that an attribute was overridden?
64c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    }
65c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    return;
66c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  }
67c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
68c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  if (api_range.start >= SDK_LOLLIPOP_MR1) {
69c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    // Since LOLLIPOP MR1, the framework can handle silently ignoring unknown public attributes,
70c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    // so we don't need to erase/version them.
71c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    // Copy.
72c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    if (!CopyAttribute(src_attr, generated, dst_el, out_string_pool)) {
73c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      // TODO(adamlesinski): Log a warning that an attribute was overridden?
74c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    }
75c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  } else {
76c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    // We are going to erase this attribute from this XML resource version, but check if
77c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    // we even need to move it anywhere. A developer may have effectively overwritten it with
78c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    // a similarly versioned XML resource.
79c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    if (src_attr_version < api_range.end) {
80c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      // There is room for another versioned XML resource between this XML resource and the next
81c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      // versioned XML resource defined by the developer.
82c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      out_apis_referenced->insert(std::min<ApiVersion>(src_attr_version, SDK_LOLLIPOP_MR1));
83c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    }
84c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  }
85c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
86c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  if (rule != nullptr) {
87c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    for (const DegradeResult& result : rule->Degrade(src_el, src_attr, out_string_pool)) {
88c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      const ResourceId attr_resid = result.attr.compiled_attribute.value().id.value();
89c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      const ApiVersion attr_version = FindAttributeSdkLevel(attr_resid);
90c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
91c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      auto iter = rules_->find(attr_resid);
92c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      ProcessRule(src_el, result.attr, attr_version,
93c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                  iter != rules_->end() ? iter->second.get() : nullptr, api_range,
94c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                  true /*generated*/, dst_el, out_apis_referenced, out_string_pool);
95c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    }
96c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  }
97c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski}
98c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
99c744ae8aca97edfb2422598ea620e8219449fa9bAdam LesinskiXmlCompatVersioner::XmlCompatVersioner(const Rules* rules) : rules_(rules) {
100c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski}
101c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
102c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinskistd::unique_ptr<xml::XmlResource> XmlCompatVersioner::ProcessDoc(
103c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    ApiVersion target_api, ApiVersion max_api, xml::XmlResource* doc,
104c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    std::set<ApiVersion>* out_apis_referenced) {
105c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  const util::Range<ApiVersion> api_range{target_api, max_api};
106c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
107c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  std::unique_ptr<xml::XmlResource> cloned_doc = util::make_unique<xml::XmlResource>(doc->file);
108c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  cloned_doc->file.config.sdkVersion = static_cast<uint16_t>(target_api);
109c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
110c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  cloned_doc->root = doc->root->Clone([&](const xml::Element& el, xml::Element* out_el) {
111c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    for (const auto& attr : el.attributes) {
112c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      if (!attr.compiled_attribute) {
113c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski        // Just copy if this isn't a compiled attribute.
114c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski        out_el->attributes.push_back(CopyAttr(attr, &cloned_doc->string_pool));
115c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski        continue;
116c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      }
117c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
118c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      const ResourceId attr_resid = attr.compiled_attribute.value().id.value();
119c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      const ApiVersion attr_version = FindAttributeSdkLevel(attr_resid);
120c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
121c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      auto rule = rules_->find(attr_resid);
122c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski      ProcessRule(el, attr, attr_version, rule != rules_->end() ? rule->second.get() : nullptr,
123c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                  api_range, false /*generated*/, out_el, out_apis_referenced,
124c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                  &cloned_doc->string_pool);
125c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    }
126c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  });
127c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  return cloned_doc;
128c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski}
129c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
130c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinskistd::vector<std::unique_ptr<xml::XmlResource>> XmlCompatVersioner::Process(
131c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    IAaptContext* context, xml::XmlResource* doc, util::Range<ApiVersion> api_range) {
132c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  // Adjust the API range so that it falls after this document and after minSdkVersion.
133c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  api_range.start = std::max(api_range.start, context->GetMinSdkVersion());
134c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  api_range.start = std::max(api_range.start, static_cast<ApiVersion>(doc->file.config.sdkVersion));
135c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
136c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs;
137c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  std::set<ApiVersion> apis_referenced;
138c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  versioned_docs.push_back(ProcessDoc(api_range.start, api_range.end, doc, &apis_referenced));
139c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
140c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  // Adjust the sdkVersion of the first XML document back to its original (this only really
141c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  // makes a difference if the sdk version was below the minSdk to start).
142c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  versioned_docs.back()->file.config.sdkVersion = doc->file.config.sdkVersion;
143c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
144c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  // Iterate from smallest to largest API version.
145c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  for (ApiVersion api : apis_referenced) {
146c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    std::set<ApiVersion> dummy;
147c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    versioned_docs.push_back(ProcessDoc(api, api_range.end, doc, &dummy));
148c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  }
149c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  return versioned_docs;
150c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski}
151c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
152c744ae8aca97edfb2422598ea620e8219449fa9bAdam LesinskiDegradeToManyRule::DegradeToManyRule(std::vector<ReplacementAttr> attrs)
153c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    : attrs_(std::move(attrs)) {
154c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski}
155c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
156c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinskistatic inline std::unique_ptr<Item> CloneIfNotNull(const std::unique_ptr<Item>& src,
157c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                                                   StringPool* out_string_pool) {
158c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  if (src == nullptr) {
159c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    return {};
160c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  }
161c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  return std::unique_ptr<Item>(src->Clone(out_string_pool));
162c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski}
163c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
164c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinskistd::vector<DegradeResult> DegradeToManyRule::Degrade(const xml::Element& src_el,
165c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                                                      const xml::Attribute& src_attr,
166c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                                                      StringPool* out_string_pool) const {
167c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  std::vector<DegradeResult> result;
168c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  result.reserve(attrs_.size());
169c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  for (const ReplacementAttr& attr : attrs_) {
170c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski    result.push_back(
171c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski        DegradeResult{xml::Attribute{xml::kSchemaAndroid, attr.name, src_attr.value,
172c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                                     xml::AaptAttribute{attr.attr, attr.id},
173c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                                     CloneIfNotNull(src_attr.compiled_value, out_string_pool)},
174c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski                      FindAttributeSdkLevel(attr.id)});
175c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  }
176c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski  return result;
177c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski}
178c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski
179c744ae8aca97edfb2422598ea620e8219449fa9bAdam Lesinski}  // namespace aapt
180