Merge "Fix an assert during jdwp debugging."
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 00ff522..be720ad 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -1028,6 +1028,9 @@
     for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
       mirror::DexCache* dex_cache =
           down_cast<mirror::DexCache*>(self->DecodeJObject(data.weak_root));
+      if (dex_cache == nullptr) {
+        continue;
+      }
       const DexFile* dex_file = dex_cache->GetDexFile();
       if (!IsInBootImage(dex_cache)) {
         dex_cache_count += image_dex_files.find(dex_file) != image_dex_files.end() ? 1u : 0u;
@@ -1044,6 +1047,9 @@
     for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
       mirror::DexCache* dex_cache =
           down_cast<mirror::DexCache*>(self->DecodeJObject(data.weak_root));
+      if (dex_cache == nullptr) {
+        continue;
+      }
       const DexFile* dex_file = dex_cache->GetDexFile();
       if (!IsInBootImage(dex_cache)) {
         non_image_dex_caches += image_dex_files.find(dex_file) != image_dex_files.end() ? 1u : 0u;
@@ -1055,6 +1061,9 @@
     for (const ClassLinker::DexCacheData& data : class_linker->GetDexCachesData()) {
       mirror::DexCache* dex_cache =
           down_cast<mirror::DexCache*>(self->DecodeJObject(data.weak_root));
+      if (dex_cache == nullptr) {
+        continue;
+      }
       const DexFile* dex_file = dex_cache->GetDexFile();
       if (!IsInBootImage(dex_cache) && image_dex_files.find(dex_file) != image_dex_files.end()) {
         dex_caches->Set<false>(i, dex_cache);
@@ -1213,18 +1222,17 @@
           AssignMethodOffset(&m, type, oat_index);
         }
         (any_dirty ? dirty_methods_ : clean_methods_) += num_methods;
-
-        // Assign offsets for all runtime methods in the IMT since these may hold conflict tables
-        // live.
-        if (as_klass->ShouldHaveEmbeddedImtAndVTable()) {
-          for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
-            ArtMethod* imt_method = as_klass->GetEmbeddedImTableEntry(i, target_ptr_size_);
-            DCHECK(imt_method != nullptr);
-            if (imt_method->IsRuntimeMethod() &&
-                !IsInBootImage(imt_method) &&
-                !NativeRelocationAssigned(imt_method)) {
-              AssignMethodOffset(imt_method, kNativeObjectRelocationTypeRuntimeMethod, oat_index);
-            }
+      }
+      // Assign offsets for all runtime methods in the IMT since these may hold conflict tables
+      // live.
+      if (as_klass->ShouldHaveEmbeddedImtAndVTable()) {
+        for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
+          ArtMethod* imt_method = as_klass->GetEmbeddedImTableEntry(i, target_ptr_size_);
+          DCHECK(imt_method != nullptr);
+          if (imt_method->IsRuntimeMethod() &&
+              !IsInBootImage(imt_method) &&
+              !NativeRelocationAssigned(imt_method)) {
+            AssignMethodOffset(imt_method, kNativeObjectRelocationTypeRuntimeMethod, oat_index);
           }
         }
       }
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 4e3ace4..de04175 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -1124,7 +1124,7 @@
   SlowPathCode* slow_path = nullptr;
   HInstruction* code_point = invoke->InputAt(1);
   if (code_point->IsIntConstant()) {
-    if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) >
+    if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) >
         std::numeric_limits<uint16_t>::max()) {
       // Always needs the slow-path. We could directly dispatch to it, but this case should be
       // rare, so for simplicity just put the full slow-path down and branch unconditionally.
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index cc5fd65..6cd1726 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1399,7 +1399,7 @@
   SlowPathCodeARM64* slow_path = nullptr;
   HInstruction* code_point = invoke->InputAt(1);
   if (code_point->IsIntConstant()) {
-    if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) > 0xFFFFU) {
+    if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) > 0xFFFFU) {
       // Always needs the slow-path. We could directly dispatch to it, but this case should be
       // rare, so for simplicity just put the full slow-path down and branch unconditionally.
       slow_path = new (allocator) IntrinsicSlowPathARM64(invoke);
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 20b61f8..fa250a3 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -2072,7 +2072,7 @@
   SlowPathCodeMIPS* slow_path = nullptr;
   HInstruction* code_point = invoke->InputAt(1);
   if (code_point->IsIntConstant()) {
-    if (!IsUint<16>(invoke->InputAt(1)->AsIntConstant()->GetValue())) {
+    if (!IsUint<16>(code_point->AsIntConstant()->GetValue())) {
       // Always needs the slow-path. We could directly dispatch to it,
       // but this case should be rare, so for simplicity just put the
       // full slow-path down and branch unconditionally.
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index 7188e1c..6c4e64e 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1568,7 +1568,7 @@
   SlowPathCodeMIPS64* slow_path = nullptr;
   HInstruction* code_point = invoke->InputAt(1);
   if (code_point->IsIntConstant()) {
-    if (!IsUint<16>(invoke->InputAt(1)->AsIntConstant()->GetValue())) {
+    if (!IsUint<16>(code_point->AsIntConstant()->GetValue())) {
       // Always needs the slow-path. We could directly dispatch to it,
       // but this case should be rare, so for simplicity just put the
       // full slow-path down and branch unconditionally.
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index d0edeca..99bc40e 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -1422,7 +1422,7 @@
   SlowPathCode* slow_path = nullptr;
   HInstruction* code_point = invoke->InputAt(1);
   if (code_point->IsIntConstant()) {
-    if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) >
+    if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) >
     std::numeric_limits<uint16_t>::max()) {
       // Always needs the slow-path. We could directly dispatch to it, but this case should be
       // rare, so for simplicity just put the full slow-path down and branch unconditionally.
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 4ee2368..06e9cc2 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -1521,7 +1521,7 @@
   SlowPathCode* slow_path = nullptr;
   HInstruction* code_point = invoke->InputAt(1);
   if (code_point->IsIntConstant()) {
-    if (static_cast<uint32_t>(invoke->InputAt(1)->AsIntConstant()->GetValue()) >
+    if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) >
     std::numeric_limits<uint16_t>::max()) {
       // Always needs the slow-path. We could directly dispatch to it, but this case should be
       // rare, so for simplicity just put the full slow-path down and branch unconditionally.
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index be38336..cb274dc 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1312,7 +1312,7 @@
     if (IsBootImage() && image_filenames_.size() > 1) {
       // If we're compiling the boot image, store the boot classpath into the Key-Value store.
       // We need this for the multi-image case.
-      key_value_store_->Put(OatHeader::kBootClassPath, GetMultiImageBootClassPath());
+      key_value_store_->Put(OatHeader::kBootClassPathKey, GetMultiImageBootClassPath());
     }
 
     if (!IsBootImage()) {
@@ -1348,12 +1348,22 @@
       // Open dex files for class path.
       const std::vector<std::string> class_path_locations =
           GetClassPathLocations(runtime_->GetClassPathString());
-      OpenClassPathFiles(class_path_locations, &class_path_files_);
+      OpenClassPathFiles(class_path_locations,
+                         &class_path_files_,
+                         &opened_oat_files_,
+                         runtime_->GetInstructionSet());
 
       // Store the classpath we have right now.
       std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(class_path_files_);
-      key_value_store_->Put(OatHeader::kClassPathKey,
-                            OatFile::EncodeDexFileDependencies(class_path_files));
+      std::string encoded_class_path;
+      if (class_path_locations.size() == 1 &&
+          class_path_locations[0] == OatFile::kSpecialSharedLibrary) {
+        // When passing the special shared library as the classpath, it is the only path.
+        encoded_class_path = OatFile::kSpecialSharedLibrary;
+      } else {
+        encoded_class_path = OatFile::EncodeDexFileDependencies(class_path_files);
+      }
+      key_value_store_->Put(OatHeader::kClassPathKey, encoded_class_path);
     }
 
     // Now that we have finalized key_value_store_, start writing the oat file.
@@ -1964,14 +1974,37 @@
     return parsed;
   }
 
-  // Opens requested class path files and appends them to opened_dex_files.
+  // Opens requested class path files and appends them to opened_dex_files. If the dex files have
+  // been stripped, this opens them from their oat files and appends them to opened_oat_files.
   static void OpenClassPathFiles(const std::vector<std::string>& class_path_locations,
-                                 std::vector<std::unique_ptr<const DexFile>>* opened_dex_files) {
-    DCHECK(opened_dex_files != nullptr) << "OpenClassPathFiles out-param is nullptr";
+                                 std::vector<std::unique_ptr<const DexFile>>* opened_dex_files,
+                                 std::vector<std::unique_ptr<OatFile>>* opened_oat_files,
+                                 InstructionSet isa) {
+    DCHECK(opened_dex_files != nullptr) << "OpenClassPathFiles dex out-param is nullptr";
+    DCHECK(opened_oat_files != nullptr) << "OpenClassPathFiles oat out-param is nullptr";
     for (const std::string& location : class_path_locations) {
+      // Stop early if we detect the special shared library, which may be passed as the classpath
+      // for dex2oat when we want to skip the shared libraries check.
+      if (location == OatFile::kSpecialSharedLibrary) {
+        break;
+      }
       std::string error_msg;
       if (!DexFile::Open(location.c_str(), location.c_str(), &error_msg, opened_dex_files)) {
-        LOG(WARNING) << "Failed to open dex file '" << location << "': " << error_msg;
+        // If we fail to open the dex file because it's been stripped, try to open the dex file
+        // from its corresponding oat file.
+        OatFileAssistant oat_file_assistant(location.c_str(), isa, false, false);
+        std::unique_ptr<OatFile> oat_file(oat_file_assistant.GetBestOatFile());
+        if (oat_file == nullptr) {
+          LOG(WARNING) << "Failed to open dex file and associated oat file for '" << location
+                       << "': " << error_msg;
+        } else {
+          std::vector<std::unique_ptr<const DexFile>> oat_dex_files =
+              oat_file_assistant.LoadDexFiles(*oat_file, location.c_str());
+          opened_oat_files->push_back(std::move(oat_file));
+          opened_dex_files->insert(opened_dex_files->end(),
+                                   std::make_move_iterator(oat_dex_files.begin()),
+                                   std::make_move_iterator(oat_dex_files.end()));
+        }
       }
     }
   }
@@ -2441,6 +2474,7 @@
   std::unique_ptr<CompilerDriver> driver_;
 
   std::vector<std::unique_ptr<MemMap>> opened_dex_files_maps_;
+  std::vector<std::unique_ptr<OatFile>> opened_oat_files_;
   std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
 
   std::vector<const DexFile*> no_inline_from_dex_files_;
diff --git a/profman/profman.cc b/profman/profman.cc
index 37a560d..4d9276f 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -189,7 +189,6 @@
       return -1;
     }
     std::string dump = info.DumpInfo(/*dex_files*/ nullptr);
-    info.Save(fd);
     std::cout << dump << "\n";
     return 0;
   }
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index e9b8643..1835c72 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1063,9 +1063,8 @@
   return true;
 }
 
-static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
-                              mirror::ClassLoader* class_loader)
-    SHARED_REQUIRES(Locks::mutator_lock_) {
+bool ClassLinker::IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+                                    mirror::ClassLoader* class_loader) {
   return class_loader == nullptr ||
       class_loader->GetClass() ==
           soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader);
@@ -1106,7 +1105,7 @@
       soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements);
   CHECK(dex_path_list_field != nullptr);
   CHECK(dex_elements_field != nullptr);
-  while (!IsBootClassLoader(soa, class_loader)) {
+  while (!ClassLinker::IsBootClassLoader(soa, class_loader)) {
     if (class_loader->GetClass() !=
         soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader)) {
       *error_msg = StringPrintf("Unknown class loader type %s", PrettyTypeOf(class_loader).c_str());
@@ -7815,7 +7814,8 @@
   return descriptor;
 }
 
-jobject ClassLinker::CreatePathClassLoader(Thread* self, std::vector<const DexFile*>& dex_files) {
+jobject ClassLinker::CreatePathClassLoader(Thread* self,
+                                           const std::vector<const DexFile*>& dex_files) {
   // SOAAlreadyRunnable is protected, and we need something to add a global reference.
   // We could move the jobject to the callers, but all call-sites do this...
   ScopedObjectAccessUnchecked soa(self);
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 45c0767..f6ce545 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -560,7 +560,7 @@
 
   // Creates a GlobalRef PathClassLoader that can be used to load classes from the given dex files.
   // Note: the objects are not completely set up. Do not use this outside of tests and the compiler.
-  jobject CreatePathClassLoader(Thread* self, std::vector<const DexFile*>& dex_files)
+  jobject CreatePathClassLoader(Thread* self, const std::vector<const DexFile*>& dex_files)
       SHARED_REQUIRES(Locks::mutator_lock_)
       REQUIRES(!dex_lock_);
 
@@ -611,6 +611,10 @@
       const std::set<DexCacheResolvedClasses>& classes)
       REQUIRES(!dex_lock_);
 
+  static bool IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+                                mirror::ClassLoader* class_loader)
+      SHARED_REQUIRES(Locks::mutator_lock_);
+
   ArtMethod* AddMethodToConflictTable(mirror::Class* klass,
                                       ArtMethod* conflict_method,
                                       ArtMethod* interface_method,
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index d241f0c..bbffbbb 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -1956,29 +1956,6 @@
   }
 
   if (item->superclass_idx_ != DexFile::kDexNoIndex16) {
-    // Check that a class does not inherit from itself directly (by having
-    // the same type idx as its super class).
-    if (UNLIKELY(item->superclass_idx_ == item->class_idx_)) {
-      ErrorStringPrintf("Class with same type idx as its superclass: '%d'", item->class_idx_);
-      return false;
-    }
-
-    // Check that a class is defined after its super class (if the
-    // latter is defined in the same Dex file).
-    const DexFile::ClassDef* superclass_def = dex_file_->FindClassDef(item->superclass_idx_);
-    if (superclass_def != nullptr) {
-      // The superclass is defined in this Dex file.
-      if (superclass_def > item) {
-        // ClassDef item for super class appearing after the class' ClassDef item.
-        ErrorStringPrintf("Invalid class definition ordering:"
-                          " class with type idx: '%d' defined before"
-                          " superclass with type idx: '%d'",
-                          item->class_idx_,
-                          item->superclass_idx_);
-        return false;
-      }
-    }
-
     LOAD_STRING_BY_TYPE(superclass_descriptor, item->superclass_idx_,
                         "inter_class_def_item superclass_idx")
     if (UNLIKELY(!IsValidDescriptor(superclass_descriptor) || superclass_descriptor[0] != 'L')) {
@@ -1987,37 +1964,12 @@
     }
   }
 
-  // Check interfaces.
   const DexFile::TypeList* interfaces = dex_file_->GetInterfacesList(*item);
   if (interfaces != nullptr) {
     uint32_t size = interfaces->Size();
+
+    // Ensure that all interfaces refer to classes (not arrays or primitives).
     for (uint32_t i = 0; i < size; i++) {
-      // Check that a class does not implement itself directly (by having the
-      // same type idx as one of its immediate implemented interfaces).
-      if (UNLIKELY(interfaces->GetTypeItem(i).type_idx_ == item->class_idx_)) {
-        ErrorStringPrintf("Class with same type idx as implemented interface: '%d'",
-                          item->class_idx_);
-        return false;
-      }
-
-      // Check that a class is defined after the interfaces it implements
-      // (if they are defined in the same Dex file).
-      const DexFile::ClassDef* interface_def =
-          dex_file_->FindClassDef(interfaces->GetTypeItem(i).type_idx_);
-      if (interface_def != nullptr) {
-        // The interface is defined in this Dex file.
-        if (interface_def > item) {
-          // ClassDef item for interface appearing after the class' ClassDef item.
-          ErrorStringPrintf("Invalid class definition ordering:"
-                            " class with type idx: '%d' defined before"
-                            " implemented interface with type idx: '%d'",
-                            item->class_idx_,
-                            interfaces->GetTypeItem(i).type_idx_);
-          return false;
-        }
-      }
-
-      // Ensure that the interface refers to a class (not an array nor a primitive type).
       LOAD_STRING_BY_TYPE(inf_descriptor, interfaces->GetTypeItem(i).type_idx_,
                           "inter_class_def_item interface type_idx")
       if (UNLIKELY(!IsValidDescriptor(inf_descriptor) || inf_descriptor[0] != 'L')) {
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index 42c3e0d..3741c1e 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -184,12 +184,6 @@
   return dex_file;
 }
 
-// To generate base64 encoded Dex file contents (such as kGoodTestDex,
-// below) from Smali files, use:
-//
-//   smali -o classes.dex class1.smali [class2.smali ...]
-//   base64 classes.dex >classes.dex.base64
-
 // For reference.
 static const char kGoodTestDex[] =
     "ZGV4CjAzNQDrVbyVkxX1HljTznNf95AglkUAhQuFtmKkAgAAcAAAAHhWNBIAAAAAAAAAAAQCAAAN"
@@ -1527,173 +1521,4 @@
   }
 }
 
-// Generated from:
-//
-//   .class public LB28685551;
-//   .super LB28685551;
-
-static const char kClassExtendsItselfTestDex[] =
-    "ZGV4CjAzNQDtGF0irRLsVfRf3hwTL3zgNupO1a8AkTEEAQAAcAAAAHhWNBIAAAAAAAAAAKwAAAAB"
-    "AAAAcAAAAAEAAAB0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAHgAAABsAAAAmAAAAJgA"
-    "AAAAAAAAAAAAAAEAAAAAAAAAAAAAAP////8AAAAAAAAAAAAAAAALTEIyODY4NTU1MTsAAAAAAAAA"
-    "AAcAAAAAAAAAAQAAAAAAAAABAAAAAQAAAHAAAAACAAAAAQAAAHQAAAAGAAAAAQAAAHgAAAACIAAA"
-    "AQAAAJgAAAADEAAAAQAAAKgAAAAAEAAAAQAAAKwAAAA=";
-
-TEST_F(DexFileVerifierTest, ClassExtendsItself) {
-  VerifyModification(
-      kClassExtendsItselfTestDex,
-      "class_extends_itself",
-      [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ },
-      "Class with same type idx as its superclass: '0'");
-}
-
-// Generated from:
-//
-//   .class public LFoo;
-//   .super LBar;
-//
-// and:
-//
-//    .class public LBar;
-//    .super LFoo;
-
-static const char kClassesExtendOneAnotherTestDex[] =
-    "ZGV4CjAzNQDFHx5oxT079TdKMJ3M37hx96CSFQA8MaUsAQAAcAAAAHhWNBIAAAAAAAAAANQAAAAC"
-    "AAAAcAAAAAIAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAIAAAABsAAAAwAAAAMAA"
-    "AADHAAAAAAAAAAEAAAABAAAAAQAAAAAAAAAAAAAA/////wAAAAAAAAAAAAAAAAAAAAABAAAAAQAA"
-    "AAAAAAD/////AAAAAAAAAAAAAAAABUxCYXI7AAVMRm9vOwAAAAAAAAAHAAAAAAAAAAEAAAAAAAAA"
-    "AQAAAAIAAABwAAAAAgAAAAIAAAB4AAAABgAAAAIAAACAAAAAAiAAAAIAAADAAAAAAxAAAAEAAADQ"
-    "AAAAABAAAAEAAADUAAAA";
-
-TEST_F(DexFileVerifierTest, ClassesExtendOneAnother) {
-  VerifyModification(
-      kClassesExtendOneAnotherTestDex,
-      "classes_extend_one_another",
-      [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ },
-      "Invalid class definition ordering: class with type idx: '1' defined before"
-      " superclass with type idx: '0'");
-}
-
-// Generated from:
-//
-//   .class public LAll;
-//   .super LYour;
-//
-// and:
-//
-//   .class public LYour;
-//   .super LBase;
-//
-// and:
-//
-//   .class public LBase;
-//   .super LAll;
-
-static const char kCircularClassInheritanceTestDex[] =
-    "ZGV4CjAzNQDJJQtdMzp/mPagoltMlDWgd9MqiEtamoVcAQAAcAAAAHhWNBIAAAAAAAAAAAQBAAAD"
-    "AAAAcAAAAAMAAAB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAIgAAAB0AAAA6AAAAOgA"
-    "AADvAAAA9wAAAAAAAAABAAAAAgAAAAEAAAABAAAAAAAAAAAAAAD/////AAAAAAAAAAAAAAAAAgAA"
-    "AAEAAAABAAAAAAAAAP////8AAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAAAAAA/////wAAAAAAAAAA"
-    "AAAAAAVMQWxsOwAGTEJhc2U7AAZMWW91cjsAAAAAAAAHAAAAAAAAAAEAAAAAAAAAAQAAAAMAAABw"
-    "AAAAAgAAAAMAAAB8AAAABgAAAAMAAACIAAAAAiAAAAMAAADoAAAAAxAAAAEAAAAAAQAAABAAAAEA"
-    "AAAEAQAA";
-
-TEST_F(DexFileVerifierTest, CircularClassInheritance) {
-  VerifyModification(
-      kCircularClassInheritanceTestDex,
-      "circular_class_inheritance",
-      [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ },
-      "Invalid class definition ordering: class with type idx: '1' defined before"
-      " superclass with type idx: '0'");
-}
-
-// Generated from:
-//
-//   .class public abstract interface LInterfaceImplementsItself;
-//   .super Ljava/lang/Object;
-//   .implements LInterfaceImplementsItself;
-
-static const char kInterfaceImplementsItselfTestDex[] =
-    "ZGV4CjAzNQBgK8ZUyZHMR7lmXBQ6uJSrndrDOAoPughEAQAAcAAAAHhWNBIAAAAAAAAAAOAAAAAC"
-    "AAAAcAAAAAIAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAIAAAACkAAAAoAAAAKAA"
-    "AAC9AAAAAAAAAAEAAAAAAAAAAQYAAAEAAADUAAAA/////wAAAAAAAAAAAAAAABtMSW50ZXJmYWNl"
-    "SW1wbGVtZW50c0l0c2VsZjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAAAAABAAAAAAAAAAAAAAAIAAAA"
-    "AAAAAAEAAAAAAAAAAQAAAAIAAABwAAAAAgAAAAIAAAB4AAAABgAAAAEAAACAAAAAAiAAAAIAAACg"
-    "AAAAARAAAAEAAADUAAAAAxAAAAEAAADcAAAAABAAAAEAAADgAAAA";
-
-TEST_F(DexFileVerifierTest, InterfaceImplementsItself) {
-  VerifyModification(
-      kInterfaceImplementsItselfTestDex,
-      "interface_implements_itself",
-      [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ },
-      "Class with same type idx as implemented interface: '0'");
-}
-
-// Generated from:
-//
-//   .class public abstract interface LPing;
-//   .super Ljava/lang/Object;
-//   .implements LPong;
-//
-// and:
-//
-//   .class public abstract interface LPong;
-//   .super Ljava/lang/Object;
-//   .implements LPing;
-
-static const char kInterfacesImplementOneAnotherTestDex[] =
-    "ZGV4CjAzNQC9LFtYXO2Se9koyKbznW5m5+lH2CaauJdkAQAAcAAAAHhWNBIAAAAAAAAAAAABAAAD"
-    "AAAAcAAAAAMAAAB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAIgAAACcAAAAyAAAAMgA"
-    "AADQAAAA2AAAAAAAAAABAAAAAgAAAAEAAAABBgAAAgAAAOwAAAD/////AAAAAAAAAAAAAAAAAAAA"
-    "AAEGAAACAAAA9AAAAP////8AAAAAAAAAAAAAAAAGTFBpbmc7AAZMUG9uZzsAEkxqYXZhL2xhbmcv"
-    "T2JqZWN0OwABAAAAAAAAAAEAAAABAAAAAAAAAAgAAAAAAAAAAQAAAAAAAAABAAAAAwAAAHAAAAAC"
-    "AAAAAwAAAHwAAAAGAAAAAgAAAIgAAAACIAAAAwAAAMgAAAABEAAAAgAAAOwAAAADEAAAAQAAAPwA"
-    "AAAAEAAAAQAAAAABAAA=";
-
-TEST_F(DexFileVerifierTest, InterfacesImplementOneAnother) {
-  VerifyModification(
-      kInterfacesImplementOneAnotherTestDex,
-      "interfaces_implement_one_another",
-      [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ },
-      "Invalid class definition ordering: class with type idx: '1' defined before"
-      " implemented interface with type idx: '0'");
-}
-
-// Generated from:
-//
-//   .class public abstract interface LA;
-//   .super Ljava/lang/Object;
-//   .implements LB;
-//
-// and:
-//
-//   .class public abstract interface LB;
-//   .super Ljava/lang/Object;
-//   .implements LC;
-//
-// and:
-//
-//   .class public abstract interface LC;
-//   .super Ljava/lang/Object;
-//   .implements LA;
-
-static const char kCircularInterfaceImplementationTestDex[] =
-    "ZGV4CjAzNQBqK5JDNvKFhgQE2DfycoggFIPHXp5VfdOUAQAAcAAAAHhWNBIAAAAAAAAAADABAAAE"
-    "AAAAcAAAAAQAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAJAAAACkAAAA8AAAAPAA"
-    "AAD1AAAA+gAAAP8AAAAAAAAAAQAAAAIAAAADAAAAAgAAAAEGAAADAAAAHAEAAP////8AAAAAAAAA"
-    "AAAAAAABAAAAAQYAAAMAAAAUAQAA/////wAAAAAAAAAAAAAAAAAAAAABBgAAAwAAACQBAAD/////"
-    "AAAAAAAAAAAAAAAAA0xBOwADTEI7AANMQzsAEkxqYXZhL2xhbmcvT2JqZWN0OwAAAQAAAAIAAAAB"
-    "AAAAAAAAAAEAAAABAAAAAAAAAAgAAAAAAAAAAQAAAAAAAAABAAAABAAAAHAAAAACAAAABAAAAIAA"
-    "AAAGAAAAAwAAAJAAAAACIAAABAAAAPAAAAABEAAAAwAAABQBAAADEAAAAQAAACwBAAAAEAAAAQAA"
-    "ADABAAA=";
-
-TEST_F(DexFileVerifierTest, CircularInterfaceImplementation) {
-  VerifyModification(
-      kCircularInterfaceImplementationTestDex,
-      "circular_interface_implementation",
-      [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ },
-      "Invalid class definition ordering: class with type idx: '2' defined before"
-      " implemented interface with type idx: '0'");
-}
-
 }  // namespace art
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index fa540c0..cdd5f2e 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -311,7 +311,7 @@
 
           const OatHeader& boot_oat_header = boot_oat_file->GetOatHeader();
           const char* boot_classpath =
-              boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPath);
+              boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey);
           if (boot_classpath == nullptr) {
             continue;
           }
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index dcc6300..b6b7eb1 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -565,6 +565,7 @@
         VLOG(jit) << "Start profiling " << PrettyMethod(method_);
       }
     }
