Add support for preloaded classes blacklist in profman

Profman will now filter out any blacklisted class for the list
of preloaded classes.

Test: gtest
Bug: 152574358
Change-Id: I755176c5d5afcd821c19152fd2ea941f1b80eaa8
diff --git a/profman/boot_image_profile.cc b/profman/boot_image_profile.cc
index fdd0a29..d4e7f2b 100644
--- a/profman/boot_image_profile.cc
+++ b/profman/boot_image_profile.cc
@@ -185,10 +185,13 @@
 
 // Returns true iff a class with the given metada should be included in the list of
 // prelaoded classes.
-static bool IncludeInPreloadedClasses(uint32_t max_aggregation_count,
+static bool IncludeInPreloadedClasses(const std::string& class_name,
+                                      uint32_t max_aggregation_count,
                                       const FlattenProfileData::ItemMetadata& metadata,
                                       const BootImageOptions& options) {
-  return IncludeItemInProfile(
+  bool blacklisted = options.preloaded_classes_blacklist.find(class_name) !=
+      options.preloaded_classes_blacklist.end();
+  return !blacklisted && IncludeItemInProfile(
       max_aggregation_count, options.preloaded_class_threshold, metadata, options);
 }
 
@@ -235,11 +238,13 @@
             options)) {
       profile_classes.Put(BootImageRepresentation(it.first), it.second);
     }
+    std::string preloaded_class_representation = PreloadedClassesRepresentation(it.first);
     if (generate_preloaded_classes && IncludeInPreloadedClasses(
+            preloaded_class_representation,
             flattenData->GetMaxAggregationForClasses(),
             metadata,
             options)) {
-      preloaded_classes.Put(PreloadedClassesRepresentation(it.first), it.second);
+      preloaded_classes.Put(preloaded_class_representation, it.second);
     }
   }
 
diff --git a/profman/boot_image_profile.h b/profman/boot_image_profile.h
index b3b4ba9..88a2a4c 100644
--- a/profman/boot_image_profile.h
+++ b/profman/boot_image_profile.h
@@ -20,6 +20,7 @@
 #include <limits>
 #include <memory>
 #include <vector>
+#include <set>
 
 #include "base/safe_map.h"
 #include "dex/dex_file.h"
@@ -63,6 +64,9 @@
   // Should be use only for debugging as it will add additional elements to the text output
   // that are not compatible with the default profile format.
   bool append_package_use_list = false;
+
+  // The set of classes that should not be preloaded in Zygote
+  std::set<std::string> preloaded_classes_blacklist;
 };
 
 // Generate a boot image profile according to the specified options.
diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc
index c4d0d39..4f42eb4 100644
--- a/profman/profile_assistant_test.cc
+++ b/profman/profile_assistant_test.cc
@@ -763,6 +763,8 @@
       "Ljava/lang/Object;->toString()Ljava/lang/String;";
   // Method used by a special package which will get a different threshold;
   const std::string kUncommonSpecialPackageMethod = "Ljava/lang/Object;->hashCode()I";
+  // Blacklisted class
+  const std::string kPreloadedBlacklistedClass = "Ljava/lang/Thread;";
 
   // Thresholds for this test.
   static const size_t kDirtyThreshold = 100;
@@ -780,30 +782,42 @@
       "{dex1}H" + kCommonHotMethod,
       "{dex1}P" + kStartupMethodForUpgrade,
       "{dex1}" + kUncommonDirtyClass,
+      "{dex1}" + kPreloadedBlacklistedClass,
 
       "{dex2}" + kCleanClass,
       "{dex2}" + kDirtyClass,
       "{dex2}P" + kCommonHotMethod,
       "{dex2}P" + kStartupMethodForUpgrade,
       "{dex2}" + kUncommonDirtyClass,
+      "{dex2}" + kPreloadedBlacklistedClass,
 
       "{dex3}P" + kUncommonMethod,
       "{dex3}PS" + kStartupMethodForUpgrade,
       "{dex3}S" + kCommonHotMethod,
       "{dex3}S" + kSpecialPackageStartupMethod,
       "{dex3}" + kDirtyClass,
+      "{dex3}" + kPreloadedBlacklistedClass,
 
       "{dex4}" + kDirtyClass,
       "{dex4}P" + kCommonHotMethod,
       "{dex4}S" + kSpecialPackageStartupMethod,
-      "{dex4}P" + kUncommonSpecialPackageMethod
+      "{dex4}P" + kUncommonSpecialPackageMethod,
+      "{dex4}" + kPreloadedBlacklistedClass,
   };
   std::string input_file_contents = JoinProfileLines(input_data);
 
