Add support for flag in resource directory names

This only applies it to the xml files with the top level element of
resources. Other file support will be in a later CL.

Test: Automated
Bug: 329436914
Flag: EXEMPT Aconfig not supported on host tools
Change-Id: I5e1e341e9de61073d05d9098b1b8b836025910b3
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 773edc3..a2383ac 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -546,15 +546,25 @@
       {"symbol", std::mem_fn(&ResourceParser::ParseSymbol)},
   });
 
-  std::string resource_type = parser->element_name();
-  out_resource->flag = GetFlag(parser);
-  std::string error;
-  auto flag_status = GetFlagStatus(out_resource->flag, options_.feature_flag_values, &error);
-  if (flag_status) {
-    out_resource->flag_status = flag_status.value();
-  } else {
-    diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << error);
-    return false;
+  std::string_view resource_type = parser->element_name();
+  if (auto flag = GetFlag(parser)) {
+    if (options_.flag) {
+      diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
+                   << "Resource flag are not allowed both in the path and in the file");
+      return false;
+    }
+    out_resource->flag = std::move(flag);
+    std::string error;
+    auto flag_status = GetFlagStatus(out_resource->flag, options_.feature_flag_values, &error);
+    if (flag_status) {
+      out_resource->flag_status = flag_status.value();
+    } else {
+      diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number())) << error);
+      return false;
+    }
+  } else if (options_.flag) {
+    out_resource->flag = options_.flag;
+    out_resource->flag_status = options_.flag_status;
   }
 
   // The value format accepted for this resource.
@@ -571,7 +581,7 @@
 
     // Items have their type encoded in the type attribute.
     if (std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
-      resource_type = std::string(maybe_type.value());
+      resource_type = maybe_type.value();
     } else {
       diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
                    << "<item> must have a 'type' attribute");
@@ -594,7 +604,7 @@
 
     // Bags have their type encoded in the type attribute.
     if (std::optional<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
-      resource_type = std::string(maybe_type.value());
+      resource_type = maybe_type.value();
     } else {
       diag_->Error(android::DiagMessage(source_.WithLine(parser->line_number()))
                    << "<bag> must have a 'type' attribute");
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index a789d3e..4ad334c 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -57,6 +57,11 @@
   std::optional<Visibility::Level> visibility;
 
   FeatureFlagValues feature_flag_values;
+
+  // The flag that should be applied to all resources parsed
+  std::optional<FeatureFlagAttribute> flag;
+
+  FlagStatus flag_status = FlagStatus::NoFlag;
 };
 
 struct FlattenedXmlSubTree {
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 2a978a5..cd8c187 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -67,6 +67,7 @@
   std::string resource_dir;
   std::string name;
   std::string extension;
+  std::string flag_name;
 
   // Original config str. We keep this because when we parse the config, we may add on
   // version qualifiers. We want to preserve the original input so the output is easily
@@ -81,6 +82,22 @@
                                                                std::string* out_error,
                                                                const CompileOptions& options) {
   std::vector<std::string> parts = util::Split(path, dir_sep);
+
+  std::string flag_name;
+  // Check for a flag
+  for (auto iter = parts.begin(); iter != parts.end();) {
+    if (iter->starts_with("flag(") && iter->ends_with(")")) {
+      if (!flag_name.empty()) {
+        if (out_error) *out_error = "resource path cannot contain more than one flag directory";
+        return {};
+      }
+      flag_name = iter->substr(5, iter->size() - 6);
+      iter = parts.erase(iter);
+    } else {
+      ++iter;
+    }
+  }
+
   if (parts.size() < 2) {
     if (out_error) *out_error = "bad resource path";
     return {};
@@ -131,6 +148,7 @@
                           std::string(dir_str),
                           std::string(name),
                           std::string(extension),
+                          std::move(flag_name),
                           std::string(config_str),
                           config};
 }
@@ -142,6 +160,9 @@
     name << "-" << data.config_str;
   }
   name << "_" << data.name;
+  if (!data.flag_name.empty()) {
+    name << ".(" << data.flag_name << ")";
+  }
   if (!data.extension.empty()) {
     name << "." << data.extension;
   }
@@ -163,7 +184,6 @@
                                        << "failed to open file: " << fin->GetError());
       return false;
     }
