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/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"),
 };