+    ProfileSaver::NotifyJitActivity();
   }
 
   void Finalize() OVERRIDE {
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index cf46893..8358ce3 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -33,14 +33,15 @@
 // TODO: read the constants from ProfileOptions,
 // Add a random delay each time we go to sleep so that we don't hammer the CPU
 // with all profile savers running at the same time.
-static constexpr const uint64_t kRandomDelayMaxMs = 30 * 1000;  // 30 seconds
-static constexpr const uint64_t kMaxBackoffMs = 10 * 60 * 1000;  // 10 minutes
-static constexpr const uint64_t kSavePeriodMs = 20 * 1000;  // 20 seconds
+static constexpr const uint64_t kMinSavePeriodNs = MsToNs(20 * 1000);  // 20 seconds
 static constexpr const uint64_t kSaveResolvedClassesDelayMs = 2 * 1000;  // 2 seconds
-static constexpr const double kBackoffCoef = 2.0;
 
 static constexpr const uint32_t kMinimumNumberOfMethodsToSave = 10;
 static constexpr const uint32_t kMinimumNumberOfClassesToSave = 10;
+static constexpr const uint32_t kMinimumNumberOfNotificationBeforeWake =
+    kMinimumNumberOfMethodsToSave;
+static constexpr const uint32_t kMaximumNumberOfNotificationBeforeWake = 50;
+
 
 ProfileSaver* ProfileSaver::instance_ = nullptr;
 pthread_t ProfileSaver::profiler_pthread_ = 0U;
@@ -55,6 +56,8 @@
       shutting_down_(false),
       last_save_number_of_methods_(0),
       last_save_number_of_classes_(0),
+      last_time_ns_saver_woke_up_(0),
+      jit_activity_notifications_(0),
       wait_lock_("ProfileSaver wait lock"),
       period_condition_("ProfileSaver period condition", wait_lock_),
       total_bytes_written_(0),
@@ -65,7 +68,9 @@
       total_ms_of_sleep_(0),
       total_ns_of_work_(0),
       total_number_of_foreign_dex_marks_(0),
-      max_number_of_profile_entries_cached_(0) {
+      max_number_of_profile_entries_cached_(0),
+      total_number_of_hot_spikes_(0),
+      total_number_of_wake_ups_(0) {
   AddTrackedLocations(output_filename, app_data_dir, code_paths);
   if (!app_data_dir.empty()) {
     // The application directory is used to determine which dex files are owned by app.
@@ -83,55 +88,89 @@
 }
 
 void ProfileSaver::Run() {
-  srand(MicroTime() * getpid());
   Thread* self = Thread::Current();
 
-  uint64_t save_period_ms = kSavePeriodMs;
-  VLOG(profiler) << "Save profiling information every " << save_period_ms << " ms";
-  bool cache_resolved_classes = true;
+  // Fetch the resolved classes for the app images after sleeping for
+  // kSaveResolvedClassesDelayMs.
+  // TODO(calin) This only considers the case of the primary profile file.
+  // Anything that gets loaded in the same VM will not have their resolved
+  // classes save (unless they started before the initial saving was done).
+  {
+    MutexLock mu(self, wait_lock_);
+    period_condition_.TimedWait(self, kSaveResolvedClassesDelayMs, 0);
+    total_ms_of_sleep_ += kSaveResolvedClassesDelayMs;
+  }
+  FetchAndCacheResolvedClasses();
+
+  // Loop for the profiled methods.
   while (!ShuttingDown(self)) {
-    uint64_t sleep_time_ms;
-    if (cache_resolved_classes) {
-      // Sleep less long for the first iteration since we want to record loaded classes shortly
-      // after app launch.
-      sleep_time_ms = kSaveResolvedClassesDelayMs;
-    } else {
-      const uint64_t random_sleep_delay_ms = rand() % kRandomDelayMaxMs;
-      sleep_time_ms = save_period_ms + random_sleep_delay_ms;
-    }
+    uint64_t sleep_start = NanoTime();
     {
       MutexLock mu(self, wait_lock_);
-      period_condition_.TimedWait(self, sleep_time_ms, 0);
+      period_condition_.Wait(self);
+      total_number_of_wake_ups_++;
+      // We might have been woken up by a huge number of notifications to guarantee saving.
+      // If we didn't meet the minimum saving period go back to sleep (only if missed by
+      // a reasonable margin).
+      uint64_t sleep_time = NanoTime() - last_time_ns_saver_woke_up_;
+      while (kMinSavePeriodNs - sleep_time > (kMinSavePeriodNs / 10)) {
+        period_condition_.TimedWait(self, NsToMs(kMinSavePeriodNs - sleep_time), 0);
+        total_number_of_wake_ups_++;
+        sleep_time = NanoTime() - last_time_ns_saver_woke_up_;
+      }
     }
-    total_ms_of_sleep_ += sleep_time_ms;
+    total_ms_of_sleep_ += NsToMs(NanoTime() - sleep_start);
+
     if (ShuttingDown(self)) {
       break;
     }
 
-    uint64_t start = NanoTime();
-    if (cache_resolved_classes) {
-      // TODO(calin) This only considers the case of the primary profile file.
-      // Anything that gets loaded in the same VM will not have their resolved
-      // classes save (unless they started before the initial saving was done).
-      FetchAndCacheResolvedClasses();
-    } else {
-      bool profile_saved_to_disk = ProcessProfilingInfo();
-      if (profile_saved_to_disk) {
-        // Reset the period to the initial value as it's highly likely to JIT again.
-        save_period_ms = kSavePeriodMs;
-        VLOG(profiler) << "Profile saver: saved something, period reset to: " << save_period_ms;
-      } else {
-        // If we don't need to save now it is less likely that we will need to do
-        // so in the future. Increase the time between saves according to the
-        // kBackoffCoef, but make it no larger than kMaxBackoffMs.
-        save_period_ms = std::min(kMaxBackoffMs,
-                                  static_cast<uint64_t>(kBackoffCoef * save_period_ms));
-        VLOG(profiler) << "Profile saver: nothing to save, delaying period to: " << save_period_ms;
-      }
+    uint16_t new_methods = 0;
+    uint64_t start_work = NanoTime();
+    bool profile_saved_to_disk = ProcessProfilingInfo(&new_methods);
+    // Update the notification counter based on result. Note that there might be contention on this
+    // but we don't care about to be 100% precise.
+    if (!profile_saved_to_disk) {
+      // If we didn't save to disk it may be because we didn't have enough new methods.
+      // Set the jit activity notifications to new_methods so we can wake up earlier if needed.
+      jit_activity_notifications_ = new_methods;
     }
-    cache_resolved_classes = false;
+    total_ns_of_work_ += NanoTime() - start_work;
+  }
+}
 
-    total_ns_of_work_ += (NanoTime() - start);
+void ProfileSaver::NotifyJitActivity() {
+  MutexLock mu(Thread::Current(), *Locks::profiler_lock_);
+  if (instance_ == nullptr || instance_->shutting_down_) {
+    return;
+  }
+  instance_->NotifyJitActivityInternal();
+}
+
+void ProfileSaver::WakeUpSaver() {
+  jit_activity_notifications_ = 0;
+  last_time_ns_saver_woke_up_ = NanoTime();
+  period_condition_.Signal(Thread::Current());
+}
+
+void ProfileSaver::NotifyJitActivityInternal() {
+  // Unlikely to overflow but if it happens,
+  // we would have waken up the saver long before that.
+  jit_activity_notifications_++;
+  // Note that we are not as precise as we could be here but we don't want to wake the saver
+  // every time we see a hot method.
+  if (jit_activity_notifications_ > kMinimumNumberOfNotificationBeforeWake) {
+    MutexLock wait_mutex(Thread::Current(), wait_lock_);
+    if ((NanoTime() - last_time_ns_saver_woke_up_) > kMinSavePeriodNs) {
+      WakeUpSaver();
+    }
+  } else if (jit_activity_notifications_ > kMaximumNumberOfNotificationBeforeWake) {
+    // Make sure to wake up the saver if we see a spike in the number of notifications.
+    // This is a precaution to avoid "loosing" a big number of methods in case
+    // this is a spike with no jit after.
+    total_number_of_hot_spikes_++;
+    MutexLock wait_mutex(Thread::Current(), wait_lock_);
+    WakeUpSaver();
   }
 }
 
@@ -175,7 +214,7 @@
       total_number_of_profile_entries_cached);
 }
 
-bool ProfileSaver::ProcessProfilingInfo() {
+bool ProfileSaver::ProcessProfilingInfo(uint16_t* new_methods) {
   ScopedTrace trace(__PRETTY_FUNCTION__);
   SafeMap<std::string, std::set<std::string>> tracked_locations;
   {
@@ -186,6 +225,8 @@
 
   bool profile_file_saved = false;
   uint64_t total_number_of_profile_entries_cached = 0;
+  *new_methods = 0;
+
   for (const auto& it : tracked_locations) {
     if (ShuttingDown(Thread::Current())) {
       return true;
@@ -216,6 +257,7 @@
       total_number_of_skipped_writes_++;
       continue;
     }
+    *new_methods = std::max(static_cast<uint16_t>(delta_number_of_methods), *new_methods);
     uint64_t bytes_written;
     // Force the save. In case the profile data is corrupted or the the profile
     // has the wrong version this will "fix" the file to the correct format.
@@ -530,7 +572,9 @@
      << "ProfileSaver total_number_of_foreign_dex_marks="
      << total_number_of_foreign_dex_marks_ << '\n'
      << "ProfileSaver max_number_profile_entries_cached="
-    << max_number_of_profile_entries_cached_ << '\n';
+     << max_number_of_profile_entries_cached_ << '\n'
+     << "ProfileSaver total_number_of_hot_spikes=" << total_number_of_hot_spikes_ << '\n'
+     << "ProfileSaver total_number_of_wake_ups=" << total_number_of_wake_ups_ << '\n';
 }
 
 
@@ -544,7 +588,8 @@
   // but we only use this in testing when we now this won't happen.
   // Refactor the way we handle the instance so that we don't end up in this situation.
   if (saver != nullptr) {
-    saver->ProcessProfilingInfo();
+    uint16_t new_methods;
+    saver->ProcessProfilingInfo(&new_methods);
   }
 }
 
diff --git a/runtime/jit/profile_saver.h b/runtime/jit/profile_saver.h
index 4f3cdc2..c6da959 100644
--- a/runtime/jit/profile_saver.h
+++ b/runtime/jit/profile_saver.h
@@ -49,6 +49,11 @@
   // If the profile saver is running, dumps statistics to the `os`. Otherwise it does nothing.
   static void DumpInstanceInfo(std::ostream& os);
 
+  // NO_THREAD_SAFETY_ANALYSIS for static function calling into member function with excludes lock.
+  static void NotifyJitActivity()
+      REQUIRES(!Locks::profiler_lock_, !wait_lock_)
+      NO_THREAD_SAFETY_ANALYSIS;
+
   // Just for testing purpose.
   static void ForceProcessProfiles();
   static bool HasSeenMethod(const std::string& profile,
@@ -71,10 +76,13 @@
   void Run() REQUIRES(!Locks::profiler_lock_, !wait_lock_);
   // Processes the existing profiling info from the jit code cache and returns
   // true if it needed to be saved to disk.
-  bool ProcessProfilingInfo()
+  bool ProcessProfilingInfo(uint16_t* new_methods)
     REQUIRES(!Locks::profiler_lock_)
     REQUIRES(!Locks::mutator_lock_);
 
+  void NotifyJitActivityInternal() REQUIRES(!wait_lock_);
+  void WakeUpSaver() REQUIRES(wait_lock_);
+
   // Returns true if the saver is shutting down (ProfileSaver::Stop() has been called).
   bool ShuttingDown(Thread* self) REQUIRES(!Locks::profiler_lock_);
 
@@ -121,6 +129,8 @@
   bool shutting_down_ GUARDED_BY(Locks::profiler_lock_);
   uint32_t last_save_number_of_methods_;
   uint32_t last_save_number_of_classes_;
+  uint64_t last_time_ns_saver_woke_up_ GUARDED_BY(wait_lock_);
+  uint32_t jit_activity_notifications_;
 
   // A local cache for the profile information. Maps each tracked file to its
   // profile information. The size of this cache is usually very small and tops
@@ -142,6 +152,8 @@
   uint64_t total_number_of_foreign_dex_marks_;
   // TODO(calin): replace with an actual size.
   uint64_t max_number_of_profile_entries_cached_;
+  uint64_t total_number_of_hot_spikes_;
+  uint64_t total_number_of_wake_ups_;
 
   DISALLOW_COPY_AND_ASSIGN(ProfileSaver);
 };
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 5ba8df7..6c943dc 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -145,6 +145,10 @@
   return Dbg::IsDebuggerActive();
 }
 
+static jboolean VMRuntime_isNativeDebuggable(JNIEnv*, jobject) {
+  return Runtime::Current()->IsNativeDebuggable();
+}
+
 static jobjectArray VMRuntime_properties(JNIEnv* env, jobject) {
   return toStringArray(env, Runtime::Current()->GetProperties());
 }
@@ -641,6 +645,7 @@
   NATIVE_METHOD(VMRuntime, disableJitCompilation, "()V"),
   NATIVE_METHOD(VMRuntime, getTargetHeapUtilization, "()F"),
   NATIVE_METHOD(VMRuntime, isDebuggerActive, "!()Z"),
+  NATIVE_METHOD(VMRuntime, isNativeDebuggable, "!()Z"),
   NATIVE_METHOD(VMRuntime, nativeSetTargetHeapUtilization, "(F)V"),
   NATIVE_METHOD(VMRuntime, newNonMovableArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"),
   NATIVE_METHOD(VMRuntime, newUnpaddedArray, "!(Ljava/lang/Class;I)Ljava/lang/Object;"),
diff --git a/runtime/oat.h b/runtime/oat.h
index 543d99f..57675dc 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -43,7 +43,7 @@
   static constexpr const char* kNativeDebuggableKey = "native-debuggable";
   static constexpr const char* kCompilerFilter = "compiler-filter";
   static constexpr const char* kClassPathKey = "classpath";
-  static constexpr const char* kBootClassPath = "bootclasspath";
+  static constexpr const char* kBootClassPathKey = "bootclasspath";
 
   static constexpr const char kTrueValue[] = "true";
   static constexpr const char kFalseValue[] = "false";
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 9470624..aa727ff 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -48,6 +48,9 @@
 
 class OatFile {
  public:
+  // Special classpath that skips shared library check.
+  static constexpr const char* kSpecialSharedLibrary = "&";
+
   typedef art::OatDexFile OatDexFile;
 
   // Opens an oat file contained within the given elf file. This is always opened as
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 713e2f3..fba10ca 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -771,7 +771,11 @@
   argv.push_back("--runtime-arg");
   argv.push_back("-classpath");
   argv.push_back("--runtime-arg");
-  argv.push_back(runtime->GetClassPathString());
+  std::string class_path = runtime->GetClassPathString();
+  if (class_path == "") {
+    class_path = OatFile::kSpecialSharedLibrary;
+  }
+  argv.push_back(class_path);
   if (runtime->IsDebuggable()) {
     argv.push_back("--debuggable");
   }
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 764b969..15a1aa4 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -1264,8 +1264,7 @@
 class RaceGenerateTask : public Task {
  public:
   explicit RaceGenerateTask(const std::string& dex_location, const std::string& oat_location)
-    : dex_location_(dex_location), oat_location_(oat_location),
-      loaded_oat_file_(nullptr)
+    : dex_location_(dex_location), oat_location_(oat_location), loaded_oat_file_(nullptr)
   {}
 
   void Run(Thread* self ATTRIBUTE_UNUSED) {
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index bc01da4..0af6716 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -36,11 +36,6 @@
 
 namespace art {
 
-// For b/21333911.
-// Only enabled for debug builds to prevent bit rot. There are too many performance regressions for
-// normal builds.
-static constexpr bool kDuplicateClassesCheck = kIsDebugBuild;
-
 // If true, then we attempt to load the application image if it exists.
 static constexpr bool kEnableAppImage = true;
 
@@ -173,7 +168,7 @@
 
   void Next() {
     ++current_class_index_;
-    cached_descriptor_ = GetClassDescriptor(dex_file_.get(), current_class_index_);
+    cached_descriptor_ = GetClassDescriptor(dex_file_, current_class_index_);
   }
 
   size_t GetCurrentClassIndex() const {
@@ -185,7 +180,12 @@
   }
 
   const DexFile* GetDexFile() const {
-    return dex_file_.get();
+    return dex_file_;
+  }
+
+  void DeleteDexFile() {
+    delete dex_file_;
+    dex_file_ = nullptr;
   }
 
  private:
@@ -196,7 +196,7 @@
   }
 
   const char* cached_descriptor_;
-  std::shared_ptr<const DexFile> dex_file_;
+  const DexFile* dex_file_;
   size_t current_class_index_;
   bool from_loaded_oat_;  // We only need to compare mismatches between what we load now
                           // and what was loaded before. Any old duplicates must have been
@@ -219,53 +219,299 @@
 }
 
 static void AddNext(/*inout*/DexFileAndClassPair* original,
-                    /*inout*/std::priority_queue<DexFileAndClassPair>* heap) {
+                    /*inout*/std::priority_queue<DexFileAndClassPair>* heap,
+                    bool owning_dex_files) {
   if (original->DexFileHasMoreClasses()) {
     original->Next();
     heap->push(std::move(*original));
+  } else if (owning_dex_files) {
+    original->DeleteDexFile();
   }
 }
 
+static void FreeDexFilesInHeap(std::priority_queue<DexFileAndClassPair>* heap,
+                               bool owning_dex_files) {
+  if (owning_dex_files) {
+    while (!heap->empty()) {
+      delete heap->top().GetDexFile();
+      heap->pop();
+    }
+  }
+}
+
+static void IterateOverJavaDexFile(mirror::Object* dex_file,
+                                   ArtField* const cookie_field,
+                                   std::function<bool(const DexFile*)> fn)
+    SHARED_REQUIRES(Locks::mutator_lock_) {
+  if (dex_file != nullptr) {
+    mirror::LongArray* long_array = cookie_field->GetObject(dex_file)->AsLongArray();
+    if (long_array == nullptr) {
+      // This should never happen so log a warning.
+      LOG(WARNING) << "Null DexFile::mCookie";
+      return;
+    }
+    int32_t long_array_size = long_array->GetLength();
+    // Start from 1 to skip the oat file.
+    for (int32_t j = 1; j < long_array_size; ++j) {
+      const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(
+          long_array->GetWithoutChecks(j)));
+      if (!fn(cp_dex_file)) {
+        return;
+      }
+    }
+  }
+}
+
+static void IterateOverPathClassLoader(
+    ScopedObjectAccessAlreadyRunnable& soa,
+    Handle<mirror::ClassLoader> class_loader,
+    MutableHandle<mirror::ObjectArray<mirror::Object>> dex_elements,
+    std::function<bool(const DexFile*)> fn) SHARED_REQUIRES(Locks::mutator_lock_) {
+  // Handle this step.
+  // Handle as if this is the child PathClassLoader.
+  // The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
+  // We need to get the DexPathList and loop through it.
+  ArtField* const cookie_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie);
+  ArtField* const dex_file_field =
+      soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
+  mirror::Object* dex_path_list =
+      soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
+      GetObject(class_loader.Get());
+  if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) {
+    // DexPathList has an array dexElements of Elements[] which each contain a dex file.
+    mirror::Object* dex_elements_obj =
+        soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
+        GetObject(dex_path_list);
+    // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
+    // at the mCookie which is a DexFile vector.
+    if (dex_elements_obj != nullptr) {
+      dex_elements.Assign(dex_elements_obj->AsObjectArray<mirror::Object>());
+      for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
+        mirror::Object* element = dex_elements->GetWithoutChecks(i);
+        if (element == nullptr) {
+          // Should never happen, fall back to java code to throw a NPE.
+          break;
+        }
+        mirror::Object* dex_file = dex_file_field->GetObject(element);
+        IterateOverJavaDexFile(dex_file, cookie_field, fn);
+      }
+    }
+  }
+}
+
+static bool GetDexFilesFromClassLoader(
+    ScopedObjectAccessAlreadyRunnable& soa,
+    mirror::ClassLoader* class_loader,
+    std::priority_queue<DexFileAndClassPair>* queue) SHARED_REQUIRES(Locks::mutator_lock_) {
+  if (ClassLinker::IsBootClassLoader(soa, class_loader)) {
+    // The boot class loader. We don't load any of these files, as we know we compiled against
+    // them correctly.
+    return true;
+  }
+
+  // Unsupported class-loader?
+  if (class_loader->GetClass() !=
+      soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader)) {
+    VLOG(class_linker) << "Unsupported class-loader " << PrettyClass(class_loader->GetClass());
+    return false;
+  }
+
+  bool recursive_result = GetDexFilesFromClassLoader(soa, class_loader->GetParent(), queue);
+  if (!recursive_result) {
+    // Something wrong up the chain.
+    return false;
+  }
+
+  // Collect all the dex files.
+  auto GetDexFilesFn = [&] (const DexFile* cp_dex_file)
+            SHARED_REQUIRES(Locks::mutator_lock_) {
+    if (cp_dex_file->NumClassDefs() > 0) {
+      queue->emplace(cp_dex_file, 0U, true);
+    }
+    return true;  // Continue looking.
+  };
+
+  // Handle for dex-cache-element.
+  StackHandleScope<3> hs(soa.Self());
+  MutableHandle<mirror::ObjectArray<mirror::Object>> dex_elements(
+      hs.NewHandle<mirror::ObjectArray<mirror::Object>>(nullptr));
+  Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(class_loader));
+
+  IterateOverPathClassLoader(soa, h_class_loader, dex_elements, GetDexFilesFn);
+
+  return true;
+}
+
+static void GetDexFilesFromDexElementsArray(
+    ScopedObjectAccessAlreadyRunnable& soa,
+    Handle<mirror::ObjectArray<mirror::Object>> dex_elements,
+    std::priority_queue<DexFileAndClassPair>* queue) SHARED_REQUIRES(Locks::mutator_lock_) {
+  if (dex_elements.Get() == nullptr) {
+    // Nothing to do.
+    return;
+  }
+
+  ArtField* const cookie_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie);
+  ArtField* const dex_file_field =
+      soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
+  const mirror::Class* const element_class = soa.Decode<mirror::Class*>(
+      WellKnownClasses::dalvik_system_DexPathList__Element);
+  const mirror::Class* const dexfile_class = soa.Decode<mirror::Class*>(
+        WellKnownClasses::dalvik_system_DexFile);
+
+  // Collect all the dex files.
+  auto GetDexFilesFn = [&] (const DexFile* cp_dex_file)
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    if (cp_dex_file != nullptr && cp_dex_file->NumClassDefs() > 0) {
+      queue->emplace(cp_dex_file, 0U, true);
+    }
+    return true;  // Continue looking.
+  };
+
+  for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
+    mirror::Object* element = dex_elements->GetWithoutChecks(i);
+    if (element == nullptr) {
+      continue;
+    }
+
+    // We support this being dalvik.system.DexPathList$Element and dalvik.system.DexFile.
+
+    mirror::Object* dex_file;
+    if (element->GetClass() == element_class) {
+      dex_file = dex_file_field->GetObject(element);
+    } else if (element->GetClass() == dexfile_class) {
+      dex_file = element;
+    } else {
+      LOG(WARNING) << "Unsupported element in dex_elements: " << PrettyClass(element->GetClass());
+      continue;
+    }
+
+    IterateOverJavaDexFile(dex_file, cookie_field, GetDexFilesFn);
+  }
+}
+
+static bool AreSharedLibrariesOk(const std::string shared_libraries,
+                                 std::priority_queue<DexFileAndClassPair>& queue) {
+  if (shared_libraries.empty()) {
+    if (queue.empty()) {
+      // No shared libraries or oat files, as expected.
+      return true;
+    }
+  } else {
+    if (shared_libraries.compare(OatFile::kSpecialSharedLibrary) == 0) {
+      // If we find the special shared library, skip the shared libraries check.
+      return true;
+    }
+    // Shared libraries is a series of dex file paths and their checksums, each separated by '*'.
+    std::vector<std::string> shared_libraries_split;
+    Split(shared_libraries, '*', &shared_libraries_split);
+
+    size_t index = 0;
+    std::priority_queue<DexFileAndClassPair> temp = queue;
+    while (!temp.empty() && index < shared_libraries_split.size() - 1) {
+      DexFileAndClassPair pair(temp.top());
+      const DexFile* dex_file = pair.GetDexFile();
+      std::string dex_filename(dex_file->GetLocation());
+      uint32_t dex_checksum = dex_file->GetLocationChecksum();
+      if (dex_filename != shared_libraries_split[index] ||
+          dex_checksum != std::stoul(shared_libraries_split[index + 1])) {
+        break;
+      }
+      temp.pop();
+      index += 2;
+    }
+
+    // Check is successful if it made it through the queue and all the shared libraries.
+    return temp.empty() && index == shared_libraries_split.size();
+  }
+  return false;
+}
+
 // Check for class-def collisions in dex files.
 //
