Make system use patchoat to relocate during runtime.

Change dalvik_system_DexFile.cc so that isDexOptNeededInternal will be
able to indicate that a patchoat is required. Change default of relocate
option to be on.

Bug: 15358152

Change-Id: Ibe92d8b55a24bbf718b0416a21b76e5df7a2de26
diff --git a/Android.mk b/Android.mk
index e536a71..b006bfe 100644
--- a/Android.mk
+++ b/Android.mk
@@ -316,7 +316,8 @@
 		--dex-location=/$(1) --oat-file=$$@ \
 		--instruction-set=$(DEX2OAT_TARGET_ARCH) \
 		--instruction-set-features=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES) \
-		--android-root=$(PRODUCT_OUT)/system --include-patch-information
+		--android-root=$(PRODUCT_OUT)/system --include-patch-information \
+		--runtime-arg -Xnorelocate
 
 endif
 
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index f85bc65..ed126ad 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -933,13 +933,13 @@
   }
   *out_is_finalizable = resolved_class->IsFinalizable();
   const bool compiling_boot = Runtime::Current()->GetHeap()->IsCompilingBoot();
+  const bool support_boot_image_fixup = GetSupportBootImageFixup();
   if (compiling_boot) {
     // boot -> boot class pointers.
     // True if the class is in the image at boot compiling time.
     const bool is_image_class = IsImage() && IsImageClass(
         dex_file.StringDataByIdx(dex_file.GetTypeId(type_idx).descriptor_idx_));
     // True if pc relative load works.
-    const bool support_boot_image_fixup = GetSupportBootImageFixup();
     if (is_image_class && support_boot_image_fixup) {
       *is_type_initialized = resolved_class->IsInitialized();
       *use_direct_type_ptr = false;
@@ -952,7 +952,7 @@
     // True if the class is in the image at app compiling time.
     const bool class_in_image =
         Runtime::Current()->GetHeap()->FindSpaceFromObject(resolved_class, false)->IsImageSpace();
-    if (class_in_image) {
+    if (class_in_image && support_boot_image_fixup) {
       // boot -> app class pointers.
       *is_type_initialized = resolved_class->IsInitialized();
       // TODO This is somewhat hacky. We should refactor all of this invoke codepath.
diff --git a/compiler/elf_patcher.cc b/compiler/elf_patcher.cc
index 6112fbb..137110f 100644
--- a/compiler/elf_patcher.cc
+++ b/compiler/elf_patcher.cc
@@ -120,6 +120,7 @@
 
 uint32_t* ElfPatcher::GetPatchLocation(uintptr_t patch_ptr) {
   CHECK_GE(patch_ptr, reinterpret_cast<uintptr_t>(oat_file_->Begin()));
+  CHECK_LE(patch_ptr, reinterpret_cast<uintptr_t>(oat_file_->End()));
   uintptr_t off = patch_ptr - reinterpret_cast<uintptr_t>(oat_file_->Begin());
   uintptr_t ret = reinterpret_cast<uintptr_t>(oat_header_) + off;
 
@@ -144,20 +145,20 @@
           cpatch->GetTargetDexFile()->GetMethodId(cpatch->GetTargetMethodIdx());
       uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
       uint32_t actual = *patch_location;
-      CHECK(actual == expected || actual == value) << std::hex
-          << "actual=" << actual
-          << "expected=" << expected
-          << "value=" << value;
+      CHECK(actual == expected || actual == value) << "Patching call failed: " << std::hex
+          << " actual=" << actual
+          << " expected=" << expected
+          << " value=" << value;
     }
     if (patch->IsType()) {
       const CompilerDriver::TypePatchInformation* tpatch = patch->AsType();
       const DexFile::TypeId& id = tpatch->GetDexFile().GetTypeId(tpatch->GetTargetTypeIdx());
       uint32_t expected = reinterpret_cast<uintptr_t>(&id) & 0xFFFFFFFF;
       uint32_t actual = *patch_location;
-      CHECK(actual == expected || actual == value) << std::hex
-          << "actual=" << actual
-          << "expected=" << expected
-          << "value=" << value;
+      CHECK(actual == expected || actual == value) << "Patching type failed: " << std::hex
+          << " actual=" << actual
+          << " expected=" << expected
+          << " value=" << value;
     }
   }
   *patch_location = value;
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 3005e56..6b23345 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -141,6 +141,8 @@
   std::string image("-Ximage:");
   image.append(image_location.GetFilename());
   options.push_back(std::make_pair(image.c_str(), reinterpret_cast<void*>(NULL)));
+  // By default the compiler this creates will not include patch information.
+  options.push_back(std::make_pair("-Xnorelocate", nullptr));
 
   if (!Runtime::Create(options, false)) {
     LOG(FATAL) << "Failed to create runtime";
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 99907e3..c1e3b25 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -615,6 +615,14 @@
     argv.push_back("--compiler-filter=verify-none");
   }
 
+  if (Runtime::Current()->MustRelocateIfPossible()) {
+    argv.push_back("--runtime-arg");
+    argv.push_back("-Xrelocate");
+  } else {
+    argv.push_back("--runtime-arg");
+    argv.push_back("-Xnorelocate");
+  }
+
   if (!kIsTargetBuild) {
     argv.push_back("--host");
   }
@@ -986,11 +994,25 @@
   return oat_file.release();
 }
 
-bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file,
-                                         const char* dex_location,
-                                         uint32_t dex_location_checksum,
-                                         const InstructionSet instruction_set,
-                                         std::string* error_msg) {
+bool ClassLinker::VerifyOatImageChecksum(const OatFile* oat_file,
+                                         const InstructionSet instruction_set) {
+  Runtime* runtime = Runtime::Current();
+  const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
+  uint32_t image_oat_checksum = 0;
+  if (instruction_set == kRuntimeISA) {
+    const ImageHeader& image_header = image_space->GetImageHeader();
+    image_oat_checksum = image_header.GetOatChecksum();
+  } else {
+    std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
+        image_space->GetImageLocation().c_str(), instruction_set));
+    image_oat_checksum = image_header->GetOatChecksum();
+  }
+  return oat_file->GetOatHeader().GetImageFileLocationOatChecksum() == image_oat_checksum;
+}
+
+bool ClassLinker::VerifyOatChecksums(const OatFile* oat_file,
+                                     const InstructionSet instruction_set,
+                                     std::string* error_msg) {
   Runtime* runtime = Runtime::Current();
   const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
 
@@ -1013,9 +1035,28 @@
     image_patch_delta = image_header->GetPatchDelta();
   }
   const OatHeader& oat_header = oat_file->GetOatHeader();
