Do not try to compile resource-only dex files.

This changes behavior in the case where we are asked to load a dex
file that does not exist or has no classes.dex entry.

Previously we would run dex2oat, which would log an error message and
fail. Now we skip running dex2oat, we report the DexOptStatus as
kNoDexOptNeeded, and we do not try to fall back to the missing
original dex files.

Bug: 21722039
(cherry picked from commit cb44b11a926696e34b3dc44288e762b4303cc128)

Change-Id: I84a85dc9ece54bcc0a5283f871e09bf68471c6e7
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 91b5000..7936dd3 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -987,13 +987,18 @@
   // Fall back to running out of the original dex file if we couldn't load any
   // dex_files from the oat file.
   if (dex_files.empty()) {
-    if (Runtime::Current()->IsDexFileFallbackEnabled()) {
-      if (!DexFile::Open(dex_location, dex_location, &error_msg, &dex_files)) {
-        LOG(WARNING) << error_msg;
-        error_msgs->push_back("Failed to open dex files from " + std::string(dex_location));
+    if (oat_file_assistant.HasOriginalDexFiles()) {
+      if (Runtime::Current()->IsDexFileFallbackEnabled()) {
+        if (!DexFile::Open(dex_location, dex_location, &error_msg, &dex_files)) {
+          LOG(WARNING) << error_msg;
+          error_msgs->push_back("Failed to open dex files from " + std::string(dex_location));
+        }
+      } else {
+        error_msgs->push_back("Fallback mode disabled, skipping dex files.");
       }
     } else {
-      error_msgs->push_back("Fallback mode disabled, skipping dex files.");
+      error_msgs->push_back("No original dex files found for dex location "
+          + std::string(dex_location));
     }
   }
   return dex_files;
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 094d8b7..b28adf9 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -151,7 +151,7 @@
     return kSelfPatchOatNeeded;
   }
 
-  return kDex2OatNeeded;
+  return HasOriginalDexFiles() ? kDex2OatNeeded : kNoDexOptNeeded;
 }
 
 bool OatFileAssistant::MakeUpToDate(std::string* error_msg) {
@@ -241,6 +241,14 @@
   return dex_files;
 }
 