-// This works by maintaining a heap with one class from each dex file, sorted by the class
-// descriptor. Then a dex-file/class pair is continually removed from the heap and compared
+// This first walks the class loader chain, getting all the dex files from the class loader. If
+// the class loader is null or one of the class loaders in the chain is unsupported, we collect
+// dex files from all open non-boot oat files to be safe.
+//
+// This first checks whether the shared libraries are in the expected order and the oat files
+// have the expected checksums. If so, we exit early. Otherwise, we do the collision check.
+//
+// The collision check works by maintaining a heap with one class from each dex file, sorted by the
+// class descriptor. Then a dex-file/class pair is continually removed from the heap and compared
 // against the following top element. If the descriptor is the same, it is now checked whether
 // the two elements agree on whether their dex file was from an already-loaded oat-file or the
 // new oat file. Any disagreement indicates a collision.
 bool OatFileManager::HasCollisions(const OatFile* oat_file,
+                                   jobject class_loader,
+                                   jobjectArray dex_elements,
                                    std::string* error_msg /*out*/) const {
   DCHECK(oat_file != nullptr);
   DCHECK(error_msg != nullptr);
-  if (!kDuplicateClassesCheck) {
-    return false;
+
+  std::priority_queue<DexFileAndClassPair> queue;
+  bool owning_dex_files = false;
+
+  // Try to get dex files from the given class loader. If the class loader is null, or we do
+  // not support one of the class loaders in the chain, conservatively compare against all
+  // (non-boot) oat files.
+  bool class_loader_ok = false;
+  {
+    ScopedObjectAccess soa(Thread::Current());
+    StackHandleScope<2> hs(Thread::Current());
+    Handle<mirror::ClassLoader> h_class_loader =
+        hs.NewHandle(soa.Decode<mirror::ClassLoader*>(class_loader));
+    Handle<mirror::ObjectArray<mirror::Object>> h_dex_elements =
+        hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Object>*>(dex_elements));
+    if (h_class_loader.Get() != nullptr &&
+        GetDexFilesFromClassLoader(soa, h_class_loader.Get(), &queue)) {
+      class_loader_ok = true;
+
+      // In this case, also take into account the dex_elements array, if given. We don't need to
+      // read it otherwise, as we'll compare against all open oat files anyways.
+      GetDexFilesFromDexElementsArray(soa, h_dex_elements, &queue);
+    } else if (h_class_loader.Get() != nullptr) {
+      VLOG(class_linker) << "Something unsupported with "
+                         << PrettyClass(h_class_loader->GetClass());
+    }
   }
 
   // Dex files are registered late - once a class is actually being loaded. We have to compare
   // against the open oat files. Take the oat_file_manager_lock_ that protects oat_files_ accesses.
   ReaderMutexLock mu(Thread::Current(), *Locks::oat_file_manager_lock_);
 
-  std::priority_queue<DexFileAndClassPair> queue;
+  if (!class_loader_ok) {
+    // Add dex files from already loaded oat files, but skip boot.
 
-  // Add dex files from already loaded oat files, but skip boot.
-  std::vector<const OatFile*> boot_oat_files = GetBootOatFiles();
-  // The same OatFile can be loaded multiple times at different addresses. In this case, we don't
-  // need to check both against each other since they would have resolved the same way at compile
-  // time.
-  std::unordered_set<std::string> unique_locations;
-  for (const std::unique_ptr<const OatFile>& loaded_oat_file : oat_files_) {
-    DCHECK_NE(loaded_oat_file.get(), oat_file);
-    const std::string& location = loaded_oat_file->GetLocation();
-    if (std::find(boot_oat_files.begin(), boot_oat_files.end(), loaded_oat_file.get()) ==
-        boot_oat_files.end() && location != oat_file->GetLocation() &&
-        unique_locations.find(location) == unique_locations.end()) {
-      unique_locations.insert(location);
-      AddDexFilesFromOat(loaded_oat_file.get(), /*already_loaded*/true, &queue);
+    // Clean up the queue.
+    while (!queue.empty()) {
+      queue.pop();
+    }
+
+    // Anything we load now is something we own and must be released later.
+    owning_dex_files = true;
+
+    std::vector<const OatFile*> boot_oat_files = GetBootOatFiles();
+    // The same OatFile can be loaded multiple times at different addresses. In this case, we don't
+    // need to check both against each other since they would have resolved the same way at compile
+    // time.
+    std::unordered_set<std::string> unique_locations;
+    for (const std::unique_ptr<const OatFile>& loaded_oat_file : oat_files_) {
+      DCHECK_NE(loaded_oat_file.get(), oat_file);
+      const std::string& location = loaded_oat_file->GetLocation();
+      if (std::find(boot_oat_files.begin(), boot_oat_files.end(), loaded_oat_file.get()) ==
+          boot_oat_files.end() && location != oat_file->GetLocation() &&
+          unique_locations.find(location) == unique_locations.end()) {
+        unique_locations.insert(location);
+        AddDexFilesFromOat(loaded_oat_file.get(), /*already_loaded*/true, &queue);
+      }
     }
   }
 
-  if (queue.empty()) {
-    // No other oat files, return early.
+  // Exit if shared libraries are ok. Do a full duplicate classes check otherwise.
+  const std::string
+      shared_libraries(oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
+  if (AreSharedLibrariesOk(shared_libraries, queue)) {
+    FreeDexFilesInHeap(&queue, owning_dex_files);
     return false;
   }
 
@@ -290,16 +536,17 @@
                            compare_pop.GetCachedDescriptor(),
                            compare_pop.GetDexFile()->GetLocation().c_str(),
                            top.GetDexFile()->GetLocation().c_str());
+          FreeDexFilesInHeap(&queue, owning_dex_files);
           return true;
         }
         queue.pop();
-        AddNext(&top, &queue);
+        AddNext(&top, &queue, owning_dex_files);
       } else {
         // Something else. Done here.
         break;
       }
     }
-    AddNext(&compare_pop, &queue);
+    AddNext(&compare_pop, &queue, owning_dex_files);
   }
 
   return false;
