Replace the static version of GetOptimizationStatus with a factory.

The static version of GetOptimizationStatus is not extensible. We need a
function that can be used in both `GetOptimizationStatus` and
`GetDexOptNeeded` use cases.

Also:
- Add DCHECK to GetOptimizationStatus to make sure it doesn't return
  unexpected values.

Bug: 229268202
Test: m test-art-host-gtest-art_runtime_tests
Change-Id: I3c6401c9d52e0766766e3ae7ee19c62af5e35a4a
Merged-In: I71d546ec25703b7ffe3a6f20de3af1cb72f3fc19
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index cf0155f..f7c9e03 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -22,6 +22,7 @@
 #include <sstream>
 
 #include "android-base/file.h"
+#include "android-base/logging.h"
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
 #include "arch/instruction_set.h"
@@ -234,6 +235,47 @@
   }
 }
 
+std::unique_ptr<OatFileAssistant> OatFileAssistant::Create(
+    const std::string& filename,
+    const std::string& isa_str,
+    const std::string& context_str,
+    bool load_executable,
+    bool only_load_trusted_executable,
+    std::unique_ptr<RuntimeOptions> runtime_options,
+    std::string* error_msg) {
+  InstructionSet isa = GetInstructionSetFromString(isa_str.c_str());
+  if (isa == InstructionSet::kNone) {
+    *error_msg = StringPrintf("Instruction set '%s' is invalid", isa_str.c_str());
+    return nullptr;
+  }
+
+  std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str.c_str());
+  if (context == nullptr) {
+    *error_msg = StringPrintf("Class loader context '%s' is invalid", context_str.c_str());
+    return nullptr;
+  }
+
+  if (!context->OpenDexFiles(android::base::Dirname(filename.c_str()),
+                             /*context_fds=*/{},
+                             /*only_read_checksums=*/true)) {
+    *error_msg =
+        StringPrintf("Failed to load class loader context files for '%s' with context '%s'",
+                     filename.c_str(),
+                     context_str.c_str());
+    return nullptr;
+  }
+
+  auto assistant = std::make_unique<OatFileAssistant>(filename.c_str(),
+                                                      isa,
+                                                      context.get(),
+                                                      load_executable,
+                                                      only_load_trusted_executable,
+                                                      std::move(runtime_options));
+
+  assistant->owned_context_ = std::move(context);
+  return assistant;
+}
+
 bool OatFileAssistant::UseFdToReadFiles() {
   return zip_fd_ >= 0;
 }
@@ -1141,57 +1183,6 @@
       &out_odex_status);
 }
 
