Merge "art: use proper nativehelper headers"
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index cf6d1ec..571c91a 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -104,7 +104,7 @@
 
 ART_GTEST_atomic_dex_ref_map_test_DEX_DEPS := Interfaces
 ART_GTEST_class_linker_test_DEX_DEPS := AllFields ErroneousA ErroneousB ErroneousInit ForClassLoaderA ForClassLoaderB ForClassLoaderC ForClassLoaderD Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
-ART_GTEST_class_loader_context_test_DEX_DEPS := Main MultiDex MyClass
+ART_GTEST_class_loader_context_test_DEX_DEPS := Main MultiDex MyClass ForClassLoaderA ForClassLoaderB ForClassLoaderC ForClassLoaderD
 ART_GTEST_class_table_test_DEX_DEPS := XandY
 ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
 ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index b3de3c9..b5d3736 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -414,16 +414,20 @@
   UsageError("      ");
   UsageError("      The chain is interpreted in the natural 'parent order', meaning that class");
   UsageError("      loader 'i+1' will be the parent of class loader 'i'.");
-  UsageError("      The compilation sources will be added to the classpath of the last class");
-  UsageError("      loader. This allows the compiled dex files to be loaded at runtime in");
-  UsageError("      a class loader that contains other dex files as well (e.g. shared libraries).");
+  UsageError("      The compilation sources will be appended to the classpath of the first class");
+  UsageError("      loader.");
+  UsageError("      ");
+  UsageError("      E.g. if the context is 'PCL[lib1.dex];DLC[lib2.dex]' and ");
+  UsageError("      --dex-file=src.dex then dex2oat will setup a PathClassLoader with classpath ");
+  UsageError("      'lib1.dex:src.dex' and set its parent to a DelegateLastClassLoader with ");
+  UsageError("      classpath 'lib2.dex'.");
   UsageError("      ");
   UsageError("      Note that the compiler will be tolerant if the source dex files specified");
   UsageError("      with --dex-file are found in the classpath. The source dex files will be");
   UsageError("      removed from any class loader's classpath possibly resulting in empty");
   UsageError("      class loaders.");
   UsageError("      ");
-  UsageError("      Example: --classloader-spec=PCL[lib1.dex:lib2.dex];DLC[lib3.dex]");
+  UsageError("      Example: --class-loader-context=PCL[lib1.dex:lib2.dex];DLC[lib3.dex]");
   UsageError("");
   std::cerr << "See log for usage error information\n";
   exit(EXIT_FAILURE);
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index ed1aee6..68ec0b5 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -1102,4 +1102,16 @@
   RunTest(context.c_str(), kEmptyClassPathKey, /*expected_success*/ true);
 }
 
+TEST_F(Dex2oatClassLoaderContextTest, ChainContext) {
+  std::vector<std::unique_ptr<const DexFile>> dex_files1 = OpenTestDexFiles("Nested");
+  std::vector<std::unique_ptr<const DexFile>> dex_files2 = OpenTestDexFiles("MultiDex");
+
+  std::string context = "PCL[" + GetTestDexFileName("Nested") + "];" +
+      "DLC[" + GetTestDexFileName("MultiDex") + "]";
+  std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "];" +
+      "DLC[" + CreateClassPathWithChecksums(dex_files2) + "]";
+
+  RunTest(context.c_str(), expected_classpath_key.c_str(), true);
+}
+
 }  // namespace art
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 1abff59..a9237ef3 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -45,6 +45,7 @@
 #include "base/value_object.h"
 #include "cha.h"
 #include "class_linker-inl.h"
+#include "class_loader_utils.h"
 #include "class_table-inl.h"
 #include "compiler_callbacks.h"
 #include "debugger.h"
@@ -2481,27 +2482,6 @@
   return ClassPathEntry(nullptr, nullptr);
 }
 
-// Returns true if the given class loader is either a PathClassLoader or a DexClassLoader.
-// (they both have the same behaviour with respect to class lockup order)
-static bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
-                                   Handle<mirror::ClassLoader> class_loader)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  mirror::Class* class_loader_class = class_loader->GetClass();
-  return
-      (class_loader_class ==
-          soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)) ||
-      (class_loader_class ==
-          soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader));
-}
-
-static bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
-                                      Handle<mirror::ClassLoader> class_loader)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  mirror::Class* class_loader_class = class_loader->GetClass();
-  return class_loader_class ==
-      soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
-}
-
 bool ClassLinker::FindClassInBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
                                                 Thread* self,
                                                 const char* descriptor,
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index 2bed1d5..eab3b86 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -16,14 +16,20 @@
 
 #include "class_loader_context.h"
 
+#include "art_field-inl.h"
 #include "base/dchecked_vector.h"
 #include "base/stl_util.h"
 #include "class_linker.h"
+#include "class_loader_utils.h"
 #include "dex_file.h"
+#include "handle_scope-inl.h"
+#include "jni_internal.h"
 #include "oat_file_assistant.h"