@@ -363,7 +610,8 @@
 
   if (oat_file != nullptr) {
     // Take the file only if it has no collisions, or we must take it because of preopting.
-    bool accept_oat_file = !HasCollisions(oat_file.get(), /*out*/ &error_msg);
+    bool accept_oat_file =
+        !HasCollisions(oat_file.get(), class_loader, dex_elements, /*out*/ &error_msg);
     if (!accept_oat_file) {
       // Failed the collision check. Print warning.
       if (Runtime::Current()->IsDexFileFallbackEnabled()) {
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index 7017dfc..a1d1275 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -116,9 +116,16 @@
   void DumpForSigQuit(std::ostream& os);
 
  private:
-  // Check for duplicate class definitions of the given oat file against all open oat files.
+  // Check that the shared libraries in the given oat file match those in the given class loader and
+  // dex elements. If the class loader is null or we do not support one of the class loaders in the
+  // chain, compare against all non-boot oat files instead. If the shared libraries are not ok,
+  // check for duplicate class definitions of the given oat file against the oat files (either from
+  // the class loader and dex elements if possible or all non-boot oat files otherwise).
   // Return true if there are any class definition collisions in the oat_file.
-  bool HasCollisions(const OatFile* oat_file, /*out*/std::string* error_msg) const
+  bool HasCollisions(const OatFile* oat_file,
+                     jobject class_loader,
+                     jobjectArray dex_elements,
+                     /*out*/ std::string* error_msg) const
       REQUIRES(!Locks::oat_file_manager_lock_);
 
   const OatFile* FindOpenedOatFileFromOatLocationLocked(const std::string& oat_location) const
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index ca8f8bb..63976d0 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -856,7 +856,7 @@
     if (index == 0) {
       // First file. See if this is a multi-image environment, and if so, enqueue the other images.
       const OatHeader& boot_oat_header = oat_file->GetOatHeader();
-      const char* boot_cp = boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPath);
+      const char* boot_cp = boot_oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey);
       if (boot_cp != nullptr) {
         gc::space::ImageSpace::CreateMultiImageLocations(image_locations[0],
                                                          boot_cp,
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 8b287ac..4248944 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -513,14 +513,20 @@
   return stack_size;
 }
 
+// Return the nearest page-aligned address below the current stack top.
+NO_INLINE
+static uint8_t* FindStackTop() {
+  return reinterpret_cast<uint8_t*>(
+      AlignDown(__builtin_frame_address(0), kPageSize));
+}
+
 // Install a protected region in the stack.  This is used to trigger a SIGSEGV if a stack
 // overflow is detected.  It is located right below the stack_begin_.
 ATTRIBUTE_NO_SANITIZE_ADDRESS
 void Thread::InstallImplicitProtection() {
   uint8_t* pregion = tlsPtr_.stack_begin - kStackOverflowProtectedSize;
-  uint8_t* stack_himem = tlsPtr_.stack_end;
-  uint8_t* stack_top = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(&stack_himem) &
-      ~(kPageSize - 1));    // Page containing current top of stack.
+  // Page containing current top of stack.
+  uint8_t* stack_top = FindStackTop();
 
   // Try to directly protect the stack.
   VLOG(threads) << "installing stack protected region at " << std::hex <<
@@ -932,8 +938,7 @@
   }
 
   // Sanity check.
-  int stack_variable;
-  CHECK_GT(&stack_variable, reinterpret_cast<void*>(tlsPtr_.stack_end));
+  CHECK_GT(FindStackTop(), reinterpret_cast<void*>(tlsPtr_.stack_end));
 
   return true;
 }
diff --git a/test/138-duplicate-classes-check/src/FancyLoader.java b/test/138-duplicate-classes-check/src/FancyLoader.java
deleted file mode 100644
index 03ec948..0000000
--- a/test/138-duplicate-classes-check/src/FancyLoader.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.lang.reflect.InvocationTargetException;
-
-/**
- * A class loader with atypical behavior: we try to load a private
- * class implementation before asking the system or boot loader.  This
- * is used to create multiple classes with identical names in a single VM.
- *
- * If DexFile is available, we use that; if not, we assume we're not in
- * Dalvik and instantiate the class with defineClass().
- *
- * The location of the DEX files and class data is dependent upon the
- * test framework.
- */
-public class FancyLoader extends ClassLoader {
-    /* this is where the "alternate" .class files live */
-    static final String CLASS_PATH = "classes-ex/";
-
-    /* this is the "alternate" DEX/Jar file */
-    static final String DEX_FILE = System.getenv("DEX_LOCATION") +
-            "/138-duplicate-classes-check-ex.jar";
-
-    /* on Dalvik, this is a DexFile; otherwise, it's null */
-    private Class mDexClass;
-
-    private Object mDexFile;
-
-    /**
-     * Construct FancyLoader, grabbing a reference to the DexFile class
-     * if we're running under Dalvik.
-     */
-    public FancyLoader(ClassLoader parent) {
-        super(parent);
-
-        try {
-            mDexClass = parent.loadClass("dalvik.system.DexFile");
-        } catch (ClassNotFoundException cnfe) {
-            // ignore -- not running Dalvik
-        }
-    }
-
-    /**
-     * Finds the class with the specified binary name.
-     *
-     * We search for a file in CLASS_PATH or pull an entry from DEX_FILE.
-     * If we don't find a match, we throw an exception.
-     */
-    protected Class<?> findClass(String name) throws ClassNotFoundException
-    {
-        if (mDexClass != null) {
-            return findClassDalvik(name);
-        } else {
-            return findClassNonDalvik(name);
-        }
-    }
-
-    /**
-     * Finds the class with the specified binary name, from a DEX file.
-     */
-    private Class<?> findClassDalvik(String name)
-        throws ClassNotFoundException {
-
-        if (mDexFile == null) {
-            synchronized (FancyLoader.class) {
-                Constructor ctor;
-                /*
-                 * Construct a DexFile object through reflection.
-                 */
-                try {
-                    ctor = mDexClass.getConstructor(new Class[] {String.class});
-                } catch (NoSuchMethodException nsme) {
-                    throw new ClassNotFoundException("getConstructor failed",
-                        nsme);
-                }
-
-                try {
-                    mDexFile = ctor.newInstance(DEX_FILE);
-                } catch (InstantiationException ie) {
-                    throw new ClassNotFoundException("newInstance failed", ie);
-                } catch (IllegalAccessException iae) {
-                    throw new ClassNotFoundException("newInstance failed", iae);
-                } catch (InvocationTargetException ite) {
-                    throw new ClassNotFoundException("newInstance failed", ite);
-                }
-            }
-        }
-
-        /*
-         * Call DexFile.loadClass(String, ClassLoader).
-         */
-        Method meth;
-
-        try {
-            meth = mDexClass.getMethod("loadClass",
-                    new Class[] { String.class, ClassLoader.class });
-        } catch (NoSuchMethodException nsme) {
-            throw new ClassNotFoundException("getMethod failed", nsme);
-        }
-
-        try {
-            meth.invoke(mDexFile, name, this);
-        } catch (IllegalAccessException iae) {
-            throw new ClassNotFoundException("loadClass failed", iae);
-        } catch (InvocationTargetException ite) {
-            throw new ClassNotFoundException("loadClass failed",
-                ite.getCause());
-        }
-
-        return null;
-    }
-
-    /**
-     * Finds the class with the specified binary name, from .class files.
-     */
-    private Class<?> findClassNonDalvik(String name)
-        throws ClassNotFoundException {
-
-        String pathName = CLASS_PATH + name + ".class";
-        //System.out.println("--- Fancy: looking for " + pathName);
-
-        File path = new File(pathName);
-        RandomAccessFile raf;
-
-        try {
-            raf = new RandomAccessFile(path, "r");
-        } catch (FileNotFoundException fnfe) {
-            throw new ClassNotFoundException("Not found: " + pathName);
-        }
-
-        /* read the entire file in */
-        byte[] fileData;
-        try {
-            fileData = new byte[(int) raf.length()];
-            raf.readFully(fileData);
-        } catch (IOException ioe) {
-            throw new ClassNotFoundException("Read error: " + pathName);
-        } finally {
-            try {
-                raf.close();
-            } catch (IOException ioe) {
-                // drop
-            }
-        }
-
-        /* create the class */
-        //System.out.println("--- Fancy: defining " + name);
-        try {
-            return defineClass(name, fileData, 0, fileData.length);
-        } catch (Throwable th) {
-            throw new ClassNotFoundException("defineClass failed", th);
-        }
-    }
-
-    /**
-     * Load a class.
-     *
-     * Normally a class loader wouldn't override this, but we want our
-     * version of the class to take precedence over an already-loaded
-     * version.
-     *
-     * We still want the system classes (e.g. java.lang.Object) from the
-     * bootstrap class loader.
-     */
-    protected Class<?> loadClass(String name, boolean resolve)
-        throws ClassNotFoundException
-    {
-        Class res;
-
-        /*
-         * 1. Invoke findLoadedClass(String) to check if the class has
-         * already been loaded.
-         *
-         * This doesn't change.
-         */
-        res = findLoadedClass(name);
-        if (res != null) {
-            System.out.println("FancyLoader.loadClass: "
-                + name + " already loaded");
-            if (resolve)
-                resolveClass(res);
-            return res;
-        }
-
-        /*
-         * 3. Invoke the findClass(String) method to find the class.
-         */
-        try {
-            res = findClass(name);
-            if (resolve)
-                resolveClass(res);
-        }
-        catch (ClassNotFoundException e) {
-            // we couldn't find it, so eat the exception and keep going
-        }
-
-        /*
-         * 2. Invoke the loadClass method on the parent class loader.  If
-         * the parent loader is null the class loader built-in to the
-         * virtual machine is used, instead.
-         *
-         * (Since we're not in java.lang, we can't actually invoke the
-         * parent's loadClass() method, but we passed our parent to the
-         * super-class which can take care of it for us.)
-         */
-        res = super.loadClass(name, resolve);   // returns class or throws
-        return res;
-    }
-}
diff --git a/test/138-duplicate-classes-check/src/Main.java b/test/138-duplicate-classes-check/src/Main.java
index a9b5bb0..a2ef281 100644
--- a/test/138-duplicate-classes-check/src/Main.java
+++ b/test/138-duplicate-classes-check/src/Main.java
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+import dalvik.system.DexClassLoader;
 import java.io.File;
 import java.lang.reflect.Method;
 
@@ -30,7 +31,11 @@
 
         // Now run the class from the -ex file.
 
-        FancyLoader loader = new FancyLoader(getClass().getClassLoader());
+        String dexPath = System.getenv("DEX_LOCATION") + "/138-duplicate-classes-check-ex.jar";
+        String optimizedDirectory = System.getenv("DEX_LOCATION");
+        String librarySearchPath = null;
+        DexClassLoader loader = new DexClassLoader(dexPath, optimizedDirectory, librarySearchPath,
+                getClass().getClassLoader());
 
         try {
             Class testEx = loader.loadClass("TestEx");
diff --git a/test/976-conflict-no-methods/expected.txt b/test/976-conflict-no-methods/expected.txt
new file mode 100644
index 0000000..656dfc5
--- /dev/null
+++ b/test/976-conflict-no-methods/expected.txt
@@ -0,0 +1 @@
+Pass
diff --git a/test/976-conflict-no-methods/info.txt b/test/976-conflict-no-methods/info.txt
new file mode 100644
index 0000000..cdc3149
--- /dev/null
+++ b/test/976-conflict-no-methods/info.txt
@@ -0,0 +1 @@
+Regression test for classes that have conflict tables but no methods. b/28707801
\ No newline at end of file
diff --git a/test/976-conflict-no-methods/smali/Iface.smali b/test/976-conflict-no-methods/smali/Iface.smali
new file mode 100644
index 0000000..aa4ec37
--- /dev/null
+++ b/test/976-conflict-no-methods/smali/Iface.smali
@@ -0,0 +1,281 @@
+# /*
+#  * Copyright (C) 2016 The Android Open Source Project
+#  *
+#  * Licensed under the Apache License, Version 2.0 (the "License");
+#  * you may not use this file except in compliance with the License.
+#  * You may obtain a copy of the License at
+#  *
+#  *      http://www.apache.org/licenses/LICENSE-2.0
+#  *
+#  * Unless required by applicable law or agreed to in writing, software
+#  * distributed under the License is distributed on an "AS IS" BASIS,
+#  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  * See the License for the specific language governing permissions and
+#  * limitations under the License.
+#  */
+#
+# public interface Iface2 {
+#    public void abstractMethod0();
+#    public void abstractMethod1();
+#    public void abstractMethod2();
+#    public void abstractMethod3();
+#    public void abstractMethod4();
+#    public void abstractMethod5();
+#    public void abstractMethod6();
+#    public void abstractMethod7();
+#    public void abstractMethod8();
+#    public void abstractMethod9();
+#    public void abstractMethod10();
+#    public void abstractMethod11();
+#    public void abstractMethod12();
+#    public void abstractMethod13();
+#    public void abstractMethod14();
+#    public void abstractMethod15();
+#    public void abstractMethod16();
+#    public void abstractMethod17();
+#    public void abstractMethod18();
+#    public void abstractMethod19();
+#    public void abstractMethod20();
+#    public void abstractMethod21();
+#    public void abstractMethod22();
+#    public void abstractMethod23();
+#    public void abstractMethod24();
+#    public void abstractMethod25();
+#    public void abstractMethod26();
+#    public void abstractMethod27();
+#    public void abstractMethod28();
+#    public void abstractMethod29();
+#    public void abstractMethod30();
+#    public void abstractMethod31();
+#    public void abstractMethod32();
+#    public void abstractMethod33();
+#    public void abstractMethod34();
+#    public void abstractMethod35();
+#    public void abstractMethod36();
+#    public void abstractMethod37();
+#    public void abstractMethod38();
+#    public void abstractMethod39();
+#    public void abstractMethod40();
+#    public void abstractMethod41();
+#    public void abstractMethod42();
+#    public void abstractMethod43();
+#    public void abstractMethod44();
+#    public void abstractMethod45();
+#    public void abstractMethod46();
+#    public void abstractMethod47();
+#    public void abstractMethod48();
+#    public void abstractMethod49();
+#    public void abstractMethod50();
+#    public void abstractMethod51();
+#    public void abstractMethod52();
+#    public void abstractMethod53();
+#    public void abstractMethod54();
+#    public void abstractMethod55();
+#    public void abstractMethod56();
+#    public void abstractMethod57();
+#    public void abstractMethod58();
+#    public void abstractMethod59();
+#    public void abstractMethod60();
+#    public void abstractMethod61();
+#    public void abstractMethod62();
+#    public void abstractMethod63();
+#    public void abstractMethod64();
+# }
+
+.class public abstract interface LIface;
+.super Ljava/lang/Object;
+
+.method public abstract abstractMethod0()V
+.end method
+
+.method public abstract abstractMethod1()V
+.end method
+
+.method public abstract abstractMethod2()V
+.end method
+
+.method public abstract abstractMethod3()V
+.end method
+
+.method public abstract abstractMethod4()V
+.end method
+
+.method public abstract abstractMethod5()V
+.end method
+
+.method public abstract abstractMethod6()V
+.end method
+
+.method public abstract abstractMethod7()V
+.end method
+
+.method public abstract abstractMethod8()V
+.end method
+
+.method public abstract abstractMethod9()V
+.end method
+
+.method public abstract abstractMethod10()V
+.end method
+
+.method public abstract abstractMethod11()V
+.end method
+
+.method public abstract abstractMethod12()V
+.end method
+
+.method public abstract abstractMethod13()V
+.end method
+
+.method public abstract abstractMethod14()V
+.end method
+
+.method public abstract abstractMethod15()V
+.end method
+
+.method public abstract abstractMethod16()V
+.end method
+
+.method public abstract abstractMethod17()V
+.end method
+
+.method public abstract abstractMethod18()V
+.end method
+
+.method public abstract abstractMethod19()V
+.end method
+
+.method public abstract abstractMethod20()V
+.end method
+
+.method public abstract abstractMethod21()V
+.end method
+
+.method public abstract abstractMethod22()V
+.end method
+
+.method public abstract abstractMethod23()V
+.end method
+
+.method public abstract abstractMethod24()V
+.end method
+
+.method public abstract abstractMethod25()V
+.end method
+
+.method public abstract abstractMethod26()V
+.end method
+
+.method public abstract abstractMethod27()V
+.end method
+
+.method public abstract abstractMethod28()V
+.end method
+
+.method public abstract abstractMethod29()V
+.end method
+
+.method public abstract abstractMethod30()V
+.end method
+
+.method public abstract abstractMethod31()V
+.end method
+
+.method public abstract abstractMethod32()V
+.end method
+
+.method public abstract abstractMethod33()V
+.end method
+
+.method public abstract abstractMethod34()V
+.end method
+
+.method public abstract abstractMethod35()V
+.end method
+
+.method public abstract abstractMethod36()V
+.end method
+
+.method public abstract abstractMethod37()V
+.end method
+
+.method public abstract abstractMethod38()V
+.end method
+
+.method public abstract abstractMethod39()V
+.end method
+
+.method public abstract abstractMethod40()V
+.end method
+
+.method public abstract abstractMethod41()V
+.end method
+
+.method public abstract abstractMethod42()V
+.end method
+
+.method public abstract abstractMethod43()V
+.end method
+
+.method public abstract abstractMethod44()V
+.end method
+
+.method public abstract abstractMethod45()V
+.end method
+
+.method public abstract abstractMethod46()V
+.end method
+
+.method public abstract abstractMethod47()V
+.end method
+
+.method public abstract abstractMethod48()V
+.end method
+
+.method public abstract abstractMethod49()V
+.end method
+
+.method public abstract abstractMethod50()V
+.end method
+
+.method public abstract abstractMethod51()V
+.end method
+
+.method public abstract abstractMethod52()V
+.end method
+
+.method public abstract abstractMethod53()V
+.end method
+
+.method public abstract abstractMethod54()V
+.end method
+
+.method public abstract abstractMethod55()V
+.end method
+
+.method public abstract abstractMethod56()V
+.end method
+
+.method public abstract abstractMethod57()V
+.end method
+
+.method public abstract abstractMethod58()V
+.end method
+
+.method public abstract abstractMethod59()V
+.end method
+
+.method public abstract abstractMethod60()V
+.end method
+
+.method public abstract abstractMethod61()V
+.end method
+
+.method public abstract abstractMethod62()V
+.end method
+
+.method public abstract abstractMethod63()V
+.end method
+
+.method public abstract abstractMethod64()V
+.end method
diff --git a/test/976-conflict-no-methods/smali/Main.smali b/test/976-conflict-no-methods/smali/Main.smali
new file mode 100644
index 0000000..7dd1160
--- /dev/null
+++ b/test/976-conflict-no-methods/smali/Main.smali
@@ -0,0 +1,358 @@
+# /*
+#  * Copyright (C) 2016 The Android Open Source Project
+#  *
+#  * Licensed under the Apache License, Version 2.0 (the "License");
+#  * you may not use this file except in compliance with the License.
+#  * You may obtain a copy of the License at
+#  *
+#  *      http://www.apache.org/licenses/LICENSE-2.0
+#  *
+#  * Unless required by applicable law or agreed to in writing, software
+#  * distributed under the License is distributed on an "AS IS" BASIS,
+#  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  * See the License for the specific language governing permissions and
+#  * limitations under the License.
+#  */
+#
+.class public LMain;
+.super Ljava/lang/Object;
+.implements LIface;
+
+.method public constructor <init>()V
+    .registers 1
+    invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+    return-void
+.end method
+
+.method public static main([Ljava/lang/String;)V
+    .locals 2
+    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+    const-string v1, "Pass"
+    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+    return-void
+.end method
+
+.method public abstractMethod0()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod1()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod2()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod3()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod4()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod5()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod6()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod7()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod8()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod9()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod10()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod11()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod12()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod13()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod14()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod15()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod16()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod17()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod18()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod19()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod20()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod21()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod22()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod23()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod24()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod25()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod26()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod27()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod28()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod29()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod30()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod31()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod32()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod33()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod34()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod35()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod36()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod37()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod38()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod39()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod40()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod41()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod42()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod43()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod44()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod45()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod46()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod47()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod48()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod49()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod50()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod51()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod52()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod53()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod54()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod55()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod56()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod57()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod58()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod59()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod60()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod61()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod62()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod63()V
+    .locals 0
+    return-void
+.end method
+
+.method public abstractMethod64()V
+    .locals 0
+    return-void
+.end method
diff --git a/test/976-conflict-no-methods/smali/NoMethods.smali b/test/976-conflict-no-methods/smali/NoMethods.smali
new file mode 100644
index 0000000..787e34a
--- /dev/null
+++ b/test/976-conflict-no-methods/smali/NoMethods.smali
@@ -0,0 +1,19 @@
+# /*
+#  * Copyright (C) 2016 The Android Open Source Project
+#  *
+#  * Licensed under the Apache License, Version 2.0 (the "License");
+#  * you may not use this file except in compliance with the License.
+#  * You may obtain a copy of the License at
+#  *
+#  *      http://www.apache.org/licenses/LICENSE-2.0
+#  *
+#  * Unless required by applicable law or agreed to in writing, software
+#  * distributed under the License is distributed on an "AS IS" BASIS,
+#  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  * See the License for the specific language governing permissions and
+#  * limitations under the License.
+#  */
+#
+
+.class public LNoMethods;
+.super LMain;