-  bool image_check = ((oat_header.GetImageFileLocationOatChecksum() == image_oat_checksum)
-                      && (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin)
-                      && (oat_header.GetImagePatchDelta() == image_patch_delta));
+  bool ret = ((oat_header.GetImageFileLocationOatChecksum() == image_oat_checksum)
+              && (oat_header.GetImagePatchDelta() == image_patch_delta)
+              && (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin));
+  if (!ret) {
+    *error_msg = StringPrintf("oat file '%s' mismatch (0x%x, %d, %d) with (0x%x, %" PRIdPTR ", %d)",
+                              oat_file->GetLocation().c_str(),
+                              oat_file->GetOatHeader().GetImageFileLocationOatChecksum(),
+                              oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(),
+                              oat_file->GetOatHeader().GetImagePatchDelta(),
+                              image_oat_checksum, image_oat_data_begin, image_patch_delta);
+  }
+  return ret;
+}
+
+bool ClassLinker::VerifyOatAndDexFileChecksums(const OatFile* oat_file,
+                                               const char* dex_location,
+                                               uint32_t dex_location_checksum,
+                                               const InstructionSet instruction_set,
+                                               std::string* error_msg) {
+  if (!VerifyOatChecksums(oat_file, instruction_set, error_msg)) {
+    return false;
+  }
 
   const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
                                                                     &dex_location_checksum);
