[aapt2] Ignore annotations in flag/enum attribute values

Bug: 258855262
Test: atest aapt2_tests
Change-Id: If67a73b02da719b049c19cee561a2f9b8945956b
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
index 8c644cf..a7f6f55 100644
--- a/tools/aapt2/java/AnnotationProcessor.cpp
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -64,29 +64,31 @@
     {"@FlaggedApi", AnnotationRule::kFlaggedApi, "@android.annotation.FlaggedApi", true},
 }};
 
-void AnnotationProcessor::AppendCommentLine(std::string comment) {
+void AnnotationProcessor::AppendCommentLine(std::string comment, bool add_api_annotations) {
   static constexpr std::string_view sDeprecated = "@deprecated";
 
-  // Treat deprecated specially, since we don't remove it from the source comment.
-  if (comment.find(sDeprecated) != std::string::npos) {
-    annotation_parameter_map_[AnnotationRule::kDeprecated] = "";
-  }
+  if (add_api_annotations) {
+    // Treat deprecated specially, since we don't remove it from the source comment.
+    if (comment.find(sDeprecated) != std::string::npos) {
+      annotation_parameter_map_[AnnotationRule::kDeprecated] = "";
+    }
 
-  for (const AnnotationRule& rule : sAnnotationRules) {
-    std::string::size_type idx = comment.find(rule.doc_str.data());
-    if (idx != std::string::npos) {
-      // Captures all parameters associated with the specified annotation rule
-      // by matching the first pair of parentheses after the rule.
-      std::regex re(std::string(rule.doc_str).append(R"(\s*\((.+)\))"));
-      std::smatch match_result;
-      const bool is_match = std::regex_search(comment, match_result, re);
-      if (is_match && rule.preserve_params) {
-        annotation_parameter_map_[rule.bit_mask] = match_result[1].str();
-        comment.erase(comment.begin() + match_result.position(),
-                      comment.begin() + match_result.position() + match_result.length());
-      } else {
-        annotation_parameter_map_[rule.bit_mask] = "";
-        comment.erase(comment.begin() + idx, comment.begin() + idx + rule.doc_str.size());
+    for (const AnnotationRule& rule : sAnnotationRules) {
+      std::string::size_type idx = comment.find(rule.doc_str.data());
+      if (idx != std::string::npos) {
+        // Captures all parameters associated with the specified annotation rule
+        // by matching the first pair of parentheses after the rule.
+        std::regex re(std::string(rule.doc_str).append(R"(\s*\((.+)\))"));
+        std::smatch match_result;
+        const bool is_match = std::regex_search(comment, match_result, re);
+        if (is_match && rule.preserve_params) {
+          annotation_parameter_map_[rule.bit_mask] = match_result[1].str();
+          comment.erase(comment.begin() + match_result.position(),
+                        comment.begin() + match_result.position() + match_result.length());
+        } else {
+          annotation_parameter_map_[rule.bit_mask] = "";
+          comment.erase(comment.begin() + idx, comment.begin() + idx + rule.doc_str.size());
+        }
       }
     }
   }
@@ -109,12 +111,12 @@
   comment_ << "\n * " << std::move(comment);
 }
 
-void AnnotationProcessor::AppendComment(StringPiece comment) {
+void AnnotationProcessor::AppendComment(StringPiece comment, bool add_api_annotations) {
   // We need to process line by line to clean-up whitespace and append prefixes.
   for (StringPiece line : util::Tokenize(comment, '\n')) {
     line = util::TrimWhitespace(line);
     if (!line.empty()) {
-      AppendCommentLine(std::string(line));
+      AppendCommentLine(std::string(line), add_api_annotations);
     }
   }
 }
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
index db3437e..2217ab3 100644
--- a/tools/aapt2/java/AnnotationProcessor.h
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -60,7 +60,10 @@
 
   // Adds more comments. Resources can have value definitions for various configurations, and
   // each of the definitions may have comments that need to be processed.
-  void AppendComment(android::StringPiece comment);
+  //
+  // If add_api_annotations is false, annotations found in the comment (e.g., "@SystemApi")
+  // will NOT be converted to Java annotations.
+  void AppendComment(android::StringPiece comment, bool add_api_annotations = true);
 
   void AppendNewLine();
 
@@ -73,7 +76,7 @@
   bool has_comments_ = false;
   std::unordered_map<uint32_t, std::string> annotation_parameter_map_;
 
-  void AppendCommentLine(std::string line);
+  void AppendCommentLine(std::string line, bool add_api_annotations);
 };
 
 }  // namespace aapt
diff --git a/tools/aapt2/java/AnnotationProcessor_test.cpp b/tools/aapt2/java/AnnotationProcessor_test.cpp
index e98e96b..e5eee34 100644
--- a/tools/aapt2/java/AnnotationProcessor_test.cpp
+++ b/tools/aapt2/java/AnnotationProcessor_test.cpp
@@ -136,7 +136,28 @@
   EXPECT_THAT(annotations, HasSubstr("This is a system API"));
 }
 
-TEST(AnnotationProcessor, ExtractsFirstSentence) {
+TEST(AnnotationProcessorTest, DoNotAddApiAnnotations) {
+  AnnotationProcessor processor;
+  processor.AppendComment(
+      "@SystemApi This is a system API\n"
+      "@FlaggedApi This is a flagged API\n"
+      "@TestApi This is a test API\n"
+      "@deprecated Deprecate me\n", /*add_api_annotations=*/
+      false);
+
+  std::string annotations;
+  StringOutputStream out(&annotations);
+  Printer printer(&out);
+  processor.Print(&printer);
+  out.Flush();
+
+  EXPECT_THAT(annotations, Not(HasSubstr("@android.annotation.SystemApi")));
+  EXPECT_THAT(annotations, Not(HasSubstr("@android.annotation.FlaggedApi")));
+  EXPECT_THAT(annotations, Not(HasSubstr("@android.annotation.TestApi")));
+  EXPECT_THAT(annotations, Not(HasSubstr("@Deprecated")));
+}
+
+TEST(AnnotationProcessorTest, ExtractsFirstSentence) {
   EXPECT_THAT(AnnotationProcessor::ExtractFirstSentence("This is the only sentence"),
               Eq("This is the only sentence"));
   EXPECT_THAT(AnnotationProcessor::ExtractFirstSentence(
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 58f6564..6e73b01 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -180,7 +180,10 @@
            << "<td>" << std::hex << symbol.value << std::dec << "</td>"
            << "<td>" << util::TrimWhitespace(symbol.symbol.GetComment())
            << "</td></tr>";
-      processor->AppendComment(line.str());
+      // add_api_annotations is false since we don't want any annotations
+      // (e.g., "@deprecated")/ found in the enum/flag values to be propagated
+      // up to the attribute.
+      processor->AppendComment(line.str(), /*add_api_annotations=*/false);
     }
     processor->AppendComment("</table>");
   }
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 40395ed..bca9f4b 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -324,7 +324,58 @@
   EXPECT_THAT(output, HasSubstr(expected_text));
 }
 
-TEST(JavaClassGeneratorTest, CommentsForEnumAndFlagAttributesArePresent) {}
+TEST(JavaClassGeneratorTest, CommentsForEnumAndFlagAttributesArePresent) {
+  std::unique_ptr<Attribute> flagAttr =
+      test::AttributeBuilder()
+          .SetTypeMask(android::ResTable_map::TYPE_FLAGS)
+          .SetComment("Flag attribute")
+          .AddItemWithComment("flagOne", 0x01, "Flag comment 1")
+          .AddItemWithComment("flagTwo", 0x02, "@deprecated Flag comment 2")
+          .Build();
+  std::unique_ptr<Attribute> enumAttr =
+      test::AttributeBuilder()
+          .SetTypeMask(android::ResTable_map::TYPE_ENUM)
+          .SetComment("Enum attribute")
+          .AddItemWithComment("enumOne", 0x01, "@TestApi Enum comment 1")
+          .AddItemWithComment("enumTwo", 0x02, "Enum comment 2")
+          .Build();
+
+  std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+                                             .AddValue("android:attr/one", std::move(flagAttr))
+                                             .AddValue("android:attr/two", std::move(enumAttr))
+                                             .Build();
+
+  std::unique_ptr<IAaptContext> context =
+      test::ContextBuilder()
+          .AddSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
+          .SetNameManglerPolicy(NameManglerPolicy{"android"})
+          .Build();
+  JavaClassGeneratorOptions options;
+  options.use_final = false;
+  JavaClassGenerator generator(context.get(), table.get(), options);
+
+  std::string output;
+  StringOutputStream out(&output);
+  ASSERT_TRUE(generator.Generate("android", &out));
+  out.Flush();
+
+  // Special annotations from the enum/flag values should NOT generate
+  // annotations for the attribute value.
+  EXPECT_THAT(output, Not(HasSubstr("@Deprecated")));
+  EXPECT_THAT(output, Not(HasSubstr("@android.annotation.TestApi")));
+
+  EXPECT_THAT(output, HasSubstr("Flag attribute"));
+  EXPECT_THAT(output, HasSubstr("flagOne"));
+  EXPECT_THAT(output, HasSubstr("Flag comment 1"));
+  EXPECT_THAT(output, HasSubstr("flagTwo"));
+  EXPECT_THAT(output, HasSubstr("@deprecated Flag comment 2"));
+
+  EXPECT_THAT(output, HasSubstr("Enum attribute"));
+  EXPECT_THAT(output, HasSubstr("enumOne"));
+  EXPECT_THAT(output, HasSubstr("@TestApi Enum comment 1"));
+  EXPECT_THAT(output, HasSubstr("enumTwo"));
+  EXPECT_THAT(output, HasSubstr("Enum comment 2"));
+}
 
 TEST(JavaClassGeneratorTest, CommentsForStyleablesAndNestedAttributesArePresent) {
   Attribute attr;