-bool OatFileAssistant::GetOptimizationStatus(const std::string& filename,
-                                             const std::string& isa_str,
-                                             const std::string& context_str,
-                                             std::unique_ptr<RuntimeOptions> runtime_options,
-                                             /*out*/ std::string* compiler_filter,
-                                             /*out*/ std::string* compilation_reason,
-                                             /*out*/ std::string* odex_location,
-                                             /*out*/ std::string* error_msg) {
-  InstructionSet isa = GetInstructionSetFromString(isa_str.c_str());
-  if (isa == InstructionSet::kNone) {
-    *error_msg = StringPrintf("Instruction set '%s' is invalid", isa_str.c_str());
-    return false;
-  }
-
-  std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str.c_str());
-  if (context == nullptr) {
-    *error_msg = StringPrintf("Class loader context '%s' is invalid", context_str.c_str());
-    return false;
-  }
-
-  std::vector<int> context_fds;
-  if (!context->OpenDexFiles(android::base::Dirname(filename.c_str()),
-                             context_fds,
-                             /*only_read_checksums=*/true)) {
-    *error_msg =
-        StringPrintf("Failed to load class loader context files for '%s' with context '%s'",
-                     filename.c_str(),
-                     context_str.c_str());
-    return false;
-  }
-
-  OatFileAssistant oat_file_assistant(filename.c_str(),
-                                      isa,
-                                      context.get(),
-                                      /*load_executable=*/false,
-                                      /*only_load_trusted_executable=*/true,
-                                      std::move(runtime_options));
-
-  // We ignore the odex_status because it is not meaningful. It can never be
-  // "boot-image-more-recent" or "context-mismatch". In the case where the boot image has changed or
-  // there is a context mismatch, the value is "up-to-date" because the vdex file is still usable.
-  // I.e., it can only be either "up-to-date" or "apk-more-recent", which means it doesn't give us
-  // information in addition to what we can learn from compiler_filter.
-  std::string ignored_odex_status;
-
-  oat_file_assistant.GetOptimizationStatus(
-      odex_location, compiler_filter, compilation_reason, &ignored_odex_status);
-
-  return true;
-}
-
 void OatFileAssistant::GetOptimizationStatus(
     std::string* out_odex_location,
     std::string* out_compilation_filter,
@@ -1218,28 +1209,25 @@
   OatStatus status = oat_file_info.Status();
   const char* reason = oat_file->GetCompilationReason();
   *out_compilation_reason = reason == nullptr ? "unknown" : reason;
+
+  // If the oat file is invalid, the vdex file will be picked, so the status is `kOatUpToDate`. If
+  // the vdex file is also invalid, then either `oat_file` is nullptr, or `status` is
+  // `kOatDexOutOfDate`.
+  DCHECK(status == kOatUpToDate || status == kOatDexOutOfDate);
+
   switch (status) {
     case kOatUpToDate:
       *out_compilation_filter = CompilerFilter::NameOfFilter(oat_file->GetCompilerFilter());
       *out_odex_status = "up-to-date";
       return;
 
-    case kOatCannotOpen:  // This should never happen, but be robust.
-      *out_compilation_filter = "error";
-      *out_compilation_reason = "error";
-      // This mostly happens when we cannot open the vdex file,
-      // or the file is corrupt.
-      *out_odex_status = "io-error-or-corruption";
-      return;
-
+    case kOatCannotOpen:
     case kOatBootImageOutOfDate:
-      *out_compilation_filter = "run-from-apk-fallback";
-      *out_odex_status = "boot-image-more-recent";
-      return;
-
     case kOatContextOutOfDate:
-      *out_compilation_filter = "run-from-apk-fallback";
-      *out_odex_status = "context-mismatch";
+      // These should never happen, but be robust.
+      *out_compilation_filter = "unexpected";
+      *out_compilation_reason = "unexpected";
+      *out_odex_status = "unexpected";
       return;
 
     case kOatDexOutOfDate:
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 527f473..1198832 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -156,6 +156,17 @@
                    int oat_fd,
                    int zip_fd);
 
+  // A convenient factory function that accepts ISA, class loader context, and compiler filter in
+  // strings. Returns the created instance on success, or returns nullptr and outputs an error
+  // message if it fails to parse the input strings.
+  static std::unique_ptr<OatFileAssistant> Create(const std::string& filename,
+                                                  const std::string& isa_str,
+                                                  const std::string& context_str,
+                                                  bool load_executable,
+                                                  bool only_load_trusted_executable,
+                                                  std::unique_ptr<RuntimeOptions> runtime_options,
+                                                  std::string* error_msg);
+
   // Returns true if the dex location refers to an element of the boot class
   // path.
   bool IsInBootClassPath();
@@ -204,7 +215,7 @@
   //   - out_compilation_reason: the optimization reason. The reason might
   //        be "unknown" if the compiler artifacts were not annotated during optimizations.
   //   - out_odex_status: a human readable refined status of the validity of the odex file.
-  //        E.g. up-to-date, apk-more-recent.
+  //        Possible values are: "up-to-date", "apk-more-recent", and "io-error-no-oat".
   //
   // This method will try to mimic the runtime effect of loading the dex file.
   // For example, if there is no usable oat file, the compiler filter will be set
@@ -220,18 +231,6 @@
                                     std::string* out_compilation_reason,
                                     std::unique_ptr<RuntimeOptions> runtime_options = nullptr);
 
-  // A convenient version of `GetOptimizationStatus` that accepts ISA and class loader context in
-  // strings. Returns true on success, or returns false and outputs an error message if it fails to
-  // parse the input strings.
-  static bool GetOptimizationStatus(const std::string& filename,
-                                    const std::string& isa_str,
-                                    const std::string& context_str,
-                                    std::unique_ptr<RuntimeOptions> runtime_options,
-                                    /*out*/ std::string* compiler_filter,
-                                    /*out*/ std::string* compilation_reason,
-                                    /*out*/ std::string* odex_location,
-                                    /*out*/ std::string* error_msg);
-
   // Open and returns an image space associated with the oat file.
   static std::unique_ptr<gc::space::ImageSpace> OpenImageSpace(const OatFile* oat_file);
 
@@ -472,6 +471,7 @@
   std::string dex_location_;
 
   ClassLoaderContext* context_;
+  std::unique_ptr<ClassLoaderContext> owned_context_;
 
   // Whether or not the parent directory of the dex file is writable.
   bool dex_parent_writable_ = false;
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index c0662aa..e414e4e 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -88,26 +88,7 @@
       ASSERT_EQ(expected_reason, compilation_reason1);
     }
 
-    // Verify the static method (called from artd).
-    std::string compilation_filter2;
-    std::string compilation_reason2;
-    std::string odex_location2;  // ignored
-    std::string error_msg;       // ignored
-
-    ASSERT_TRUE(
-        OatFileAssistant::GetOptimizationStatus(file,
-                                                GetInstructionSetString(kRuntimeISA),
-                                                context->EncodeContextForDex2oat(/*base_dir=*/""),
-                                                MaybeCreateRuntimeOptions(),
-                                                &compilation_filter2,
-                                                &compilation_reason2,
-                                                &odex_location2,
-                                                &error_msg));
-
-    ASSERT_EQ(expected_filter_name, compilation_filter2);
-    ASSERT_EQ(expected_reason, compilation_reason2);
-
-    // Verify the instance methods (called at runtime).
+    // Verify the instance methods (called at runtime and from artd).
     OatFileAssistant assistant = CreateOatFileAssistant(file.c_str(), context);
 
     std::string odex_location3;  // ignored