+bool OatFileAssistant::HasOriginalDexFiles() {
+  // Ensure GetRequiredDexChecksum has been run so that
+  // has_original_dex_files_ is initialized. We don't care about the result of
+  // GetRequiredDexChecksum.
+  GetRequiredDexChecksum();
+  return has_original_dex_files_;
+}
+
 const std::string* OatFileAssistant::OdexFileName() {
   if (!cached_odex_file_name_attempted_) {
     CHECK(dex_location_ != nullptr) << "OatFileAssistant: null dex location";
@@ -817,17 +825,19 @@
 }
 
 const uint32_t* OatFileAssistant::GetRequiredDexChecksum() {
-  if (!required_dex_checksum_attempted) {
-    required_dex_checksum_attempted = true;
-    required_dex_checksum_found = false;
+  if (!required_dex_checksum_attempted_) {
+    required_dex_checksum_attempted_ = true;
+    required_dex_checksum_found_ = false;
     std::string error_msg;
     CHECK(dex_location_ != nullptr) << "OatFileAssistant provided no dex location";
-    if (DexFile::GetChecksum(dex_location_, &cached_required_dex_checksum, &error_msg)) {
-      required_dex_checksum_found = true;
+    if (DexFile::GetChecksum(dex_location_, &cached_required_dex_checksum_, &error_msg)) {
+      required_dex_checksum_found_ = true;
+      has_original_dex_files_ = true;
     } else {
       // This can happen if the original dex file has been stripped from the
       // apk.
       VLOG(oat) << "OatFileAssistant: " << error_msg;
+      has_original_dex_files_ = false;
 
       // Get the checksum from the odex if we can.
       const OatFile* odex_file = GetOdexFile();
@@ -835,13 +845,13 @@
         const OatFile::OatDexFile* odex_dex_file = odex_file->GetOatDexFile(
             dex_location_, nullptr, false);
         if (odex_dex_file != nullptr) {
-          cached_required_dex_checksum = odex_dex_file->GetDexFileLocationChecksum();
-          required_dex_checksum_found = true;
+          cached_required_dex_checksum_ = odex_dex_file->GetDexFileLocationChecksum();
+          required_dex_checksum_found_ = true;
         }
       }
     }
   }
-  return required_dex_checksum_found ? &cached_required_dex_checksum : nullptr;
+  return required_dex_checksum_found_ ? &cached_required_dex_checksum_ : nullptr;
 }
 
 const OatFile* OatFileAssistant::GetOdexFile() {
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 4c0b0e2..7216fc7 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -174,6 +174,12 @@
   static std::vector<std::unique_ptr<const DexFile>> LoadDexFiles(
       const OatFile& oat_file, const char* dex_location);
 
+  // Returns true if there are dex files in the original dex location that can
+  // be compiled with dex2oat for this dex location.
+  // Returns false if there is no original dex file, or if the original dex
+  // file is an apk/zip without a classes.dex entry.
+  bool HasOriginalDexFiles();
+
   // If the dex file has been installed with a compiled oat file alongside
   // it, the compiled oat file will have the extension .odex, and is referred
   // to as the odex file. It is called odex for legacy reasons; the file is
@@ -312,6 +318,8 @@
   // Returns dex_checksum if a required checksum was located. Returns
   // null if the required checksum was not found.
   // The caller shouldn't clean up or free the returned pointer.
+  // This sets the has_original_dex_files_ field to true if a checksum was
+  // found for the dex_location_ dex file.
   const uint32_t* GetRequiredDexChecksum();
 
   // Returns the loaded odex file.
@@ -374,9 +382,10 @@
 
   // Cached value of the required dex checksum.
   // This should be accessed only by the GetRequiredDexChecksum() method.
-  uint32_t cached_required_dex_checksum;
-  bool required_dex_checksum_attempted = false;
-  bool required_dex_checksum_found;
+  uint32_t cached_required_dex_checksum_;
+  bool required_dex_checksum_attempted_ = false;
+  bool required_dex_checksum_found_;
+  bool has_original_dex_files_;
 
   // Cached value of the odex file name.
   // This should be accessed only by the OdexFileName() method.
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index d8e3797..570c59c 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -309,16 +309,24 @@
   EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
   EXPECT_EQ(OatFileAssistant::kOatOutOfDate, oat_file_assistant.OatFileStatus());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
 // Case: We have no DEX file and no OAT file.
-// Expect: Status is kDex2OatNeeded. Loading should fail, but not crash.
+// Expect: Status is kNoDexOptNeeded. Loading should fail, but not crash.
 TEST_F(OatFileAssistantTest, NoDexNoOat) {
   std::string dex_location = GetScratchDir() + "/NoDexNoOat.jar";
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
 
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
+
+  // Trying to make the oat file up to date should not fail or crash.
+  std::string error_msg;
+  EXPECT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg));
+
+  // Trying to get the best oat file should fail, but not crash.
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   EXPECT_EQ(nullptr, oat_file.get());
 }
@@ -342,6 +350,7 @@
   EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
   EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
   EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OatFileStatus());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
 // Case: We have a MultiDEX file and up-to-date OAT file for it.
@@ -353,6 +362,7 @@
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
   EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   // Verify we can load both dex files.
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -378,6 +388,7 @@
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
   EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
 // Case: We have a MultiDEX file and up-to-date OAT file for it with relative
@@ -432,6 +443,7 @@
   EXPECT_TRUE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
 // Case: We have a DEX file and an ODEX file, but no OAT file.
@@ -457,6 +469,7 @@
   EXPECT_FALSE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
 // Case: We have a stripped DEX file and an ODEX file, but no OAT file.
@@ -484,6 +497,7 @@
   EXPECT_FALSE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Make the oat file up to date.
   std::string error_msg;
@@ -498,6 +512,7 @@
   EXPECT_TRUE(oat_file_assistant.OatFileExists());
   EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Verify we can load the dex files from it.
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -538,6 +553,7 @@
   EXPECT_TRUE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Make the oat file up to date.
   std::string error_msg;
