1 /* 2 * Copyright (C) 2025 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/FlaggedXmlVersioner.h" 18 19 #include "SdkConstants.h" 20 #include "androidfw/Util.h" 21 22 using ::aapt::xml::Element; 23 using ::aapt::xml::NodeCast; 24 25 namespace aapt { 26 27 // An xml visitor that goes through the a doc and removes any elements that are behind non-negated 28 // flags. It also removes the featureFlag attribute from elements behind negated flags. 29 class AllDisabledFlagsVisitor : public xml::Visitor { 30 public: Visit(xml::Element * node)31 void Visit(xml::Element* node) override { 32 std::erase_if(node->children, [this](const std::unique_ptr<xml::Node>& node) { 33 return FixupOrShouldRemove(node); 34 }); 35 VisitChildren(node); 36 } 37 38 private: FixupOrShouldRemove(const std::unique_ptr<xml::Node> & node)39 bool FixupOrShouldRemove(const std::unique_ptr<xml::Node>& node) { 40 if (auto* el = NodeCast<Element>(node.get())) { 41 auto* attr = el->FindAttribute(xml::kSchemaAndroid, xml::kAttrFeatureFlag); 42 if (attr == nullptr) { 43 return false; 44 } 45 46 // This class assumes all flags are disabled so we want to remove any elements behind flags 47 // unless the flag specification is negated. In the negated case we remove the featureFlag 48 // attribute because we have already determined whether we are keeping the element or not. 49 std::string_view flag_name = util::TrimWhitespace(attr->value); 50 if (flag_name.starts_with('!')) { 51 el->RemoveAttribute(xml::kSchemaAndroid, xml::kAttrFeatureFlag); 52 return false; 53 } else { 54 return true; 55 } 56 } 57 58 return false; 59 } 60 }; 61 Process(IAaptContext * context,xml::XmlResource * doc)62std::vector<std::unique_ptr<xml::XmlResource>> FlaggedXmlVersioner::Process(IAaptContext* context, 63 xml::XmlResource* doc) { 64 std::vector<std::unique_ptr<xml::XmlResource>> docs; 65 if (!doc->file.uses_readwrite_feature_flags) { 66 docs.push_back(doc->Clone()); 67 } else if ((static_cast<ApiVersion>(doc->file.config.sdkVersion) >= SDK_BAKLAVA) || 68 (static_cast<ApiVersion>(context->GetMinSdkVersion()) >= SDK_BAKLAVA)) { 69 // Support for read/write flags was added in baklava so if the doc will only get used on 70 // baklava or later we can just return the original doc. 71 docs.push_back(doc->Clone()); 72 } else { 73 auto preBaklavaVersion = doc->Clone(); 74 AllDisabledFlagsVisitor visitor; 75 preBaklavaVersion->root->Accept(&visitor); 76 docs.push_back(std::move(preBaklavaVersion)); 77 78 auto baklavaVersion = doc->Clone(); 79 baklavaVersion->file.config.sdkVersion = SDK_BAKLAVA; 80 docs.push_back(std::move(baklavaVersion)); 81 } 82 return docs; 83 } 84 85 } // namespace aapt