-
     // Parse the values file from XML.
     xml::XmlPullParser xml_parser(fin.get());
 
@@ -177,6 +197,27 @@
     // we try to parse the <public>, <public-group>, <java-symbol> or <symbol> tags.
     parser_options.visibility = options.visibility;
 
+    if (!path_data.flag_name.empty()) {
+      FeatureFlagAttribute flag;
+      const auto& name = path_data.flag_name;
+      if (name.starts_with('!')) {
+        flag.negated = true;
+        flag.name = name.substr(1);
+      } else {
+        flag.name = name;
+      }
+      parser_options.flag = flag;
+
+      std::string error;
+      auto flag_status = GetFlagStatus(flag, options.feature_flag_values, &error);
+      if (flag_status) {
+        parser_options.flag_status = std::move(flag_status.value());
+      } else {
+        context->GetDiagnostics()->Error(android::DiagMessage(path_data.source) << error);
+        return false;
+      }
+    }
+
     ResourceParser res_parser(context->GetDiagnostics(), &table, path_data.source, path_data.config,
         parser_options);
     if (!res_parser.Parse(&xml_parser)) {
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp b/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp
index c456e5c..432b84e 100644
--- a/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/Android.bp
@@ -31,9 +31,17 @@
         "res/values/ints.xml",
         "res/values/strings.xml",
         "res/layout/layout1.xml",
+        "res/flag(test.package.falseFlag)/values/bools.xml",
+        "res/values/flag(test.package.trueFlag)/bools.xml",
+        "res/values/flag(!test.package.trueFlag)/bools.xml",
+        "res/values/flag(!test.package.falseFlag)/bools.xml",
     ],
     out: [
         "values_bools.arsc.flat",
+        "values_bools.(test.package.falseFlag).arsc.flat",
+        "values_bools.(test.package.trueFlag).arsc.flat",
+        "values_bools.(!test.package.falseFlag).arsc.flat",
+        "values_bools.(!test.package.trueFlag).arsc.flat",
         "values_bools2.arsc.flat",
         "values_ints.arsc.flat",
         "values_strings.arsc.flat",
diff --git "a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/flag\050test.package.falseFlag\051/values/bools.xml" "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/flag\050test.package.falseFlag\051/values/bools.xml"
new file mode 100644
index 0000000..c46c4d4
--- /dev/null
+++ "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/flag\050test.package.falseFlag\051/values/bools.xml"
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <bool name="bool7">false</bool>
+</resources>
\ No newline at end of file
diff --git a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools.xml b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools.xml
index 7837e17..35975ed 100644
--- a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools.xml
+++ b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/bools.xml
@@ -15,4 +15,9 @@
 
     <bool name="bool6">true</bool>
     <bool name="bool6" android:featureFlag="!test.package.trueFlag">false</bool>
+
+    <bool name="bool7">true</bool>
+    <bool name="bool8">false</bool>
+    <bool name="bool9">true</bool>
+    <bool name="bool10">false</bool>
 </resources>
\ No newline at end of file
diff --git "a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/flag\050\041test.package.falseFlag\051/bools.xml" "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/flag\050\041test.package.falseFlag\051/bools.xml"
new file mode 100644
index 0000000..a63749c
--- /dev/null
+++ "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/flag\050\041test.package.falseFlag\051/bools.xml"
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <bool name="bool10">true</bool>
+</resources>
\ No newline at end of file
diff --git "a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/flag\050\041test.package.trueFlag\051/bools.xml" "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/flag\050\041test.package.trueFlag\051/bools.xml"
new file mode 100644
index 0000000..bb5526e
--- /dev/null
+++ "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/flag\050\041test.package.trueFlag\051/bools.xml"
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <bool name="bool9">false</bool>
+</resources>
\ No newline at end of file
diff --git "a/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/flag\050test.package.trueFlag\051/bools.xml" "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/flag\050test.package.trueFlag\051/bools.xml"
new file mode 100644
index 0000000..eba780e
--- /dev/null
+++ "b/tools/aapt2/integration-tests/FlaggedResourcesTest/res/values/flag\050test.package.trueFlag\051/bools.xml"
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <bool name="bool8">true</bool>
+</resources>
\ No newline at end of file