• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)62 std::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