AAPT2: add flag for forcing visibility level

Test: manual
Bug: 72735798
Change-Id: I29480e66384dd2da27e17ab454ac1fe8a033ee3e
diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp
index 1b6f882..2260ba4 100644
--- a/tools/aapt2/ResourceParser.cpp
+++ b/tools/aapt2/ResourceParser.cpp
@@ -447,6 +447,9 @@
     parsed_resource.config = config_;
     parsed_resource.source = source_.WithLine(parser->line_number());
     parsed_resource.comment = std::move(comment);
+    if (options_.visibility) {
+      parsed_resource.visibility_level = options_.visibility.value();
+    }
 
     // Extract the product name if it exists.
     if (Maybe<StringPiece> maybe_product = xml::FindNonEmptyAttribute(parser, "product")) {
@@ -811,6 +814,12 @@
 }
 
 bool ResourceParser::ParsePublic(xml::XmlPullParser* parser, ParsedResource* out_resource) {
+  if (options_.visibility) {
+    diag_->Error(DiagMessage(out_resource->source)
+                 << "<public> tag not allowed with --visibility flag");
+    return false;
+  }
+
   if (out_resource->config != ConfigDescription::DefaultConfig()) {
     diag_->Warn(DiagMessage(out_resource->source)
                 << "ignoring configuration '" << out_resource->config << "' for <public> tag");
@@ -853,6 +862,12 @@
 }
 
 bool ResourceParser::ParsePublicGroup(xml::XmlPullParser* parser, ParsedResource* out_resource) {
+  if (options_.visibility) {
+    diag_->Error(DiagMessage(out_resource->source)
+                 << "<public-group> tag not allowed with --visibility flag");
+    return false;
+  }
+
   if (out_resource->config != ConfigDescription::DefaultConfig()) {
     diag_->Warn(DiagMessage(out_resource->source)
                 << "ignoring configuration '" << out_resource->config
@@ -974,6 +989,11 @@
 }
 
 bool ResourceParser::ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource) {
+  if (options_.visibility) {
+    diag_->Error(DiagMessage(out_resource->source)
+                 << "<java-symbol> and <symbol> tags not allowed with --visibility flag");
+    return false;
+  }
   if (out_resource->config != ConfigDescription::DefaultConfig()) {
     diag_->Warn(DiagMessage(out_resource->source)
                 << "ignoring configuration '" << out_resource->config << "' for <"
@@ -1045,6 +1065,9 @@
       child_resource.name.entry = maybe_name.value().to_string();
       child_resource.source = item_source;
       child_resource.overlayable = true;
+      if (options_.visibility) {
+        child_resource.visibility_level = options_.visibility.value();
+      }
       out_resource->child_resources.push_back(std::move(child_resource));
 
       xml::XmlPullParser::SkipCurrentElement(parser);
@@ -1187,6 +1210,9 @@
         child_resource.name = symbol.symbol.name.value();
         child_resource.source = item_source;
         child_resource.value = util::make_unique<Id>();
+        if (options_.visibility) {
+          child_resource.visibility_level = options_.visibility.value();
+        }
         out_resource->child_resources.push_back(std::move(child_resource));
 
         symbol.symbol.SetComment(std::move(comment));
@@ -1564,6 +1590,9 @@
       child_resource.name = child_ref.name.value();
       child_resource.source = item_source;
       child_resource.comment = std::move(comment);
+      if (options_.visibility) {
+        child_resource.visibility_level = options_.visibility.value();
+      }
 
       if (!ParseAttrImpl(parser, &child_resource, true)) {
         error = true;
diff --git a/tools/aapt2/ResourceParser.h b/tools/aapt2/ResourceParser.h
index fb9dbd0..68130c2 100644
--- a/tools/aapt2/ResourceParser.h
+++ b/tools/aapt2/ResourceParser.h
@@ -45,6 +45,10 @@
    * warnings.
    */
   bool error_on_positional_arguments = true;
+
+  // If visibility was forced, we need to use it when creating a new resource and also error if we
+  // try to parse the <public>, <public-group>, <java-symbol> or <symbol> tags.
+  Maybe<Visibility::Level> visibility;
 };
 
 /*
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 06e84ef..6ba9d44 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -114,6 +114,7 @@
   std::string output_path;
   Maybe<std::string> res_dir;
   Maybe<std::string> generate_text_symbols_path;
+  Maybe<Visibility::Level> visibility;
   bool pseudolocalize = false;
   bool no_png_crunch = false;
   bool legacy_mode = false;
@@ -216,6 +217,10 @@
     // If the filename includes donottranslate, then the default translatable is false.
     parser_options.translatable = path_data.name.find("donottranslate") == std::string::npos;
 
+    // If visibility was forced, we need to use it when creating a new resource and also error if
+    // we try to parse the <public>, <public-group>, <java-symbol> or <symbol> tags.
+    parser_options.visibility = options.visibility;
+
     ResourceParser res_parser(context->GetDiagnostics(), &table, path_data.source, path_data.config,
                               parser_options);
     if (!res_parser.Parse(&xml_parser)) {
@@ -309,6 +314,8 @@
             if (!entry->values.empty()) {
               auto styleable = static_cast<const Styleable*>(entry->values.front()->value.get());
               for (const auto& attr : styleable->entries) {
+                // The visibility of the children under the styleable does not matter as they are
+                // nested under their parent and use its visibility.
                 r_txt_printer.Print("default int styleable ");
                 r_txt_printer.Print(entry->name);
                 r_txt_printer.Print("_");
@@ -694,6 +701,7 @@
   CompileOptions options;
 
   bool verbose = false;
+  Maybe<std::string> visibility;
   Flags flags =
       Flags()
           .RequiredFlag("-o", "Output path", &options.output_path)
@@ -709,13 +717,32 @@
           .OptionalSwitch("--no-crunch", "Disables PNG processing", &options.no_png_crunch)
           .OptionalSwitch("--legacy", "Treat errors that used to be valid in AAPT as warnings",
                           &options.legacy_mode)
-          .OptionalSwitch("-v", "Enables verbose logging", &verbose);
+          .OptionalSwitch("-v", "Enables verbose logging", &verbose)
+          .OptionalFlag("--visibility",
+                        "Sets the visibility of the compiled resources to the specified\n"
+                        "level. Accepted levels: public, private, default",
+                        &visibility);
   if (!flags.Parse("aapt2 compile", args, &std::cerr)) {
     return 1;
   }
 
   context.SetVerbose(verbose);
 
+  if (visibility) {
+    if (visibility.value() == "public") {
+      options.visibility = Visibility::Level::kPublic;
+    } else if (visibility.value() == "private") {
+      options.visibility = Visibility::Level::kPrivate;
+    } else if (visibility.value() == "default") {
+      options.visibility = Visibility::Level::kUndefined;
+    } else {
+      context.GetDiagnostics()->Error(
+          DiagMessage() << "Unrecognized visibility level passes to --visibility: '"
+                        << visibility.value() << "'. Accepted levels: public, private, default");
+      return 1;
+    }
+  }
+
   std::unique_ptr<IArchiveWriter> archive_writer;
 
   std::vector<ResourcePathData> input_data;