Merge "Prevent spurious dexopts in 32-64 builds."
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 703229c..18afa02 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -803,13 +803,29 @@
 bool ClassLinker::VerifyOatFileChecksums(const OatFile* oat_file,
                                          const char* dex_location,
                                          uint32_t dex_location_checksum,
+                                         const InstructionSet instruction_set,
                                          std::string* error_msg) {
   Runtime* runtime = Runtime::Current();
-  const ImageHeader& image_header = runtime->GetHeap()->GetImageSpace()->GetImageHeader();
-  uint32_t image_oat_checksum = image_header.GetOatChecksum();
-  uintptr_t image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin());
-  bool image_check = ((oat_file->GetOatHeader().GetImageFileLocationOatChecksum() == image_oat_checksum)
-                      && (oat_file->GetOatHeader().GetImageFileLocationOatDataBegin() == image_oat_data_begin));
+  const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
+
+  // If the requested instruction set is the same as the current runtime,
+  // we can use the checksums directly. If it isn't, we'll have to read the
+  // image header from the image for the right instruction set.
+  uint32_t image_oat_checksum = 0;
+  uintptr_t image_oat_data_begin = 0;
+  if (instruction_set == kRuntimeISA) {
+    const ImageHeader& image_header = image_space->GetImageHeader();
+    image_oat_checksum = image_header.GetOatChecksum();
+    image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin());
+  } else {
+    UniquePtr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
+        image_space->GetImageLocation().c_str(), instruction_set));
+    image_oat_checksum = image_header->GetOatChecksum();
+    image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin());
+  }
+  const OatHeader& oat_header = oat_file->GetOatHeader();
+  bool image_check = ((oat_header.GetImageFileLocationOatChecksum() == image_oat_checksum)
+                      && (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin));
 
   const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, &dex_location_checksum);
   if (oat_dex_file == NULL) {
@@ -873,7 +889,7 @@
     dex_file = oat_dex_file->OpenDexFile(error_msg);
   } else {
     bool verified = VerifyOatFileChecksums(oat_file.get(), dex_location, dex_location_checksum,
-                                           error_msg);
+                                           kRuntimeISA, error_msg);
     if (!verified) {
       return nullptr;
     }
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index a23add0..2c6873e 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -288,6 +288,7 @@
   static bool VerifyOatFileChecksums(const OatFile* oat_file,
                                      const char* dex_location,
                                      uint32_t dex_location_checksum,
+                                     const InstructionSet instruction_set,
                                      std::string* error_msg);
 
   // TODO: replace this with multiple methods that allocate the correct managed type.
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 3de1ba4..858582e 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -33,15 +33,16 @@
 
 Atomic<uint32_t> ImageSpace::bitmap_index_(0);
 