@@ -1031,27 +1072,15 @@
     }
     return false;
   }
-  bool dex_check = dex_location_checksum == oat_dex_file->GetDexFileLocationChecksum();
 
-  if (image_check && dex_check) {
-    return true;
-  }
-
-  if (!image_check) {
-    ScopedObjectAccess soa(Thread::Current());
-    *error_msg = StringPrintf("oat file '%s' mismatch (0x%x, %d) with (0x%x, %" PRIdPTR ")",
-                              oat_file->GetLocation().c_str(),
-                              oat_file->GetOatHeader().GetImageFileLocationOatChecksum(),
-                              oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(),
-                              image_oat_checksum, image_oat_data_begin);
-  }
-  if (!dex_check) {
+  if (dex_location_checksum != oat_dex_file->GetDexFileLocationChecksum()) {
     *error_msg = StringPrintf("oat file '%s' mismatch (0x%x) with '%s' (0x%x)",
                               oat_file->GetLocation().c_str(),
                               oat_dex_file->GetDexFileLocationChecksum(),
                               dex_location, dex_location_checksum);
+    return false;
   }
-  return false;
+  return true;
 }
 
 bool ClassLinker::VerifyOatWithDexFile(const OatFile* oat_file,
@@ -1074,8 +1103,8 @@
     }
     dex_file.reset(oat_dex_file->OpenDexFile(error_msg));
   } else {
-    bool verified = VerifyOatFileChecksums(oat_file, dex_location, dex_location_checksum,
-                                           kRuntimeISA, error_msg);
+    bool verified = VerifyOatAndDexFileChecksums(oat_file, dex_location, dex_location_checksum,
+                                                 kRuntimeISA, error_msg);
     if (!verified) {
       return false;
     }
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 1bb1635..8c09042 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -274,12 +274,18 @@
                            std::vector<const DexFile*>* dex_files)
       LOCKS_EXCLUDED(dex_lock_, Locks::mutator_lock_);
 
+  // Returns true if the given oat file has the same image checksum as the image it is paired with.
+  static bool VerifyOatImageChecksum(const OatFile* oat_file, const InstructionSet instruction_set);
+  // Returns true if the oat file checksums match with the image and the offsets are such that it
+  // could be loaded with it.
+  static bool VerifyOatChecksums(const OatFile* oat_file, const InstructionSet instruction_set,
+                                 std::string* error_msg);
   // Returns true if oat file contains the dex file with the given location and checksum.
-  static bool VerifyOatFileChecksums(const OatFile* oat_file,
-                                     const char* dex_location,
-                                     uint32_t dex_location_checksum,
-                                     InstructionSet instruction_set,
-                                     std::string* error_msg);
+  static bool VerifyOatAndDexFileChecksums(const OatFile* oat_file,
+                                           const char* dex_location,
+                                           uint32_t dex_location_checksum,
+                                           InstructionSet instruction_set,
+                                           std::string* error_msg);
 
   // TODO: replace this with multiple methods that allocate the correct managed type.
   template <class T>
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index fc6d2ef..1d10af2 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -166,7 +166,8 @@
     return true;
 }
 
-bool ImageSpace::RelocateImage(const char* image_location, const char* dest_filename,
+// Relocate the image at image_location to dest_filename and relocate it by a random amount.
+static bool RelocateImage(const char* image_location, const char* dest_filename,
                                InstructionSet isa, std::string* error_msg) {
   std::string patchoat(Runtime::Current()->GetPatchoatExecutable());
 
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index debca52..6be3b8f 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -124,9 +124,6 @@
                           bool validate_oat_file, std::string* error_msg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  static bool RelocateImage(const char* image_location, const char* dest_filename,
-                            InstructionSet isa, std::string* error_msg);
-
   OatFile* OpenOatFile(const char* image, std::string* error_msg) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
diff --git a/runtime/globals.h b/runtime/globals.h
index 1d9f22c..107e064 100644
--- a/runtime/globals.h
+++ b/runtime/globals.h
@@ -118,6 +118,8 @@
 static constexpr TraceClockSource kDefaultTraceClockSource = kTraceClockSourceWall;
 #endif
 
+static constexpr bool kDefaultMustRelocate = true;
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_GLOBALS_H_
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index ac1a310..0af2c22 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -275,8 +275,96 @@
   }
 }
 