+#include "obj_ptr-inl.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread.h"
+#include "well_known_classes.h"
 
 namespace art {
 
@@ -38,7 +44,29 @@
 ClassLoaderContext::ClassLoaderContext()
     : special_shared_library_(false),
       dex_files_open_attempted_(false),
-      dex_files_open_result_(false) {}
+      dex_files_open_result_(false),
+      owns_the_dex_files_(true) {}
+
+ClassLoaderContext::ClassLoaderContext(bool owns_the_dex_files)
+    : special_shared_library_(false),
+      dex_files_open_attempted_(true),
+      dex_files_open_result_(true),
+      owns_the_dex_files_(owns_the_dex_files) {}
+
+ClassLoaderContext::~ClassLoaderContext() {
+  if (!owns_the_dex_files_) {
+    // If the context does not own the dex/oat files release the unique pointers to
+    // make sure we do not de-allocate them.
+    for (ClassLoaderInfo& info : class_loader_chain_) {
+      for (std::unique_ptr<OatFile>& oat_file : info.opened_oat_files) {
+        oat_file.release();
+      }
+      for (std::unique_ptr<const DexFile>& dex_file : info.opened_dex_files) {
+        dex_file.release();
+      }
+    }
+  }
+}
 
 std::unique_ptr<ClassLoaderContext> ClassLoaderContext::Create(const std::string& spec) {
   std::unique_ptr<ClassLoaderContext> result(new ClassLoaderContext());
@@ -277,24 +305,41 @@
   Thread* self = Thread::Current();
   ScopedObjectAccess soa(self);
 
-  std::vector<const DexFile*> class_path_files;
+  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
 
-  // TODO(calin): Transition period: assume we only have a classloader until
-  // the oat file assistant implements the full class loader check.
-  if (!class_loader_chain_.empty()) {
-    CHECK_EQ(1u, class_loader_chain_.size());
-    CHECK_EQ(kPathClassLoader, class_loader_chain_[0].type);
-    class_path_files = MakeNonOwningPointerVector(class_loader_chain_[0].opened_dex_files);
+  if (class_loader_chain_.empty()) {
+    return class_linker->CreatePathClassLoader(self, compilation_sources);
   }
 
-  // Classpath: first the class-path given; then the dex files we'll compile.
-  // Thus we'll resolve the class-path first.
-  class_path_files.insert(class_path_files.end(),
-                          compilation_sources.begin(),
-                          compilation_sources.end());
+  // Create the class loaders starting from the top most parent (the one on the last position
+  // in the chain) but omit the first class loader which will contain the compilation_sources and
+  // needs special handling.
+  jobject current_parent = nullptr;  // the starting parent is the BootClassLoader.
+  for (size_t i = class_loader_chain_.size() - 1; i > 0; i--) {
+    std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(
+        class_loader_chain_[i].opened_dex_files);
+    current_parent = class_linker->CreateWellKnownClassLoader(
+        self,
+        class_path_files,
+        GetClassLoaderClass(class_loader_chain_[i].type),
+        current_parent);
+  }
 
-  ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
-  return class_linker->CreatePathClassLoader(self, class_path_files);
+  // We set up all the parents. Move on to create the first class loader.
+  // Its classpath comes first, followed by compilation sources. This ensures that whenever
+  // we need to resolve classes from it the classpath elements come first.
+
+  std::vector<const DexFile*> first_class_loader_classpath = MakeNonOwningPointerVector(
+      class_loader_chain_[0].opened_dex_files);
+  first_class_loader_classpath.insert(first_class_loader_classpath.end(),
+                                    compilation_sources.begin(),
+                                    compilation_sources.end());
+
+  return class_linker->CreateWellKnownClassLoader(
+      self,
+      first_class_loader_classpath,
+      GetClassLoaderClass(class_loader_chain_[0].type),
+      current_parent);
 }
 
 std::vector<const DexFile*> ClassLoaderContext::FlattenOpenedDexFiles() const {
@@ -325,36 +370,285 @@
       << "attempt=" << dex_files_open_attempted_ << ", result=" << dex_files_open_result_;
 }
 
-bool ClassLoaderContext::DecodePathClassLoaderContextFromOatFileKey(
-    const std::string& context_spec,
-    std::vector<std::string>* out_classpath,
-    std::vector<uint32_t>* out_checksums,
-    bool* out_is_special_shared_library) {
-  ClassLoaderContext context;
-  if (!context.Parse(context_spec, /*parse_checksums*/ true)) {
-    LOG(ERROR) << "Invalid class loader context: " << context_spec;
+// Collects the dex files from the give Java dex_file object. Only the dex files with
+// at least 1 class are collected. If a null java_dex_file is passed this method does nothing.
+static bool CollectDexFilesFromJavaDexFile(ObjPtr<mirror::Object> java_dex_file,
+                                           ArtField* const cookie_field,
+                                           std::vector<const DexFile*>* out_dex_files)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (java_dex_file == nullptr) {
+    return true;
+  }
+  // On the Java side, the dex files are stored in the cookie field.
+  mirror::LongArray* long_array = cookie_field->GetObject(java_dex_file)->AsLongArray();
+  if (long_array == nullptr) {
+    // This should never happen so log a warning.
+    LOG(ERROR) << "Unexpected null cookie";
+    return false;
+  }
+  int32_t long_array_size = long_array->GetLength();
+  // Index 0 from the long array stores the oat file. The dex files start at index 1.
+  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 (cp_dex_file != nullptr && cp_dex_file->NumClassDefs() > 0) {
+      // TODO(calin): It's unclear why the dex files with no classes are skipped here and when
+      // cp_dex_file can be null.
+      out_dex_files->push_back(cp_dex_file);
+    }
+  }
+  return true;
+}
+
+// Collects all the dex files loaded by the given class loader.
+// Returns true for success or false if an unexpected state is discovered (e.g. a null dex cookie,
+// a null list of dex elements or a null dex element).
+static bool CollectDexFilesFromSupportedClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+                                                    Handle<mirror::ClassLoader> class_loader,
+                                                    std::vector<const DexFile*>* out_dex_files)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+  CHECK(IsPathOrDexClassLoader(soa, class_loader) || IsDelegateLastClassLoader(soa, class_loader));
+
+  // All supported class loaders inherit from BaseDexClassLoader.
+  // We need to get the DexPathList and loop through it.
+  ArtField* const cookie_field =
+      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
+  ArtField* const dex_file_field =
+      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
+  ObjPtr<mirror::Object> dex_path_list =
+      jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
+          GetObject(class_loader.Get());
+  CHECK(cookie_field != nullptr);
+  CHECK(dex_file_field != nullptr);
+  if (dex_path_list == nullptr) {
+    // This may be null if the current class loader is under construction and it does not
+    // have its fields setup yet.
+    return true;
+  }
+  // DexPathList has an array dexElements of Elements[] which each contain a dex file.
+  ObjPtr<mirror::Object> dex_elements_obj =
+      jni::DecodeArtField(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) {
+    // TODO(calin): It's unclear if we should just assert here. For now be prepared for the worse
+    // and assume we have no elements.
+    return true;
+  } else {
+    StackHandleScope<1> hs(soa.Self());
+    Handle<mirror::ObjectArray<mirror::Object>> dex_elements(
+        hs.NewHandle(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, log an error and break.
+        // TODO(calin): It's unclear if we should just assert here.
+        // This code was propagated to oat_file_manager from the class linker where it would
+        // throw a NPE. For now, return false which will mark this class loader as unsupported.
+        LOG(ERROR) << "Unexpected null in the dex element list";
+        return false;
+      }
+      ObjPtr<mirror::Object> dex_file = dex_file_field->GetObject(element);
+      if (!CollectDexFilesFromJavaDexFile(dex_file, cookie_field, out_dex_files)) {
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+static bool GetDexFilesFromDexElementsArray(
+    ScopedObjectAccessAlreadyRunnable& soa,
+    Handle<mirror::ObjectArray<mirror::Object>> dex_elements,
+    std::vector<const DexFile*>* out_dex_files) REQUIRES_SHARED(Locks::mutator_lock_) {
+  DCHECK(dex_elements != nullptr);
+
+  ArtField* const cookie_field =
+      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
+  ArtField* const dex_file_field =
+      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
+  ObjPtr<mirror::Class> const element_class = soa.Decode<mirror::Class>(
+      WellKnownClasses::dalvik_system_DexPathList__Element);
+  ObjPtr<mirror::Class> const dexfile_class = soa.Decode<mirror::Class>(
+      WellKnownClasses::dalvik_system_DexFile);
+
+  for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
+    mirror::Object* element = dex_elements->GetWithoutChecks(i);
+    // We can hit a null element here because this is invoked with a partially filled dex_elements
+    // array from DexPathList. DexPathList will open each dex sequentially, each time passing the
+    // list of dex files which were opened before.
+    if (element == nullptr) {
+      continue;
+    }
+
+    // We support this being dalvik.system.DexPathList$Element and dalvik.system.DexFile.
+    // TODO(calin): Code caried over oat_file_manager: supporting both classes seem to be
+    // a historical glitch. All the java code opens dex files using an array of Elements.
+    ObjPtr<mirror::Object> dex_file;
+    if (element_class == element->GetClass()) {
+      dex_file = dex_file_field->GetObject(element);
+    } else if (dexfile_class == element->GetClass()) {
+      dex_file = element;
+    } else {
+      LOG(ERROR) << "Unsupported element in dex_elements: "
+                 << mirror::Class::PrettyClass(element->GetClass());
+      return false;
+    }
+
+    if (!CollectDexFilesFromJavaDexFile(dex_file, cookie_field, out_dex_files)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+// Adds the `class_loader` info to the `context`.
+// The dex file present in `dex_elements` array (if not null) will be added at the end of
+// the classpath.
+// This method is recursive (w.r.t. the class loader parent) and will stop once it reaches the
+// BootClassLoader. Note that the class loader chain is expected to be short.
+bool ClassLoaderContext::AddInfoToContextFromClassLoader(
+      ScopedObjectAccessAlreadyRunnable& soa,
+      Handle<mirror::ClassLoader> class_loader,
+      Handle<mirror::ObjectArray<mirror::Object>> dex_elements)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (ClassLinker::IsBootClassLoader(soa, class_loader.Get())) {
+    // Nothing to do for the boot class loader as we don't add its dex files to the context.
+    return true;
+  }
+
+  ClassLoaderContext::ClassLoaderType type;
+  if (IsPathOrDexClassLoader(soa, class_loader)) {
+    type = kPathClassLoader;
+  } else if (IsDelegateLastClassLoader(soa, class_loader)) {
+    type = kDelegateLastClassLoader;
+  } else {
+    LOG(WARNING) << "Unsupported class loader";
     return false;
   }
 
-  *out_is_special_shared_library = context.special_shared_library_;
-  if (context.special_shared_library_) {
+  // Inspect the class loader for its dex files.
+  std::vector<const DexFile*> dex_files_loaded;
+  CollectDexFilesFromSupportedClassLoader(soa, class_loader, &dex_files_loaded);
+
+  // If we have a dex_elements array extract its dex elements now.
+  // This is used in two situations:
+  //   1) when a new ClassLoader is created DexPathList will open each dex file sequentially
+  //      passing the list of already open dex files each time. This ensures that we see the
+  //      correct context even if the ClassLoader under construction is not fully build.
+  //   2) when apk splits are loaded on the fly, the framework will load their dex files by
+  //      appending them to the current class loader. When the new code paths are loaded in
+  //      BaseDexClassLoader, the paths already present in the class loader will be passed
+  //      in the dex_elements array.
+  if (dex_elements != nullptr) {
+    GetDexFilesFromDexElementsArray(soa, dex_elements, &dex_files_loaded);
+  }
+
+  class_loader_chain_.push_back(ClassLoaderContext::ClassLoaderInfo(type));
+  ClassLoaderInfo& info = class_loader_chain_.back();
+  for (const DexFile* dex_file : dex_files_loaded) {
+    info.classpath.push_back(dex_file->GetLocation());
+    info.checksums.push_back(dex_file->GetLocationChecksum());
+    info.opened_dex_files.emplace_back(dex_file);
+  }
+
+  // We created the ClassLoaderInfo for the current loader. Move on to its parent.
+
+  StackHandleScope<1> hs(Thread::Current());
+  Handle<mirror::ClassLoader> parent = hs.NewHandle(class_loader->GetParent());
+
+  // Note that dex_elements array is null here. The elements are considered to be part of the
+  // current class loader and are not passed to the parents.
+  ScopedNullHandle<mirror::ObjectArray<mirror::Object>> null_dex_elements;
+  return AddInfoToContextFromClassLoader(soa, parent, null_dex_elements);
+}
+
+std::unique_ptr<ClassLoaderContext> ClassLoaderContext::CreateContextForClassLoader(
+    jobject class_loader,
+    jobjectArray dex_elements) {
+  CHECK(class_loader != nullptr);
+
+  ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<2> hs(soa.Self());
+  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));
+
+  std::unique_ptr<ClassLoaderContext> result(new ClassLoaderContext(/*owns_the_dex_files*/ false));
+  if (result->AddInfoToContextFromClassLoader(soa, h_class_loader, h_dex_elements)) {
+    return result;
+  } else {
+    return nullptr;
+  }
+}
+
+bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec) {
+  ClassLoaderContext expected_context;
+  if (!expected_context.Parse(context_spec, /*parse_checksums*/ true)) {
+    LOG(WARNING) << "Invalid class loader context: " << context_spec;
+    return false;
+  }
+
+  if (expected_context.special_shared_library_) {
     return true;
   }
 
-  if (context.class_loader_chain_.empty()) {
-    return true;
+  if (expected_context.class_loader_chain_.size() != class_loader_chain_.size()) {
+    LOG(WARNING) << "ClassLoaderContext size mismatch. expected="
+        << expected_context.class_loader_chain_.size()
+        << ", actual=" << class_loader_chain_.size();
+    return false;
   }
 
-  // TODO(calin): assert that we only have a PathClassLoader until the logic for
-  // checking the context covers all case.
-  CHECK_EQ(1u, context.class_loader_chain_.size());
-  const ClassLoaderInfo& info = context.class_loader_chain_[0];
-  CHECK_EQ(kPathClassLoader, info.type);
-  DCHECK_EQ(info.classpath.size(), info.checksums.size());
+  for (size_t i = 0; i < class_loader_chain_.size(); i++) {
+    const ClassLoaderInfo& info = class_loader_chain_[i];
+    const ClassLoaderInfo& expected_info = expected_context.class_loader_chain_[i];
+    if (info.type != expected_info.type) {
+      LOG(WARNING) << "ClassLoaderContext type mismatch for position " << i
+          << ". expected=" << GetClassLoaderTypeName(expected_info.type)
+          << ", found=" << GetClassLoaderTypeName(info.type);
+      return false;
+    }
+    if (info.classpath.size() != expected_info.classpath.size()) {
+      LOG(WARNING) << "ClassLoaderContext classpath size mismatch for position " << i
+            << ". expected=" << expected_info.classpath.size()
+            << ", found=" << info.classpath.size();
+      return false;
+    }
 
-  *out_classpath = info.classpath;
-  *out_checksums = info.checksums;
+    DCHECK_EQ(info.classpath.size(), info.checksums.size());
+    DCHECK_EQ(expected_info.classpath.size(), expected_info.checksums.size());
+
+    for (size_t k = 0; k < info.classpath.size(); k++) {
+      if (info.classpath[k] != expected_info.classpath[k]) {
+        LOG(WARNING) << "ClassLoaderContext classpath element mismatch for position " << i
+            << ". expected=" << expected_info.classpath[k]
+            << ", found=" << info.classpath[k];
+        return false;
+      }
+      if (info.checksums[k] != expected_info.checksums[k]) {
+        LOG(WARNING) << "ClassLoaderContext classpath element checksum mismatch for position " << i
+            << ". expected=" << expected_info.checksums[k]
+            << ", found=" << info.checksums[k];
+        return false;
+      }
+    }
+  }
   return true;
 }
+
+jclass ClassLoaderContext::GetClassLoaderClass(ClassLoaderType type) {
+  switch (type) {
+    case kPathClassLoader: return WellKnownClasses::dalvik_system_PathClassLoader;
+    case kDelegateLastClassLoader: return WellKnownClasses::dalvik_system_DelegateLastClassLoader;
+    case kInvalidClassLoader: break;  // will fail after the switch.
+  }
+  LOG(FATAL) << "Invalid class loader type " << type;
+  UNREACHABLE();
+}
+
 }  // namespace art
 
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
index 9727a3b..37dd02b 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -22,7 +22,9 @@
 
 #include "arch/instruction_set.h"
 #include "base/dchecked_vector.h"
-#include "jni.h"
+#include "handle_scope.h"
+#include "mirror/class_loader.h"
+#include "scoped_thread_state_change.h"
 
 namespace art {
 
@@ -35,6 +37,8 @@
   // Creates an empty context (with no class loaders).
   ClassLoaderContext();
 
+  ~ClassLoaderContext();
+
   // Opens requested class path files and appends them to ClassLoaderInfo::opened_dex_files.
   // If the dex files have been stripped, the method opens them from their oat files which are added
   // to ClassLoaderInfo::opened_oat_files. The 'classpath_dir' argument specifies the directory to
@@ -56,11 +60,22 @@
   bool RemoveLocationsFromClassPaths(const dchecked_vector<std::string>& compilation_sources);
 
   // Creates the entire class loader hierarchy according to the current context.
-  // The compilation sources are appended to the classpath of the top class loader
-  // (i.e the class loader whose parent is the BootClassLoader).
-  // Should only be called if OpenDexFiles() returned true.
+  // Returns the first class loader from the chain.
+  //
+  // For example: if the context was built from the spec
+  // "ClassLoaderType1[ClasspathElem1:ClasspathElem2...];ClassLoaderType2[...]..."
+  // the method returns the class loader correponding to ClassLoader1. The parent chain will be
+  // ClassLoader1 --> ClassLoader2 --> ... --> BootClassLoader.
+  //
+  // The compilation sources are appended to the classpath of the first class loader (in the above
+  // example ClassLoader1).
+  //
   // If the context is empty, this method only creates a single PathClassLoader with the
   // given compilation_sources.
+  //
+  // Notes:
+  //   1) the objects are not completely set up. Do not use this outside of tests and the compiler.
+  //   2) should only be called before the first call to OpenDexFiles().
   jobject CreateClassLoader(const std::vector<const DexFile*>& compilation_sources) const;
 
   // Encodes the context as a string suitable to be added in oat files.
@@ -74,24 +89,35 @@
   // Should only be called if OpenDexFiles() returned true.
   std::vector<const DexFile*> FlattenOpenedDexFiles() const;
 
+  // Verifies that the current context is identical to the context encoded as `context_spec`.
+  // Identical means:
+  //    - the number and type of the class loaders from the chain matches
+  //    - the class loader from the same position have the same classpath
+  //      (the order and checksum of the dex files matches)
+  bool VerifyClassLoaderContextMatch(const std::string& context_spec);
+
   // Creates the class loader context from the given string.
   // The format: ClassLoaderType1[ClasspathElem1:ClasspathElem2...];ClassLoaderType2[...]...
   // ClassLoaderType is either "PCL" (PathClassLoader) or "DLC" (DelegateLastClassLoader).
   // ClasspathElem is the path of dex/jar/apk file.
+  //
+  // The spec represents a class loader chain with the natural interpretation:
+  // ClassLoader1 has ClassLoader2 as parent which has ClassLoader3 as a parent and so on.
+  // The last class loader is assumed to have the BootClassLoader as a parent.
+  //
   // Note that we allowed class loaders with an empty class path in order to support a custom
   // class loader for the source dex files.
   static std::unique_ptr<ClassLoaderContext> Create(const std::string& spec);
 
-  // Decodes the class loader context stored in the oat file with EncodeContextForOatFile.
-  // Returns true if the format matches, or false otherwise. If the return is true, the out
-  // arguments will contain the classpath dex files, their checksums and whether or not the
-  // context is a special shared library.
-  // The method asserts that the context is made out of only one PathClassLoader.
-  static bool DecodePathClassLoaderContextFromOatFileKey(
-      const std::string& context_spec,
-      std::vector<std::string>* out_classpath,
-      std::vector<uint32_t>* out_checksums,
-      bool* out_is_special_shared_library);
+  // Creates a context for the given class_loader and dex_elements.
+  // The method will walk the parent chain starting from `class_loader` and add their dex files
+  // to the current class loaders chain. The `dex_elements` will be added at the end of the
+  // classpath belonging to the `class_loader` argument.
+  // The ownership of the opened dex files will be retained by the given `class_loader`.
+  // If there are errors in processing the class loader chain (e.g. unsupported elements) the
+  // method returns null.
+  static std::unique_ptr<ClassLoaderContext> CreateContextForClassLoader(jobject class_loader,
+                                                                         jobjectArray dex_elements);
 
  private:
   enum ClassLoaderType {
@@ -118,6 +144,13 @@
     explicit ClassLoaderInfo(ClassLoaderType cl_type) : type(cl_type) {}
   };
 
+  // Constructs an empty context.
+  // `owns_the_dex_files` specifies whether or not the context will own the opened dex files
+  // present in the class loader chain. If `owns_the_dex_files` is true then OpenDexFiles cannot
+  // be called on this context (dex_files_open_attempted_ and dex_files_open_result_ will be set
+  // to true as well)
+  explicit ClassLoaderContext(bool owns_the_dex_files);
+
   // Reads the class loader spec in place and returns true if the spec is valid and the
   // compilation context was constructed.
   bool Parse(const std::string& spec, bool parse_checksums = false);
@@ -129,6 +162,19 @@
                             ClassLoaderType class_loader_type,
                             bool parse_checksums = false);
 
+  // CHECKs that the dex files were opened (OpenDexFiles was called and set dex_files_open_result_
+  // to true). Aborts if not. The `calling_method` is used in the log message to identify the source
+  // of the call.
+  void CheckDexFilesOpened(const std::string& calling_method) const;
+
+  // Adds the `class_loader` info to the context.
+  // The dex file present in `dex_elements` array (if not null) will be added at the end of
+  // the classpath.
+  bool AddInfoToContextFromClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+                                       Handle<mirror::ClassLoader> class_loader,
+                                       Handle<mirror::ObjectArray<mirror::Object>> dex_elements)
+  REQUIRES_SHARED(Locks::mutator_lock_);
+
   // Extracts the class loader type from the given spec.
   // Return ClassLoaderContext::kInvalidClassLoader if the class loader type is not
   // recognized.
@@ -138,8 +184,8 @@
   // The returned format can be used when parsing a context spec.
   static const char* GetClassLoaderTypeName(ClassLoaderType type);
 
-  // CHECKs that the dex files were opened (OpenDexFiles was called). Aborts if not.
-  void CheckDexFilesOpened(const std::string& calling_method) const;
+  // Returns the WellKnownClass for the given class loader type.
+  static jclass GetClassLoaderClass(ClassLoaderType type);
 
   // The class loader chain represented as a vector.
   // The parent of class_loader_chain_[i] is class_loader_chain_[i++].
@@ -158,6 +204,13 @@
   // The result of the last OpenDexFiles() operation.
   bool dex_files_open_result_;
 
+  // Whether or not the context owns the opened dex and oat files.
+  // If true, the opened dex files will be de-allocated when the context is destructed.
+  // If false, the objects will continue to be alive.
+  // Note that for convenience the the opened dex/oat files are stored as unique pointers
+  // which will release their ownership in the destructor based on this flag.
+  const bool owns_the_dex_files_;
+
   friend class ClassLoaderContextTest;
 
   DISALLOW_COPY_AND_ASSIGN(ClassLoaderContext);
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index 03eb0e4..1b46f67 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -45,18 +45,32 @@
 
   void VerifyClassLoaderPCL(ClassLoaderContext* context,
                             size_t index,
-                            std::string classpath) {
+                            const std::string& classpath) {
     VerifyClassLoaderInfo(
         context, index, ClassLoaderContext::kPathClassLoader, classpath);
   }
 
   void VerifyClassLoaderDLC(ClassLoaderContext* context,
                             size_t index,
-                            std::string classpath) {
+                            const std::string& classpath) {
     VerifyClassLoaderInfo(
         context, index, ClassLoaderContext::kDelegateLastClassLoader, classpath);
   }
 
+  void VerifyClassLoaderPCLFromTestDex(ClassLoaderContext* context,
+                                       size_t index,
+                                       const std::string& test_name) {
+    VerifyClassLoaderFromTestDex(
+        context, index, ClassLoaderContext::kPathClassLoader, test_name);
+  }
+
+  void VerifyClassLoaderDLCFromTestDex(ClassLoaderContext* context,
+                                       size_t index,
+                                       const std::string& test_name) {
+    VerifyClassLoaderFromTestDex(
+        context, index, ClassLoaderContext::kDelegateLastClassLoader, test_name);
+  }
+
   void VerifyOpenDexFiles(
       ClassLoaderContext* context,
       size_t index,
@@ -83,11 +97,49 @@
     }
   }
 
+  std::unique_ptr<ClassLoaderContext> CreateContextForClassLoader(jobject class_loader) {
+    return ClassLoaderContext::CreateContextForClassLoader(class_loader, nullptr);
+  }
+
+  std::unique_ptr<ClassLoaderContext> ParseContextWithChecksums(const std::string& context_spec) {
+    std::unique_ptr<ClassLoaderContext> context(new ClassLoaderContext());
+    if (!context->Parse(context_spec, /*parse_checksums*/ true)) {
+      return nullptr;
+    }
+    return context;
+  }
+
+  void VerifyContextForClassLoader(ClassLoaderContext* context) {
+    ASSERT_TRUE(context != nullptr);
+    ASSERT_TRUE(context->dex_files_open_attempted_);
+    ASSERT_TRUE(context->dex_files_open_result_);
+    ASSERT_FALSE(context->owns_the_dex_files_);
+    ASSERT_FALSE(context->special_shared_library_);
+  }
+
+  void VerifyClassLoaderDexFiles(ScopedObjectAccess& soa,
+                                 Handle<mirror::ClassLoader> class_loader,
+                                 jclass type,
+                                 std::vector<const DexFile*>& expected_dex_files)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    ASSERT_TRUE(class_loader->GetClass() == soa.Decode<mirror::Class>(type));
+
+    std::vector<const DexFile*> class_loader_dex_files = GetDexFiles(soa, class_loader);
+    ASSERT_EQ(expected_dex_files.size(), class_loader_dex_files.size());
+
+    for (size_t i = 0; i < expected_dex_files.size(); i++) {
+      ASSERT_EQ(expected_dex_files[i]->GetLocation(),
+                class_loader_dex_files[i]->GetLocation());
+      ASSERT_EQ(expected_dex_files[i]->GetLocationChecksum(),
+                class_loader_dex_files[i]->GetLocationChecksum());
+    }
+  }
+
  private:
   void VerifyClassLoaderInfo(ClassLoaderContext* context,
                              size_t index,
                              ClassLoaderContext::ClassLoaderType type,
-                             std::string classpath) {
+                             const std::string& classpath) {
     ASSERT_TRUE(context != nullptr);
     ASSERT_GT(context->class_loader_chain_.size(), index);
     ClassLoaderContext::ClassLoaderInfo& info = context->class_loader_chain_[index];
@@ -96,6 +148,18 @@
     Split(classpath, ':', &expected_classpath);
     ASSERT_EQ(expected_classpath, info.classpath);
   }
