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 1106b372991296c9f2bd6f8f8847dcd23d50970d06dAdam Lesinski cloned_doc->root = doc->root->CloneElement([&](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