-static jboolean IsDexOptNeededInternal(JNIEnv* env, const char* filename,
+// Java: dalvik.system.DexFile.UP_TO_DATE
+static const jbyte kUpToDate = 0;
+// Java: dalvik.system.DexFile.DEXOPT_NEEDED
+static const jbyte kPatchoatNeeded = 1;
+// Java: dalvik.system.DexFile.PATCHOAT_NEEDED
+static const jbyte kDexoptNeeded = 2;
+
+template <const bool kVerboseLogging, const bool kReasonLogging>
+static jbyte IsDexOptNeededForFile(const std::string& oat_filename, const char* filename,
+                                   InstructionSet target_instruction_set) {
+  std::string error_msg;
+  std::unique_ptr<const OatFile> oat_file(OatFile::Open(oat_filename, oat_filename, nullptr,
+                                                        false, &error_msg));
+  if (oat_file.get() == nullptr) {
+    if (kVerboseLogging) {
+      LOG(INFO) << "DexFile_isDexOptNeeded failed to open oat file '" << oat_filename
+          << "' for file location '" << filename << "': " << error_msg;
+    }
+    error_msg.clear();
+    return kDexoptNeeded;
+  }
+  bool should_relocate_if_possible = Runtime::Current()->ShouldRelocate();
+  uint32_t location_checksum = 0;
+  const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename, nullptr,
+                                                                          kReasonLogging);
+  if (oat_dex_file != nullptr) {
+    // If its not possible to read the classes.dex assume up-to-date as we won't be able to
+    // compile it anyway.
+    if (!DexFile::GetChecksum(filename, &location_checksum, &error_msg)) {
+      if (kVerboseLogging) {
+        LOG(INFO) << "DexFile_isDexOptNeeded found precompiled stripped file: "
+            << filename << " for " << oat_filename << ": " << error_msg;
+      }
+      if (ClassLinker::VerifyOatChecksums(oat_file.get(), target_instruction_set, &error_msg)) {
+        if (kVerboseLogging) {
+          LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
+                    << " is up-to-date for " << filename;
+        }
+        return kUpToDate;
+      } else if (should_relocate_if_possible &&
+                  ClassLinker::VerifyOatImageChecksum(oat_file.get(), target_instruction_set)) {
+        if (kVerboseLogging) {
+          LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
+                    << " needs to be relocated for " << filename;
+        }
+        return kPatchoatNeeded;
+      } else {
+        if (kVerboseLogging) {
+          LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
+                    << " is out of date for " << filename;
+        }
+        return kDexoptNeeded;
+      }
+      // If we get here the file is out of date and we should use the system one to relocate.
+    } else {
+      if (ClassLinker::VerifyOatAndDexFileChecksums(oat_file.get(), filename, location_checksum,
+                                                    target_instruction_set, &error_msg)) {
+        if (kVerboseLogging) {
+          LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
+                    << " is up-to-date for " << filename;
+        }
+        return kUpToDate;
+      } else if (location_checksum == oat_dex_file->GetDexFileLocationChecksum()
+                  && should_relocate_if_possible
+                  && ClassLinker::VerifyOatImageChecksum(oat_file.get(), target_instruction_set)) {
+        if (kVerboseLogging) {
+          LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
+                    << " needs to be relocated for " << filename;
+        }
+        return kPatchoatNeeded;
+      } else {
+        if (kVerboseLogging) {
+          LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
+                    << " is out of date for " << filename;
+        }
+        return kDexoptNeeded;
+      }
+    }
+  } else {
+    if (kVerboseLogging) {
+      LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename
+                << " does not contain " << filename;
+    }
+    return kDexoptNeeded;
+  }
+}
+
+static jbyte IsDexOptNeededInternal(JNIEnv* env, const char* filename,
     const char* pkgname, const char* instruction_set, const jboolean defer) {
+  // TODO disable this logging.
   const bool kVerboseLogging = false;  // Spammy logging.
   const bool kReasonLogging = true;  // Logging of reason for returning JNI_TRUE.
 
@@ -285,7 +373,7 @@
     ScopedLocalRef<jclass> fnfe(env, env->FindClass("java/io/FileNotFoundException"));
     const char* message = (filename == nullptr) ? "<empty file name>" : filename;
     env->ThrowNew(fnfe.get(), message);
-    return JNI_FALSE;
+    return kUpToDate;
   }
 
   // Always treat elements of the bootclasspath as up-to-date.  The