+  ScratchFile preloaded_class_blacklist;
+  std::string blacklist_content = DescriptorToDot(kPreloadedBlacklistedClass.c_str());
+  EXPECT_TRUE(preloaded_class_blacklist.GetFile()->WriteFully(
+      blacklist_content.c_str(), blacklist_content.length()));
+
+  EXPECT_EQ(0, preloaded_class_blacklist.GetFile()->Flush());
+  EXPECT_TRUE(preloaded_class_blacklist.GetFile()->ResetOffset());
   // Expected data
   std::vector<std::string> expected_data = {
       kCleanClass,
       kDirtyClass,
+      kPreloadedBlacklistedClass,
       "HSP" + kCommonHotMethod,
       "HS" + kSpecialPackageStartupMethod,
       "HSP" + kStartupMethodForUpgrade
@@ -840,7 +854,7 @@
   args.push_back("--out-preloaded-classes-path=" + out_preloaded_classes.GetFilename());
   args.push_back("--apk=" + core_dex);
   args.push_back("--dex-location=" + core_dex);
-
+  args.push_back("--preloaded-classes-blacklist=" + preloaded_class_blacklist.GetFilename());
 
   std::string error;
   ASSERT_EQ(ExecAndReturnCode(args, &error), 0) << error;
diff --git a/profman/profman.cc b/profman/profman.cc
index 8d46ad4..eea83cc 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -149,23 +149,25 @@
   UsageError("");
   UsageError("  --generate-boot-image-profile: Generate a boot image profile based on input");
   UsageError("      profiles. Requires passing in dex files to inspect properties of classes.");
-  UsageError("  --method-threshold=percentage between 0 and 100"
-             "      what threshold to apply to the methods when deciding whether or not to"
-             "      include it in the final profile.");
-  UsageError("  --class-threshold=percentage between 0 and 100"
-             "      what threshold to apply to the classes when deciding whether or not to"
-             "      include it in the final profile.");
-  UsageError("  --clean-class-threshold=percentage between 0 and 100"
-             "      what threshold to apply to the clean classes when deciding whether or not to"
-             "      include it in the final profile.");
-  UsageError("  --preloaded-class-threshold=percentage between 0 and 100"
-            "      what threshold to apply to the classes when deciding whether or not to"
-            "       include it in the final preloaded classes.");
-  UsageError("  --upgrade-startup-to-hot=true|false:"
-             "      whether or not to upgrade startup methods to hot");
-  UsageError("  --special-package=pkg_name:percentage between 0 and 100"
-             "      what threshold to apply to the methods/classes that are used by the given"
-             "      package when deciding whether or not to include it in the final profile.");
+  UsageError("  --method-threshold=percentage between 0 and 100");
+  UsageError("      what threshold to apply to the methods when deciding whether or not to");
+  UsageError("      include it in the final profile.");
+  UsageError("  --class-threshold=percentage between 0 and 100");
+  UsageError("      what threshold to apply to the classes when deciding whether or not to");
+  UsageError("      include it in the final profile.");
+  UsageError("  --clean-class-threshold=percentage between 0 and 100");
+  UsageError("      what threshold to apply to the clean classes when deciding whether or not to");
+  UsageError("      include it in the final profile.");
+  UsageError("  --preloaded-class-threshold=percentage between 0 and 100");
+  UsageError("      what threshold to apply to the classes when deciding whether or not to");
+  UsageError("      include it in the final preloaded classes.");
+  UsageError("  --preloaded-classes-blacklist=file");
+  UsageError("      a file listing the classes that should not be preloaded in Zygote");
+  UsageError("  --upgrade-startup-to-hot=true|false:");
+  UsageError("      whether or not to upgrade startup methods to hot");
+  UsageError("  --special-package=pkg_name:percentage between 0 and 100");
+  UsageError("      what threshold to apply to the methods/classes that are used by the given");
+  UsageError("      package when deciding whether or not to include it in the final profile.");
   UsageError("  --debug-append-uses=bool: whether or not to append package use as debug info.");
   UsageError("  --out-profile-path=path: boot image profile output path");
   UsageError("  --out-preloaded-classes-path=path: preloaded classes output path");
@@ -343,6 +345,15 @@
                         &boot_image_options_.preloaded_class_threshold,
                         0u,
                         100u);
+      } else if (StartsWith(option, "--preloaded-classes-blacklist=")) {
+        std::string preloaded_classes_blacklist =
+            std::string(option.substr(strlen("--preloaded-classes-blacklist=")));
+        // Read the user-specified list of methods.
+        std::unique_ptr<std::set<std::string>>
+            blacklist(ReadCommentedInputFromFile<std::set<std::string>>(
+                preloaded_classes_blacklist.c_str(), nullptr));  // No post-processing.
+        boot_image_options_.preloaded_classes_blacklist.insert(
+            blacklist->begin(), blacklist->end());
       } else if (StartsWith(option, "--upgrade-startup-to-hot=")) {
         ParseBoolOption(raw_option,
                         "--upgrade-startup-to-hot=",