+
+  void VerifyClassLoaderFromTestDex(ClassLoaderContext* context,
+                                    size_t index,
+                                    ClassLoaderContext::ClassLoaderType type,
+                                    const std::string& test_name) {
+    std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(test_name.c_str());
+    std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files;
+    all_dex_files.push_back(&dex_files);
+
+    VerifyClassLoaderInfo(context, index, type, GetTestDexFileName(test_name.c_str()));
+    VerifyOpenDexFiles(context, index, all_dex_files);
+  }
 };
 
 TEST_F(ClassLoaderContextTest, ParseValidContextPCL) {
@@ -200,7 +264,7 @@
 
   ScopedObjectAccess soa(Thread::Current());
 
-  StackHandleScope<2> hs(soa.Self());
+  StackHandleScope<1> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader = hs.NewHandle(
       soa.Decode<mirror::ClassLoader>(jclass_loader));
 
@@ -209,25 +273,17 @@
   ASSERT_TRUE(class_loader->GetParent()->GetClass() ==
       soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
 
-
-  std::vector<const DexFile*> class_loader_dex_files = GetDexFiles(jclass_loader);
-  ASSERT_EQ(classpath_dex.size() + compilation_sources.size(), class_loader_dex_files.size());
-
-  // The classpath dex files must come first.
-  for (size_t i = 0; i < classpath_dex.size(); i++) {
-    ASSERT_EQ(classpath_dex[i]->GetLocation(),
-              class_loader_dex_files[i]->GetLocation());
-    ASSERT_EQ(classpath_dex[i]->GetLocationChecksum(),
-              class_loader_dex_files[i]->GetLocationChecksum());
+  // For the first class loader the class path dex files must come first and then the
+  // compilation sources.
+  std::vector<const DexFile*> expected_classpath = MakeNonOwningPointerVector(classpath_dex);
+  for (auto& dex : compilation_sources_raw) {
+    expected_classpath.push_back(dex);
   }
 
-  // The compilation dex files must come second.
-  for (size_t i = 0, k = classpath_dex.size(); i < compilation_sources.size(); i++, k++) {
-    ASSERT_EQ(compilation_sources[i]->GetLocation(),
-              class_loader_dex_files[k]->GetLocation());
-    ASSERT_EQ(compilation_sources[i]->GetLocationChecksum(),
-              class_loader_dex_files[k]->GetLocationChecksum());
-  }
+  VerifyClassLoaderDexFiles(soa,
+                            class_loader,
+                            WellKnownClasses::dalvik_system_PathClassLoader,
+                            expected_classpath);
 }
 
 TEST_F(ClassLoaderContextTest, CreateClassLoaderWithEmptyContext) {
@@ -244,28 +300,90 @@
 
   ScopedObjectAccess soa(Thread::Current());
 
-  StackHandleScope<2> hs(soa.Self());
+  StackHandleScope<1> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader = hs.NewHandle(
       soa.Decode<mirror::ClassLoader>(jclass_loader));
 
-  ASSERT_TRUE(class_loader->GetClass() ==
-      soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader));
+  // An empty context should create a single PathClassLoader with only the compilation sources.
+  VerifyClassLoaderDexFiles(soa,
+                            class_loader,
+                            WellKnownClasses::dalvik_system_PathClassLoader,
+                            compilation_sources_raw);
   ASSERT_TRUE(class_loader->GetParent()->GetClass() ==
       soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
-
-
-  std::vector<const DexFile*> class_loader_dex_files = GetDexFiles(jclass_loader);
-
-  // The compilation sources should be the only files present in the class loader
-  ASSERT_EQ(compilation_sources.size(), class_loader_dex_files.size());
-  for (size_t i = 0; i < compilation_sources.size(); i++) {
-    ASSERT_EQ(compilation_sources[i]->GetLocation(),
-        class_loader_dex_files[i]->GetLocation());
-    ASSERT_EQ(compilation_sources[i]->GetLocationChecksum(),
-        class_loader_dex_files[i]->GetLocationChecksum());
-  }
 }
 
+TEST_F(ClassLoaderContextTest, CreateClassLoaderWithComplexChain) {
+  // Setup the context.
+  std::vector<std::unique_ptr<const DexFile>> classpath_dex_a = OpenTestDexFiles("ForClassLoaderA");
+  std::vector<std::unique_ptr<const DexFile>> classpath_dex_b = OpenTestDexFiles("ForClassLoaderB");
+  std::vector<std::unique_ptr<const DexFile>> classpath_dex_c = OpenTestDexFiles("ForClassLoaderC");
+  std::vector<std::unique_ptr<const DexFile>> classpath_dex_d = OpenTestDexFiles("ForClassLoaderD");
+
+  std::string context_spec =
+      "PCL[" + CreateClassPath(classpath_dex_a) + ":" + CreateClassPath(classpath_dex_b) + "];" +
+      "DLC[" + CreateClassPath(classpath_dex_c) + "];" +
+      "PCL[" + CreateClassPath(classpath_dex_d) + "]";
+
+  std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_spec);
+  ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, ""));
+
+  // Setup the compilation sources.
+  std::vector<std::unique_ptr<const DexFile>> compilation_sources = OpenTestDexFiles("MultiDex");
+  std::vector<const DexFile*> compilation_sources_raw =
+      MakeNonOwningPointerVector(compilation_sources);
+
+  // Create the class loader.
+  jobject jclass_loader = context->CreateClassLoader(compilation_sources_raw);
+  ASSERT_TRUE(jclass_loader != nullptr);
+
+  // Verify the class loader.
+  ScopedObjectAccess soa(Thread::Current());
+
+  StackHandleScope<3> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle(
+      soa.Decode<mirror::ClassLoader>(jclass_loader));
+
+  // Verify the first class loader
+
+  // For the first class loader the class path dex files must come first and then the
+  // compilation sources.
+  std::vector<const DexFile*> class_loader_1_dex_files =
+      MakeNonOwningPointerVector(classpath_dex_a);
+  for (auto& dex : classpath_dex_b) {
+    class_loader_1_dex_files.push_back(dex.get());
+  }
+  for (auto& dex : compilation_sources_raw) {
+    class_loader_1_dex_files.push_back(dex);
+  }
+  VerifyClassLoaderDexFiles(soa,
+                            class_loader_1,
+                            WellKnownClasses::dalvik_system_PathClassLoader,
+                            class_loader_1_dex_files);
+
+  // Verify the second class loader
+  Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(class_loader_1->GetParent());
+  std::vector<const DexFile*> class_loader_2_dex_files =
+      MakeNonOwningPointerVector(classpath_dex_c);
+  VerifyClassLoaderDexFiles(soa,
+                            class_loader_2,
+                            WellKnownClasses::dalvik_system_DelegateLastClassLoader,
+                            class_loader_2_dex_files);
+
+  // Verify the third class loader
+  Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(class_loader_2->GetParent());
+  std::vector<const DexFile*> class_loader_3_dex_files =
+      MakeNonOwningPointerVector(classpath_dex_d);
+  VerifyClassLoaderDexFiles(soa,
+                            class_loader_3,
+                            WellKnownClasses::dalvik_system_PathClassLoader,
+                            class_loader_3_dex_files);
+  // The last class loader should have the BootClassLoader as a parent.
+  ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+}
+
+
 TEST_F(ClassLoaderContextTest, RemoveSourceLocations) {
   std::unique_ptr<ClassLoaderContext> context =
       ClassLoaderContext::Create("PCL[a.dex]");
@@ -292,46 +410,82 @@
   std::vector<std::unique_ptr<const DexFile>> dex1 = OpenTestDexFiles("Main");
   std::vector<std::unique_ptr<const DexFile>> dex2 = OpenTestDexFiles("MyClass");
   std::string encoding = context->EncodeContextForOatFile("");
-  std::string expected_encoding = "PCL[" +
-      dex1[0]->GetLocation() + "*" + std::to_string(dex1[0]->GetLocationChecksum()) + ":" +
-      dex2[0]->GetLocation() + "*" + std::to_string(dex2[0]->GetLocationChecksum()) + "]";
+  std::string expected_encoding = "PCL[" + CreateClassPathWithChecksums(dex1) + ":" +
+      CreateClassPathWithChecksums(dex2) + "]";
   ASSERT_EQ(expected_encoding, context->EncodeContextForOatFile(""));
 }
 