@@ -301,78 +389,45 @@
       if (kVerboseLogging) {
         LOG(INFO) << "DexFile_isDexOptNeeded ignoring boot class path file: " << filename;
       }
-      return JNI_FALSE;
+      return kUpToDate;
     }
   }
 
-  const InstructionSet target_instruction_set = GetInstructionSetFromString(instruction_set);
-
-  // Check if we have an odex file next to the dex file.
-  std::string odex_filename(DexFilenameToOdexFilename(filename, kRuntimeISA));
-  std::string error_msg;
-  std::unique_ptr<const OatFile> oat_file(OatFile::Open(odex_filename, odex_filename, NULL, false,
-                                                        &error_msg));
-  if (oat_file.get() == nullptr) {
-    if (kVerboseLogging) {
-      LOG(INFO) << "DexFile_isDexOptNeeded failed to open oat file '" << filename
-          << "': " << error_msg;
-    }
-    error_msg.clear();
-  } else {
-    const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename, NULL,
-                                                                           kReasonLogging);
-    if (oat_dex_file != nullptr) {
-      uint32_t location_checksum;
-      // If its not possible to read the classes.dex assume up-to-date as we won't be able to
-      // compile it anyway.
-      if (!DexFile::GetChecksum(filename, &location_checksum, &error_msg)) {
-        if (kVerboseLogging) {
-          LOG(INFO) << "DexFile_isDexOptNeeded ignoring precompiled stripped file: "
-              << filename << ": " << error_msg;
-        }
-        return JNI_FALSE;
-      }
-      if (ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename, location_checksum,
-                                              target_instruction_set,
-                                              &error_msg)) {
-        if (kVerboseLogging) {
-          LOG(INFO) << "DexFile_isDexOptNeeded precompiled file " << odex_filename
-              << " has an up-to-date checksum compared to " << filename;
-        }
-        return JNI_FALSE;
-      } else {
-        if (kVerboseLogging) {
-          LOG(INFO) << "DexFile_isDexOptNeeded found precompiled file " << odex_filename
-              << " with an out-of-date checksum compared to " << filename
-              << ": " << error_msg;
-        }
-        error_msg.clear();
-      }
-    }
-  }
+  bool force_system_only = false;
+  bool require_system_version = false;
 
   // Check the profile file.  We need to rerun dex2oat if the profile has changed significantly
   // since the last time, or it's new.
   // If the 'defer' argument is true then this will be retried later.  In this case we
   // need to make sure that the profile file copy is not made so that we will get the
   // same result second time.
