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