@@ -1796,6 +1777,29 @@
   }
 }
 
+TEST_P(OatFileAssistantTest, Create) {
+  std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
+  std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
+  Copy(GetDexSrc1(), dex_location);
+  GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
+
+  auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+
+  std::string error_msg;
+  std::unique_ptr<OatFileAssistant> oat_file_assistant =
+      OatFileAssistant::Create(dex_location,
+                               GetInstructionSetString(kRuntimeISA),
+                               default_context_->EncodeContextForDex2oat(/*base_dir=*/""),
+                               /*load_executable=*/false,
+                               /*only_load_trusted_executable=*/true,
+                               MaybeCreateRuntimeOptions(),
+                               &error_msg);
+  ASSERT_NE(oat_file_assistant, nullptr);
+
+  // Verify that the created instance is usable.
+  VerifyOptimizationStatus(dex_location, default_context_.get(), "speed", "install", "up-to-date");
+}
+
 TEST_P(OatFileAssistantTest, ErrorOnInvalidIsaString) {
   std::string dex_location = GetScratchDir() + "/OdexUpToDate.jar";
   std::string odex_location = GetOdexDir() + "/OdexUpToDate.odex";
@@ -1804,19 +1808,15 @@
 
   auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
 
-  std::string ignored_compilation_filter;
-  std::string ignored_compilation_reason;
-  std::string ignored_odex_location;
   std::string error_msg;
-  EXPECT_FALSE(OatFileAssistant::GetOptimizationStatus(
-      dex_location,
-      /*isa_str=*/"foo",
-      default_context_->EncodeContextForDex2oat(/*base_dir=*/""),
-      MaybeCreateRuntimeOptions(),
-      &ignored_compilation_filter,
-      &ignored_compilation_reason,
-      &ignored_odex_location,
-      &error_msg));
+  EXPECT_EQ(OatFileAssistant::Create(dex_location,
+                                     /*isa_str=*/"foo",
+                                     default_context_->EncodeContextForDex2oat(/*base_dir=*/""),
+                                     /*load_executable=*/false,
+                                     /*only_load_trusted_executable=*/true,
+                                     MaybeCreateRuntimeOptions(),
+                                     &error_msg),
+            nullptr);
   EXPECT_EQ(error_msg, "Instruction set 'foo' is invalid");
 }
 
@@ -1828,18 +1828,15 @@
 
   auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
 
-  std::string ignored_compilation_filter;
-  std::string ignored_compilation_reason;
-  std::string ignored_odex_location;
   std::string error_msg;
-  EXPECT_FALSE(OatFileAssistant::GetOptimizationStatus(dex_location,
-                                                       GetInstructionSetString(kRuntimeISA),
-                                                       /*context_str=*/"foo",
-                                                       MaybeCreateRuntimeOptions(),
-                                                       &ignored_compilation_filter,
-                                                       &ignored_compilation_reason,
-                                                       &ignored_odex_location,
-                                                       &error_msg));
+  EXPECT_EQ(OatFileAssistant::Create(dex_location,
+                                     GetInstructionSetString(kRuntimeISA),
+                                     /*context_str=*/"foo",
+                                     /*load_executable=*/false,
+                                     /*only_load_trusted_executable=*/true,
+                                     MaybeCreateRuntimeOptions(),
+                                     &error_msg),
+            nullptr);
   EXPECT_EQ(error_msg, "Class loader context 'foo' is invalid");
 }
 
@@ -1856,19 +1853,15 @@
 
   auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
 
-  std::string ignored_compilation_filter;
-  std::string ignored_compilation_reason;
-  std::string ignored_odex_location;
   std::string error_msg;
-  EXPECT_FALSE(
-      OatFileAssistant::GetOptimizationStatus(dex_location,
-                                              GetInstructionSetString(kRuntimeISA),
-                                              /*context_str=*/"PCL[" + context_location + "]",
-                                              MaybeCreateRuntimeOptions(),
-                                              &ignored_compilation_filter,
-                                              &ignored_compilation_reason,
-                                              &ignored_odex_location,
-                                              &error_msg));
+  EXPECT_EQ(OatFileAssistant::Create(dex_location,
+                                     GetInstructionSetString(kRuntimeISA),
+                                     /*context_str=*/"PCL[" + context_location + "]",
+                                     /*load_executable=*/false,
+                                     /*only_load_trusted_executable=*/true,
+                                     MaybeCreateRuntimeOptions(),
+                                     &error_msg),
+            nullptr);
   EXPECT_EQ(error_msg,
             "Failed to load class loader context files for '" + dex_location +
                 "' with context 'PCL[" + context_location + "]'");