+  std::string profile_file;
+  std::string prev_profile_file;
+  bool should_copy_profile = false;
   if (Runtime::Current()->GetProfilerOptions().IsEnabled() && (pkgname != nullptr)) {
-    const std::string profile_file = GetDalvikCacheOrDie("profiles", false /* create_if_absent */)
+    profile_file = GetDalvikCacheOrDie("profiles", false /* create_if_absent */)
         + std::string("/") + pkgname;
-    const std::string prev_profile_file = profile_file + std::string("@old");
+    prev_profile_file = profile_file + std::string("@old");
 
     struct stat profstat, prevstat;
     int e1 = stat(profile_file.c_str(), &profstat);
+    int e1_errno = errno;
     int e2 = stat(prev_profile_file.c_str(), &prevstat);
+    int e2_errno = errno;
     if (e1 < 0) {
-      // No profile file, need to run dex2oat
-      if (kReasonLogging) {
-        LOG(INFO) << "DexFile_isDexOptNeeded profile file " << profile_file << " doesn't exist";
+      if (e1_errno != EACCES) {
+        // No profile file, need to run dex2oat, unless we find a file in system
+        if (kReasonLogging) {
+          LOG(INFO) << "DexFile_isDexOptNeededInternal profile file " << profile_file << " doesn't exist. "
+                    << "Will check odex to see if we can find a working version.";
+        }
+        // Force it to only accept system files/files with versions in system.
+        require_system_version = true;
+      } else {
+        LOG(INFO) << "DexFile_isDexOptNeededInternal recieved EACCES trying to stat profile file "
+                  << profile_file;
       }
-      return JNI_TRUE;
-    }
-
-    if (e2 == 0) {
+    } else if (e2 == 0) {
       // There is a previous profile file.  Check if the profile has changed significantly.
       // A change in profile is considered significant if X% (change_thr property) of the top K%
       // (compile_thr property) samples has changed.
@@ -384,7 +439,7 @@
       bool old_ok = old_profile.LoadFile(prev_profile_file);
       if (!new_ok || !old_ok) {
         if (kVerboseLogging) {
-          LOG(INFO) << "DexFile_isDexOptNeeded Ignoring invalid profiles: "
+          LOG(INFO) << "DexFile_isDexOptNeededInternal Ignoring invalid profiles: "
                     << (new_ok ?  "" : profile_file) << " " << (old_ok ? "" : prev_profile_file);
         }
       } else {
@@ -393,7 +448,7 @@
         old_profile.GetTopKSamples(old_top_k, top_k_threshold);
         if (new_top_k.empty()) {
           if (kVerboseLogging) {
-            LOG(INFO) << "DexFile_isDexOptNeeded empty profile: " << profile_file;
+            LOG(INFO) << "DexFile_isDexOptNeededInternal empty profile: " << profile_file;
           }
           // If the new topK is empty we shouldn't optimize so we leave the change_percent at 0.0.
         } else {
@@ -405,7 +460,7 @@
           if (kVerboseLogging) {
             std::set<std::string>::iterator end = diff.end();
             for (std::set<std::string>::iterator it = diff.begin(); it != end; it++) {
-              LOG(INFO) << "DexFile_isDexOptNeeded new in topK: " << *it;
+              LOG(INFO) << "DexFile_isDexOptNeededInternal new in topK: " << *it;
             }
           }
         }
@@ -413,67 +468,84 @@
 
       if (change_percent > change_threshold) {
         if (kReasonLogging) {
-          LOG(INFO) << "DexFile_isDexOptNeeded size of new profile file " << profile_file <<
+          LOG(INFO) << "DexFile_isDexOptNeededInternal size of new profile file " << profile_file <<
           " is significantly different from old profile file " << prev_profile_file << " (top "
           << top_k_threshold << "% samples changed in proportion of " << change_percent << "%)";
         }
-        if (!defer) {
-          CopyProfileFile(profile_file.c_str(), prev_profile_file.c_str());
-        }
-        return JNI_TRUE;
+        should_copy_profile = !defer;
+        // Force us to only accept system files.
+        force_system_only = true;
       }
-    } else {
+    } else if (e2_errno == ENOENT) {
       // Previous profile does not exist.  Make a copy of the current one.
       if (kVerboseLogging) {
-        LOG(INFO) << "DexFile_isDexOptNeeded previous profile doesn't exist: " << prev_profile_file;
+        LOG(INFO) << "DexFile_isDexOptNeededInternal previous profile doesn't exist: " << prev_profile_file;
       }
-      if (!defer) {
-        CopyProfileFile(profile_file.c_str(), prev_profile_file.c_str());
-      }
+      should_copy_profile = !defer;
+    } else {
+      PLOG(INFO) << "Unable to stat previous profile file " << prev_profile_file;
     }
   }
 
-  // Check if we have an oat file in the cache
-  const std::string cache_dir(GetDalvikCacheOrDie(instruction_set));
-  const std::string cache_location(
-      GetDalvikCacheFilenameOrDie(filename, cache_dir.c_str()));
-  oat_file.reset(OatFile::Open(cache_location, filename, NULL, false, &error_msg));
-  if (oat_file.get() == nullptr) {
-    if (kReasonLogging) {
-      LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
-          << " does not exist for " << filename << ": " << error_msg;
+  const InstructionSet target_instruction_set = GetInstructionSetFromString(instruction_set);
+
+  // Get the filename for odex file next to the dex file.
+  std::string odex_filename(DexFilenameToOdexFilename(filename, target_instruction_set));
+  // Get the filename for the dalvik-cache file
+  std::string cache_dir;
+  bool have_android_data = false;
+  bool dalvik_cache_exists = false;
+  GetDalvikCache(instruction_set, false, &cache_dir, &have_android_data, &dalvik_cache_exists);
+  std::string cache_filename;  // was cache_location
+  bool have_cache_filename = false;
+  if (dalvik_cache_exists) {
+    std::string error_msg;
+    have_cache_filename = GetDalvikCacheFilename(filename, cache_dir.c_str(), &cache_filename,
+                                                 &error_msg);
+    if (!have_cache_filename && kVerboseLogging) {
+      LOG(INFO) << "DexFile_isDexOptNeededInternal failed to find cache file for dex file " << filename
+                << ": " << error_msg;
     }
-    return JNI_TRUE;
   }
 
-  uint32_t location_checksum;
-  if (!DexFile::GetChecksum(filename, &location_checksum, &error_msg)) {
-    if (kReasonLogging) {
-      LOG(ERROR) << "DexFile_isDexOptNeeded failed to compute checksum of " << filename
-            << " (error " << error_msg << ")";
+  bool should_relocate_if_possible = Runtime::Current()->ShouldRelocate();
+
+  InstructionSet isa = Runtime::Current()->GetInstructionSet();
+  jbyte dalvik_cache_decision = -1;
+  // Lets try the cache first (since we want to load from there since thats where the relocated
+  // versions will be).
+  if (have_cache_filename && !force_system_only) {
+    // We can use the dalvik-cache if we find a good file.
+    dalvik_cache_decision =
+        IsDexOptNeededForFile<kVerboseLogging, kReasonLogging>(cache_filename, filename, isa);
+    // We will only return DexOptNeeded if both the cache and system return it.
+    if (dalvik_cache_decision != kDexoptNeeded && !require_system_version) {
+      CHECK(!(dalvik_cache_decision == kPatchoatNeeded && !should_relocate_if_possible))
+          << "May not return PatchoatNeeded when patching is disabled.";
+      return dalvik_cache_decision;
     }
-    return JNI_TRUE;
+    // We couldn't find one thats easy. We should now try the system.
   }
 
-  if (!ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename, location_checksum,
-                                           target_instruction_set, &error_msg)) {
-    if (kReasonLogging) {
-      LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
-          << " has out-of-date checksum compared to " << filename
-          << " (error " << error_msg << ")";
-    }
-    return JNI_TRUE;
+  jbyte system_decision =
+      IsDexOptNeededForFile<kVerboseLogging, kReasonLogging>(odex_filename, filename, isa);
+  CHECK(!(system_decision == kPatchoatNeeded && !should_relocate_if_possible))
+      << "May not return PatchoatNeeded when patching is disabled.";
+
+  if (require_system_version && system_decision == kPatchoatNeeded
+                             && dalvik_cache_decision == kUpToDate) {
+    // We have a version from system relocated to the cache. Return it.
+    return dalvik_cache_decision;
   }
 
-  if (kVerboseLogging) {
-    LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
-              << " is up-to-date for " << filename;
+  if (should_copy_profile && system_decision == kDexoptNeeded) {
+    CopyProfileFile(profile_file.c_str(), prev_profile_file.c_str());
   }
-  CHECK(error_msg.empty()) << error_msg;
-  return JNI_FALSE;
+
+  return system_decision;
 }
 
-static jboolean DexFile_isDexOptNeededInternal(JNIEnv* env, jclass, jstring javaFilename,
+static jbyte DexFile_isDexOptNeededInternal(JNIEnv* env, jclass, jstring javaFilename,
     jstring javaPkgname, jstring javaInstructionSet, jboolean defer) {
   ScopedUtfChars filename(env, javaFilename);
   NullableScopedUtfChars pkgname(env, javaPkgname);
@@ -487,8 +559,8 @@
 static jboolean DexFile_isDexOptNeeded(JNIEnv* env, jclass, jstring javaFilename) {
   const char* instruction_set = GetInstructionSetString(kRuntimeISA);
   ScopedUtfChars filename(env, javaFilename);
-  return IsDexOptNeededInternal(env, filename.c_str(), nullptr /* pkgname */,
-                                instruction_set, false /* defer */);
+  return kUpToDate != IsDexOptNeededInternal(env, filename.c_str(), nullptr /* pkgname */,
+                                             instruction_set, false /* defer */);
 }
 
 
@@ -497,7 +569,7 @@
   NATIVE_METHOD(DexFile, defineClassNative, "(Ljava/lang/String;Ljava/lang/ClassLoader;J)Ljava/lang/Class;"),
   NATIVE_METHOD(DexFile, getClassNameList, "(J)[Ljava/lang/String;"),
   NATIVE_METHOD(DexFile, isDexOptNeeded, "(Ljava/lang/String;)Z"),
-  NATIVE_METHOD(DexFile, isDexOptNeededInternal, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)Z"),
+  NATIVE_METHOD(DexFile, isDexOptNeededInternal, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)B"),
   NATIVE_METHOD(DexFile, openDexFile, "(Ljava/lang/String;Ljava/lang/String;I)J"),
 };
 
diff --git a/runtime/parsed_options.h b/runtime/parsed_options.h
index 668ed9e..3dbe26f 100644
--- a/runtime/parsed_options.h
+++ b/runtime/parsed_options.h
@@ -48,8 +48,6 @@
   std::string native_bridge_library_string_;
   CompilerCallbacks* compiler_callbacks_;
   bool is_zygote_;
-  // TODO Change this to true when we want it on by default.
-  static constexpr bool kDefaultMustRelocate = false;
   bool must_relocate_;
   std::string patchoat_executable_;
   bool interpreter_only_;
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 52cdcc1..4d49809 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -1236,7 +1236,7 @@
     return false;
   }
   std::string cache_file(&location[1]);  // skip leading slash
-  if (!EndsWith(location, ".dex") && !EndsWith(location, ".art")) {
+  if (!EndsWith(location, ".dex") && !EndsWith(location, ".art") && !EndsWith(location, ".oat")) {
     cache_file += "/";
     cache_file += DexFile::kClassesDex;
   }
diff --git a/runtime/utils_test.cc b/runtime/utils_test.cc
index 7cd5980..d6c90e1 100644
--- a/runtime/utils_test.cc
+++ b/runtime/utils_test.cc
@@ -350,6 +350,8 @@
                GetDalvikCacheFilenameOrDie("/system/framework/core.jar", "/foo").c_str());
   EXPECT_STREQ("/foo/system@framework@boot.art",
                GetDalvikCacheFilenameOrDie("/system/framework/boot.art", "/foo").c_str());
+  EXPECT_STREQ("/foo/system@framework@boot.oat",
+               GetDalvikCacheFilenameOrDie("/system/framework/boot.oat", "/foo").c_str());
 }
 
 TEST_F(UtilsTest, GetSystemImageFilename) {