xref: /aosp_15_r20/frameworks/base/tools/aapt2/link/FeatureFlagsFilter.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
2  * Copyright 2023 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/FeatureFlagsFilter.h"
18 
19 #include <string_view>
20 
21 #include "androidfw/IDiagnostics.h"
22 #include "androidfw/Source.h"
23 #include "util/Util.h"
24 #include "xml/XmlDom.h"
25 #include "xml/XmlUtil.h"
26 
27 using ::aapt::xml::Element;
28 using ::aapt::xml::Node;
29 using ::aapt::xml::NodeCast;
30 
31 namespace aapt {
32 
33 class FlagsVisitor : public xml::Visitor {
34  public:
FlagsVisitor(android::IDiagnostics * diagnostics,const FeatureFlagValues & feature_flag_values,const FeatureFlagsFilterOptions & options)35   explicit FlagsVisitor(android::IDiagnostics* diagnostics,
36                         const FeatureFlagValues& feature_flag_values,
37                         const FeatureFlagsFilterOptions& options)
38       : diagnostics_(diagnostics), feature_flag_values_(feature_flag_values), options_(options) {
39   }
40 
Visit(xml::Element * node)41   void Visit(xml::Element* node) override {
42     std::erase_if(node->children,
43                   [this](std::unique_ptr<xml::Node>& node) { return ShouldRemove(node); });
44     VisitChildren(node);
45   }
46 
HasError() const47   bool HasError() const {
48     return has_error_;
49   }
50 
51  private:
ShouldRemove(std::unique_ptr<xml::Node> & node)52   bool ShouldRemove(std::unique_ptr<xml::Node>& node) {
53     if (const auto* el = NodeCast<Element>(node.get())) {
54       auto* attr = el->FindAttribute(xml::kSchemaAndroid, "featureFlag");
55       if (attr == nullptr) {
56         return false;
57       }
58 
59       bool negated = false;
60       std::string_view flag_name = util::TrimWhitespace(attr->value);
61       if (flag_name.starts_with('!')) {
62         negated = true;
63         flag_name = flag_name.substr(1);
64       }
65 
66       if (auto it = feature_flag_values_.find(flag_name); it != feature_flag_values_.end()) {
67         if (it->second.enabled.has_value()) {
68           if (options_.flags_must_be_readonly && !it->second.read_only) {
69             diagnostics_->Error(android::DiagMessage(node->line_number)
70                                 << "attribute 'android:featureFlag' has flag '" << flag_name
71                                 << "' which must be readonly but is not");
72             has_error_ = true;
73             return false;
74           }
75           if (options_.remove_disabled_elements) {
76             // Remove if flag==true && attr=="!flag" (negated) OR flag==false && attr=="flag"
77             return *it->second.enabled == negated;
78           }
79         } else if (options_.flags_must_have_value) {
80           diagnostics_->Error(android::DiagMessage(node->line_number)
81                               << "attribute 'android:featureFlag' has flag '" << flag_name
82                               << "' without a true/false value from --feature_flags parameter");
83           has_error_ = true;
84           return false;
85         }
86       } else if (options_.fail_on_unrecognized_flags) {
87         diagnostics_->Error(android::DiagMessage(node->line_number)
88                             << "attribute 'android:featureFlag' has flag '" << flag_name
89                             << "' not found in flags from --feature_flags parameter");
90         has_error_ = true;
91         return false;
92       }
93     }
94 
95     return false;
96   }
97 
98   android::IDiagnostics* diagnostics_;
99   const FeatureFlagValues& feature_flag_values_;
100   const FeatureFlagsFilterOptions& options_;
101   bool has_error_ = false;
102 };
103 
Consume(IAaptContext * context,xml::XmlResource * doc)104 bool FeatureFlagsFilter::Consume(IAaptContext* context, xml::XmlResource* doc) {
105   FlagsVisitor visitor(context->GetDiagnostics(), feature_flag_values_, options_);
106   doc->root->Accept(&visitor);
107   return !visitor.HasError();
108 }
109 
110 }  // namespace aapt
111