• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(std::string(flag_name));
67           it != feature_flag_values_.end()) {
68         if (it->second.has_value()) {
69           if (options_.remove_disabled_elements) {
70             // Remove if flag==true && attr=="!flag" (negated) OR flag==false && attr=="flag"
71             return *it->second == negated;
72           }
73         } else if (options_.flags_must_have_value) {
74           diagnostics_->Error(android::DiagMessage(node->line_number)
75                               << "attribute 'android:featureFlag' has flag '" << flag_name
76                               << "' without a true/false value from --feature_flags parameter");
77           has_error_ = true;
78           return false;
79         }
80       } else if (options_.fail_on_unrecognized_flags) {
81         diagnostics_->Error(android::DiagMessage(node->line_number)
82                             << "attribute 'android:featureFlag' has flag '" << flag_name
83                             << "' not found in flags from --feature_flags parameter");
84         has_error_ = true;
85         return false;
86       }
87     }
88 
89     return false;
90   }
91 
92   android::IDiagnostics* diagnostics_;
93   const FeatureFlagValues& feature_flag_values_;
94   const FeatureFlagsFilterOptions& options_;
95   bool has_error_ = false;
96 };
97 
Consume(IAaptContext * context,xml::XmlResource * doc)98 bool FeatureFlagsFilter::Consume(IAaptContext* context, xml::XmlResource* doc) {
99   FlagsVisitor visitor(context->GetDiagnostics(), feature_flag_values_, options_);
100   doc->root->Accept(&visitor);
101   return !visitor.HasError();
102 }
103 
104 }  // namespace aapt
105