-ImageSpace::ImageSpace(const std::string& name, MemMap* mem_map,
-                       accounting::ContinuousSpaceBitmap* live_bitmap)
-    : MemMapSpace(name, mem_map, mem_map->Begin(), mem_map->End(), mem_map->End(),
-                  kGcRetentionPolicyNeverCollect) {
+ImageSpace::ImageSpace(const std::string& image_filename, const char* image_location,
+                       MemMap* mem_map, accounting::ContinuousSpaceBitmap* live_bitmap)
+    : MemMapSpace(image_filename, mem_map, mem_map->Begin(), mem_map->End(), mem_map->End(),
+                  kGcRetentionPolicyNeverCollect),
+      image_location_(image_location) {
   DCHECK(live_bitmap != nullptr);
   live_bitmap_.reset(live_bitmap);
 }
 
-static bool GenerateImage(const std::string& image_file_name, std::string* error_msg) {
+static bool GenerateImage(const std::string& image_filename, std::string* error_msg) {
   const std::string boot_class_path_string(Runtime::Current()->GetBootClassPathString());
   std::vector<std::string> boot_class_path;
   Split(boot_class_path_string, ':', boot_class_path);
@@ -57,7 +58,7 @@
   arg_vector.push_back(dex2oat);
 
   std::string image_option_string("--image=");
-  image_option_string += image_file_name;
+  image_option_string += image_filename;
   arg_vector.push_back(image_option_string);
 
   arg_vector.push_back("--runtime-arg");
@@ -72,7 +73,7 @@
   }
 
   std::string oat_file_option_string("--oat-file=");
-  oat_file_option_string += image_file_name;
+  oat_file_option_string += image_filename;
   oat_file_option_string.erase(oat_file_option_string.size() - 3);
   oat_file_option_string += "oat";
   arg_vector.push_back(oat_file_option_string);
@@ -98,37 +99,72 @@
   return Exec(arg_vector, error_msg);
 }
 
-ImageSpace* ImageSpace::Create(const char* original_image_file_name,
-                               const InstructionSet image_isa) {
-  if (OS::FileExists(original_image_file_name)) {
-    // If the /system file exists, it should be up-to-date, don't try to generate
-    std::string error_msg;
-    ImageSpace* space = ImageSpace::Init(original_image_file_name, false, &error_msg);
-    if (space == nullptr) {
-      LOG(FATAL) << "Failed to load image '" << original_image_file_name << "': " << error_msg;
-    }
-    return space;
+bool ImageSpace::FindImageFilename(const char* image_location,
+                                   const InstructionSet image_isa,
+                                   std::string* image_filename,
+                                   bool *is_system) {
+  if (OS::FileExists(image_location)) {
+    *image_filename = image_location;
+    *is_system = true;
+    return true;
   }
-  // If the /system file didn't exist, we need to use one from the dalvik-cache.
-  // If the cache file exists, try to open, but if it fails, regenerate.
-  // If it does not exist, generate.
+
   const std::string dalvik_cache = GetDalvikCacheOrDie(GetInstructionSetString(image_isa));
-  std::string image_file_name(GetDalvikCacheFilenameOrDie(original_image_file_name,
-                                                          dalvik_cache.c_str()));
+
+  // Always set output location even if it does not exist,
+  // so that the caller knows where to create the image.
+  *image_filename = GetDalvikCacheFilenameOrDie(image_location, dalvik_cache.c_str());
+  *is_system = false;
+  return OS::FileExists(image_filename->c_str());
+}
+
+ImageHeader* ImageSpace::ReadImageHeaderOrDie(const char* image_location,
+                                              const InstructionSet image_isa) {
+  std::string image_filename;
+  bool is_system = false;
+  if (FindImageFilename(image_location, image_isa, &image_filename, &is_system)) {
+    UniquePtr<File> image_file(OS::OpenFileForReading(image_filename.c_str()));
+    UniquePtr<ImageHeader> image_header(new ImageHeader);
+    const bool success = image_file->ReadFully(image_header.get(), sizeof(ImageHeader));
+    if (!success || !image_header->IsValid()) {
+      LOG(FATAL) << "Invalid Image header for: " << image_filename;
+      return nullptr;
+    }
+
+    return image_header.release();
+  }
+
+  LOG(FATAL) << "Unable to find image file for: " << image_location;
+  return nullptr;
+}
+
+ImageSpace* ImageSpace::Create(const char* image_location,
+                               const InstructionSet image_isa) {
+  std::string image_filename;
   std::string error_msg;
-  if (OS::FileExists(image_file_name.c_str())) {
-    space::ImageSpace* image_space = ImageSpace::Init(image_file_name.c_str(), true, &error_msg);
-    if (image_space != nullptr) {
-      return image_space;
+  bool is_system = false;
+  if (FindImageFilename(image_location, image_isa, &image_filename, &is_system)) {
+    ImageSpace* space = ImageSpace::Init(image_filename.c_str(), image_location,
+                                         !is_system, &error_msg);
+    if (space != nullptr) {
+      return space;
+    }
+
+    // If the /system file exists, it should be up-to-date, don't try to generate it.
+    // If it's not the /system file, log a warning and fall through to GenerateImage.
+    if (is_system) {
+      LOG(FATAL) << "Failed to load image '" << image_filename << "': " << error_msg;
+      return nullptr;
     } else {
       LOG(WARNING) << error_msg;
     }
   }
-  CHECK(GenerateImage(image_file_name, &error_msg))
-      << "Failed to generate image '" << image_file_name << "': " << error_msg;
-  ImageSpace* space = ImageSpace::Init(image_file_name.c_str(), true, &error_msg);
+
+  CHECK(GenerateImage(image_filename, &error_msg))
+      << "Failed to generate image '" << image_filename << "': " << error_msg;
+  ImageSpace* space = ImageSpace::Init(image_filename.c_str(), image_location, true, &error_msg);
   if (space == nullptr) {
-    LOG(FATAL) << "Failed to load image '" << original_image_file_name << "': " << error_msg;
+    LOG(FATAL) << "Failed to load image '" << image_filename << "': " << error_msg;
   }
   return space;
 }
@@ -147,25 +183,26 @@
   }
 }
 
-ImageSpace* ImageSpace::Init(const char* image_file_name, bool validate_oat_file,
-                             std::string* error_msg) {
-  CHECK(image_file_name != nullptr);
+ImageSpace* ImageSpace::Init(const char* image_filename, const char* image_location,
+                             bool validate_oat_file, std::string* error_msg) {
+  CHECK(image_filename != nullptr);
+  CHECK(image_location != nullptr);
 
   uint64_t start_time = 0;
   if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) {
     start_time = NanoTime();
-    LOG(INFO) << "ImageSpace::Init entering image_file_name=" << image_file_name;
+    LOG(INFO) << "ImageSpace::Init entering image_filename=" << image_filename;
   }
 
-  UniquePtr<File> file(OS::OpenFileForReading(image_file_name));
+  UniquePtr<File> file(OS::OpenFileForReading(image_filename));
   if (file.get() == NULL) {
-    *error_msg = StringPrintf("Failed to open '%s'", image_file_name);
+    *error_msg = StringPrintf("Failed to open '%s'", image_filename);
     return nullptr;
   }
   ImageHeader image_header;
   bool success = file->ReadFully(&image_header, sizeof(image_header));
   if (!success || !image_header.IsValid()) {
-    *error_msg = StringPrintf("Invalid image header in '%s'", image_file_name);
+    *error_msg = StringPrintf("Invalid image header in '%s'", image_filename);
     return nullptr;
   }
 
@@ -177,7 +214,7 @@
                                                  file->Fd(),
                                                  0,
                                                  false,
-                                                 image_file_name,
+                                                 image_filename,
                                                  error_msg));
   if (map.get() == NULL) {
     DCHECK(!error_msg->empty());
@@ -190,14 +227,14 @@
                                                        PROT_READ, MAP_PRIVATE,
                                                        file->Fd(), image_header.GetBitmapOffset(),
                                                        false,
-                                                       image_file_name,
+                                                       image_filename,
                                                        error_msg));
   if (image_map.get() == nullptr) {
     *error_msg = StringPrintf("Failed to map image bitmap: %s", error_msg->c_str());
     return nullptr;
   }
   uint32_t bitmap_index = bitmap_index_.FetchAndAdd(1);
-  std::string bitmap_name(StringPrintf("imagespace %s live-bitmap %u", image_file_name,
+  std::string bitmap_name(StringPrintf("imagespace %s live-bitmap %u", image_filename,
                                        bitmap_index));
   UniquePtr<accounting::ContinuousSpaceBitmap> bitmap(
       accounting::ContinuousSpaceBitmap::CreateFromMemMap(bitmap_name, image_map.release(),
@@ -223,12 +260,13 @@
   callee_save_method = image_header.GetImageRoot(ImageHeader::kRefsAndArgsSaveMethod);
   runtime->SetCalleeSaveMethod(down_cast<mirror::ArtMethod*>(callee_save_method), Runtime::kRefsAndArgs);
 
-  UniquePtr<ImageSpace> space(new ImageSpace(image_file_name, map.release(), bitmap.release()));
+  UniquePtr<ImageSpace> space(new ImageSpace(image_filename, image_location,
+                                             map.release(), bitmap.release()));
   if (kIsDebugBuild) {
     space->VerifyImageAllocations();
   }
 
-  space->oat_file_.reset(space->OpenOatFile(image_file_name, error_msg));
+  space->oat_file_.reset(space->OpenOatFile(image_filename, error_msg));
   if (space->oat_file_.get() == nullptr) {
     DCHECK(!error_msg->empty());
     return nullptr;
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 1652ec9..622371f 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -46,6 +46,11 @@
   static ImageSpace* Create(const char* image, const InstructionSet image_isa)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Reads the image header from the specified image location for the
+  // instruction set image_isa.
+  static ImageHeader* ReadImageHeaderOrDie(const char* image_location,
+                                           const InstructionSet image_isa);
+
   // Releases the OatFile from the ImageSpace so it can be transfer to
   // the caller, presumably the ClassLinker.
   OatFile* ReleaseOatFile()
@@ -58,10 +63,18 @@
     return *reinterpret_cast<ImageHeader*>(Begin());
   }
 
+  // Actual filename where image was loaded from.
+  // For example: /data/dalvik-cache/arm/system@framework@boot.art
   const std::string GetImageFilename() const {
     return GetName();
   }
 
+  // Symbolic location for image.
+  // For example: /system/framework/boot.art
+  const std::string GetImageLocation() const {
+    return image_location_;
+  }
+
   accounting::ContinuousSpaceBitmap* GetLiveBitmap() const OVERRIDE {
     return live_bitmap_.get();
   }
@@ -90,9 +103,21 @@
   // image's OatFile is up-to-date relative to its DexFile
   // inputs. Otherwise (for /data), validate the inputs and generate
   // the OatFile in /data/dalvik-cache if necessary.
-  static ImageSpace* Init(const char* image, bool validate_oat_file, std::string* error_msg)
+  static ImageSpace* Init(const char* image_filename, const char* image_location,
+                          bool validate_oat_file, std::string* error_msg)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  // Returns the filename of the image corresponding to
+  // requested image_location, or the filename where a new image
+  // should be written if one doesn't exist. Looks for a generated
+  // image in the specified location and then in the dalvik-cache.
+  //
+  // Returns true if an image was found, false otherwise.
+  static bool FindImageFilename(const char* image_location,
+                                const InstructionSet image_isa,
+                                std::string* location,
+                                bool* is_system);
+
   OatFile* OpenOatFile(const char* image, std::string* error_msg) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
@@ -105,14 +130,16 @@
 
   UniquePtr<accounting::ContinuousSpaceBitmap> live_bitmap_;
 
-  ImageSpace(const std::string& name, MemMap* mem_map,
-             accounting::ContinuousSpaceBitmap* live_bitmap);
+  ImageSpace(const std::string& name, const char* image_location,
+             MemMap* mem_map, accounting::ContinuousSpaceBitmap* live_bitmap);
 
   // The OatFile associated with the image during early startup to
   // reserve space contiguous to the image. It is later released to
   // the ClassLinker during it's initialization.
   UniquePtr<OatFile> oat_file_;
 
+  const std::string image_location_;
+
   DISALLOW_COPY_AND_ASSIGN(ImageSpace);
 };
 
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index d9c1309..ed1ee7a 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -377,6 +377,8 @@
     }
   }
 
+  const InstructionSet target_instruction_set = GetInstructionSetFromString(instruction_set);
+
   // Check if we have an odex file next to the dex file.
   std::string odex_filename(OatFile::DexFilenameToOdexFilename(filename));
   std::string error_msg;
@@ -403,6 +405,7 @@
         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
@@ -433,33 +436,6 @@
     return JNI_TRUE;
   }
 
-  for (const auto& space : runtime->GetHeap()->GetContinuousSpaces()) {
-    if (space->IsImageSpace()) {
-      // TODO: Ensure this works with multiple image spaces.
-      const ImageHeader& image_header = space->AsImageSpace()->GetImageHeader();
-      if (oat_file->GetOatHeader().GetImageFileLocationOatChecksum() !=
-          image_header.GetOatChecksum()) {
-        if (kReasonLogging) {
-          ScopedObjectAccess soa(env);
-          LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
-              << " has out-of-date oat checksum compared to "
-              << oat_file->GetLocation();
-        }
-        return JNI_TRUE;
-      }
-      if (oat_file->GetOatHeader().GetImageFileLocationOatDataBegin()
-          != reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin())) {
-        if (kReasonLogging) {
-          ScopedObjectAccess soa(env);
-          LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
-              << " has out-of-date oat begin compared to "
-              << oat_file->GetLocation();
-        }
-        return JNI_TRUE;
-      }
-    }
-  }
-
   uint32_t location_checksum;
   if (!DexFile::GetChecksum(filename, &location_checksum, &error_msg)) {
     if (kReasonLogging) {
@@ -470,7 +446,7 @@
   }
 
   if (!ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename, location_checksum,
-                                           &error_msg)) {
+                                           target_instruction_set, &error_msg)) {
     if (kReasonLogging) {
       LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
           << " has out-of-date checksum compared to " << filename