@@ -554,6 +570,7 @@
   EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
   EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
   // Verify we can load the dex files from it.
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -564,6 +581,45 @@
   EXPECT_EQ(1u, dex_files.size());
 }
 
+// Case: We have a stripped (or resource-only) DEX file, no ODEX file and no
+// OAT file. Expect: The status is kNoDexOptNeeded.
+TEST_F(OatFileAssistantTest, ResourceOnlyDex) {
+  std::string dex_location = GetScratchDir() + "/ResourceOnlyDex.jar";
+
+  Copy(GetStrippedDexSrc1(), dex_location);
+
+  // Verify the status.
+  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileExists());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
+
+  // Make the oat file up to date. This should have no effect.
+  std::string error_msg;
+  EXPECT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg)) << error_msg;
+
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
+
+  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
+  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
+  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OdexFileNeedsRelocation());
+  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileExists());
+  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
+  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
+}
+
 // Case: We have a DEX file, no ODEX file and an OAT file that needs
 // relocation.
 // Expect: The status is kSelfPatchOatNeeded.
@@ -589,6 +645,7 @@
   EXPECT_TRUE(oat_file_assistant.OatFileNeedsRelocation());
   EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   // Make the oat file up to date.
   std::string error_msg;
@@ -605,6 +662,7 @@
   EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileNeedsRelocation());
   EXPECT_TRUE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
   ASSERT_TRUE(oat_file.get() != nullptr);
@@ -643,6 +701,7 @@
   EXPECT_TRUE(oat_file_assistant.OatFileExists());
   EXPECT_FALSE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 
   // Things aren't relocated, so it should fall back to interpreted.
   std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
@@ -688,6 +747,7 @@
   EXPECT_FALSE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
 }
 
 // Case: We have a DEX file and up-to-date OAT file for it.
@@ -756,27 +816,6 @@
   EXPECT_FALSE(ofm.OatFileExists());
 }
 
-// Case: Non-existent Dex location.
-// Expect: The dex code is out of date, and trying to update it fails.
-TEST_F(OatFileAssistantTest, NonExsistentDexLocation) {
-  std::string dex_location = GetScratchDir() + "/BadDexLocation.jar";
-
-  OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
-
-  EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
-  EXPECT_FALSE(oat_file_assistant.OdexFileExists());
-  EXPECT_FALSE(oat_file_assistant.OatFileExists());
-  EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
-  EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
-  EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
-
-  std::string error_msg;
-  EXPECT_FALSE(oat_file_assistant.MakeUpToDate(&error_msg));
-  EXPECT_FALSE(error_msg.empty());
-}
-
 // Turn an absolute path into a path relative to the current working
 // directory.
 static std::string MakePathRelative(std::string target) {
@@ -833,24 +872,26 @@
 }
 
 // Case: Very short, non-existent Dex location.
-// Expect: Dex code is out of date, and trying to update it fails.
+// Expect: kNoDexOptNeeded.
 TEST_F(OatFileAssistantTest, ShortDexLocation) {
   std::string dex_location = "/xx";
 
   OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
 
   EXPECT_FALSE(oat_file_assistant.IsInBootClassPath());
-  EXPECT_EQ(OatFileAssistant::kDex2OatNeeded, oat_file_assistant.GetDexOptNeeded());
+  EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded, oat_file_assistant.GetDexOptNeeded());
   EXPECT_FALSE(oat_file_assistant.OdexFileExists());
   EXPECT_FALSE(oat_file_assistant.OatFileExists());
   EXPECT_TRUE(oat_file_assistant.OdexFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OdexFileIsUpToDate());
   EXPECT_TRUE(oat_file_assistant.OatFileIsOutOfDate());
   EXPECT_FALSE(oat_file_assistant.OatFileIsUpToDate());
+  EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
 
+  // Trying to make it up to date should have no effect.
   std::string error_msg;
-  EXPECT_FALSE(oat_file_assistant.MakeUpToDate(&error_msg));
-  EXPECT_FALSE(error_msg.empty());
+  EXPECT_TRUE(oat_file_assistant.MakeUpToDate(&error_msg));
+  EXPECT_TRUE(error_msg.empty());
 }
 
 // Case: Non-standard extension for dex file.