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/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index ca09339..5529434 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -181,12 +181,14 @@
 int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target,
                                       bool profile_changed,
                                       bool downgrade,
-                                      ClassLoaderContext* class_loader_context) {
+                                      ClassLoaderContext* class_loader_context,
+                                      const std::vector<int>& context_fds) {
   OatFileInfo& info = GetBestInfo();
   DexOptNeeded dexopt_needed = info.GetDexOptNeeded(target,
                                                     profile_changed,
                                                     downgrade,
-                                                    class_loader_context);
+                                                    class_loader_context,
+                                                    context_fds);
   if (info.IsOatLocation() || dexopt_needed == kDex2OatFromScratch) {
     return dexopt_needed;
   }
@@ -748,10 +750,11 @@
     CompilerFilter::Filter target,
     bool profile_changed,
     bool downgrade,
-    ClassLoaderContext* context) {
+    ClassLoaderContext* context,
+    const std::vector<int>& context_fds) {
 
   bool filter_okay = CompilerFilterIsOkay(target, profile_changed, downgrade);
-  bool class_loader_context_okay = ClassLoaderContextIsOkay(context);
+  bool class_loader_context_okay = ClassLoaderContextIsOkay(context, context_fds);
 
   // Only check the filter and relocation if the class loader context is ok.
   // If it is not, we will return kDex2OatFromScratch as the compilation needs to be redone.
@@ -838,7 +841,8 @@
     CompilerFilter::IsAsGoodAs(current, target);
 }
 
-bool OatFileAssistant::OatFileInfo::ClassLoaderContextIsOkay(ClassLoaderContext* context) {
+bool OatFileAssistant::OatFileInfo::ClassLoaderContextIsOkay(ClassLoaderContext* context,
+                                                             const std::vector<int>& context_fds) {
   if (context == nullptr) {
     VLOG(oat) << "ClassLoaderContext check ignored: null context";
     return true;
@@ -855,12 +859,11 @@
       ? oat_file_assistant_->dex_location_.substr(0, dir_index)
       : "";
 
-  if (!context->OpenDexFiles(oat_file_assistant_->isa_, classpath_dir)) {
+  if (!context->OpenDexFiles(oat_file_assistant_->isa_, classpath_dir, context_fds)) {
     VLOG(oat) << "ClassLoaderContext check failed: dex files from the context could not be opened";
     return false;
   }
 
-
   const bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext()) !=
       ClassLoaderContext::VerificationResult::kMismatch;
   if (!result) {