Support FDs for class loader context dex files

When compiling secondary dex files, dex2oat/dexoptanalyzer must open
all dex files in the given class loader context. However, these tools
do not have the SELinux permission to open app data files and instead
rely on installd to open them and pass file descriptors via command
line arguments.

This patch extends ClassLoaderContext::OpenDexFiles to support opening
dex files from a provided list of FDs, assuming the order corresponds
to the flattened class loader context. FDs can be passed to dex2oat/
dexoptanalyzer using a new '--class-loader-context-fds=' command line
argument. The format is a colon-separated list of integers.

dexoptanalyzer is also extended with a separate mode in which
dexopt-needed analysis is not performed, only the class loader context
is flattened and list of its dex files is printed to standard output
as a colon-separated list of paths. This mode is enabled with
'--flatten-class-loader-context' and is used by installd to obtain a
list of files it should open for dex2oat/dexoptanalyzer.

Bug: 126674985
Test: atest installd_dexopt_test
Change-Id: I46a671c90d14ad8615508c106a88ac1ee8a4ef28
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index d04fa4e..e09b391 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -460,7 +460,7 @@
   UsageError("      --dex-file=src.dex then dex2oat will setup a PathClassLoader with classpath ");
   UsageError("      'lib1.dex:src.dex' and set its parent to a DelegateLastClassLoader with ");
   UsageError("      classpath 'lib2.dex'.");
-  UsageError("      ");
+  UsageError("");
   UsageError("      Note that the compiler will be tolerant if the source dex files specified");
   UsageError("      with --dex-file are found in the classpath. The source dex files will be");
   UsageError("      removed from any class loader's classpath possibly resulting in empty");
@@ -468,6 +468,10 @@
   UsageError("");
   UsageError("      Example: --class-loader-context=PCL[lib1.dex:lib2.dex];DLC[lib3.dex]");
   UsageError("");
+  UsageError("  --class-loader-context-fds=<fds>: a colon-separated list of file descriptors");
+  UsageError("      for dex files in --class-loader-context. Their order must be the same as");
+  UsageError("      dex files in flattened class loader context.");
+  UsageError("");
   UsageError("  --dirty-image-objects=<directory-path>: list of known dirty objects in the image.");
   UsageError("      The image writer will group them together.");
   UsageError("");
@@ -1171,6 +1175,17 @@
         Usage("Option --class-loader-context has an incorrect format: %s",
               class_loader_context_arg.c_str());
       }
+      if (args.Exists(M::ClassLoaderContextFds)) {
+        std::string str_fds_arg = *args.Get(M::ClassLoaderContextFds);
+        std::vector<std::string> str_fds = android::base::Split(str_fds_arg, ":");
+        for (const std::string& str_fd : str_fds) {
+          class_loader_context_fds_.push_back(std::stoi(str_fd, nullptr, 0));
+          if (class_loader_context_fds_.back() < 0) {
+            Usage("Option --class-loader-context-fds has incorrect format: %s",
+                str_fds_arg.c_str());
+          }
+        }
+      }
       if (args.Exists(M::StoredClassLoaderContext)) {
         const std::string stored_context_arg = *args.Get(M::StoredClassLoaderContext);
         stored_class_loader_context_ = ClassLoaderContext::Create(stored_context_arg);
@@ -1506,7 +1521,9 @@
       // (because the encoding adds the dex checksum...)
       // TODO(calin): consider redesigning this so we don't have to open the dex files before
       // creating the actual class loader.
-      if (!class_loader_context_->OpenDexFiles(runtime_->GetInstructionSet(), classpath_dir_)) {
+      if (!class_loader_context_->OpenDexFiles(runtime_->GetInstructionSet(),
+                                               classpath_dir_,
+                                               class_loader_context_fds_)) {
         // Do not abort if we couldn't open files from the classpath. They might be
         // apks without dex files and right now are opening flow will fail them.
         LOG(WARNING) << "Failed to open classpath dex files";
@@ -2690,6 +2707,10 @@
   // The spec describing how the class loader should be setup for compilation.
   std::unique_ptr<ClassLoaderContext> class_loader_context_;
 
+  // Optional list of file descriptors corresponding to dex file locations in
+  // flattened `class_loader_context_`.
+  std::vector<int> class_loader_context_fds_;
+
   // The class loader context stored in the oat file. May be equal to class_loader_context_.
   std::unique_ptr<ClassLoaderContext> stored_class_loader_context_;