Support oat files compiled with partial boot class path.

Test: oat_file_assistant_test
Bug: 119868597
Bug: 122937705
Change-Id: I07c59957983c0ec61ade5215bb83c41e7cb4b672
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 8b81bb9..80ac01f 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -563,8 +563,17 @@
 bool OatFileAssistant::ImageInfo::ValidateBootClassPathChecksums(const OatFile& oat_file) const {
   const char* oat_boot_class_path_checksums =
       oat_file.GetOatHeader().GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey);
-  return oat_boot_class_path_checksums != nullptr &&
-         oat_boot_class_path_checksums == boot_class_path_checksums;
+  if (oat_boot_class_path_checksums == nullptr) {
+    return false;
+  }
+  // The checksums can be either the same or a prefix of the expected checksums,
+  // ending before the ':' delimiter.
+  size_t length = strlen(oat_boot_class_path_checksums);
+  if (length > boot_class_path_checksums.length() ||
+      (length < boot_class_path_checksums.length() && boot_class_path_checksums[length] != ':')) {
+    return false;
+  }
+  return boot_class_path_checksums.compare(0u, length, oat_boot_class_path_checksums) == 0;
 }
 
 std::unique_ptr<OatFileAssistant::ImageInfo>
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 521e419..a99bd51 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -61,6 +61,14 @@
       VerifyOptimizationStatus(
           file, CompilerFilter::NameOfFilter(expected_filter), expected_reason);
   }
+  void InsertNewBootClasspathEntry() {
+    std::string extra_dex_filename = GetMultiDexSrc1();
+    Runtime* runtime = Runtime::Current();
+    runtime->boot_class_path_.push_back(extra_dex_filename);
+    if (!runtime->boot_class_path_locations_.empty()) {
+      runtime->boot_class_path_locations_.push_back(extra_dex_filename);
+    }
+  }
 };
 
 class ScopedNonWritable {
@@ -236,17 +244,50 @@
   Copy(GetDexSrc1(), dex_location);
   GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
 
-  // For the use of oat location by making the dex parent not writable.
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+  // Force the use of oat location by making the dex parent not writable.
+  OatFileAssistant oat_file_assistant(
+      dex_location.c_str(), kRuntimeISA, /*load_executable=*/ false);
 
   EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
-      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+            oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
   EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
-      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken));
+            oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken));
   EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
-      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kExtract));
+            oat_file_assistant.GetDexOptNeeded(CompilerFilter::kExtract));
   EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
-      oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
+            oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
+  EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
+
+  VerifyOptimizationStatus(dex_location, CompilerFilter::kSpeed, "install");
+}
+
+// Case: We have an ODEX file compiled against partial boot image.
+// Expect: The status is kNoDexOptNeeded.
+TEST_F(OatFileAssistantTest, OdexUpToDatePartialBootImage) {
+  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");
+
+  // Insert an extra dex file to the boot class path.
+  InsertNewBootClasspathEntry();
+
+  // Force the use of oat location by making the dex parent not writable.
+  OatFileAssistant oat_file_assistant(
+      dex_location.c_str(), kRuntimeISA, /*load_executable=*/ false);
+
+  EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+            oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
+  EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+            oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken));
+  EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+            oat_file_assistant.GetDexOptNeeded(CompilerFilter::kExtract));
+  EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
+            oat_file_assistant.GetDexOptNeeded(CompilerFilter::kEverything));
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
   EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
@@ -302,7 +343,7 @@
   Copy(GetDexSrc1(), dex_location);
   GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
 
-  // For the use of oat location by making the dex parent not writable.
+  // Force the use of oat location by making the dex parent not writable.
   ScopedNonWritable scoped_non_writable(dex_location);
   ASSERT_TRUE(scoped_non_writable.IsSuccessful());
 
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 1acb75b..1d1d0d3 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -91,6 +91,7 @@
 class MonitorList;
 class MonitorPool;
 class NullPointerHandler;
+class OatFileAssistantTest;
 class OatFileManager;
 class Plugin;
 struct RuntimeArgumentMap;
@@ -1145,6 +1146,7 @@
   // Note: See comments on GetFaultMessage.
   friend std::string GetFaultMessageForAbortLogging();
   friend class ScopedThreadPoolUsage;
+  friend class OatFileAssistantTest;
 
   DISALLOW_COPY_AND_ASSIGN(Runtime);
 };