-TEST_F(ClassLoaderContextTest, DecodeOatFileKey) {
-  std::string oat_file_encoding = "PCL[a.dex*123:b.dex*456]";
-  std::vector<std::string> classpath;
-  std::vector<uint32_t> checksums;
-  bool is_special_shared_library;
-  bool result = ClassLoaderContext::DecodePathClassLoaderContextFromOatFileKey(
-      oat_file_encoding,
-      &classpath,
-      &checksums,
-      &is_special_shared_library);
-  ASSERT_TRUE(result);
-  ASSERT_FALSE(is_special_shared_library);
-  ASSERT_EQ(2u, classpath.size());
-  ASSERT_EQ(2u, checksums.size());
-  ASSERT_EQ("a.dex", classpath[0]);
-  ASSERT_EQ(123u, checksums[0]);
-  ASSERT_EQ("b.dex", classpath[1]);
-  ASSERT_EQ(456u, checksums[1]);
+// TODO(calin) add a test which creates the context for a class loader together with dex_elements.
+TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) {
+  // The chain is
+  //    ClassLoaderA (PathClassLoader)
+  //       ^
+  //       |
+  //    ClassLoaderB (DelegateLastClassLoader)
+  //       ^
+  //       |
+  //    ClassLoaderC (PathClassLoader)
+  //       ^
+  //       |
+  //    ClassLoaderD (DelegateLastClassLoader)
+
+  jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
+  jobject class_loader_b = LoadDexInDelegateLastClassLoader("ForClassLoaderB", class_loader_a);
+  jobject class_loader_c = LoadDexInPathClassLoader("ForClassLoaderC", class_loader_b);
+  jobject class_loader_d = LoadDexInDelegateLastClassLoader("ForClassLoaderD", class_loader_c);
+
+  std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d);
+
+  VerifyContextForClassLoader(context.get());
+  VerifyContextSize(context.get(), 4);
+
+  VerifyClassLoaderDLCFromTestDex(context.get(), 0, "ForClassLoaderD");
+  VerifyClassLoaderPCLFromTestDex(context.get(), 1, "ForClassLoaderC");
+  VerifyClassLoaderDLCFromTestDex(context.get(), 2, "ForClassLoaderB");
+  VerifyClassLoaderPCLFromTestDex(context.get(), 3, "ForClassLoaderA");
 }
 
-TEST_F(ClassLoaderContextTest, DecodeOatFileKeySpecialLibrary) {
-  std::string oat_file_encoding = "&";
-  std::vector<std::string> classpath;
-  std::vector<uint32_t> checksums;
-  bool is_special_shared_library;
-  bool result = ClassLoaderContext::DecodePathClassLoaderContextFromOatFileKey(
-      oat_file_encoding,
-      &classpath,
-      &checksums,
-      &is_special_shared_library);
-  ASSERT_TRUE(result);
-  ASSERT_TRUE(is_special_shared_library);
-  ASSERT_TRUE(classpath.empty());
-  ASSERT_TRUE(checksums.empty());
+TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) {
+  std::string context_spec = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890]";
+  std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
+
+  VerifyContextSize(context.get(), 2);
+  VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex");
+  VerifyClassLoaderDLC(context.get(), 1, "c.dex");
+
+  ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_spec));
+
+  std::string wrong_class_loader_type = "PCL[a.dex*123:b.dex*456];PCL[c.dex*890]";
+  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_type));
+
+  std::string wrong_class_loader_order = "DLC[c.dex*890];PCL[a.dex*123:b.dex*456]";
+  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_order));
+
+  std::string wrong_classpath_order = "PCL[b.dex*456:a.dex*123];DLC[c.dex*890]";
+  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_classpath_order));
+
+  std::string wrong_checksum = "PCL[a.dex*999:b.dex*456];DLC[c.dex*890]";
+  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_checksum));
+
+  std::string wrong_extra_class_loader = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890];PCL[d.dex*321]";
+  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader));
+
+  std::string wrong_extra_classpath = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890:d.dex*321]";
+  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_classpath));
+
+  std::string wrong_spec = "PCL[a.dex*999:b.dex*456];DLC[";
+  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_spec));
+}
+
+TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) {
+  jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
+  jobject class_loader_b = LoadDexInDelegateLastClassLoader("ForClassLoaderB", class_loader_a);
+  jobject class_loader_c = LoadDexInPathClassLoader("ForClassLoaderC", class_loader_b);
+  jobject class_loader_d = LoadDexInDelegateLastClassLoader("ForClassLoaderD", class_loader_c);
+
+  std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d);
+
+  ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")));
 }
 
 }  // namespace art
diff --git a/runtime/class_loader_utils.h b/runtime/class_loader_utils.h
new file mode 100644
index 0000000..d160a51
--- /dev/null
+++ b/runtime/class_loader_utils.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef ART_RUNTIME_CLASS_LOADER_UTILS_H_
+#define ART_RUNTIME_CLASS_LOADER_UTILS_H_
+
+#include "handle_scope.h"
+#include "mirror/class_loader.h"
+#include "scoped_thread_state_change-inl.h"
+#include "well_known_classes.h"
+
+namespace art {
+
+// Returns true if the given class loader is either a PathClassLoader or a DexClassLoader.
+// (they both have the same behaviour with respect to class lockup order)
+static bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+                                   Handle<mirror::ClassLoader> class_loader)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  mirror::Class* class_loader_class = class_loader->GetClass();
+  return
+      (class_loader_class ==
+          soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)) ||
+      (class_loader_class ==
+          soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DexClassLoader));
+}
+
+static bool IsDelegateLastClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+                                      Handle<mirror::ClassLoader> class_loader)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  mirror::Class* class_loader_class = class_loader->GetClass();
+  return class_loader_class ==
+      soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader);
+}
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_CLASS_LOADER_UTILS_H_
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index a4b9053..a425224 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -589,18 +589,24 @@
 }
 
 std::vector<const DexFile*> CommonRuntimeTestImpl::GetDexFiles(jobject jclass_loader) {
-  std::vector<const DexFile*> ret;
-
   ScopedObjectAccess soa(Thread::Current());
 
-  StackHandleScope<2> hs(soa.Self());
+  StackHandleScope<1> hs(soa.Self());
   Handle<mirror::ClassLoader> class_loader = hs.NewHandle(
       soa.Decode<mirror::ClassLoader>(jclass_loader));
+  return GetDexFiles(soa, class_loader);
+}
 
-  DCHECK_EQ(class_loader->GetClass(),
-            soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader));
-  DCHECK_EQ(class_loader->GetParent()->GetClass(),
-            soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+std::vector<const DexFile*> CommonRuntimeTestImpl::GetDexFiles(
+    ScopedObjectAccess& soa,
+    Handle<mirror::ClassLoader> class_loader) {
+  std::vector<const DexFile*> ret;
+
+  DCHECK(
+      (class_loader->GetClass() ==
+          soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader)) ||
+      (class_loader->GetClass() ==
+          soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_DelegateLastClassLoader)));
 
   // The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
   // We need to get the DexPathList and loop through it.
@@ -618,6 +624,7 @@
     // 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) {
+      StackHandleScope<1> hs(soa.Self());
       Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
           hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
       for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
@@ -757,6 +764,28 @@
   return location;
 }
 
+std::string CommonRuntimeTestImpl::CreateClassPath(
+    const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
+  CHECK(!dex_files.empty());
+  std::string classpath = dex_files[0]->GetLocation();
+  for (size_t i = 1; i < dex_files.size(); i++) {
+    classpath += ":" + dex_files[i]->GetLocation();
+  }
+  return classpath;
+}
+
+std::string CommonRuntimeTestImpl::CreateClassPathWithChecksums(
+    const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
+  CHECK(!dex_files.empty());
+  std::string classpath = dex_files[0]->GetLocation() + "*" +
+      std::to_string(dex_files[0]->GetLocationChecksum());
+  for (size_t i = 1; i < dex_files.size(); i++) {
+    classpath += ":" + dex_files[i]->GetLocation() + "*" +
+        std::to_string(dex_files[i]->GetLocationChecksum());
+  }
+  return classpath;
+}
+
 CheckJniAbortCatcher::CheckJniAbortCatcher() : vm_(Runtime::Current()->GetJavaVM()) {
   vm_->SetCheckJniAbortHook(Hook, &actual_);
 }
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index fcf3a31..daf9ac3 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -28,6 +28,7 @@
 // TODO: Add inl file and avoid including inl.
 #include "obj_ptr-inl.h"
 #include "os.h"
+#include "scoped_thread_state_change-inl.h"
 
 namespace art {
 
@@ -159,9 +160,12 @@
   const DexFile* java_lang_dex_file_;
   std::vector<const DexFile*> boot_class_path_;
 
-  // Get the dex files from a PathClassLoader. This in order of the dex elements and their dex
-  // arrays.
+  // Get the dex files from a PathClassLoader or DelegateLastClassLoader.
+  // This only looks into the current class loader and does not recurse into the parents.
   std::vector<const DexFile*> GetDexFiles(jobject jclass_loader);
+  std::vector<const DexFile*> GetDexFiles(ScopedObjectAccess& soa,
+                                          Handle<mirror::ClassLoader> class_loader)
+    REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Get the first dex file from a PathClassLoader. Will abort if it is null.
   const DexFile* GetFirstDexFile(jobject jclass_loader);
@@ -176,6 +180,15 @@
   // initializers, initialize well-known classes, and creates the heap thread pool.
   virtual void FinalizeSetup();
 
+  // Creates the class path string for the given dex files (the list of dex file locations
+  // separated by ':').
+  std::string CreateClassPath(
+      const std::vector<std::unique_ptr<const DexFile>>& dex_files);
+  // Same as CreateClassPath but add the dex file checksum after each location. The separator
+  // is '*'.
+  std::string CreateClassPathWithChecksums(
+      const std::vector<std::unique_ptr<const DexFile>>& dex_files);
+
  private:
   static std::string GetCoreFileLocation(const char* suffix);
 
diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc
index a186f4c..1154620 100644
--- a/runtime/gc/space/malloc_space.cc
+++ b/runtime/gc/space/malloc_space.cc
@@ -191,10 +191,14 @@
   VLOG(heap) << "Size " << GetMemMap()->Size();
   VLOG(heap) << "GrowthLimit " << PrettySize(growth_limit);
   VLOG(heap) << "Capacity " << PrettySize(capacity);
-  // Remap the tail.
+  // Remap the tail. Pass MAP_PRIVATE since we don't want to share the same ashmem as the zygote
+  // space.
   std::string error_msg;
-  std::unique_ptr<MemMap> mem_map(GetMemMap()->RemapAtEnd(End(), alloc_space_name,
-                                                          PROT_READ | PROT_WRITE, &error_msg));
+  std::unique_ptr<MemMap> mem_map(GetMemMap()->RemapAtEnd(End(),
+                                                          alloc_space_name,
+                                                          PROT_READ | PROT_WRITE,
+                                                          MAP_PRIVATE,
+                                                          &error_msg));
   CHECK(mem_map.get() != nullptr) << error_msg;
   void* allocator = CreateAllocator(End(), starting_size_, initial_size_, capacity,
                                     low_memory_mode);
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 1c36bde..8295f46 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -47,9 +47,13 @@
 static constexpr int kProtAll = PROT_READ | PROT_WRITE | PROT_EXEC;
 static constexpr int kProtData = PROT_READ | PROT_WRITE;
 static constexpr int kProtCode = PROT_READ | PROT_EXEC;
+static constexpr int kProtReadOnly = PROT_READ;
+static constexpr int kProtNone = PROT_NONE;
 
 static constexpr size_t kCodeSizeLogThreshold = 50 * KB;
 static constexpr size_t kStackMapSizeLogThreshold = 50 * KB;
+static constexpr size_t kMinMapSpacingPages = 1;
+static constexpr size_t kMaxMapSpacingPages = 128;
 
 #define CHECKED_MPROTECT(memory, size, prot)                \
   do {                                                      \
@@ -60,19 +64,52 @@
     }                                                       \
   } while (false)                                           \
 
+static MemMap* SplitMemMap(MemMap* existing_map,
+                           const char* name,
+                           size_t split_offset,
+                           int split_prot,
+                           std::string* error_msg,
+                           bool use_ashmem,
+                           unique_fd* shmem_fd = nullptr) {
+  std::string error_str;
+  uint8_t* divider = existing_map->Begin() + split_offset;
+  MemMap* new_map = existing_map->RemapAtEnd(divider,
+                                             name,
+                                             split_prot,
+                                             MAP_SHARED,
+                                             &error_str,
+                                             use_ashmem,
+                                             shmem_fd);
+  if (new_map == nullptr) {
+    std::ostringstream oss;
+    oss << "Failed to create spacing for " << name << ": "
+        << error_str << " offset=" << split_offset;
+    *error_msg = oss.str();
+    return nullptr;
+  }
+  return new_map;
+}
+
+
 JitCodeCache* JitCodeCache::Create(size_t initial_capacity,
                                    size_t max_capacity,
                                    bool generate_debug_info,
                                    std::string* error_msg) {
   ScopedTrace trace(__PRETTY_FUNCTION__);
-  CHECK_GE(max_capacity, initial_capacity);
+  CHECK_GT(max_capacity, initial_capacity);
+  CHECK_GE(max_capacity - kMaxMapSpacingPages * kPageSize, initial_capacity);
 
-  // Generating debug information is mostly for using the 'perf' tool, which does
-  // not work with ashmem.
+  // Generating debug information is for using the Linux perf tool on
+  // host which does not work with ashmem.
   bool use_ashmem = !generate_debug_info;
+
   // With 'perf', we want a 1-1 mapping between an address and a method.
   bool garbage_collect_code = !generate_debug_info;
 
+  // We only use two mappings (separating rw from rx) if we are able to use ashmem.
+  // See the above comment for debug information and not using ashmem.
+  bool use_two_mappings = !generate_debug_info;
+
   // We need to have 32 bit offsets from method headers in code cache which point to things
   // in the data cache. If the maps are more than 4G apart, having multiple maps wouldn't work.
   // Ensure we're below 1 GB to be safe.
@@ -109,30 +146,114 @@
   initial_capacity = RoundDown(initial_capacity, 2 * kPageSize);
   max_capacity = RoundDown(max_capacity, 2 * kPageSize);
 
-  // Data cache is 1 / 2 of the map.
-  // TODO: Make this variable?
-  size_t data_size = max_capacity / 2;
-  size_t code_size = max_capacity - data_size;
-  DCHECK_EQ(code_size + data_size, max_capacity);
-  uint8_t* divider = data_map->Begin() + data_size;
+  // Create a region for JIT data and executable code. This will be
+  // laid out as:
+  //
+  //          +----------------+ --------------------
+  //          :                : ^                  ^
+  //          :  post_code_map : | post_code_size   |
+  //          :   [padding]    : v                  |
+  //          +----------------+ -                  |
+  //          |                | ^                  |
+  //          |   code_map     | | code_size        |
+  //          |   [JIT Code]   | v                  |
+  //          +----------------+ -                  | total_mapping_size
+  //          :                : ^                  |
+  //          :  pre_code_map  : | pre_code_size    |
+  //          :   [padding]    : v                  |
+  //          +----------------+ -                  |
+  //          |                | ^                  |
+  //          |    data_map    | | data_size        |
+  //          |   [Jit Data]   | v                  v
+  //          +----------------+ --------------------
+  //
+  // The padding regions - pre_code_map and post_code_map - exist to
+  // put some random distance between the writable JIT code mapping
+  // and the executable mapping. The padding is discarded at the end
+  // of this function.
+  size_t total_mapping_size = kMaxMapSpacingPages * kPageSize;
+  size_t data_size = RoundUp((max_capacity - total_mapping_size) / 2, kPageSize);
+  size_t pre_code_size =
+      GetRandomNumber(kMinMapSpacingPages, kMaxMapSpacingPages) * kPageSize;
+  size_t code_size = max_capacity - total_mapping_size - data_size;
+  size_t post_code_size = total_mapping_size - pre_code_size;
+  DCHECK_EQ(code_size + data_size + total_mapping_size, max_capacity);
 
-  MemMap* code_map =
-      data_map->RemapAtEnd(divider, "jit-code-cache", kProtAll, &error_str, use_ashmem);
-  if (code_map == nullptr) {
-    std::ostringstream oss;
-    oss << "Failed to create read write execute cache: " << error_str << " size=" << max_capacity;
-    *error_msg = oss.str();
+  // Create pre-code padding region after data region, discarded after
+  // code and data regions are set-up.
+  std::unique_ptr<MemMap> pre_code_map(SplitMemMap(data_map.get(),
+                                                   "jit-code-cache-padding",
+                                                   data_size,
+                                                   kProtNone,
+                                                   error_msg,
+                                                   use_ashmem));
+  if (pre_code_map == nullptr) {
     return nullptr;
   }
-  DCHECK_EQ(code_map->Begin(), divider);
+  DCHECK_EQ(data_map->Size(), data_size);
+  DCHECK_EQ(pre_code_map->Size(), pre_code_size + code_size + post_code_size);
+
+  // Create code region.
+  unique_fd writable_code_fd;
+  std::unique_ptr<MemMap> code_map(SplitMemMap(pre_code_map.get(),
+                                               "jit-code-cache",
+                                               pre_code_size,
+                                               use_two_mappings ? kProtCode : kProtAll,
+                                               error_msg,
+                                               use_ashmem,
+                                               &writable_code_fd));
+  if (code_map == nullptr) {
+    return nullptr;
+  }
+  DCHECK_EQ(pre_code_map->Size(), pre_code_size);
+  DCHECK_EQ(code_map->Size(), code_size + post_code_size);
+
+  // Padding after code region, discarded after code and data regions
+  // are set-up.
+  std::unique_ptr<MemMap> post_code_map(SplitMemMap(code_map.get(),
+                                                    "jit-code-cache-padding",
+                                                    code_size,
+                                                    kProtNone,
+                                                    error_msg,
+                                                    use_ashmem));
+  if (post_code_map == nullptr) {
+    return nullptr;
+  }
+  DCHECK_EQ(code_map->Size(), code_size);
+  DCHECK_EQ(post_code_map->Size(), post_code_size);
+
+  std::unique_ptr<MemMap> writable_code_map;
+  if (use_two_mappings) {
+    // Allocate the R/W view.
+    writable_code_map.reset(MemMap::MapFile(code_size,
+                                            kProtData,
+                                            MAP_SHARED,
+                                            writable_code_fd.get(),
+                                            /* start */ 0,
+                                            /* low_4gb */ true,
+                                            "jit-writable-code",
+                                            &error_str));
+    if (writable_code_map == nullptr) {
+      std::ostringstream oss;
+      oss << "Failed to create writable code cache: " << error_str << " size=" << code_size;
+      *error_msg = oss.str();
+      return nullptr;
+    }
+  }
   data_size = initial_capacity / 2;
   code_size = initial_capacity - data_size;
   DCHECK_EQ(code_size + data_size, initial_capacity);
-  return new JitCodeCache(
-      code_map, data_map.release(), code_size, data_size, max_capacity, garbage_collect_code);
+  return new JitCodeCache(writable_code_map.release(),
+                          code_map.release(),
+                          data_map.release(),
+                          code_size,
+                          data_size,
+                          max_capacity,
+                          garbage_collect_code);
 }
 
-JitCodeCache::JitCodeCache(MemMap* code_map,
+JitCodeCache::JitCodeCache(MemMap* writable_code_map,
+                           MemMap* executable_code_map,
                            MemMap* data_map,
                            size_t initial_code_capacity,
                            size_t initial_data_capacity,
@@ -141,8 +262,9 @@
     : lock_("Jit code cache", kJitCodeCacheLock),
       lock_cond_("Jit code cache condition variable", lock_),
       collection_in_progress_(false),
-      code_map_(code_map),
       data_map_(data_map),
+      executable_code_map_(executable_code_map),
+      writable_code_map_(writable_code_map),
       max_capacity_(max_capacity),
       current_capacity_(initial_code_capacity + initial_data_capacity),
       code_end_(initial_code_capacity),
@@ -162,7 +284,8 @@
       inline_cache_cond_("Jit inline cache condition variable", lock_) {
 
   DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity);
-  code_mspace_ = create_mspace_with_base(code_map_->Begin(), code_end_, false /*locked*/);
+  MemMap* writable_map = GetWritableMemMap();
+  code_mspace_ = create_mspace_with_base(writable_map->Begin(), code_end_, false /*locked*/);
   data_mspace_ = create_mspace_with_base(data_map_->Begin(), data_end_, false /*locked*/);
 
   if (code_mspace_ == nullptr || data_mspace_ == nullptr) {
@@ -171,7 +294,10 @@
 
   SetFootprintLimit(current_capacity_);
 
-  CHECKED_MPROTECT(code_map_->Begin(), code_map_->Size(), kProtCode);
+  if (writable_code_map_ != nullptr) {
+    CHECKED_MPROTECT(writable_code_map_->Begin(), writable_code_map_->Size(), kProtReadOnly);
+  }
+  CHECKED_MPROTECT(executable_code_map_->Begin(), executable_code_map_->Size(), kProtCode);
   CHECKED_MPROTECT(data_map_->Begin(), data_map_->Size(), kProtData);
 
   VLOG(jit) << "Created jit code cache: initial data size="
@@ -181,7 +307,7 @@
 }
 
 bool JitCodeCache::ContainsPc(const void* ptr) const {
-  return code_map_->Begin() <= ptr && ptr < code_map_->End();
+  return executable_code_map_->Begin() <= ptr && ptr < executable_code_map_->End();
 }
 
 bool JitCodeCache::ContainsMethod(ArtMethod* method) {
@@ -194,27 +320,96 @@
   return false;
 }
 
+/* This method is only for CHECK/DCHECK that pointers are within to a region. */
+static bool IsAddressInMap(const void* addr,
+                           const MemMap* mem_map,
+                           const char* check_name) {
+  if (addr == nullptr || mem_map->HasAddress(addr)) {
+    return true;
+  }
+  LOG(ERROR) << "Is" << check_name << "Address " << addr
+             << " not in [" << reinterpret_cast<void*>(mem_map->Begin())
+             << ", " << reinterpret_cast<void*>(mem_map->Begin() + mem_map->Size()) << ")";
+  return false;
+}
+
+bool JitCodeCache::IsDataAddress(const void* raw_addr) const {
+  return IsAddressInMap(raw_addr, data_map_.get(), "Data");
+}
+
+bool JitCodeCache::IsExecutableAddress(const void* raw_addr) const {
+  return IsAddressInMap(raw_addr, executable_code_map_.get(), "Executable");
+}
+
+bool JitCodeCache::IsWritableAddress(const void* raw_addr) const {
+  return IsAddressInMap(raw_addr, GetWritableMemMap(), "Writable");
+}
+
+// Convert one address within the source map to the same offset within the destination map.
+static void* ConvertAddress(const void* source_address,
+                            const MemMap* source_map,
+                            const MemMap* destination_map) {
+  DCHECK(source_map->HasAddress(source_address)) << source_address;
+  ptrdiff_t offset = reinterpret_cast<const uint8_t*>(source_address) - source_map->Begin();
+  uintptr_t address = reinterpret_cast<uintptr_t>(destination_map->Begin()) + offset;
+  return reinterpret_cast<void*>(address);
+}
+
+template <typename T>
+T* JitCodeCache::ToExecutableAddress(T* writable_address) const {
+  CHECK(IsWritableAddress(writable_address));
+  if (writable_address == nullptr) {
+    return nullptr;
+  }
+  void* executable_address = ConvertAddress(writable_address,
+                                            GetWritableMemMap(),
+                                            executable_code_map_.get());
+  CHECK(IsExecutableAddress(executable_address));
+  return reinterpret_cast<T*>(executable_address);
+}
+
+void* JitCodeCache::ToWritableAddress(const void* executable_address) const {
+  CHECK(IsExecutableAddress(executable_address));
+  if (executable_address == nullptr) {
+    return nullptr;
+  }
+  void* writable_address = ConvertAddress(executable_address,
+                                          executable_code_map_.get(),
+                                          GetWritableMemMap());
+  CHECK(IsWritableAddress(writable_address));
+  return writable_address;
+}
+
 class ScopedCodeCacheWrite : ScopedTrace {
  public:
-  explicit ScopedCodeCacheWrite(MemMap* code_map, bool only_for_tlb_shootdown = false)
-      : ScopedTrace("ScopedCodeCacheWrite"),
-        code_map_(code_map),
-        only_for_tlb_shootdown_(only_for_tlb_shootdown) {
+  explicit ScopedCodeCacheWrite(JitCodeCache* code_cache, bool only_for_tlb_shootdown = false)
+      : ScopedTrace("ScopedCodeCacheWrite") {
     ScopedTrace trace("mprotect all");
-    CHECKED_MPROTECT(
-        code_map_->Begin(), only_for_tlb_shootdown_ ? kPageSize : code_map_->Size(), kProtAll);
+    int prot_to_start_writing = kProtAll;
+    if (code_cache->writable_code_map_ == nullptr) {
+      // If there is only one mapping, use the executable mapping and toggle between rwx and rx.
+      prot_to_start_writing = kProtAll;
+      prot_to_stop_writing_ = kProtCode;
+    } else {
+      // If there are two mappings, use the writable mapping and toggle between rw and r.
+      prot_to_start_writing = kProtData;
+      prot_to_stop_writing_ = kProtReadOnly;
+    }
+    writable_map_ = code_cache->GetWritableMemMap();
+    // If we're using ScopedCacheWrite only for TLB shootdown, we limit the scope of mprotect to
+    // one page.
+    size_ = only_for_tlb_shootdown ? kPageSize : writable_map_->Size();
+    CHECKED_MPROTECT(writable_map_->Begin(), size_, prot_to_start_writing);
   }
   ~ScopedCodeCacheWrite() {
     ScopedTrace trace("mprotect code");
-    CHECKED_MPROTECT(
-        code_map_->Begin(), only_for_tlb_shootdown_ ? kPageSize : code_map_->Size(), kProtCode);
+    CHECKED_MPROTECT(writable_map_->Begin(), size_, prot_to_stop_writing_);
   }
- private:
-  MemMap* const code_map_;
 
-  // If we're using ScopedCacheWrite only for TLB shootdown, we limit the scope of mprotect to
-  // one page.
-  const bool only_for_tlb_shootdown_;
+ private:
+  int prot_to_stop_writing_;
+  MemMap* writable_map_;
+  size_t size_;
 
   DISALLOW_COPY_AND_ASSIGN(ScopedCodeCacheWrite);
 };
@@ -324,8 +519,10 @@
   }
 }
 
-static uint8_t* GetRootTable(const void* code_ptr, uint32_t* number_of_roots = nullptr) {
+uint8_t* JitCodeCache::GetRootTable(const void* code_ptr, uint32_t* number_of_roots) {
+  CHECK(IsExecutableAddress(code_ptr));
   OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
+  // GetOptimizedCodeInfoPtr uses offsets relative to the EXECUTABLE address.
   uint8_t* data = method_header->GetOptimizedCodeInfoPtr();
   uint32_t roots = GetNumberOfRoots(data);
   if (number_of_roots != nullptr) {
@@ -370,6 +567,8 @@
 void JitCodeCache::SweepRootTables(IsMarkedVisitor* visitor) {
   MutexLock mu(Thread::Current(), lock_);
   for (const auto& entry : method_code_map_) {
+    // GetRootTable takes an EXECUTABLE address.
+    CHECK(IsExecutableAddress(entry.first));
     uint32_t number_of_roots = 0;
     uint8_t* roots_data = GetRootTable(entry.first, &number_of_roots);
     GcRoot<mirror::Object>* roots = reinterpret_cast<GcRoot<mirror::Object>*>(roots_data);
@@ -407,17 +606,19 @@
   }
 }
 
-void JitCodeCache::FreeCode(const void* code_ptr) {
-  uintptr_t allocation = FromCodeToAllocation(code_ptr);
+void JitCodeCache::FreeCodeAndData(const void* code_ptr) {
+  CHECK(IsExecutableAddress(code_ptr));
   // Notify native debugger that we are about to remove the code.
   // It does nothing if we are not using native debugger.
   DeleteJITCodeEntryForAddress(reinterpret_cast<uintptr_t>(code_ptr));
+  // GetRootTable takes an EXECUTABLE address.
   FreeData(GetRootTable(code_ptr));
-  FreeCode(reinterpret_cast<uint8_t*>(allocation));
+  FreeRawCode(reinterpret_cast<uint8_t*>(FromCodeToAllocation(code_ptr)));
 }
 
 void JitCodeCache::FreeAllMethodHeaders(
     const std::unordered_set<OatQuickMethodHeader*>& method_headers) {
+  // method_headers are expected to be in the executable region.
   {
     MutexLock mu(Thread::Current(), *Locks::cha_lock_);
     Runtime::Current()->GetClassHierarchyAnalysis()
@@ -429,9 +630,9 @@
   // so it's possible for the same method_header to start representing
   // different compile code.
   MutexLock mu(Thread::Current(), lock_);
-  ScopedCodeCacheWrite scc(code_map_.get());
+  ScopedCodeCacheWrite scc(this);
   for (const OatQuickMethodHeader* method_header : method_headers) {
-    FreeCode(method_header->GetCode());
+    FreeCodeAndData(method_header->GetCode());
   }
 }
 
@@ -448,9 +649,10 @@
     // with the classlinker_classes_lock_ held, and suspending ourselves could
     // lead to a deadlock.
     {
-      ScopedCodeCacheWrite scc(code_map_.get());
+      ScopedCodeCacheWrite scc(this);
       for (auto it = method_code_map_.begin(); it != method_code_map_.end();) {
         if (alloc.ContainsUnsafe(it->second)) {
+          CHECK(IsExecutableAddress(OatQuickMethodHeader::FromCodePointer(it->first)));
           method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->first));
           it = method_code_map_.erase(it);
         } else {
@@ -572,16 +774,21 @@
     MutexLock mu(self, lock_);
     WaitForPotentialCollectionToComplete(self);
     {
-      ScopedCodeCacheWrite scc(code_map_.get());
+      ScopedCodeCacheWrite scc(this);
       memory = AllocateCode(total_size);
       if (memory == nullptr) {
         return nullptr;
       }
-      code_ptr = memory + header_size;
+      uint8_t* writable_ptr = memory + header_size;
+      code_ptr = ToExecutableAddress(writable_ptr);
 
-      std::copy(code, code + code_size, code_ptr);
-      method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
-      new (method_header) OatQuickMethodHeader(
+      std::copy(code, code + code_size, writable_ptr);
+      OatQuickMethodHeader* writable_method_header =
+          OatQuickMethodHeader::FromCodePointer(writable_ptr);
+      // We need to be able to write the OatQuickMethodHeader, so we use writable_method_header.
+      // Otherwise, the offsets encoded in OatQuickMethodHeader are used relative to an executable
+      // address, so we use code_ptr.
+      new (writable_method_header) OatQuickMethodHeader(
           code_ptr - stack_map,
           code_ptr - method_info,
           frame_size_in_bytes,
@@ -597,10 +804,16 @@
       // https://android.googlesource.com/kernel/msm/+/3fbe6bc28a6b9939d0650f2f17eb5216c719950c
       FlushInstructionCache(reinterpret_cast<char*>(code_ptr),
                             reinterpret_cast<char*>(code_ptr + code_size));
+      if (writable_ptr != code_ptr) {
+        FlushDataCache(reinterpret_cast<char*>(writable_ptr),
+                       reinterpret_cast<char*>(writable_ptr + code_size));
+      }
       DCHECK(!Runtime::Current()->IsAotCompiler());
       if (has_should_deoptimize_flag) {
-        method_header->SetHasShouldDeoptimizeFlag();
+        writable_method_header->SetHasShouldDeoptimizeFlag();
       }
+      // All the pointers exported from the cache are executable addresses.
+      method_header = ToExecutableAddress(writable_method_header);
     }
 
     number_of_compilations_++;
@@ -639,13 +852,14 @@
     // but below we still make the compiled code valid for the method.
     MutexLock mu(self, lock_);
     // Fill the root table before updating the entry point.
+    CHECK(IsDataAddress(roots_data));
     DCHECK_EQ(FromStackMapToRoots(stack_map), roots_data);
     DCHECK_LE(roots_data, stack_map);
     FillRootTable(roots_data, roots);
     {
       // Flush data cache, as compiled code references literals in it.
       // We also need a TLB shootdown to act as memory barrier across cores.
-      ScopedCodeCacheWrite ccw(code_map_.get(), /* only_for_tlb_shootdown */ true);
+      ScopedCodeCacheWrite ccw(this, /* only_for_tlb_shootdown */ true);
       FlushDataCache(reinterpret_cast<char*>(roots_data),
                      reinterpret_cast<char*>(roots_data + data_size));
     }
@@ -696,11 +910,11 @@
 
   bool in_cache = false;
   {
-    ScopedCodeCacheWrite ccw(code_map_.get());
+    ScopedCodeCacheWrite ccw(this);
     for (auto code_iter = method_code_map_.begin(); code_iter != method_code_map_.end();) {
       if (code_iter->second == method) {
         if (release_memory) {
-          FreeCode(code_iter->first);
+          FreeCodeAndData(code_iter->first);
         }
         code_iter = method_code_map_.erase(code_iter);
         in_cache = true;
@@ -754,10 +968,10 @@
     profiling_infos_.erase(profile);
   }
   method->SetProfilingInfo(nullptr);
-  ScopedCodeCacheWrite ccw(code_map_.get());
+  ScopedCodeCacheWrite ccw(this);
   for (auto code_iter = method_code_map_.begin(); code_iter != method_code_map_.end();) {
     if (code_iter->second == method) {
-      FreeCode(code_iter->first);
+      FreeCodeAndData(code_iter->first);
       code_iter = method_code_map_.erase(code_iter);
       continue;
     }
@@ -823,6 +1037,7 @@
                              uint8_t* stack_map_data,
                              uint8_t* roots_data) {
   DCHECK_EQ(FromStackMapToRoots(stack_map_data), roots_data);
+  CHECK(IsDataAddress(roots_data));
   MutexLock mu(self, lock_);
   FreeData(reinterpret_cast<uint8_t*>(roots_data));
 }
@@ -944,11 +1159,11 @@
 
 void JitCodeCache::SetFootprintLimit(size_t new_footprint) {
   size_t per_space_footprint = new_footprint / 2;
-  DCHECK(IsAlignedParam(per_space_footprint, kPageSize));
+  CHECK(IsAlignedParam(per_space_footprint, kPageSize));
   DCHECK_EQ(per_space_footprint * 2, new_footprint);
   mspace_set_footprint_limit(data_mspace_, per_space_footprint);
   {
-    ScopedCodeCacheWrite scc(code_map_.get());
+    ScopedCodeCacheWrite scc(this);
     mspace_set_footprint_limit(code_mspace_, per_space_footprint);
   }
 }
@@ -1026,8 +1241,8 @@
       number_of_collections_++;
       live_bitmap_.reset(CodeCacheBitmap::Create(
           "code-cache-bitmap",
-          reinterpret_cast<uintptr_t>(code_map_->Begin()),
-          reinterpret_cast<uintptr_t>(code_map_->Begin() + current_capacity_ / 2)));
+          reinterpret_cast<uintptr_t>(executable_code_map_->Begin()),
+          reinterpret_cast<uintptr_t>(executable_code_map_->Begin() + current_capacity_ / 2)));
       collection_in_progress_ = true;
     }
   }
@@ -1103,14 +1318,16 @@
   std::unordered_set<OatQuickMethodHeader*> method_headers;
   {
     MutexLock mu(self, lock_);
-    ScopedCodeCacheWrite scc(code_map_.get());
+    ScopedCodeCacheWrite scc(this);
     // Iterate over all compiled code and remove entries that are not marked.
     for (auto it = method_code_map_.begin(); it != method_code_map_.end();) {
       const void* code_ptr = it->first;
+      CHECK(IsExecutableAddress(code_ptr));
       uintptr_t allocation = FromCodeToAllocation(code_ptr);
       if (GetLiveBitmap()->Test(allocation)) {
         ++it;
       } else {
+        CHECK(IsExecutableAddress(it->first));
         method_headers.insert(OatQuickMethodHeader::FromCodePointer(it->first));
         it = method_code_map_.erase(it);
       }
@@ -1153,6 +1370,7 @@
     for (const auto& it : method_code_map_) {
       ArtMethod* method = it.second;
       const void* code_ptr = it.first;
+      CHECK(IsExecutableAddress(code_ptr));
       const OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
       if (method_header->GetEntryPoint() == method->GetEntryPointFromQuickCompiledCode()) {
         GetLiveBitmap()->AtomicTestAndSet(FromCodeToAllocation(code_ptr));
@@ -1178,6 +1396,7 @@
     // Free all profiling infos of methods not compiled nor being compiled.
     auto profiling_kept_end = std::remove_if(profiling_infos_.begin(), profiling_infos_.end(),
       [this] (ProfilingInfo* info) NO_THREAD_SAFETY_ANALYSIS {
+        CHECK(IsDataAddress(info));
         const void* ptr = info->GetMethod()->GetEntryPointFromQuickCompiledCode();
         // We have previously cleared the ProfilingInfo pointer in the ArtMethod in the hope
         // that the compiled code would not get revived. As mutator threads run concurrently,
@@ -1238,6 +1457,7 @@
   --it;
 
   const void* code_ptr = it->first;
+  CHECK(IsExecutableAddress(code_ptr));
   OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromCodePointer(code_ptr);
   if (!method_header->Contains(pc)) {
     return nullptr;
@@ -1320,6 +1540,7 @@
   // store in the ArtMethod's ProfilingInfo pointer.
   QuasiAtomic::ThreadFenceRelease();
 
+  CHECK(IsDataAddress(info));
   method->SetProfilingInfo(info);
   profiling_infos_.push_back(info);
   histogram_profiling_info_memory_use_.AddValue(profile_info_size);
@@ -1332,7 +1553,8 @@
   if (code_mspace_ == mspace) {
     size_t result = code_end_;
     code_end_ += increment;
-    return reinterpret_cast<void*>(result + code_map_->Begin());
+    MemMap* writable_map = GetWritableMemMap();
+    return reinterpret_cast<void*>(result + writable_map->Begin());
   } else {
     DCHECK_EQ(data_mspace_, mspace);
     size_t result = data_end_;
@@ -1484,6 +1706,7 @@
 
 size_t JitCodeCache::GetMemorySizeOfCodePointer(const void* ptr) {
   MutexLock mu(Thread::Current(), lock_);
+  CHECK(IsExecutableAddress(ptr));
   return mspace_usable_size(reinterpret_cast<const void*>(FromCodeToAllocation(ptr)));
 }
 
@@ -1519,22 +1742,27 @@
   size_t header_size = RoundUp(sizeof(OatQuickMethodHeader), alignment);
   // Ensure the header ends up at expected instruction alignment.
   DCHECK_ALIGNED_PARAM(reinterpret_cast<uintptr_t>(result + header_size), alignment);
+  CHECK(IsWritableAddress(result));
   used_memory_for_code_ += mspace_usable_size(result);
   return result;
 }
 
-void JitCodeCache::FreeCode(uint8_t* code) {
-  used_memory_for_code_ -= mspace_usable_size(code);
-  mspace_free(code_mspace_, code);
+void JitCodeCache::FreeRawCode(void* code) {
+  CHECK(IsExecutableAddress(code));
+  void* writable_code = ToWritableAddress(code);
+  used_memory_for_code_ -= mspace_usable_size(writable_code);
+  mspace_free(code_mspace_, writable_code);
 }
 
 uint8_t* JitCodeCache::AllocateData(size_t data_size) {
   void* result = mspace_malloc(data_mspace_, data_size);
+  CHECK(IsDataAddress(reinterpret_cast<uint8_t*>(result)));
   used_memory_for_data_ += mspace_usable_size(result);
   return reinterpret_cast<uint8_t*>(result);
 }
 
 void JitCodeCache::FreeData(uint8_t* data) {
+  CHECK(IsDataAddress(data));
   used_memory_for_data_ -= mspace_usable_size(data);
   mspace_free(data_mspace_, data);
 }
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index daa1d61..a062ce4 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -229,6 +229,8 @@
       REQUIRES(!lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  uint8_t* GetRootTable(const void* code_ptr, uint32_t* number_of_roots = nullptr);
+
   // The GC needs to disallow the reading of inline caches when it processes them,
   // to avoid having a class being used while it is being deleted.
   void AllowInlineCacheAccess() REQUIRES(!lock_);
@@ -247,9 +249,12 @@
   }
 
  private:
+  friend class ScopedCodeCacheWrite;
+
   // Take ownership of maps.
   JitCodeCache(MemMap* code_map,
                MemMap* data_map,
+               MemMap* writable_code_map,
                size_t initial_code_capacity,
                size_t initial_data_capacity,
                size_t max_capacity,
@@ -292,7 +297,7 @@
       REQUIRES(!Locks::cha_lock_);
 
   // Free in the mspace allocations for `code_ptr`.
-  void FreeCode(const void* code_ptr) REQUIRES(lock_);
+  void FreeCodeAndData(const void* code_ptr) REQUIRES(lock_);
 
   // Number of bytes allocated in the code cache.
   size_t CodeCacheSizeLocked() REQUIRES(lock_);
@@ -325,7 +330,7 @@
   bool CheckLiveCompiledCodeHasProfilingInfo()
       REQUIRES(lock_);
 
-  void FreeCode(uint8_t* code) REQUIRES(lock_);
+  void FreeRawCode(void* code) REQUIRES(lock_);
   uint8_t* AllocateCode(size_t code_size) REQUIRES(lock_);
   void FreeData(uint8_t* data) REQUIRES(lock_);
   uint8_t* AllocateData(size_t data_size) REQUIRES(lock_);
@@ -335,25 +340,58 @@
       REQUIRES(!lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  MemMap* GetWritableMemMap() const {
+    if (writable_code_map_ == nullptr) {
+      // The system required us to map the JIT Code Cache RWX (see
+      // JitCodeCache::Create()).
+      return executable_code_map_.get();
+    } else {
+      // Executable code is mapped RX, and writable code is mapped RW
+      // to the underlying same memory, but at a different address.
+      return writable_code_map_.get();
+    }
+  }
+
+  bool IsDataAddress(const void* raw_addr) const;
+
+  bool IsExecutableAddress(const void* raw_addr) const;
+
+  bool IsWritableAddress(const void* raw_addr) const;
+
+  template <typename T>
+  T* ToExecutableAddress(T* writable_address) const;
+
+  void* ToWritableAddress(const void* executable_address) const;
+
   // Lock for guarding allocations, collections, and the method_code_map_.
   Mutex lock_;
   // Condition to wait on during collection.
   ConditionVariable lock_cond_ GUARDED_BY(lock_);
   // Whether there is a code cache collection in progress.
   bool collection_in_progress_ GUARDED_BY(lock_);
-  // Mem map which holds code.
-  std::unique_ptr<MemMap> code_map_;
+  // JITting methods obviously requires both write and execute permissions on a region of memory.
+  // In tye typical (non-debugging) case, we separate the memory mapped view that can write the code
+  // from a view that the runtime uses to execute the code. Having these two views eliminates any
+  // single address region having rwx permissions.  An attacker could still write the writable
+  // address and then execute the executable address. We allocate the mappings with a random
+  // address relationship to each other which makes the attacker need two addresses rather than
+  // just one.  In the debugging case there is no file descriptor to back the
+  // shared memory, and hence we have to use a single mapping.
   // Mem map which holds data (stack maps and profiling info).
   std::unique_ptr<MemMap> data_map_;
+  // Mem map which holds a non-writable view of code for JIT.
+  std::unique_ptr<MemMap> executable_code_map_;
+  // Mem map which holds a non-executable view of code for JIT.
+  std::unique_ptr<MemMap> writable_code_map_;
   // The opaque mspace for allocating code.
   void* code_mspace_ GUARDED_BY(lock_);
   // The opaque mspace for allocating data.
   void* data_mspace_ GUARDED_BY(lock_);
   // Bitmap for collecting code and data.
   std::unique_ptr<CodeCacheBitmap> live_bitmap_;
-  // Holds compiled code associated to the ArtMethod.
+  // Holds non-writable compiled code associated to the ArtMethod.
   SafeMap<const void*, ArtMethod*> method_code_map_ GUARDED_BY(lock_);
-  // Holds osr compiled code associated to the ArtMethod.
+  // Holds non-writable osr compiled code associated to the ArtMethod.
   SafeMap<ArtMethod*, const void*> osr_code_map_ GUARDED_BY(lock_);
   // ProfilingInfo objects we have allocated.
   std::vector<ProfilingInfo*> profiling_infos_ GUARDED_BY(lock_);
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 7b41608..17035dd 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -536,8 +536,13 @@
   }
 }
 
-MemMap* MemMap::RemapAtEnd(uint8_t* new_end, const char* tail_name, int tail_prot,
-                           std::string* error_msg, bool use_ashmem) {
+MemMap* MemMap::RemapAtEnd(uint8_t* new_end,
+                           const char* tail_name,
+                           int tail_prot,
+                           int sharing_flags,
+                           std::string* error_msg,
+                           bool use_ashmem,
+                           unique_fd* shmem_fd) {
   use_ashmem = use_ashmem && !kIsTargetLinux;
   DCHECK_GE(new_end, Begin());
   DCHECK_LE(new_end, End());
@@ -563,14 +568,14 @@
   DCHECK_ALIGNED(tail_base_size, kPageSize);
 
   unique_fd fd;
-  int flags = MAP_PRIVATE | MAP_ANONYMOUS;
+  int flags = MAP_ANONYMOUS | sharing_flags;
   if (use_ashmem) {
     // android_os_Debug.cpp read_mapinfo assumes all ashmem regions associated with the VM are
     // prefixed "dalvik-".
     std::string debug_friendly_name("dalvik-");
     debug_friendly_name += tail_name;
     fd.reset(ashmem_create_region(debug_friendly_name.c_str(), tail_base_size));
-    flags = MAP_PRIVATE | MAP_FIXED;
+    flags = MAP_FIXED | sharing_flags;
     if (fd.get() == -1) {
       *error_msg = StringPrintf("ashmem_create_region failed for '%s': %s",
                                 tail_name, strerror(errno));
@@ -604,6 +609,9 @@
                               fd.get());
     return nullptr;
   }
+  if (shmem_fd != nullptr) {
+    shmem_fd->reset(fd.release());
+  }
   return new MemMap(tail_name, actual, tail_size, actual, tail_base_size, tail_prot, false);
 }
 
diff --git a/runtime/mem_map.h b/runtime/mem_map.h
index 5603963..d8908ad 100644
--- a/runtime/mem_map.h
+++ b/runtime/mem_map.h
@@ -25,6 +25,7 @@
 #include <string>
 
 #include "android-base/thread_annotations.h"
+#include "android-base/unique_fd.h"
 
 namespace art {
 
@@ -37,6 +38,8 @@
 #define USE_ART_LOW_4G_ALLOCATOR 0
 #endif
 
+using android::base::unique_fd;
+
 #ifdef __linux__
 static constexpr bool kMadviseZeroes = true;
 #else
@@ -168,11 +171,14 @@
   }
 
   // Unmap the pages at end and remap them to create another memory map.
+  // sharing_flags should be either MAP_PRIVATE or MAP_SHARED.
   MemMap* RemapAtEnd(uint8_t* new_end,
                      const char* tail_name,
                      int tail_prot,
+                     int sharing_flags,
                      std::string* error_msg,
-                     bool use_ashmem = true);
+                     bool use_ashmem = true,
+                     unique_fd* shmem_fd = nullptr);
 
   static bool CheckNoGaps(MemMap* begin_map, MemMap* end_map)
       REQUIRES(!MemMap::mem_maps_lock_);
diff --git a/runtime/mem_map_test.cc b/runtime/mem_map_test.cc
index 5f027b1..8d6bb38 100644
--- a/runtime/mem_map_test.cc
+++ b/runtime/mem_map_test.cc
@@ -74,6 +74,7 @@
     MemMap* m1 = m0->RemapAtEnd(base0 + page_size,
                                 "MemMapTest_RemapAtEndTest_map1",
                                 PROT_READ | PROT_WRITE,
+                                MAP_PRIVATE,
                                 &error_msg);
     // Check the states of the two maps.
     EXPECT_EQ(m0->Begin(), base0) << error_msg;
@@ -456,6 +457,7 @@
   std::unique_ptr<MemMap> m1(m0->RemapAtEnd(base0 + 3 * page_size,
                                             "MemMapTest_AlignByTest_map1",
                                             PROT_READ | PROT_WRITE,
+                                            MAP_PRIVATE,
                                             &error_msg));
   uint8_t* base1 = m1->Begin();
   ASSERT_TRUE(base1 != nullptr) << error_msg;
@@ -465,6 +467,7 @@
   std::unique_ptr<MemMap> m2(m1->RemapAtEnd(base1 + 4 * page_size,
                                             "MemMapTest_AlignByTest_map2",
                                             PROT_READ | PROT_WRITE,
+                                            MAP_PRIVATE,
                                             &error_msg));
   uint8_t* base2 = m2->Begin();
   ASSERT_TRUE(base2 != nullptr) << error_msg;
@@ -474,6 +477,7 @@
   std::unique_ptr<MemMap> m3(m2->RemapAtEnd(base2 + 3 * page_size,
                                             "MemMapTest_AlignByTest_map1",
                                             PROT_READ | PROT_WRITE,
+                                            MAP_PRIVATE,
                                             &error_msg));
   uint8_t* base3 = m3->Begin();
   ASSERT_TRUE(base3 != nullptr) << error_msg;
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index dc542d4..e950fca 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -264,212 +264,6 @@
   }
 }
 
-template <typename T>
-static void IterateOverJavaDexFile(ObjPtr<mirror::Object> dex_file,
-                                   ArtField* const cookie_field,
-                                   const T& fn)
-    REQUIRES_SHARED(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;
-      }
-    }
-  }
-}
-
-template <typename T>
-static void IterateOverPathClassLoader(
-    Handle<mirror::ClassLoader> class_loader,
-    MutableHandle<mirror::ObjectArray<mirror::Object>> dex_elements,
-    const T& fn) REQUIRES_SHARED(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 =
-      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
-  ArtField* const dex_file_field =
-      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
-  ObjPtr<mirror::Object> dex_path_list =
-      jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_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.
-    ObjPtr<mirror::Object> dex_elements_obj =
-        jni::DecodeArtField(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;
-        }
-        ObjPtr<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::vector<const DexFile*>* dex_files)
-    REQUIRES_SHARED(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 (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) !=
-      class_loader->GetClass()) {
-    VLOG(class_linker) << "Unsupported class-loader "
-                       << mirror::Class::PrettyClass(class_loader->GetClass());
-    return false;
-  }
-
-  bool recursive_result = GetDexFilesFromClassLoader(soa, class_loader->GetParent(), dex_files);
-  if (!recursive_result) {
-    // Something wrong up the chain.
-    return false;
-  }
-
-  // Collect all the dex files.
-  auto GetDexFilesFn = [&] (const DexFile* cp_dex_file)
-            REQUIRES_SHARED(Locks::mutator_lock_) {
-    if (cp_dex_file->NumClassDefs() > 0) {
-      dex_files->push_back(cp_dex_file);
-    }
-    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(h_class_loader, dex_elements, GetDexFilesFn);
-
-  return true;
-}
-
-static void GetDexFilesFromDexElementsArray(
-    ScopedObjectAccessAlreadyRunnable& soa,
-    Handle<mirror::ObjectArray<mirror::Object>> dex_elements,
-    std::vector<const DexFile*>* dex_files)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  if (dex_elements == nullptr) {
-    // Nothing to do.
-    return;
-  }
-
-  ArtField* const cookie_field =
-      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
-  ArtField* const dex_file_field =
-      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
-  ObjPtr<mirror::Class> const element_class = soa.Decode<mirror::Class>(
-      WellKnownClasses::dalvik_system_DexPathList__Element);
-  ObjPtr<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)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    if (cp_dex_file != nullptr && cp_dex_file->NumClassDefs() > 0) {
-      dex_files->push_back(cp_dex_file);
-    }
-    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.
-
-    ObjPtr<mirror::Object> dex_file;
-    if (element_class == element->GetClass()) {
-      dex_file = dex_file_field->GetObject(element);
-    } else if (dexfile_class == element->GetClass()) {
-      dex_file = element;
-    } else {
-      LOG(WARNING) << "Unsupported element in dex_elements: "
-                   << mirror::Class::PrettyClass(element->GetClass());
-      continue;
-    }
-
-    IterateOverJavaDexFile(dex_file, cookie_field, GetDexFilesFn);
-  }
-}
-
-static bool AreSharedLibrariesOk(const std::string& context_spec,
-                                 std::vector<const DexFile*>& dex_files,
-                                 std::string* error_msg) {
-  std::vector<std::string> classpath;
-  std::vector<uint32_t> checksums;
-  bool is_special_shared_library;
-  if (!ClassLoaderContext::DecodePathClassLoaderContextFromOatFileKey(
-          context_spec, &classpath, &checksums, &is_special_shared_library)) {
-    *error_msg = "Could not decode the class loader context from the oat file key.";
-    return false;
-  }
-
-  DCHECK_EQ(classpath.size(), checksums.size());
-
-  // The classpath size should match the number of dex files.
-  if (classpath.size() != dex_files.size()) {
-    *error_msg = "The number of loaded dex files does not match the number of files "
-        "specified in the context. Expected=" + std::to_string(classpath.size()) +
-        ", found=" + std::to_string(dex_files.size());
-    return false;
-  }
-
-  // If we find the special shared library, skip the shared libraries check.
-  if (is_special_shared_library) {
-    return true;
-  }
-
-  // Check that the loaded dex files have the same order and checksums as the shared libraries.
-  for (size_t i = 0; i < dex_files.size(); ++i) {
-    const std::string& dex_location = dex_files[i]->GetLocation();
-    uint32_t dex_location_checksum = dex_files[i]->GetLocationChecksum();
-    std::string absolute_library_path =
-        OatFile::ResolveRelativeEncodedDexLocation(dex_location.c_str(), classpath[i]);
-    if (dex_location != absolute_library_path) {
-      *error_msg = "SharedLibraryCheck: expected=" + absolute_library_path + ", found=" +
-          dex_location;
-      return false;
-    }
-    if (dex_location_checksum  != checksums[i]) {
-      *error_msg = "SharedLibraryCheck: checksum mismatch for " + dex_location + ". Expected=" +
-          std::to_string(checksums[i]) + ", found=" + std::to_string(dex_location_checksum);
-      return false;
-    }
-  }
-
-  return true;
-}
-
 static bool CollisionCheck(std::vector<const DexFile*>& dex_files_loaded,
                            std::vector<const DexFile*>& dex_files_unloaded,
                            std::string* error_msg /*out*/) {
@@ -554,52 +348,38 @@
   DCHECK(oat_file != nullptr);
   DCHECK(error_msg != nullptr);
 
-  std::vector<const DexFile*> dex_files_loaded;
-
-  // 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, we do nothing and assume the collision
-  // check has succeeded.
-  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 != nullptr &&
-        GetDexFilesFromClassLoader(soa, h_class_loader.Get(), &dex_files_loaded)) {
-      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, &dex_files_loaded);
-    } else if (h_class_loader != nullptr) {
-      VLOG(class_linker) << "Something unsupported with "
-                         << mirror::Class::PrettyClass(h_class_loader->GetClass());
-
-      // This is a class loader we don't recognize. Our earlier strategy would
-      // be to perform a global duplicate class check (with all loaded oat files)
-      // but that seems overly conservative - we have no way of knowing that
-      // those files are present in the same loader hierarchy. Among other
-      // things, it hurt GMS core and its filtering class loader.
-    }
+  // If the class_loader is null there's not much we can do. This happens if a dex files is loaded
+  // directly with DexFile APIs instead of using class loaders.
+  if (class_loader == nullptr) {
+    LOG(WARNING) << "Opening an oat file without a class loader. "
+        << "Are you using the deprecated DexFile APIs?";
+    return false;
   }
 
-  // Exit if we find a class loader we don't recognize. Proceed to check shared
-  // libraries and do a full class loader check otherwise.
-  if (!class_loader_ok) {
-      LOG(WARNING) << "Skipping duplicate class check due to unrecognized classloader";
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::CreateContextForClassLoader(class_loader, dex_elements);
+
+  // The context might be null if there are unrecognized class loaders in the chain or they
+  // don't meet sensible sanity conditions. In this case we assume that the app knows what it's
+  // doing and accept the oat file.
+  // Note that this has correctness implications as we cannot guarantee that the class resolution
+  // used during compilation is OK (b/37777332).
+  if (context == nullptr) {
+      LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader";
       return false;
   }
 
-  // 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, dex_files_loaded, error_msg)) {
+  // If the pat file loading context matches the context used during compilation then we accept
+  // the oat file without addition checks
+  if (context->VerifyClassLoaderContextMatch(
+      oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey))) {
     return false;
   }
 
+  // The class loader context does not match. Perform a full duplicate classes check.
+
+  std::vector<const DexFile*> dex_files_loaded = context->FlattenOpenedDexFiles();
+
   // Vector that holds the newly opened dex files live, this is done to prevent leaks.
   std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
 
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 68e1856..2c64b56 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -50,6 +50,7 @@
 import json
 import multiprocessing
 import os
+import operator
 import re
 import subprocess
 import sys
@@ -75,9 +76,11 @@
 OPTIMIZING_COMPILER_TYPES = set()
 JVMTI_TYPES = set()
 ADDRESS_SIZES_TARGET = {'host': set(), 'target': set()}
+TIME_STATS = {}
 # timeout for individual tests.
 # TODO: make it adjustable per tests and for buildbots
 timeout = 3000 # 50 minutes
+global_timeout = 14100  # 235 minutes (The go/ab timeout is 14500)
 
 # DISABLED_TEST_CONTAINER holds information about the disabled tests. It is a map
 # that has key as the test name (like 001-HelloWorld), and value as set of
@@ -355,7 +358,7 @@
         # stops creating any any thread and wait for all the exising threads
         # to end.
         while threading.active_count() > 2:
-          time.sleep(0.1)
+          time.sleep(1)
           return
       test_name = 'test-art-'
       test_name += target + '-run-test-'
@@ -506,11 +509,13 @@
       test_skipped = True
     else:
       test_skipped = False
+      start_recording_time(test_name)
       if gdb:
         proc = subprocess.Popen(command.split(), stderr=subprocess.STDOUT, universal_newlines=True)
       else:
         proc = subprocess.Popen(command.split(), stderr=subprocess.STDOUT, stdout = subprocess.PIPE,
                                 universal_newlines=True)
+      stop_recording_time(test_name)
       script_output = proc.communicate(timeout=timeout)[0]
       test_passed = not proc.wait()
 
@@ -729,6 +734,7 @@
   sys.stdout.flush()
 
 def print_analysis():
+  print_mutex.acquire()
   if not verbose:
     # Without --verbose, the testrunner erases passing test info. It
     # does that by overriding the printed text with white spaces all across
@@ -762,6 +768,7 @@
     print_text(COLOR_ERROR + '----------' + COLOR_NORMAL + '\n')
     for failed_test in sorted([test_info[0] for test_info in failed_tests]):
       print_text(('%s\n' % (failed_test)))
+  print_mutex.release()
 
 
 def parse_test_name(test_name):
@@ -990,7 +997,33 @@
 
   return test
 
+def start_recording_time(key):
+  """To begin recording time for the event associated with the key.
+  """
+  TIME_STATS[key] = -(time.time())
+
+def stop_recording_time(key):
+  """To stop timer for the event associated with the key.
+  """
+  TIME_STATS[key] = time.time() + TIME_STATS[key]
+
+def print_time_info():
+  """Print time information for different invocation.
+  """
+  print_mutex.acquire()
+  print_text('\nTIME INFO\n')
+  for key in TIME_STATS:
+    # Handle unfinised jobs.
+    if TIME_STATS[key] < 0:
+      TIME_STATS[key] = time.time() + TIME_STATS[key]
+
+  info_list = sorted(TIME_STATS.items(), key=operator.itemgetter(1), reverse=True)
+  for time_info_tuple in info_list:
+    print_text('%s : %.2f sec\n' % (time_info_tuple[0], time_info_tuple[1]))
+  print_mutex.release()
+
 def main():
+  start_time = time.time()
   gather_test_info()
   user_requested_test = parse_option()
   setup_test_env()
@@ -1006,8 +1039,10 @@
     build_command += ' ' + build_targets
     # Add 'dist' to avoid Jack issues b/36169180.
     build_command += ' dist'
+    start_recording_time(build_command)
     if subprocess.call(build_command.split()):
       sys.exit(1)
+    stop_recording_time(build_command)
   if user_requested_test:
     test_runner_thread = threading.Thread(target=run_tests, args=(user_requested_test,))
   else:
@@ -1016,6 +1051,13 @@
   try:
     test_runner_thread.start()
     while threading.active_count() > 1:
+      if (time.time() - start_time > global_timeout):
+        # to ensure that the run ends before the go/ab bots
+        # time out the invocation.
+        print_text("FAILED: timeout reached")
+        print_time_info()
+        print_analysis()
+        sys.exit(1)
       time.sleep(0.1)
     print_analysis()
   except Exception as e:
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/InvokeChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/InvokeChanger.java
index 3488503..8750fc6 100644
--- a/tools/dexfuzz/src/dexfuzz/program/mutators/InvokeChanger.java
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/InvokeChanger.java
@@ -136,7 +136,7 @@
 
     String oldInsnString = invokeInsn.toString();
 
-    Opcode newOpcode = isInvokeCalIInst(invokeInsn);
+    Opcode newOpcode = getDifferentInvokeCallOpcode(invokeInsn);
 
     invokeInsn.insn.info = Instruction.getOpcodeInfo(newOpcode);
 
@@ -148,7 +148,7 @@
     invokeCallInsns = null;
   }
 
-  private Opcode isInvokeCalIInst(MInsn mInsn) {
+  private Opcode getDifferentInvokeCallOpcode(MInsn mInsn) {
     Opcode opcode = mInsn.insn.info.opcode;
     if (isSimpleInvokeInst(opcode)) {
       int index = opcode.ordinal() - Opcode.INVOKE_VIRTUAL.ordinal();
@@ -159,7 +159,7 @@
       int length = INVOKE_RANGE_LIST.length;
       return INVOKE_RANGE_LIST[(index + 1 + rng.nextInt(length - 1)) % length];
     }
-      return opcode;
+    return opcode;
   }
 
   private boolean isSimpleInvokeInst(Opcode opcode){