Merge "C++17 compatibility: unreachable `return`."
diff --git a/Android.mk b/Android.mk
index b9f6170..3388b35 100644
--- a/Android.mk
+++ b/Android.mk
@@ -326,6 +326,53 @@
 
 
 #######################
+# Android Runtime APEX.
+
+include $(CLEAR_VARS)
+
+# The Android Runtime APEX comes in two flavors:
+# - the release module (`com.android.runtime.release`), containing
+#   only "release" artifacts;
+# - the debug module (`com.android.runtime.debug`), containing both
+#   "release" and "debug" artifacts, as well as additional tools.
+#
+# The Android Runtime APEX module (`com.android.runtime`) is an
+# "alias" for one of the previous modules. By default, "user" build
+# variants contain the release module, while "userdebug" and "eng"
+# build variant contain the debug module. However, if
+# `PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD` is defined, it overrides
+# the previous logic:
+# - if `PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD` is set to `false`, the
+#   build will include the release module (whatever the build
+#   variant);
+# - if `PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD` is set to `true`, the
+#   build will include the debug module (whatever the build variant).
+
+art_target_include_debug_build := $(PRODUCT_ART_TARGET_INCLUDE_DEBUG_BUILD)
+ifneq (false,$(art_target_include_debug_build))
+  ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+    art_target_include_debug_build := true
+  endif
+endif
+ifeq (true,$(art_target_include_debug_build))
+  # Module with both release and debug variants, as well as
+  # additional tools.
+  TARGET_RUNTIME_APEX := com.android.runtime.debug
+else
+  # Release module (without debug variants nor tools).
+  TARGET_RUNTIME_APEX := com.android.runtime.release
+endif
+
+LOCAL_MODULE := com.android.runtime
+LOCAL_REQUIRED_MODULES := $(TARGET_RUNTIME_APEX)
+
+# Clear locally used variable.
+art_target_include_debug_build :=
+
+include $(BUILD_PHONY_PACKAGE)
+
+
+#######################
 # Fake packages for ART
 
 # The art-runtime package depends on the core ART libraries and binaries. It exists so we can
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index 8bddb5d..159e5c1 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -68,12 +68,45 @@
 
 apex_key {
     name: "com.android.runtime.key",
-    public_key: "runtime.avbpubkey",
-    private_key: "runtime.pem",
+    public_key: "com.android.runtime.avbpubkey",
+    private_key: "com.android.runtime.pem",
 }
 
+// TODO: Introduce `apex_defaults` to factor common parts of `apex`
+// module definitions below?
+
+// Release version of the Runtime APEX module (not containing debug
+// variants nor tools), included in user builds. Also used for
+// storage-constrained devices in userdebug and eng builds.
 apex {
-    name: "com.android.runtime",
+    name: "com.android.runtime.release",
+    compile_multilib: "both",
+    manifest: "manifest.json",
+    native_shared_libs: art_runtime_base_native_shared_libs
+        + art_runtime_fake_native_shared_libs,
+    multilib: {
+        both: {
+            // TODO: Add logic to create a `dalvikvm` symlink to `dalvikvm32` or `dalvikvm64`
+            // (see `symlink_preferred_arch` in art/dalvikvm/Android.bp).
+            binaries: art_runtime_base_binaries_both,
+        },
+        prefer32: {
+            binaries: art_runtime_base_binaries_prefer32,
+        },
+        first: {
+            binaries: [],
+        }
+    },
+    key: "com.android.runtime.key",
+    // TODO: Also package a `ld.config.txt` config file (to be placed in `etc/`).
+    // ...
+}
+
+// "Debug" version of the Runtime APEX module (containing both release and
+// debug variants, as well as additional tools), included in userdebug and
+// eng build.
+apex {
+    name: "com.android.runtime.debug",
     compile_multilib: "both",
     manifest: "manifest.json",
     native_shared_libs: art_runtime_base_native_shared_libs
@@ -87,7 +120,7 @@
         },
         prefer32: {
             binaries: art_runtime_base_binaries_prefer32
-                + art_runtime_debug_binaries_prefer32
+                + art_runtime_debug_binaries_prefer32,
         },
         first: {
             binaries: art_tools_binaries,
diff --git a/build/apex/runtime.avbpubkey b/build/apex/com.android.runtime.avbpubkey
similarity index 100%
rename from build/apex/runtime.avbpubkey
rename to build/apex/com.android.runtime.avbpubkey
Binary files differ
diff --git a/build/apex/runtime.pem b/build/apex/com.android.runtime.pem
similarity index 100%
rename from build/apex/runtime.pem
rename to build/apex/com.android.runtime.pem
diff --git a/build/apex/runtests.sh b/build/apex/runtests.sh
index 6af2a8b..86cd8cb 100755
--- a/build/apex/runtests.sh
+++ b/build/apex/runtests.sh
@@ -79,7 +79,8 @@
 
 trap finish EXIT
 
-apex_module="com.android.runtime"
+# TODO: Also exercise the Release Runtime APEX (`com.android.runtime.release`).
+apex_module="com.android.runtime.debug"
 
 # Build the Android Runtime APEX package (optional).
 $build_apex_p && say "Building package" && make "$apex_module"
@@ -190,10 +191,8 @@
 #   libsigchain.so
 #   libtombstoned_client.so
 #   libunwindstack.so
-#   libvixl-arm64.so
-#   libvixl-arm.so
-#   libvixld-arm64.so
-#   libvixld-arm.so
+#   libvixl.so
+#   libvixld.so
 #   ...
 #
 # ?
diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc
index 29f004c..ef9d919 100644
--- a/compiler/compiled_method.cc
+++ b/compiler/compiled_method.cc
@@ -17,21 +17,20 @@
 #include "compiled_method.h"
 
 #include "driver/compiled_method_storage.h"
-#include "driver/compiler_driver.h"
 #include "utils/swap_space.h"
 
 namespace art {
 
-CompiledCode::CompiledCode(CompilerDriver* compiler_driver,
+CompiledCode::CompiledCode(CompiledMethodStorage* storage,
                            InstructionSet instruction_set,
                            const ArrayRef<const uint8_t>& quick_code)
-    : compiler_driver_(compiler_driver),
-      quick_code_(compiler_driver_->GetCompiledMethodStorage()->DeduplicateCode(quick_code)),
+    : storage_(storage),
+      quick_code_(storage->DeduplicateCode(quick_code)),
       packed_fields_(InstructionSetField::Encode(instruction_set)) {
 }
 
 CompiledCode::~CompiledCode() {
-  compiler_driver_->GetCompiledMethodStorage()->ReleaseCode(quick_code_);
+  GetStorage()->ReleaseCode(quick_code_);
 }
 
 bool CompiledCode::operator==(const CompiledCode& rhs) const {
@@ -99,29 +98,29 @@
   }
 }
 
-CompiledMethod::CompiledMethod(CompilerDriver* driver,
+CompiledMethod::CompiledMethod(CompiledMethodStorage* storage,
                                InstructionSet instruction_set,
                                const ArrayRef<const uint8_t>& quick_code,
                                const ArrayRef<const uint8_t>& vmap_table,
                                const ArrayRef<const uint8_t>& cfi_info,
                                const ArrayRef<const linker::LinkerPatch>& patches)
-    : CompiledCode(driver, instruction_set, quick_code),
-      vmap_table_(driver->GetCompiledMethodStorage()->DeduplicateVMapTable(vmap_table)),
-      cfi_info_(driver->GetCompiledMethodStorage()->DeduplicateCFIInfo(cfi_info)),
-      patches_(driver->GetCompiledMethodStorage()->DeduplicateLinkerPatches(patches)) {
+    : CompiledCode(storage, instruction_set, quick_code),
+      vmap_table_(storage->DeduplicateVMapTable(vmap_table)),
+      cfi_info_(storage->DeduplicateCFIInfo(cfi_info)),
+      patches_(storage->DeduplicateLinkerPatches(patches)) {
 }
 
 CompiledMethod* CompiledMethod::SwapAllocCompiledMethod(
-    CompilerDriver* driver,
+    CompiledMethodStorage* storage,
     InstructionSet instruction_set,
     const ArrayRef<const uint8_t>& quick_code,
     const ArrayRef<const uint8_t>& vmap_table,
     const ArrayRef<const uint8_t>& cfi_info,
     const ArrayRef<const linker::LinkerPatch>& patches) {
-  SwapAllocator<CompiledMethod> alloc(driver->GetCompiledMethodStorage()->GetSwapSpaceAllocator());
+  SwapAllocator<CompiledMethod> alloc(storage->GetSwapSpaceAllocator());
   CompiledMethod* ret = alloc.allocate(1);
   alloc.construct(ret,
-                  driver,
+                  storage,
                   instruction_set,
                   quick_code,
                   vmap_table,
@@ -129,14 +128,15 @@
   return ret;
 }
 
-void CompiledMethod::ReleaseSwapAllocatedCompiledMethod(CompilerDriver* driver, CompiledMethod* m) {
-  SwapAllocator<CompiledMethod> alloc(driver->GetCompiledMethodStorage()->GetSwapSpaceAllocator());
+void CompiledMethod::ReleaseSwapAllocatedCompiledMethod(CompiledMethodStorage* storage,
+                                                        CompiledMethod* m) {
+  SwapAllocator<CompiledMethod> alloc(storage->GetSwapSpaceAllocator());
   alloc.destroy(m);
   alloc.deallocate(m, 1);
 }
 
 CompiledMethod::~CompiledMethod() {
-  CompiledMethodStorage* storage = GetCompilerDriver()->GetCompiledMethodStorage();
+  CompiledMethodStorage* storage = GetStorage();
   storage->ReleaseLinkerPatches(patches_);
   storage->ReleaseCFIInfo(cfi_info_);
   storage->ReleaseVMapTable(vmap_table_);
diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h
index 864ce58..75790c9 100644
--- a/compiler/compiled_method.h
+++ b/compiler/compiled_method.h
@@ -28,7 +28,6 @@
 namespace art {
 
 template <typename T> class ArrayRef;
-class CompilerDriver;
 class CompiledMethodStorage;
 template<typename T> class LengthPrefixedArray;
 
@@ -39,7 +38,7 @@
 class CompiledCode {
  public:
   // For Quick to supply an code blob
-  CompiledCode(CompilerDriver* compiler_driver,
+  CompiledCode(CompiledMethodStorage* storage,
                InstructionSet instruction_set,
                const ArrayRef<const uint8_t>& quick_code);
 
@@ -78,8 +77,8 @@
   template <typename T>
   static ArrayRef<const T> GetArray(const LengthPrefixedArray<T>* array);
 
-  CompilerDriver* GetCompilerDriver() {
-    return compiler_driver_;
+  CompiledMethodStorage* GetStorage() {
+    return storage_;
   }
 
   template <typename BitFieldType>
@@ -96,7 +95,7 @@
  private:
   using InstructionSetField = BitField<InstructionSet, 0u, kInstructionSetFieldSize>;
 
-  CompilerDriver* const compiler_driver_;
+  CompiledMethodStorage* const storage_;
 
   // Used to store the compiled code.
   const LengthPrefixedArray<uint8_t>* const quick_code_;
@@ -109,7 +108,7 @@
   // Constructs a CompiledMethod.
   // Note: Consider using the static allocation methods below that will allocate the CompiledMethod
   //       in the swap space.
-  CompiledMethod(CompilerDriver* driver,
+  CompiledMethod(CompiledMethodStorage* storage,
                  InstructionSet instruction_set,
                  const ArrayRef<const uint8_t>& quick_code,
                  const ArrayRef<const uint8_t>& vmap_table,
@@ -119,14 +118,14 @@
   virtual ~CompiledMethod();
 
   static CompiledMethod* SwapAllocCompiledMethod(
-      CompilerDriver* driver,
+      CompiledMethodStorage* storage,
       InstructionSet instruction_set,
       const ArrayRef<const uint8_t>& quick_code,
       const ArrayRef<const uint8_t>& vmap_table,
       const ArrayRef<const uint8_t>& cfi_info,
       const ArrayRef<const linker::LinkerPatch>& patches);
 
-  static void ReleaseSwapAllocatedCompiledMethod(CompilerDriver* driver, CompiledMethod* m);
+  static void ReleaseSwapAllocatedCompiledMethod(CompiledMethodStorage* storage, CompiledMethod* m);
 
   bool IsIntrinsic() const {
     return GetPackedField<IsIntrinsicField>();
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index fe99eaa..04ad10c 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -614,7 +614,7 @@
     instruction_set = InstructionSet::kArm;
   }
   CompiledMethod* ret = CompiledMethod::SwapAllocCompiledMethod(
-      driver_,
+      driver_->GetCompiledMethodStorage(),
       instruction_set,
       ArrayRef<const uint8_t>(),                   // no code
       ArrayRef<const uint8_t>(quicken_data),       // vmap_table
@@ -665,7 +665,8 @@
           // There is up to one compiled method for each method ref. Releasing it leaves the
           // deduped data intact, this means its safe to do even when other threads might be
           // compiling.
-          CompiledMethod::ReleaseSwapAllocatedCompiledMethod(driver_, method);
+          CompiledMethod::ReleaseSwapAllocatedCompiledMethod(driver_->GetCompiledMethodStorage(),
+                                                             method);
         }
       }
     }
diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc
index 8b35bd3..9fac2bc 100644
--- a/compiler/driver/compiled_method_storage_test.cc
+++ b/compiler/driver/compiled_method_storage_test.cc
@@ -19,24 +19,13 @@
 #include <gtest/gtest.h>
 
 #include "compiled_method-inl.h"
-#include "compiler_driver.h"
-#include "compiler_options.h"
-#include "dex/verification_results.h"
 
 namespace art {
 
 TEST(CompiledMethodStorage, Deduplicate) {
-  CompilerOptions compiler_options;
-  VerificationResults verification_results(&compiler_options);
-  CompilerDriver driver(&compiler_options,
-                        &verification_results,
-                        Compiler::kOptimizing,
-                        /* image_classes */ nullptr,
-                        /* thread_count */ 1u,
-                        /* swap_fd */ -1);
-  CompiledMethodStorage* storage = driver.GetCompiledMethodStorage();
+  CompiledMethodStorage storage(/* swap_fd */ -1);
 
-  ASSERT_TRUE(storage->DedupeEnabled());  // The default.
+  ASSERT_TRUE(storage.DedupeEnabled());  // The default.
 
   const uint8_t raw_code1[] = { 1u, 2u, 3u };
   const uint8_t raw_code2[] = { 4u, 3u, 2u, 1u };
@@ -76,7 +65,7 @@
       for (auto&& f : cfi_info) {
         for (auto&& p : patches) {
           compiled_methods.push_back(CompiledMethod::SwapAllocCompiledMethod(
-              &driver, InstructionSet::kNone, c, v, f, p));
+              &storage, InstructionSet::kNone, c, v, f, p));
         }
       }
     }
@@ -105,7 +94,7 @@
     }
   }
   for (CompiledMethod* method : compiled_methods) {
-    CompiledMethod::ReleaseSwapAllocatedCompiledMethod(&driver, method);
+    CompiledMethod::ReleaseSwapAllocatedCompiledMethod(&storage, method);
   }
 }
 
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 0d0a7f2..67cabef 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -24,6 +24,7 @@
 #include <malloc.h>  // For mallinfo
 #endif
 
+#include "android-base/logging.h"
 #include "android-base/strings.h"
 
 #include "art_field-inl.h"
@@ -276,7 +277,7 @@
   compiled_methods_.Visit([this](const DexFileReference& ref ATTRIBUTE_UNUSED,
                                  CompiledMethod* method) {
     if (method != nullptr) {
-      CompiledMethod::ReleaseSwapAllocatedCompiledMethod(this, method);
+      CompiledMethod::ReleaseSwapAllocatedCompiledMethod(GetCompiledMethodStorage(), method);
     }
   });
   compiler_->UnInit();
@@ -598,6 +599,29 @@
                                                          class_loader,
                                                          dex_file,
                                                          dex_cache);
+        ProfileMethodsCheck check_type =
+            driver->GetCompilerOptions().CheckProfiledMethodsCompiled();
+        if (UNLIKELY(check_type != ProfileMethodsCheck::kNone)) {
+          bool violation = driver->ShouldCompileBasedOnProfile(method_ref) &&
+                               (compiled_method == nullptr);
+          if (violation) {
+            std::ostringstream oss;
+            oss << "Failed to compile "
+                << method_ref.dex_file->PrettyMethod(method_ref.index)
+                << "[" << method_ref.dex_file->GetLocation() << "]"
+                << " as expected by profile";
+            switch (check_type) {
+              case ProfileMethodsCheck::kNone:
+                break;
+              case ProfileMethodsCheck::kLog:
+                LOG(ERROR) << oss.str();
+                break;
+              case ProfileMethodsCheck::kAbort:
+                LOG(FATAL_WITHOUT_ABORT) << oss.str();
+                _exit(1);
+            }
+          }
+        }
       }
       if (compiled_method == nullptr &&
           dex_to_dex_compilation_level !=
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 3610f18..bef5be1 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -70,6 +70,7 @@
       deduplicate_code_(true),
       count_hotness_in_compiled_code_(false),
       resolve_startup_const_strings_(false),
+      check_profiled_methods_(ProfileMethodsCheck::kNone),
       register_allocation_strategy_(RegisterAllocator::kRegisterAllocatorDefault),
       passes_to_run_(nullptr) {
 }
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 12fa251..f0970a9 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -44,6 +44,13 @@
 class InstructionSetFeatures;
 class ProfileCompilationInfo;
 
+// Enum for CheckProfileMethodsCompiled. Outside CompilerOptions so it can be forward-declared.
+enum class ProfileMethodsCheck : uint8_t {
+  kNone,
+  kLog,
+  kAbort,
+};
+
 class CompilerOptions final {
  public:
   // Guide heuristics to determine whether to compile method if profile data not available.
@@ -324,6 +331,10 @@
     return resolve_startup_const_strings_;
   }
 
+  ProfileMethodsCheck CheckProfiledMethodsCompiled() const {
+    return check_profiled_methods_;
+  }
+
  private:
   bool ParseDumpInitFailures(const std::string& option, std::string* error_msg);
   void ParseDumpCfgPasses(const StringPiece& option, UsageFn Usage);
@@ -409,6 +420,10 @@
   // profile.
   bool resolve_startup_const_strings_;
 
+  // When running profile-guided compilation, check that methods intended to be compiled end
+  // up compiled and are not punted.
+  ProfileMethodsCheck check_profiled_methods_;
+
   RegisterAllocator::Strategy register_allocation_strategy_;
 
   // If not null, specifies optimization passes which will be run instead of defaults.
diff --git a/compiler/driver/compiler_options_map-inl.h b/compiler/driver/compiler_options_map-inl.h
index 5a84495..c7334a7 100644
--- a/compiler/driver/compiler_options_map-inl.h
+++ b/compiler/driver/compiler_options_map-inl.h
@@ -81,6 +81,9 @@
     options->count_hotness_in_compiled_code_ = true;
   }
   map.AssignIfExists(Base::ResolveStartupConstStrings, &options->resolve_startup_const_strings_);
+  if (map.Exists(Base::CheckProfiledMethods)) {
+    options->check_profiled_methods_ = *map.Get(Base::CheckProfiledMethods);
+  }
 
   if (map.Exists(Base::DumpTimings)) {
     options->dump_timings_ = true;
@@ -145,6 +148,12 @@
       .Define({"--count-hotness-in-compiled-code"})
           .IntoKey(Map::CountHotnessInCompiledCode)
 
+      .Define({"--check-profiled-methods=_"})
+          .template WithType<ProfileMethodsCheck>()
+          .WithValueMap({{"log", ProfileMethodsCheck::kLog},
+                         {"abort", ProfileMethodsCheck::kAbort}})
+          .IntoKey(Map::CheckProfiledMethods)
+
       .Define({"--dump-timings"})
           .IntoKey(Map::DumpTimings)
 
diff --git a/compiler/driver/compiler_options_map.def b/compiler/driver/compiler_options_map.def
index a593240..c2fac5e 100644
--- a/compiler/driver/compiler_options_map.def
+++ b/compiler/driver/compiler_options_map.def
@@ -61,6 +61,7 @@
 COMPILER_OPTIONS_KEY (ParseStringList<','>,        VerboseMethods)
 COMPILER_OPTIONS_KEY (bool,                        DeduplicateCode,            true)
 COMPILER_OPTIONS_KEY (Unit,                        CountHotnessInCompiledCode)
+COMPILER_OPTIONS_KEY (ProfileMethodsCheck,         CheckProfiledMethods)
 COMPILER_OPTIONS_KEY (Unit,                        DumpTimings)
 COMPILER_OPTIONS_KEY (Unit,                        DumpPassTimings)
 COMPILER_OPTIONS_KEY (Unit,                        DumpStats)
diff --git a/compiler/driver/compiler_options_map.h b/compiler/driver/compiler_options_map.h
index b9bc8b6..af212d6 100644
--- a/compiler/driver/compiler_options_map.h
+++ b/compiler/driver/compiler_options_map.h
@@ -25,6 +25,8 @@
 
 namespace art {
 
+enum class ProfileMethodsCheck : uint8_t;
+
 // Defines a type-safe heterogeneous key->value map. This is to be used as the base for
 // an extended map.
 template <typename Base, template <typename TV> class KeyType>
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index d8e442c..c2e83cd 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -1126,6 +1126,7 @@
   if (osr) {
     DCHECK_EQ(info->GetSuspendCheck(), instruction);
     DCHECK(info->IsIrreducible());
+    DCHECK(environment != nullptr);
     if (kIsDebugBuild) {
       for (size_t i = 0, environment_size = environment->Size(); i < environment_size; ++i) {
         HInstruction* in_environment = environment->GetInstructionAt(i);
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 14cff05..e7212cd 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -3608,9 +3608,17 @@
   CpuRegister tmp = locations->GetTemp(0).AsRegister<CpuRegister>();
 
   if (instruction->GetResultType() == DataType::Type::kInt32) {
-    __ leal(tmp, Address(numerator, abs_imm - 1));
-    __ testl(numerator, numerator);
-    __ cmov(kGreaterEqual, tmp, numerator);
+    // When denominator is equal to 2, we can add signed bit and numerator to tmp.
+    // Below we are using addl instruction instead of cmov which give us 1 cycle benefit.
+    if (abs_imm == 2) {
+      __ leal(tmp, Address(numerator, 0));
+      __ shrl(tmp, Immediate(31));
+      __ addl(tmp, numerator);
+    } else {
+      __ leal(tmp, Address(numerator, abs_imm - 1));
+      __ testl(numerator, numerator);
+      __ cmov(kGreaterEqual, tmp, numerator);
+    }
     int shift = CTZ(imm);
     __ sarl(tmp, Immediate(shift));
 
@@ -3622,11 +3630,16 @@
   } else {
     DCHECK_EQ(instruction->GetResultType(), DataType::Type::kInt64);
     CpuRegister rdx = locations->GetTemp(0).AsRegister<CpuRegister>();
-
-    codegen_->Load64BitValue(rdx, abs_imm - 1);
-    __ addq(rdx, numerator);
-    __ testq(numerator, numerator);
-    __ cmov(kGreaterEqual, rdx, numerator);
+    if (abs_imm == 2) {
+      __ movq(rdx, numerator);
+      __ shrq(rdx, Immediate(63));
+      __ addq(rdx, numerator);
+    } else {
+      codegen_->Load64BitValue(rdx, abs_imm - 1);
+      __ addq(rdx, numerator);
+      __ testq(numerator, numerator);
+      __ cmov(kGreaterEqual, rdx, numerator);
+    }
     int shift = CTZ(imm);
     __ sarq(rdx, Immediate(shift));
 
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 6dd4681..a73f4e8 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -545,6 +545,96 @@
   __ cfi().AdjustCFAOffset(-16);
 }
 
+static void CreateLowestOneBitLocations(ArenaAllocator* allocator, bool is_long, HInvoke* invoke) {
+  LocationSummary* locations =
+      new (allocator) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
+  if (is_long) {
+    locations->SetInAt(0, Location::RequiresRegister());
+  } else {
+    locations->SetInAt(0, Location::Any());
+  }
+  locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap);
+}
+
+static void GenLowestOneBit(X86Assembler* assembler,
+                      CodeGeneratorX86* codegen,
+                      bool is_long,
+                      HInvoke* invoke) {
+  LocationSummary* locations = invoke->GetLocations();
+  Location src = locations->InAt(0);
+  Location out_loc = locations->Out();
+
+  if (invoke->InputAt(0)->IsConstant()) {
+    // Evaluate this at compile time.
+    int64_t value = Int64FromConstant(invoke->InputAt(0)->AsConstant());
+    if (value == 0) {
+      if (is_long) {
+        __ xorl(out_loc.AsRegisterPairLow<Register>(), out_loc.AsRegisterPairLow<Register>());
+        __ xorl(out_loc.AsRegisterPairHigh<Register>(), out_loc.AsRegisterPairHigh<Register>());
+      } else {
+        __ xorl(out_loc.AsRegister<Register>(), out_loc.AsRegister<Register>());
+      }
+      return;
+    }
+    // Nonzero value.
+    value = is_long ? CTZ(static_cast<uint64_t>(value))
+                    : CTZ(static_cast<uint32_t>(value));
+    if (is_long) {
+      if (value >= 32) {
+        int shift = value-32;
+        codegen->Load32BitValue(out_loc.AsRegisterPairLow<Register>(), 0);
+        codegen->Load32BitValue(out_loc.AsRegisterPairHigh<Register>(), 1 << shift);
+      } else {
+        codegen->Load32BitValue(out_loc.AsRegisterPairLow<Register>(), 1 << value);
+        codegen->Load32BitValue(out_loc.AsRegisterPairHigh<Register>(), 0);
+      }
+    } else {
+      codegen->Load32BitValue(out_loc.AsRegister<Register>(), 1 << value);
+    }
+    return;
+  }
+  // Handle non constant case
+  if (is_long) {
+    DCHECK(src.IsRegisterPair());
+    Register src_lo = src.AsRegisterPairLow<Register>();
+    Register src_hi = src.AsRegisterPairHigh<Register>();
+
+    Register out_lo = out_loc.AsRegisterPairLow<Register>();
+    Register out_hi = out_loc.AsRegisterPairHigh<Register>();
+
+    __ movl(out_lo, src_lo);
+    __ movl(out_hi, src_hi);
+
+    __ negl(out_lo);
+    __ adcl(out_hi, Immediate(0));
+    __ negl(out_hi);
+
+    __ andl(out_lo, src_lo);
+    __ andl(out_hi, src_hi);
+  } else {
+    if (codegen->GetInstructionSetFeatures().HasAVX2() && src.IsRegister()) {
+      Register out = out_loc.AsRegister<Register>();
+      __ blsi(out, src.AsRegister<Register>());
+    } else {
+      Register out = out_loc.AsRegister<Register>();
+      // Do tmp & -tmp
+      if (src.IsRegister()) {
+        __ movl(out, src.AsRegister<Register>());
+      } else {
+        DCHECK(src.IsStackSlot());
+        __ movl(out, Address(ESP, src.GetStackIndex()));
+      }
+      __ negl(out);
+
+      if (src.IsRegister()) {
+        __ andl(out, src.AsRegister<Register>());
+      } else {
+        __ andl(out, Address(ESP, src.GetStackIndex()));
+      }
+    }
+  }
+}
+
 void IntrinsicLocationsBuilderX86::VisitMathCos(HInvoke* invoke) {
   CreateFPToFPCallLocations(allocator_, invoke);
 }
@@ -657,6 +747,21 @@
   GenFPToFPCall(invoke, codegen_, kQuickTanh);
 }
 
+void IntrinsicLocationsBuilderX86::VisitIntegerLowestOneBit(HInvoke* invoke) {
+  CreateLowestOneBitLocations(allocator_, /*is_long=*/ false, invoke);
+}
+void IntrinsicCodeGeneratorX86::VisitIntegerLowestOneBit(HInvoke* invoke) {
+  GenLowestOneBit(GetAssembler(), codegen_, /*is_long=*/ false, invoke);
+}
+
+void IntrinsicLocationsBuilderX86::VisitLongLowestOneBit(HInvoke* invoke) {
+  CreateLowestOneBitLocations(allocator_, /*is_long=*/ true, invoke);
+}
+
+void IntrinsicCodeGeneratorX86::VisitLongLowestOneBit(HInvoke* invoke) {
+  GenLowestOneBit(GetAssembler(), codegen_, /*is_long=*/ true, invoke);
+}
+
 static void CreateFPFPToFPCallLocations(ArenaAllocator* allocator, HInvoke* invoke) {
   LocationSummary* locations =
       new (allocator) LocationSummary(invoke, LocationSummary::kCallOnMainOnly, kIntrinsified);
@@ -2965,8 +3070,6 @@
 UNIMPLEMENTED_INTRINSIC(X86, DoubleIsInfinite)
 UNIMPLEMENTED_INTRINSIC(X86, IntegerHighestOneBit)
 UNIMPLEMENTED_INTRINSIC(X86, LongHighestOneBit)
-UNIMPLEMENTED_INTRINSIC(X86, IntegerLowestOneBit)
-UNIMPLEMENTED_INTRINSIC(X86, LongLowestOneBit)
 UNIMPLEMENTED_INTRINSIC(X86, CRC32Update)
 
 UNIMPLEMENTED_INTRINSIC(X86, StringStringIndexOf);
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 7db26dc..88c766f 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2413,59 +2413,64 @@
   }
 
   // Handle the non-constant cases.
-  CpuRegister tmp = locations->GetTemp(0).AsRegister<CpuRegister>();
-  if (is_high) {
-    // Use architectural support: basically 1 << bsr.
-    if (src.IsRegister()) {
-      if (is_long) {
-        __ bsrq(tmp, src.AsRegister<CpuRegister>());
+  if (!is_high && codegen->GetInstructionSetFeatures().HasAVX2() &&
+      src.IsRegister()) {
+      __ blsi(out, src.AsRegister<CpuRegister>());
+  } else {
+    CpuRegister tmp = locations->GetTemp(0).AsRegister<CpuRegister>();
+    if (is_high) {
+      // Use architectural support: basically 1 << bsr.
+      if (src.IsRegister()) {
+        if (is_long) {
+          __ bsrq(tmp, src.AsRegister<CpuRegister>());
+        } else {
+          __ bsrl(tmp, src.AsRegister<CpuRegister>());
+        }
+      } else if (is_long) {
+        DCHECK(src.IsDoubleStackSlot());
+        __ bsrq(tmp, Address(CpuRegister(RSP), src.GetStackIndex()));
       } else {
-        __ bsrl(tmp, src.AsRegister<CpuRegister>());
+        DCHECK(src.IsStackSlot());
+        __ bsrl(tmp, Address(CpuRegister(RSP), src.GetStackIndex()));
       }
-    } else if (is_long) {
-      DCHECK(src.IsDoubleStackSlot());
-      __ bsrq(tmp, Address(CpuRegister(RSP), src.GetStackIndex()));
-    } else {
-      DCHECK(src.IsStackSlot());
-      __ bsrl(tmp, Address(CpuRegister(RSP), src.GetStackIndex()));
-    }
-    // BSR sets ZF if the input was zero.
-    NearLabel is_zero, done;
-    __ j(kEqual, &is_zero);
-    __ movl(out, Immediate(1));  // Clears upper bits too.
-    if (is_long) {
-      __ shlq(out, tmp);
-    } else {
-      __ shll(out, tmp);
-    }
-    __ jmp(&done);
-    __ Bind(&is_zero);
-    __ xorl(out, out);  // Clears upper bits too.
-    __ Bind(&done);
-  } else  {
-    // Copy input into temporary.
-    if (src.IsRegister()) {
+      // BSR sets ZF if the input was zero.
+      NearLabel is_zero, done;
+      __ j(kEqual, &is_zero);
+      __ movl(out, Immediate(1));  // Clears upper bits too.
       if (is_long) {
-        __ movq(tmp, src.AsRegister<CpuRegister>());
+        __ shlq(out, tmp);
       } else {
-        __ movl(tmp, src.AsRegister<CpuRegister>());
+        __ shll(out, tmp);
       }
-    } else if (is_long) {
-      DCHECK(src.IsDoubleStackSlot());
-      __ movq(tmp, Address(CpuRegister(RSP), src.GetStackIndex()));
-    } else {
-      DCHECK(src.IsStackSlot());
-      __ movl(tmp, Address(CpuRegister(RSP), src.GetStackIndex()));
-    }
-    // Do the bit twiddling: basically tmp & -tmp;
-    if (is_long) {
-      __ movq(out, tmp);
-      __ negq(tmp);
-      __ andq(out, tmp);
-    } else {
-      __ movl(out, tmp);
-      __ negl(tmp);
-      __ andl(out, tmp);
+      __ jmp(&done);
+      __ Bind(&is_zero);
+      __ xorl(out, out);  // Clears upper bits too.
+      __ Bind(&done);
+    } else  {
+      // Copy input into temporary.
+      if (src.IsRegister()) {
+        if (is_long) {
+          __ movq(tmp, src.AsRegister<CpuRegister>());
+        } else {
+          __ movl(tmp, src.AsRegister<CpuRegister>());
+        }
+      } else if (is_long) {
+        DCHECK(src.IsDoubleStackSlot());
+        __ movq(tmp, Address(CpuRegister(RSP), src.GetStackIndex()));
+      } else {
+        DCHECK(src.IsStackSlot());
+        __ movl(tmp, Address(CpuRegister(RSP), src.GetStackIndex()));
+      }
+      // Do the bit twiddling: basically tmp & -tmp;
+      if (is_long) {
+        __ movq(out, tmp);
+        __ negq(tmp);
+        __ andq(out, tmp);
+      } else {
+        __ movl(out, tmp);
+        __ negl(tmp);
+        __ andl(out, tmp);
+      }
     }
   }
 }
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index fe6abd4..1db20fc 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -765,7 +765,7 @@
   ScopedArenaVector<uint8_t> stack_map = codegen->BuildStackMaps(code_item_for_osr_check);
 
   CompiledMethod* compiled_method = CompiledMethod::SwapAllocCompiledMethod(
-      GetCompilerDriver(),
+      GetCompilerDriver()->GetCompiledMethodStorage(),
       codegen->GetInstructionSet(),
       code_allocator->GetMemory(),
       ArrayRef<const uint8_t>(stack_map),
@@ -1222,7 +1222,7 @@
   ScopedArenaVector<uint8_t> stack_map = CreateJniStackMap(&stack_map_allocator,
                                                            jni_compiled_method);
   return CompiledMethod::SwapAllocCompiledMethod(
-      GetCompilerDriver(),
+      GetCompilerDriver()->GetCompiledMethodStorage(),
       jni_compiled_method.GetInstructionSet(),
       jni_compiled_method.GetCode(),
       ArrayRef<const uint8_t>(stack_map),
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index 8ce96a4..f8bdb16 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -66,15 +66,20 @@
         android: {
             // For atrace.
             shared_libs: ["libcutils"],
+            static_libs: [
+                "libz",
+            ],
+        },
+        host: {
+            shared_libs: [
+                "libz",
+            ],
         },
     },
     generated_sources: ["art_dex2oat_operator_srcs"],
     shared_libs: [
         "libbase",
     ],
-    include_dirs: [
-        "external/zlib",
-    ],
     export_include_dirs: ["."],
 
     // For SHA-1 checksumming of build ID
@@ -95,6 +100,7 @@
     },
     static_libs: [
         "libbase",
+        "libz",
     ],
 }
 
@@ -261,6 +267,14 @@
             lto: {
                  thin: true,
             },
+            static_libs: [
+                "libz",
+            ],
+        },
+        host: {
+            shared_libs: [
+                "libz",
+            ],
         },
     },
 }
@@ -284,6 +298,18 @@
     static_libs: [
         "libartd-dex2oat",
     ],
+    target: {
+        android: {
+            static_libs: [
+                "libz",
+            ],
+        },
+        host: {
+            shared_libs: [
+                "libz",
+            ],
+        },
+    },
 }
 
 cc_defaults {
@@ -309,6 +335,7 @@
     static_libs: [
         "libbase",
         "libsigchain_dummy",
+        "libz",
     ],
 }
 
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 12a8354..3a24542 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -115,6 +115,7 @@
 
 static std::string CommandLine() {
   std::vector<std::string> command;
+  command.reserve(original_argc);
   for (int i = 0; i < original_argc; ++i) {
     command.push_back(original_argv[i]);
   }
@@ -620,7 +621,7 @@
   explicit Dex2Oat(TimingLogger* timings) :
       compiler_kind_(Compiler::kOptimizing),
       // Take the default set of instruction features from the build.
-      image_file_location_oat_checksum_(0),
+      boot_image_checksum_(0),
       key_value_store_(nullptr),
       verification_results_(nullptr),
       runtime_(nullptr),
@@ -1492,7 +1493,7 @@
       key_value_store_->Put(OatHeader::kCompilationReasonKey, compilation_reason_);
     }
 
-    if (IsBootImage() && image_filenames_.size() > 1) {
+    if (IsBootImage()) {
       // If we're compiling the boot image, store the boot classpath into the Key-Value store.
       // We need this for the multi-image case.
       key_value_store_->Put(OatHeader::kBootClassPathKey,
@@ -1512,9 +1513,9 @@
         TimingLogger::ScopedTiming t3("Loading image checksum", timings_);
         std::vector<gc::space::ImageSpace*> image_spaces =
             Runtime::Current()->GetHeap()->GetBootImageSpaces();
-        image_file_location_oat_checksum_ = image_spaces[0]->GetImageHeader().GetOatChecksum();
+        boot_image_checksum_ = image_spaces[0]->GetImageHeader().GetImageChecksum();
       } else {
-        image_file_location_oat_checksum_ = 0u;
+        boot_image_checksum_ = 0u;
       }
 
       // Open dex files for class path.
@@ -1570,7 +1571,7 @@
         if (!oat_writers_[i]->WriteAndOpenDexFiles(
             vdex_files_[i].get(),
             rodata_.back(),
-            key_value_store_.get(),
+            (i == 0u) ? key_value_store_.get() : nullptr,
             verify,
             update_input_vdex_,
             copy_dex_files_,
@@ -1929,7 +1930,7 @@
   // ImageWriter, if necessary.
   // Note: Flushing (and closing) the file is the caller's responsibility, except for the failure
   //       case (when the file will be explicitly erased).
-  bool WriteOutputFiles() {
+  bool WriteOutputFiles(jobject class_loader) {
     TimingLogger::ScopedTiming t("dex2oat Oat", timings_);
 
     // Sync the data to the file, in case we did dex2dex transformations.
@@ -1964,6 +1965,7 @@
                                                   image_storage_mode_,
                                                   oat_filenames_,
                                                   dex_file_oat_index_map_,
+                                                  class_loader,
                                                   dirty_image_objects_.get()));
 
       // We need to prepare method offsets in the image address space for direct method patching.
@@ -2036,14 +2038,6 @@
                                              oat_writer->GetOatDataOffset(),
                                              oat_writer->GetOatSize());
         }
-
-        if (IsBootImage()) {
-          // Have the image_file_location_oat_checksum_ for boot oat files
-          // depend on the contents of all the boot oat files. This way only
-          // the primary image checksum needs to be checked to determine
-          // whether any of the images are out of date.
-          image_file_location_oat_checksum_ ^= oat_writer->GetOatHeader().GetChecksum();
-        }
       }
 
       for (size_t i = 0, size = oat_files_.size(); i != size; ++i) {
@@ -2082,7 +2076,7 @@
           elf_writer->EndDataBimgRelRo(data_bimg_rel_ro);
         }
 
-        if (!oat_writer->WriteHeader(elf_writer->GetStream(), image_file_location_oat_checksum_)) {
+        if (!oat_writer->WriteHeader(elf_writer->GetStream(), boot_image_checksum_)) {
           LOG(ERROR) << "Failed to write oat header to the ELF file " << oat_file->GetPath();
           return false;
         }
@@ -2709,7 +2703,7 @@
   std::unique_ptr<CompilerOptions> compiler_options_;
   Compiler::Kind compiler_kind_;
 
-  uint32_t image_file_location_oat_checksum_;
+  uint32_t boot_image_checksum_;
   std::unique_ptr<SafeMap<std::string, std::string> > key_value_store_;
 
   std::unique_ptr<VerificationResults> verification_results_;
@@ -2846,11 +2840,12 @@
 
 static dex2oat::ReturnCode CompileImage(Dex2Oat& dex2oat) {
   dex2oat.LoadClassProfileDescriptors();
+  jobject class_loader = dex2oat.Compile();
   // Keep the class loader that was used for compilation live for the rest of the compilation
   // process.
-  ScopedGlobalRef class_loader(dex2oat.Compile());
+  ScopedGlobalRef global_ref(class_loader);
 
-  if (!dex2oat.WriteOutputFiles()) {
+  if (!dex2oat.WriteOutputFiles(class_loader)) {
     dex2oat.EraseOutputFiles();
     return dex2oat::ReturnCode::kOther;
   }
@@ -2890,11 +2885,12 @@
 }
 
 static dex2oat::ReturnCode CompileApp(Dex2Oat& dex2oat) {
+  jobject class_loader = dex2oat.Compile();
   // Keep the class loader that was used for compilation live for the rest of the compilation
   // process.
-  ScopedGlobalRef class_loader(dex2oat.Compile());
+  ScopedGlobalRef global_ref(class_loader);
 
-  if (!dex2oat.WriteOutputFiles()) {
+  if (!dex2oat.WriteOutputFiles(class_loader)) {
     dex2oat.EraseOutputFiles();
     return dex2oat::ReturnCode::kOther;
   }
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index baeebd9..97a5f24 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -1069,7 +1069,8 @@
   void RunTest(const char* class_loader_context,
                const char* expected_classpath_key,
                bool expected_success,
-               bool use_second_source = false) {
+               bool use_second_source = false,
+               bool generate_image = false) {
     std::string dex_location = GetUsedDexLocation();
     std::string odex_location = GetUsedOatLocation();
 
@@ -1080,6 +1081,9 @@
     if (class_loader_context != nullptr) {
       extra_args.push_back(std::string("--class-loader-context=") + class_loader_context);
     }
+    if (generate_image) {
+      extra_args.push_back(std::string("--app-image-file=") + GetUsedImageLocation());
+    }
     auto check_oat = [expected_classpath_key](const OatFile& oat_file) {
       ASSERT_TRUE(expected_classpath_key != nullptr);
       const char* classpath = oat_file.GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey);
@@ -1104,6 +1108,10 @@
     return GetOdexDir() + "/Context.odex";
   }
 
+  std::string GetUsedImageLocation() {
+    return GetOdexDir() + "/Context.art";
+  }
+
   const char* kEmptyClassPathKey = "PCL[]";
 };
 
@@ -1202,6 +1210,66 @@
   RunTest(context.c_str(), expected_classpath_key.c_str(), true);
 }
 
+TEST_F(Dex2oatClassLoaderContextTest, ContextWithSharedLibrary) {
+  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") + "]" +
+      "{PCL[" + GetTestDexFileName("MultiDex") + "]}";
+  std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "]" +
+      "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]}";
+  RunTest(context.c_str(), expected_classpath_key.c_str(), true);
+}
+
+TEST_F(Dex2oatClassLoaderContextTest, ContextWithSharedLibraryAndImage) {
+  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") + "]" +
+      "{PCL[" + GetTestDexFileName("MultiDex") + "]}";
+  std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "]" +
+      "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]}";
+  RunTest(context.c_str(),
+          expected_classpath_key.c_str(),
+          /*expected_success=*/ true,
+          /*use_second_source=*/ false,
+          /*generate_image=*/ true);
+}
+
+TEST_F(Dex2oatClassLoaderContextTest, ContextWithSameSharedLibrariesAndImage) {
+  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") + "]" +
+      "{PCL[" + GetTestDexFileName("MultiDex") + "]" +
+      "#PCL[" + GetTestDexFileName("MultiDex") + "]}";
+  std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "]" +
+      "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]" +
+      "#PCL[" + CreateClassPathWithChecksums(dex_files2) + "]}";
+  RunTest(context.c_str(),
+          expected_classpath_key.c_str(),
+          /*expected_success=*/ true,
+          /*use_second_source=*/ false,
+          /*generate_image=*/ true);
+}
+
+TEST_F(Dex2oatClassLoaderContextTest, ContextWithSharedLibrariesDependenciesAndImage) {
+  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") + "]" +
+      "{PCL[" + GetTestDexFileName("MultiDex") + "]" +
+      "{PCL[" + GetTestDexFileName("Nested") + "]}}";
+  std::string expected_classpath_key = "PCL[" + CreateClassPathWithChecksums(dex_files1) + "]" +
+      "{PCL[" + CreateClassPathWithChecksums(dex_files2) + "]" +
+      "{PCL[" + CreateClassPathWithChecksums(dex_files1) + "]}}";
+  RunTest(context.c_str(),
+          expected_classpath_key.c_str(),
+          /*expected_success=*/ true,
+          /*use_second_source=*/ false,
+          /*generate_image=*/ true);
+}
+
 class Dex2oatDeterminism : public Dex2oatTest {};
 
 TEST_F(Dex2oatDeterminism, UnloadCompile) {
diff --git a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc
index b93e091..d8cbbaf 100644
--- a/dex2oat/linker/arm/relative_patcher_thumb2_test.cc
+++ b/dex2oat/linker/arm/relative_patcher_thumb2_test.cc
@@ -18,6 +18,7 @@
 
 #include "arch/arm/instruction_set_features_arm.h"
 #include "base/casts.h"
+#include "driver/compiler_options.h"
 #include "linker/relative_patcher_test.h"
 #include "lock_word.h"
 #include "mirror/array-inl.h"
@@ -196,8 +197,8 @@
                                     /*out*/ std::string* debug_name = nullptr) {
     OptimizingUnitTestHelper helper;
     HGraph* graph = helper.CreateGraph();
-    std::string error_msg;
-    arm::CodeGeneratorARMVIXL codegen(graph, *compiler_options_);
+    CompilerOptions compiler_options;
+    arm::CodeGeneratorARMVIXL codegen(graph, compiler_options);
     ArenaVector<uint8_t> code(helper.GetAllocator()->Adapter());
     codegen.EmitThunkCode(patch, &code, debug_name);
     return std::vector<uint8_t>(code.begin(), code.end());
diff --git a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc
index 0fc4610..f242ae2 100644
--- a/dex2oat/linker/arm64/relative_patcher_arm64_test.cc
+++ b/dex2oat/linker/arm64/relative_patcher_arm64_test.cc
@@ -18,6 +18,7 @@
 
 #include "arch/arm64/instruction_set_features_arm64.h"
 #include "base/casts.h"
+#include "driver/compiler_options.h"
 #include "linker/relative_patcher_test.h"
 #include "lock_word.h"
 #include "mirror/array-inl.h"
@@ -175,8 +176,8 @@
                                     /*out*/ std::string* debug_name = nullptr) {
     OptimizingUnitTestHelper helper;
     HGraph* graph = helper.CreateGraph();
-    std::string error_msg;
-    arm64::CodeGeneratorARM64 codegen(graph, *compiler_options_);
+    CompilerOptions compiler_options;
+    arm64::CodeGeneratorARM64 codegen(graph, compiler_options);
     ArenaVector<uint8_t> code(helper.GetAllocator()->Adapter());
     codegen.EmitThunkCode(patch, &code, debug_name);
     return std::vector<uint8_t>(code.begin(), code.end());
diff --git a/dex2oat/linker/image_test.cc b/dex2oat/linker/image_test.cc
index 69dac19..64b98cd 100644
--- a/dex2oat/linker/image_test.cc
+++ b/dex2oat/linker/image_test.cc
@@ -74,13 +74,11 @@
                              oat_data_begin,
                              oat_data_end,
                              oat_file_end,
-                             /*boot_image_begin*/0U,
-                             /*boot_image_size*/0U,
-                             /*boot_oat_begin*/0U,
-                             /*boot_oat_size_*/0U,
+                             /*boot_image_begin=*/ 0u,
+                             /*boot_image_size=*/ 0u,
                              sizeof(void*),
                              ImageHeader::kDefaultStorageMode,
-                             /*data_size*/0u);
+                             /*data_size=*/ 0u);
     ASSERT_TRUE(image_header.IsValid());
     ASSERT_TRUE(!image_header.IsAppImage());
 
@@ -110,7 +108,7 @@
   // Test the pointer to quick code is the same in origin method
   // and in the copied method form the same oat file.
   ObjPtr<mirror::Class> iface_klass =
-      class_linker_->LookupClass(self, "LIface;", /* class_loader */ nullptr);
+      class_linker_->LookupClass(self, "LIface;", /*class_loader=*/ nullptr);
   ASSERT_NE(nullptr, iface_klass);
   ArtMethod* origin = iface_klass->FindInterfaceMethod("defaultMethod", "()V", pointer_size);
   ASSERT_NE(nullptr, origin);
@@ -120,7 +118,7 @@
   ASSERT_NE(nullptr, code);
   ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code));
   ObjPtr<mirror::Class> impl_klass =
-      class_linker_->LookupClass(self, "LImpl;", /* class_loader */ nullptr);
+      class_linker_->LookupClass(self, "LImpl;", /*class_loader=*/ nullptr);
   ASSERT_NE(nullptr, impl_klass);
   ArtMethod* copied = FindCopiedMethod(origin, impl_klass);
   ASSERT_NE(nullptr, copied);
@@ -131,7 +129,7 @@
   // but the copied method has pointer to interpreter
   // because these methods are in different oat files.
   ObjPtr<mirror::Class> iterable_klass =
-      class_linker_->LookupClass(self, "Ljava/lang/Iterable;", /* class_loader */ nullptr);
+      class_linker_->LookupClass(self, "Ljava/lang/Iterable;", /*class_loader=*/ nullptr);
   ASSERT_NE(nullptr, iterable_klass);
   origin = iterable_klass->FindClassMethod(
       "forEach", "(Ljava/util/function/Consumer;)V", pointer_size);
@@ -143,7 +141,7 @@
   ASSERT_NE(nullptr, code);
   ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code));
   ObjPtr<mirror::Class> iterablebase_klass =
-      class_linker_->LookupClass(self, "LIterableBase;", /* class_loader */ nullptr);
+      class_linker_->LookupClass(self, "LIterableBase;", /*class_loader=*/ nullptr);
   ASSERT_NE(nullptr, iterablebase_klass);
   copied = FindCopiedMethod(origin, iterablebase_klass);
   ASSERT_NE(nullptr, copied);
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 9ef2875..c90eadd 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -219,6 +219,7 @@
                                                       storage_mode,
                                                       oat_filename_vector,
                                                       dex_file_to_oat_index_map,
+                                                      /*class_loader=*/ nullptr,
                                                       /*dirty_image_objects=*/ nullptr));
   {
     {
@@ -341,7 +342,7 @@
         }
 
         bool header_ok = oat_writer->WriteHeader(elf_writer->GetStream(),
-                                                 /* image_file_location_oat_checksum */ 0u);
+                                                 /*boot_image_checksum=*/ 0u);
         ASSERT_TRUE(header_ok);
 
         writer->UpdateOatFileHeader(i, oat_writer->GetOatHeader());
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index a3fc1cd..75b3555 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -19,6 +19,7 @@
 #include <lz4.h>
 #include <lz4hc.h>
 #include <sys/stat.h>
+#include <zlib.h>
 
 #include <memory>
 #include <numeric>
@@ -145,9 +146,11 @@
 // Separate objects into multiple bins to optimize dirty memory use.
 static constexpr bool kBinObjects = true;
 
-ObjPtr<mirror::ClassLoader> ImageWriter::GetClassLoader() {
-  CHECK_EQ(class_loaders_.size(), compiler_options_.IsAppImage() ? 1u : 0u);
-  return compiler_options_.IsAppImage() ? *class_loaders_.begin() : nullptr;
+ObjPtr<mirror::ClassLoader> ImageWriter::GetAppClassLoader() const
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return compiler_options_.IsAppImage()
+      ? ObjPtr<mirror::ClassLoader>::DownCast(Thread::Current()->DecodeJObject(app_class_loader_))
+      : nullptr;
 }
 
 // Return true if an object is already in an image space.
@@ -606,6 +609,58 @@
          referred_obj->IsString();
 }
 
+// Helper class that erases the image file if it isn't properly flushed and closed.
+class ImageWriter::ImageFileGuard {
+ public:
+  ImageFileGuard() noexcept = default;
+  ImageFileGuard(ImageFileGuard&& other) noexcept = default;
+  ImageFileGuard& operator=(ImageFileGuard&& other) noexcept = default;
+
+  ~ImageFileGuard() {
+    if (image_file_ != nullptr) {
+      // Failure, erase the image file.
+      image_file_->Erase();
+    }
+  }
+
+  void reset(File* image_file) {
+    image_file_.reset(image_file);
+  }
+
+  bool operator==(std::nullptr_t) {
+    return image_file_ == nullptr;
+  }
+
+  bool operator!=(std::nullptr_t) {
+    return image_file_ != nullptr;
+  }
+
+  File* operator->() const {
+    return image_file_.get();
+  }
+
+  bool WriteHeaderAndClose(const std::string& image_filename, const ImageHeader* image_header) {
+    // The header is uncompressed since it contains whether the image is compressed or not.
+    if (!image_file_->PwriteFully(image_header, sizeof(ImageHeader), 0)) {
+      PLOG(ERROR) << "Failed to write image file header " << image_filename;
+      return false;
+    }
+
+    // FlushCloseOrErase() takes care of erasing, so the destructor does not need
+    // to do that whether the FlushCloseOrErase() succeeds or fails.
+    std::unique_ptr<File> image_file = std::move(image_file_);
+    if (image_file->FlushCloseOrErase() != 0) {
+      PLOG(ERROR) << "Failed to flush and close image file " << image_filename;
+      return false;
+    }
+
+    return true;
+  }
+
+ private:
+  std::unique_ptr<File> image_file_;
+};
+
 bool ImageWriter::Write(int image_fd,
                         const std::vector<const char*>& image_filenames,
                         const std::vector<const char*>& oat_filenames) {
@@ -622,7 +677,7 @@
   {
     // Preload deterministic contents to the dex cache arrays we're going to write.
     ScopedObjectAccess soa(self);
-    ObjPtr<mirror::ClassLoader> class_loader = GetClassLoader();
+    ObjPtr<mirror::ClassLoader> class_loader = GetAppClassLoader();
     std::vector<ObjPtr<mirror::DexCache>> dex_caches = FindDexCaches(self);
     for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) {
       if (IsInBootImage(dex_cache.Ptr())) {
@@ -651,10 +706,18 @@
     CopyMetadata();
   }
 
+  // Primary image header shall be written last for two reasons. First, this ensures
+  // that we shall not end up with a valid primary image and invalid secondary image.
+  // Second, its checksum shall include the checksums of the secondary images (XORed).
+  // This way only the primary image checksum needs to be checked to determine whether
+  // any of the images or oat files are out of date. (Oat file checksums are included
+  // in the image checksum calculation.)
+  ImageHeader* primary_header = reinterpret_cast<ImageHeader*>(image_infos_[0].image_.Begin());
+  ImageFileGuard primary_image_file;
   for (size_t i = 0; i < image_filenames.size(); ++i) {
     const char* image_filename = image_filenames[i];
     ImageInfo& image_info = GetImageInfo(i);
-    std::unique_ptr<File> image_file;
+    ImageFileGuard image_file;
     if (image_fd != kInvalidFd) {
       if (strlen(image_filename) == 0u) {
         image_file.reset(new File(image_fd, unix_file::kCheckSafeUsage));
@@ -677,7 +740,6 @@
 
     if (!compiler_options_.IsAppImage() && fchmod(image_file->Fd(), 0644) != 0) {
       PLOG(ERROR) << "Failed to make image file world readable: " << image_filename;
-      image_file->Erase();
       return EXIT_FAILURE;
     }
 
@@ -690,11 +752,11 @@
     std::vector<uint8_t> compressed_data;
     ArrayRef<const uint8_t> image_data =
         MaybeCompressData(raw_image_data, image_storage_mode_, &compressed_data);
+    image_header->data_size_ = image_data.size();  // Fill in the data size.
 
     // Write out the image + fields + methods.
     if (!image_file->PwriteFully(image_data.data(), image_data.size(), sizeof(ImageHeader))) {
       PLOG(ERROR) << "Failed to write image file data " << image_filename;
-      image_file->Erase();
       return false;
     }
 
@@ -710,28 +772,25 @@
                                  bitmap_section.Size(),
                                  bitmap_position_in_file)) {
       PLOG(ERROR) << "Failed to write image file bitmap " << image_filename;
-      image_file->Erase();
       return false;
     }
 
     int err = image_file->Flush();
     if (err < 0) {
       PLOG(ERROR) << "Failed to flush image file " << image_filename << " with result " << err;
-      image_file->Erase();
       return false;
     }
 
-    // Write header last in case the compiler gets killed in the middle of image writing.
-    // We do not want to have a corrupted image with a valid header.
-    // The header is uncompressed since it contains whether the image is compressed or not.
-    image_header->data_size_ = image_data.size();
-    if (!image_file->PwriteFully(reinterpret_cast<char*>(image_info.image_.Begin()),
-                                 sizeof(ImageHeader),
-                                 0)) {
-      PLOG(ERROR) << "Failed to write image file header " << image_filename;
-      image_file->Erase();
-      return false;
-    }
+    // Calculate the image checksum.
+    uint32_t image_checksum = adler32(0L, Z_NULL, 0);
+    image_checksum = adler32(image_checksum,
+                             reinterpret_cast<const uint8_t*>(image_header),
+                             sizeof(ImageHeader));
+    image_checksum = adler32(image_checksum, image_data.data(), image_data.size());
+    image_checksum = adler32(image_checksum,
+                             reinterpret_cast<const uint8_t*>(image_info.image_bitmap_->Begin()),
+                             bitmap_section.Size());
+    image_header->SetImageChecksum(image_checksum);
 
     if (VLOG_IS_ON(compiler)) {
       size_t separately_written_section_size = bitmap_section.Size() + sizeof(ImageHeader);
@@ -748,11 +807,24 @@
     CHECK_EQ(bitmap_position_in_file + bitmap_section.Size(),
              static_cast<size_t>(image_file->GetLength()));
 
-    if (image_file->FlushCloseOrErase() != 0) {
-      PLOG(ERROR) << "Failed to flush and close image file " << image_filename;
-      return false;
+    // Write header last in case the compiler gets killed in the middle of image writing.
+    // We do not want to have a corrupted image with a valid header.
+    // Delay the writing of the primary image header until after writing secondary images.
+    if (i == 0u) {
+      primary_image_file = std::move(image_file);
+    } else {
+      if (!image_file.WriteHeaderAndClose(image_filename, image_header)) {
+        return false;
+      }
+      // Update the primary image checksum with the secondary image checksum.
+      primary_header->SetImageChecksum(primary_header->GetImageChecksum() ^ image_checksum);
     }
   }
+  DCHECK(primary_image_file != nullptr);
+  if (!primary_image_file.WriteHeaderAndClose(image_filenames[0], primary_header)) {
+    return false;
+  }
+
   return true;
 }
 
@@ -1400,27 +1472,15 @@
         Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(class_loader);
     class_table->Visit(classes_visitor);
     removed_class_count_ += classes_visitor.Prune();
-
-    // Record app image class loader. The fake boot class loader should not get registered
-    // and we should end up with only one class loader for an app and none for boot image.
-    if (class_loader != nullptr && class_table != nullptr) {
-      DCHECK(class_loader_ == nullptr);
-      class_loader_ = class_loader;
-    }
   }
 
   size_t GetRemovedClassCount() const {
     return removed_class_count_;
   }
 
-  ObjPtr<mirror::ClassLoader> GetClassLoader() const REQUIRES_SHARED(Locks::mutator_lock_) {
-    return class_loader_;
-  }
-
  private:
   ImageWriter* const image_writer_;
   size_t removed_class_count_;
-  ObjPtr<mirror::ClassLoader> class_loader_;
 };
 
 void ImageWriter::VisitClassLoaders(ClassLoaderVisitor* visitor) {
@@ -1631,13 +1691,10 @@
   });
 
   // Remove the undesired classes from the class roots.
-  ObjPtr<mirror::ClassLoader> class_loader;
   {
     PruneClassLoaderClassesVisitor class_loader_visitor(this);
     VisitClassLoaders(&class_loader_visitor);
     VLOG(compiler) << "Pruned " << class_loader_visitor.GetRemovedClassCount() << " classes";
-    class_loader = class_loader_visitor.GetClassLoader();
-    DCHECK_EQ(class_loader != nullptr, compiler_options_.IsAppImage());
   }
 
   // Clear references to removed classes from the DexCaches.
@@ -1645,7 +1702,7 @@
   for (ObjPtr<mirror::DexCache> dex_cache : dex_caches) {
     // Pass the class loader associated with the DexCache. This can either be
     // the app's `class_loader` or `nullptr` if boot class loader.
-    PruneDexCache(dex_cache, IsInBootImage(dex_cache.Ptr()) ? nullptr : class_loader);
+    PruneDexCache(dex_cache, IsInBootImage(dex_cache.Ptr()) ? nullptr : GetAppClassLoader());
   }
 
   // Drop the array class cache in the ClassLinker, as these are roots holding those classes live.
@@ -1964,18 +2021,17 @@
       }
     } else if (obj->IsClassLoader()) {
       // Register the class loader if it has a class table.
-      // The fake boot class loader should not get registered and we should end up with only one
-      // class loader.
+      // The fake boot class loader should not get registered.
       mirror::ClassLoader* class_loader = obj->AsClassLoader();
       if (class_loader->GetClassTable() != nullptr) {
         DCHECK(compiler_options_.IsAppImage());
-        DCHECK(class_loaders_.empty());
-        class_loaders_.insert(class_loader);
-        ImageInfo& image_info = GetImageInfo(oat_index);
-        // Note: Avoid locking to prevent lock order violations from root visiting;
-        // image_info.class_table_ table is only accessed from the image writer
-        // and class_loader->GetClassTable() is iterated but not modified.
-        image_info.class_table_->CopyWithoutLocks(*class_loader->GetClassTable());
+        if (class_loader == GetAppClassLoader()) {
+          ImageInfo& image_info = GetImageInfo(oat_index);
+          // Note: Avoid locking to prevent lock order violations from root visiting;
+          // image_info.class_table_ table is only accessed from the image writer
+          // and class_loader->GetClassTable() is iterated but not modified.
+          image_info.class_table_->CopyWithoutLocks(*class_loader->GetClassTable());
+        }
       }
     }
     AssignImageBinSlot(obj, oat_index);
@@ -2253,10 +2309,8 @@
     ProcessWorkStack(&work_stack);
 
     // Store the class loader in the class roots.
-    CHECK_EQ(class_loaders_.size(), 1u);
     CHECK_EQ(image_roots.size(), 1u);
-    CHECK(*class_loaders_.begin() != nullptr);
-    image_roots[0]->Set<false>(ImageHeader::kAppImageClassLoader, *class_loaders_.begin());
+    image_roots[0]->Set<false>(ImageHeader::kAppImageClassLoader, GetAppClassLoader());
   }
 
   // Verify that all objects have assigned image bin slots.
@@ -2519,9 +2573,7 @@
       PointerToLowMemUInt32(oat_data_end),
       PointerToLowMemUInt32(oat_file_end),
       boot_image_begin,
-      boot_image_end - boot_image_begin,
-      boot_oat_begin,
-      boot_oat_end - boot_oat_begin,
+      boot_oat_end - boot_image_begin,
       static_cast<uint32_t>(target_ptr_size_),
       image_storage_mode_,
       /*data_size*/0u);
@@ -3347,11 +3399,14 @@
                                       size_t oat_loaded_size,
                                       size_t oat_data_offset,
                                       size_t oat_data_size) {
+  DCHECK_GE(oat_loaded_size, oat_data_offset);
+  DCHECK_GE(oat_loaded_size - oat_data_offset, oat_data_size);
+
   const uint8_t* images_end = image_infos_.back().image_begin_ + image_infos_.back().image_size_;
+  DCHECK(images_end != nullptr);  // Image space must be ready.
   for (const ImageInfo& info : image_infos_) {
     DCHECK_LE(info.image_begin_ + info.image_size_, images_end);
   }
-  DCHECK(images_end != nullptr);  // Image space must be ready.
 
   ImageInfo& cur_image_info = GetImageInfo(oat_index);
   cur_image_info.oat_file_begin_ = images_end + cur_image_info.oat_offset_;
@@ -3401,6 +3456,7 @@
     ImageHeader::StorageMode image_storage_mode,
     const std::vector<const char*>& oat_filenames,
     const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map,
+    jobject class_loader,
     const HashSet<std::string>* dirty_image_objects)
     : compiler_options_(compiler_options),
       global_image_begin_(reinterpret_cast<uint8_t*>(image_begin)),
@@ -3409,6 +3465,7 @@
       image_infos_(oat_filenames.size()),
       dirty_methods_(0u),
       clean_methods_(0u),
+      app_class_loader_(class_loader),
       boot_image_live_objects_(nullptr),
       image_storage_mode_(image_storage_mode),
       oat_filenames_(oat_filenames),
diff --git a/dex2oat/linker/image_writer.h b/dex2oat/linker/image_writer.h
index 3c377a3..782bbd2 100644
--- a/dex2oat/linker/image_writer.h
+++ b/dex2oat/linker/image_writer.h
@@ -81,6 +81,7 @@
               ImageHeader::StorageMode image_storage_mode,
               const std::vector<const char*>& oat_filenames,
               const std::unordered_map<const DexFile*, size_t>& dex_file_oat_index_map,
+              jobject class_loader,
               const HashSet<std::string>* dirty_image_objects);
 
   /*
@@ -111,7 +112,7 @@
     return true;
   }
 
-  ObjPtr<mirror::ClassLoader> GetClassLoader();
+  ObjPtr<mirror::ClassLoader> GetAppClassLoader() const REQUIRES_SHARED(Locks::mutator_lock_);
 
   template <typename T>
   T* GetImageAddress(T* object) const REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -771,10 +772,8 @@
   // Prune class memoization table to speed up ContainsBootClassLoaderNonImageClass.
   std::unordered_map<mirror::Class*, bool> prune_class_memo_;
 
-  // Class loaders with a class table to write out. There should only be one class loader because
-  // dex2oat loads the dex files to be compiled into a single class loader. For the boot image,
-  // null is a valid entry.
-  std::unordered_set<mirror::ClassLoader*> class_loaders_;
+  // The application class loader. Null for boot image.
+  jobject app_class_loader_;
 
   // Boot image live objects, null for app image.
   mirror::ObjectArray<mirror::Object>* boot_image_live_objects_;
@@ -794,7 +793,7 @@
   // Set of objects known to be dirty in the image. Can be nullptr if there are none.
   const HashSet<std::string>* dirty_image_objects_;
 
-  class ComputeLazyFieldsForClassesVisitor;
+  class ImageFileGuard;
   class FixupClassVisitor;
   class FixupRootVisitor;
   class FixupVisitor;
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 7f2877f..9aaabc4 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -99,13 +99,28 @@
   return reinterpret_cast<const UnalignedDexFileHeader*>(raw_data);
 }
 
-class ChecksumUpdatingOutputStream : public OutputStream {
+inline uint32_t CodeAlignmentSize(uint32_t header_offset, const CompiledMethod& compiled_method) {
+  // We want to align the code rather than the preheader.
+  uint32_t unaligned_code_offset = header_offset + sizeof(OatQuickMethodHeader);
+  uint32_t aligned_code_offset =  compiled_method.AlignCode(unaligned_code_offset);
+  return aligned_code_offset - unaligned_code_offset;
+}
+
+}  // anonymous namespace
+
+class OatWriter::ChecksumUpdatingOutputStream : public OutputStream {
  public:
-  ChecksumUpdatingOutputStream(OutputStream* out, OatHeader* oat_header)
-      : OutputStream(out->GetLocation()), out_(out), oat_header_(oat_header) { }
+  ChecksumUpdatingOutputStream(OutputStream* out, OatWriter* writer)
+      : OutputStream(out->GetLocation()), out_(out), writer_(writer) { }
 
   bool WriteFully(const void* buffer, size_t byte_count) override {
-    oat_header_->UpdateChecksum(buffer, byte_count);
+    if (buffer != nullptr) {
+      const uint8_t* bytes = reinterpret_cast<const uint8_t*>(buffer);
+      uint32_t old_checksum = writer_->oat_checksum_;
+      writer_->oat_checksum_ = adler32(old_checksum, bytes, byte_count);
+    } else {
+      DCHECK_EQ(0U, byte_count);
+    }
     return out_->WriteFully(buffer, byte_count);
   }
 
@@ -119,18 +134,9 @@
 
  private:
   OutputStream* const out_;
-  OatHeader* const oat_header_;
+  OatWriter* const writer_;
 };
 
-inline uint32_t CodeAlignmentSize(uint32_t header_offset, const CompiledMethod& compiled_method) {
-  // We want to align the code rather than the preheader.
-  uint32_t unaligned_code_offset = header_offset + sizeof(OatQuickMethodHeader);
-  uint32_t aligned_code_offset =  compiled_method.AlignCode(unaligned_code_offset);
-  return aligned_code_offset - unaligned_code_offset;
-}
-
-}  // anonymous namespace
-
 // Defines the location of the raw dex file to write.
 class OatWriter::DexFileSource {
  public:
@@ -379,6 +385,7 @@
     vdex_dex_shared_data_offset_(0u),
     vdex_verifier_deps_offset_(0u),
     vdex_quickening_info_offset_(0u),
+    oat_checksum_(adler32(0L, Z_NULL, 0)),
     code_size_(0u),
     oat_size_(0u),
     data_bimg_rel_ro_start_(0u),
@@ -675,7 +682,7 @@
   oat_size_ = InitOatHeader(dchecked_integral_cast<uint32_t>(oat_dex_files_.size()),
                             key_value_store);
 
-  ChecksumUpdatingOutputStream checksum_updating_rodata(oat_rodata, oat_header_.get());
+  ChecksumUpdatingOutputStream checksum_updating_rodata(oat_rodata, this);
 
   std::unique_ptr<BufferedOutputStream> vdex_out =
       std::make_unique<BufferedOutputStream>(std::make_unique<FileOutputStream>(vdex_file));
@@ -1482,7 +1489,7 @@
                          const std::vector<const DexFile*>* dex_files)
       : OatDexMethodVisitor(writer, offset),
         pointer_size_(GetInstructionSetPointerSize(writer_->compiler_options_.GetInstructionSet())),
-        class_loader_(writer->HasImage() ? writer->image_writer_->GetClassLoader() : nullptr),
+        class_loader_(writer->HasImage() ? writer->image_writer_->GetAppClassLoader() : nullptr),
         dex_files_(dex_files),
         class_linker_(Runtime::Current()->GetClassLinker()) {}
 
@@ -1623,7 +1630,7 @@
         offset_(relative_offset),
         dex_file_(nullptr),
         pointer_size_(GetInstructionSetPointerSize(writer_->compiler_options_.GetInstructionSet())),
-        class_loader_(writer->HasImage() ? writer->image_writer_->GetClassLoader() : nullptr),
+        class_loader_(writer->HasImage() ? writer->image_writer_->GetAppClassLoader() : nullptr),
         out_(out),
         file_offset_(file_offset),
         class_linker_(Runtime::Current()->GetClassLinker()),
@@ -2264,6 +2271,7 @@
   }
 
   if (HasImage()) {
+    ScopedAssertNoThreadSuspension sants("Init image method visitor", Thread::Current());
     InitImageMethodVisitor image_visitor(this, offset, dex_files_);
     success = VisitDexMethods(&image_visitor);
     image_visitor.Postprocess();
@@ -2349,7 +2357,7 @@
   size_t relative_offset = current_offset - file_offset;
 
   // Wrap out to update checksum with each write.
-  ChecksumUpdatingOutputStream checksum_updating_out(out, oat_header_.get());
+  ChecksumUpdatingOutputStream checksum_updating_out(out, this);
   out = &checksum_updating_out;
 
   relative_offset = WriteClassOffsets(out, file_offset, relative_offset);
@@ -2658,7 +2666,7 @@
   CHECK(write_state_ == WriteState::kWriteText);
 
   // Wrap out to update checksum with each write.
-  ChecksumUpdatingOutputStream checksum_updating_out(out, oat_header_.get());
+  ChecksumUpdatingOutputStream checksum_updating_out(out, this);
   out = &checksum_updating_out;
 
   SetMultiOatRelativePatcherAdjustment();
@@ -2694,7 +2702,7 @@
   CHECK(write_state_ == WriteState::kWriteDataBimgRelRo);
 
   // Wrap out to update checksum with each write.
-  ChecksumUpdatingOutputStream checksum_updating_out(out, oat_header_.get());
+  ChecksumUpdatingOutputStream checksum_updating_out(out, this);
   out = &checksum_updating_out;
 
   const size_t file_offset = oat_data_offset_;
@@ -2800,11 +2808,18 @@
   return true;
 }
 
-bool OatWriter::WriteHeader(OutputStream* out, uint32_t image_file_location_oat_checksum) {
+bool OatWriter::WriteHeader(OutputStream* out, uint32_t boot_image_checksum) {
   CHECK(write_state_ == WriteState::kWriteHeader);
 
-  oat_header_->SetImageFileLocationOatChecksum(image_file_location_oat_checksum);
-  oat_header_->UpdateChecksumWithHeaderData();
+  oat_header_->SetBootImageChecksum(boot_image_checksum);
+
+  // Update checksum with header data.
+  DCHECK_EQ(oat_header_->GetChecksum(), 0u);  // For checksum calculation.
+  const uint8_t* header_begin = reinterpret_cast<const uint8_t*>(oat_header_.get());
+  const uint8_t* header_end = oat_header_->GetKeyValueStore() + oat_header_->GetKeyValueStoreSize();
+  uint32_t old_checksum = oat_checksum_;
+  oat_checksum_ = adler32(old_checksum, header_begin, header_end - header_begin);
+  oat_header_->SetChecksum(oat_checksum_);
 
   const size_t file_offset = oat_data_offset_;
 
diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h
index c049518..9cd2fd0 100644
--- a/dex2oat/linker/oat_writer.h
+++ b/dex2oat/linker/oat_writer.h
@@ -198,7 +198,7 @@
   // Check the size of the written oat file.
   bool CheckOatSize(OutputStream* out, size_t file_offset, size_t relative_offset);
   // Write the oat header. This finalizes the oat file.
-  bool WriteHeader(OutputStream* out, uint32_t image_file_location_oat_checksum);
+  bool WriteHeader(OutputStream* out, uint32_t boot_image_checksum);
 
   // Returns whether the oat file has an associated image.
   bool HasImage() const {
@@ -256,6 +256,7 @@
   }
 
  private:
+  class ChecksumUpdatingOutputStream;
   class DexFileSource;
   class OatClassHeader;
   class OatClass;
@@ -400,6 +401,9 @@
   // Offset of section holding quickening info inside Vdex.
   size_t vdex_quickening_info_offset_;
 
+  // OAT checksum.
+  uint32_t oat_checksum_;
+
   // Size of the .text segment.
   size_t code_size_;
 
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index af02bfc..7450964 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -236,8 +236,7 @@
       elf_writer->EndDataBimgRelRo(data_bimg_rel_ro);
     }
 
-    if (!oat_writer.WriteHeader(elf_writer->GetStream(),
-                                /*image_file_location_oat_checksum=*/ 42U)) {
+    if (!oat_writer.WriteHeader(elf_writer->GetStream(), /*boot_image_checksum=*/ 42u)) {
       return false;
     }
 
@@ -416,7 +415,7 @@
   const OatHeader& oat_header = oat_file->GetOatHeader();
   ASSERT_TRUE(oat_header.IsValid());
   ASSERT_EQ(class_linker->GetBootClassPath().size(), oat_header.GetDexFileCount());  // core
-  ASSERT_EQ(42U, oat_header.GetImageFileLocationOatChecksum());
+  ASSERT_EQ(42u, oat_header.GetBootImageChecksum());
 
   ASSERT_TRUE(java_lang_dex_file_ != nullptr);
   const DexFile& dex_file = *java_lang_dex_file_;
@@ -842,29 +841,5 @@
   TestZipFileInputWithEmptyDex();
 }
 
-TEST_F(OatTest, UpdateChecksum) {
-  InstructionSet insn_set = InstructionSet::kX86;
-  std::string error_msg;
-  std::unique_ptr<const InstructionSetFeatures> insn_features(
-    InstructionSetFeatures::FromVariant(insn_set, "default", &error_msg));
-  ASSERT_TRUE(insn_features.get() != nullptr) << error_msg;
-  std::unique_ptr<OatHeader> oat_header(OatHeader::Create(insn_set,
-                                                          insn_features.get(),
-                                                          0u,
-                                                          nullptr));
-  // The starting adler32 value is 1.
-  EXPECT_EQ(1U, oat_header->GetChecksum());
-
-  oat_header->UpdateChecksum(OatHeader::kOatMagic, sizeof(OatHeader::kOatMagic));
-  EXPECT_EQ(64291151U, oat_header->GetChecksum());
-
-  // Make sure that null data does not reset the checksum.
-  oat_header->UpdateChecksum(nullptr, 0);
-  EXPECT_EQ(64291151U, oat_header->GetChecksum());
-
-  oat_header->UpdateChecksum(OatHeader::kOatMagic, sizeof(OatHeader::kOatMagic));
-  EXPECT_EQ(216138397U, oat_header->GetChecksum());
-}
-
 }  // namespace linker
 }  // namespace art
diff --git a/dex2oat/linker/relative_patcher_test.h b/dex2oat/linker/relative_patcher_test.h
index 56ff0ef..4329ee1 100644
--- a/dex2oat/linker/relative_patcher_test.h
+++ b/dex2oat/linker/relative_patcher_test.h
@@ -17,19 +17,17 @@
 #ifndef ART_DEX2OAT_LINKER_RELATIVE_PATCHER_TEST_H_
 #define ART_DEX2OAT_LINKER_RELATIVE_PATCHER_TEST_H_
 
+#include <gtest/gtest.h>
+
 #include "arch/instruction_set.h"
 #include "arch/instruction_set_features.h"
 #include "base/array_ref.h"
 #include "base/globals.h"
 #include "base/macros.h"
-#include "common_compiler_test.h"
 #include "compiled_method-inl.h"
-#include "dex/verification_results.h"
 #include "dex/method_reference.h"
 #include "dex/string_reference.h"
-#include "driver/compiler_driver.h"
-#include "driver/compiler_options.h"
-#include "gtest/gtest.h"
+#include "driver/compiled_method_storage.h"
 #include "linker/relative_patcher.h"
 #include "linker/vector_output_stream.h"
 #include "oat.h"
@@ -39,10 +37,12 @@
 namespace linker {
 
 // Base class providing infrastructure for architecture-specific tests.
-class RelativePatcherTest : public CommonCompilerTest {
+class RelativePatcherTest : public testing::Test {
  protected:
   RelativePatcherTest(InstructionSet instruction_set, const std::string& variant)
-      : variant_(variant),
+      : storage_(/*swap_fd=*/ -1),
+        instruction_set_(instruction_set),
+        instruction_set_features_(nullptr),
         method_offset_map_(),
         patcher_(nullptr),
         bss_begin_(0u),
@@ -51,23 +51,29 @@
         patched_code_(),
         output_(),
         out_(nullptr) {
-    // Override CommonCompilerTest's defaults.
-    instruction_set_ = instruction_set;
-    number_of_threads_ = 1u;
+    std::string error_msg;
+    instruction_set_features_ =
+        InstructionSetFeatures::FromVariant(instruction_set, variant, &error_msg);
+    CHECK(instruction_set_features_ != nullptr) << error_msg;
+
     patched_code_.reserve(16 * KB);
   }
 
   void SetUp() override {
-    OverrideInstructionSetFeatures(instruction_set_, variant_);
-    CommonCompilerTest::SetUp();
-
     Reset();
   }
 
   void TearDown() override {
+    thunk_provider_.Reset();
     compiled_methods_.clear();
     patcher_.reset();
-    CommonCompilerTest::TearDown();
+    bss_begin_ = 0u;
+    string_index_to_offset_map_.clear();
+    compiled_method_refs_.clear();
+    compiled_methods_.clear();
+    patched_code_.clear();
+    output_.clear();
+    out_.reset();
   }
 
   // Reset the helper to start another test. Creating and tearing down the Runtime is expensive,
@@ -75,8 +81,8 @@
   void Reset() {
     thunk_provider_.Reset();
     method_offset_map_.map.clear();
-    patcher_ = RelativePatcher::Create(compiler_options_->GetInstructionSet(),
-                                       compiler_options_->GetInstructionSetFeatures(),
+    patcher_ = RelativePatcher::Create(instruction_set_,
+                                       instruction_set_features_.get(),
                                        &thunk_provider_,
                                        &method_offset_map_);
     bss_begin_ = 0u;
@@ -99,7 +105,7 @@
       const ArrayRef<const LinkerPatch>& patches = ArrayRef<const LinkerPatch>()) {
     compiled_method_refs_.push_back(method_ref);
     compiled_methods_.emplace_back(new CompiledMethod(
-        compiler_driver_.get(),
+        &storage_,
         instruction_set_,
         code,
         /* vmap_table */ ArrayRef<const uint8_t>(),
@@ -351,7 +357,10 @@
   static const uint32_t kTrampolineSize = 4u;
   static const uint32_t kTrampolineOffset = 0u;
 
-  std::string variant_;
+  CompiledMethodStorage storage_;
+  InstructionSet instruction_set_;
+  std::unique_ptr<const InstructionSetFeatures> instruction_set_features_;
+
   ThunkProvider thunk_provider_;
   MethodOffsetMap method_offset_map_;
   std::unique_ptr<RelativePatcher> patcher_;
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 09f0b20..32122eb 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -34,6 +34,7 @@
 #include "android-base/stringprintf.h"
 
 #include "base/logging.h"  // For VLOG_IS_ON.
+#include "base/hiddenapi_flags.h"
 #include "base/mem_map.h"
 #include "base/os.h"
 #include "base/utils.h"
@@ -222,15 +223,10 @@
   return str;
 }
 
-static const char* GetHiddenapiFlagStr(uint32_t hiddenapi_flags) {
-  static const char* const kValue[] = {
-    "WHITELIST",             /* 0x0 */
-    "LIGHT_GREYLIST",        /* 0x1 */
-    "DARK_GREYLIST",         /* 0x2 */
-    "BLACKLIST",             /* 0x3 */
-  };
-  DCHECK_LT(hiddenapi_flags, arraysize(kValue));
-  return kValue[hiddenapi_flags];
+static std::string GetHiddenapiFlagStr(uint32_t hiddenapi_flags) {
+  std::string api_list(hiddenapi::ApiList::FromDexFlags(hiddenapi_flags).GetName());
+  std::transform(api_list.begin(), api_list.end(), api_list.begin(), ::toupper);
+  return api_list;
 }
 
 static std::string GetSignatureForProtoId(const dex_ir::ProtoId* proto) {
@@ -1173,7 +1169,6 @@
   char* type_descriptor = strdup(GetSignatureForProtoId(method_id->Proto()).c_str());
   const char* back_descriptor = method_id->Class()->GetStringId()->Data();
   char* access_str = CreateAccessFlagStr(flags, kAccessForMethod);
-  const char* hiddenapi_str = GetHiddenapiFlagStr(hiddenapi_flags);
 
   if (options_.output_format_ == kOutputPlain) {
     fprintf(out_file_, "    #%d              : (in %s)\n", i, back_descriptor);
@@ -1181,7 +1176,10 @@
     fprintf(out_file_, "      type          : '%s'\n", type_descriptor);
     fprintf(out_file_, "      access        : 0x%04x (%s)\n", flags, access_str);
     if (hiddenapi_flags != 0u) {
-      fprintf(out_file_, "      hiddenapi     : 0x%04x (%s)\n", hiddenapi_flags, hiddenapi_str);
+      fprintf(out_file_,
+              "      hiddenapi     : 0x%04x (%s)\n",
+              hiddenapi_flags,
+              GetHiddenapiFlagStr(hiddenapi_flags).c_str());
     }
     if (code == nullptr) {
       fprintf(out_file_, "      code          : (none)\n");
@@ -1291,7 +1289,6 @@
   const char* type_descriptor = field_id->Type()->GetStringId()->Data();
   const char* back_descriptor = field_id->Class()->GetStringId()->Data();
   char* access_str = CreateAccessFlagStr(flags, kAccessForField);
-  const char* hiddenapi_str = GetHiddenapiFlagStr(hiddenapi_flags);
 
   if (options_.output_format_ == kOutputPlain) {
     fprintf(out_file_, "    #%d              : (in %s)\n", i, back_descriptor);
@@ -1299,7 +1296,10 @@
     fprintf(out_file_, "      type          : '%s'\n", type_descriptor);
     fprintf(out_file_, "      access        : 0x%04x (%s)\n", flags, access_str);
     if (hiddenapi_flags != 0u) {
-      fprintf(out_file_, "      hiddenapi     : 0x%04x (%s)\n", hiddenapi_flags, hiddenapi_str);
+      fprintf(out_file_,
+              "      hiddenapi     : 0x%04x (%s)\n",
+              hiddenapi_flags,
+              GetHiddenapiFlagStr(hiddenapi_flags).c_str());
     }
     if (init != nullptr) {
       fputs("      value         : ", out_file_);
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc
index 21cdcf1..2b97fb4 100644
--- a/dexoptanalyzer/dexoptanalyzer.cc
+++ b/dexoptanalyzer/dexoptanalyzer.cc
@@ -52,6 +52,7 @@
 
 static std::string CommandLine() {
   std::vector<std::string> command;
+  command.reserve(original_argc);
   for (int i = 0; i < original_argc; ++i) {
     command.push_back(original_argv[i]);
   }
diff --git a/libartbase/Android.bp b/libartbase/Android.bp
index 6a667bc..58d12a1 100644
--- a/libartbase/Android.bp
+++ b/libartbase/Android.bp
@@ -27,6 +27,7 @@
         "base/file_magic.cc",
         "base/file_utils.cc",
         "base/hex_dump.cc",
+        "base/hiddenapi_flags.cc",
         "base/logging.cc",
         "base/malloc_arena_pool.cc",
         "base/membarrier.cc",
diff --git a/libartbase/arch/instruction_set.cc b/libartbase/arch/instruction_set.cc
index d47f936..8d4fbf4 100644
--- a/libartbase/arch/instruction_set.cc
+++ b/libartbase/arch/instruction_set.cc
@@ -133,11 +133,11 @@
 static_assert(ART_FRAME_SIZE_LIMIT < kX86_64StackOverflowReservedBytes,
               "Frame size limit too large");
 
-}  // namespace instruction_set_details
-
 NO_RETURN void GetStackOverflowReservedBytesFailure(const char* error_msg) {
   LOG(FATAL) << error_msg;
   UNREACHABLE();
 }
 
+}  // namespace instruction_set_details
+
 }  // namespace art
diff --git a/libartbase/base/hiddenapi_flags.cc b/libartbase/base/hiddenapi_flags.cc
new file mode 100644
index 0000000..6caa75c
--- /dev/null
+++ b/libartbase/base/hiddenapi_flags.cc
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#include "hiddenapi_flags.h"
+
+namespace art {
+namespace hiddenapi {
+
+constexpr const char* ApiList::kNames[ApiList::kValueCount];
+constexpr SdkVersion ApiList::kMaxSdkVersions[ApiList::kValueCount];
+
+}  // namespace hiddenapi
+}  // namespace art
diff --git a/libartbase/base/hiddenapi_flags.h b/libartbase/base/hiddenapi_flags.h
new file mode 100644
index 0000000..8e7269c
--- /dev/null
+++ b/libartbase/base/hiddenapi_flags.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 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_LIBARTBASE_BASE_HIDDENAPI_FLAGS_H_
+#define ART_LIBARTBASE_BASE_HIDDENAPI_FLAGS_H_
+
+#include "sdk_version.h"
+
+#include "android-base/logging.h"
+
+namespace art {
+namespace hiddenapi {
+
+/*
+ * This class represents the information whether a field/method is in
+ * public API (whitelist) or if it isn't, apps targeting which SDK
+ * versions are allowed to access it.
+ */
+class ApiList {
+ private:
+  using IntValueType = uint32_t;
+
+  enum class Value : IntValueType {
+    // Values independent of target SDK version of app
+    kWhitelist =     0,
+    kGreylist =      1,
+    kBlacklist =     2,
+
+    // Values dependent on target SDK version of app. Put these last as
+    // their list will be extended in future releases.
+    // The max release code implicitly includes all maintenance releases,
+    // e.g. GreylistMaxO is accessible to targetSdkVersion <= 27 (O_MR1).
+    kGreylistMaxO =  3,
+
+    // Special values
+    kInvalid =       static_cast<uint32_t>(-1),
+    kMinValue =      kWhitelist,
+    kMaxValue =      kGreylistMaxO,
+  };
+
+  static constexpr const char* kNames[] = {
+    "whitelist",
+    "greylist",
+    "blacklist",
+    "greylist-max-o",
+  };
+
+  static constexpr SdkVersion kMaxSdkVersions[] {
+    /* whitelist */ SdkVersion::kMax,
+    /* greylist */ SdkVersion::kMax,
+    /* blacklist */ SdkVersion::kMin,
+    /* greylist-max-o */ SdkVersion::kO_MR1,
+  };
+
+  static ApiList MinValue() { return ApiList(Value::kMinValue); }
+  static ApiList MaxValue() { return ApiList(Value::kMaxValue); }
+
+  explicit ApiList(Value value) : value_(value) {}
+
+  const Value value_;
+
+ public:
+  static ApiList Whitelist() { return ApiList(Value::kWhitelist); }
+  static ApiList Greylist() { return ApiList(Value::kGreylist); }
+  static ApiList Blacklist() { return ApiList(Value::kBlacklist); }
+  static ApiList GreylistMaxO() { return ApiList(Value::kGreylistMaxO); }
+  static ApiList Invalid() { return ApiList(Value::kInvalid); }
+
+  // Decodes ApiList from dex hiddenapi flags.
+  static ApiList FromDexFlags(uint32_t dex_flags) {
+    if (MinValue().GetIntValue() <= dex_flags && dex_flags <= MaxValue().GetIntValue()) {
+      return ApiList(static_cast<Value>(dex_flags));
+    }
+    return Invalid();
+  }
+
+  // Returns the ApiList with a given name.
+  static ApiList FromName(const std::string& str) {
+    for (IntValueType i = MinValue().GetIntValue(); i <= MaxValue().GetIntValue(); i++) {
+      ApiList current = ApiList(static_cast<Value>(i));
+      if (str == current.GetName()) {
+        return current;
+      }
+    }
+    return Invalid();
+  }
+
+  bool operator==(const ApiList other) const { return value_ == other.value_; }
+  bool operator!=(const ApiList other) const { return !(*this == other); }
+
+  bool IsValid() const { return *this != Invalid(); }
+
+  IntValueType GetIntValue() const {
+    DCHECK(IsValid());
+    return static_cast<IntValueType>(value_);
+  }
+
+  const char* GetName() const { return kNames[GetIntValue()]; }
+
+  SdkVersion GetMaxAllowedSdkVersion() const { return kMaxSdkVersions[GetIntValue()]; }
+
+  static constexpr size_t kValueCount = static_cast<size_t>(Value::kMaxValue) + 1;
+};
+
+inline std::ostream& operator<<(std::ostream& os, ApiList value) {
+  os << value.GetName();
+  return os;
+}
+
+inline bool AreValidDexFlags(uint32_t dex_flags) {
+  return ApiList::FromDexFlags(dex_flags).IsValid();
+}
+
+}  // namespace hiddenapi
+}  // namespace art
+
+
+#endif  // ART_LIBARTBASE_BASE_HIDDENAPI_FLAGS_H_
diff --git a/libdexfile/dex/class_accessor-inl.h b/libdexfile/dex/class_accessor-inl.h
index e9e3a98..f0f14c6 100644
--- a/libdexfile/dex/class_accessor-inl.h
+++ b/libdexfile/dex/class_accessor-inl.h
@@ -19,6 +19,7 @@
 
 #include "class_accessor.h"
 
+#include "base/hiddenapi_flags.h"
 #include "base/leb128.h"
 #include "class_iterator.h"
 #include "code_item_accessors-inl.h"
@@ -65,7 +66,7 @@
   code_off_ = DecodeUnsignedLeb128(&ptr_pos_);
   if (hiddenapi_ptr_pos_ != nullptr) {
     hiddenapi_flags_ = DecodeUnsignedLeb128(&hiddenapi_ptr_pos_);
-    DCHECK(hiddenapi::AreValidFlags(hiddenapi_flags_));
+    DCHECK(hiddenapi::AreValidDexFlags(hiddenapi_flags_));
   }
 }
 
@@ -74,7 +75,7 @@
   access_flags_ = DecodeUnsignedLeb128(&ptr_pos_);
   if (hiddenapi_ptr_pos_ != nullptr) {
     hiddenapi_flags_ = DecodeUnsignedLeb128(&hiddenapi_ptr_pos_);
-    DCHECK(hiddenapi::AreValidFlags(hiddenapi_flags_));
+    DCHECK(hiddenapi::AreValidDexFlags(hiddenapi_flags_));
   }
 }
 
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index b3e7ad4..83f47fe 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -29,7 +29,6 @@
 #include "base/value_object.h"
 #include "class_iterator.h"
 #include "dex_file_types.h"
-#include "hidden_api_access_flags.h"
 #include "jni.h"
 #include "modifiers.h"
 
diff --git a/libdexfile/dex/dex_file_layout.cc b/libdexfile/dex/dex_file_layout.cc
index 1e36e05..75a3111 100644
--- a/libdexfile/dex/dex_file_layout.cc
+++ b/libdexfile/dex/dex_file_layout.cc
@@ -18,6 +18,7 @@
 
 #include <sys/mman.h>
 
+#include "base/bit_utils.h"
 #include "dex_file.h"
 
 namespace art {
diff --git a/libdexfile/dex/dex_file_verifier.cc b/libdexfile/dex/dex_file_verifier.cc
index 4d33cd5..78e4618 100644
--- a/libdexfile/dex/dex_file_verifier.cc
+++ b/libdexfile/dex/dex_file_verifier.cc
@@ -1632,7 +1632,7 @@
         failure = true;
         return;
       }
-      if (!hiddenapi::AreValidFlags(decoded_flags)) {
+      if (!hiddenapi::AreValidDexFlags(decoded_flags)) {
         ErrorStringPrintf("Hiddenapi class data flags invalid (%u) for %s %i",
                           decoded_flags, member_type, member.GetIndex());
         failure = true;
diff --git a/libdexfile/dex/hidden_api_access_flags.h b/libdexfile/dex/hidden_api_access_flags.h
deleted file mode 100644
index 77bfbc9..0000000
--- a/libdexfile/dex/hidden_api_access_flags.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2018 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_LIBDEXFILE_DEX_HIDDEN_API_ACCESS_FLAGS_H_
-#define ART_LIBDEXFILE_DEX_HIDDEN_API_ACCESS_FLAGS_H_
-
-#include "base/bit_utils.h"
-#include "base/macros.h"
-#include "dex/modifiers.h"
-
-/* This class is used for encoding and decoding access flags of class members
- * from the boot class path. These access flags might contain additional two bits
- * of information on whether the given class member should be hidden from apps
- * and under what circumstances.
- *
- * Two bits are encoded for each class member in the HiddenapiClassData item,
- * stored in a stream of uleb128-encoded values for each ClassDef item.
- * The two bits correspond to values in the ApiList enum below.
- *
- * At runtime, two bits are set aside in the uint32_t access flags in the
- * intrinsics ordinal space (thus intrinsics need to be special-cased). These are
- * two consecutive bits and they are directly used to store the integer value of
- * the ApiList enum values.
- *
- */
-
-namespace art {
-namespace hiddenapi {
-
-enum class ApiList {
-  kWhitelist = 0,
-  kLightGreylist,
-  kDarkGreylist,
-  kBlacklist,
-  kNoList,
-};
-
-inline bool AreValidFlags(uint32_t flags) {
-  return flags <= static_cast<uint32_t>(ApiList::kBlacklist);
-}
-
-inline std::ostream& operator<<(std::ostream& os, ApiList value) {
-  switch (value) {
-    case ApiList::kWhitelist:
-      os << "whitelist";
-      break;
-    case ApiList::kLightGreylist:
-      os << "light greylist";
-      break;
-    case ApiList::kDarkGreylist:
-      os << "dark greylist";
-      break;
-    case ApiList::kBlacklist:
-      os << "blacklist";
-      break;
-    case ApiList::kNoList:
-      os << "no list";
-      break;
-  }
-  return os;
-}
-
-}  // namespace hiddenapi
-}  // namespace art
-
-
-#endif  // ART_LIBDEXFILE_DEX_HIDDEN_API_ACCESS_FLAGS_H_
diff --git a/libdexfile/dex/test_dex_file_builder.h b/libdexfile/dex/test_dex_file_builder.h
index 2d8a0bb..072aafb 100644
--- a/libdexfile/dex/test_dex_file_builder.h
+++ b/libdexfile/dex/test_dex_file_builder.h
@@ -26,6 +26,7 @@
 
 #include <android-base/logging.h>
 
+#include "base/bit_utils.h"
 #include "dex/dex_file_loader.h"
 #include "dex/standard_dex_file.h"
 
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index e26ec95..51f6008 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -473,8 +473,8 @@
                            GetQuickToInterpreterBridgeOffset);
 #undef DUMP_OAT_HEADER_OFFSET
 
-    os << "IMAGE FILE LOCATION OAT CHECKSUM:\n";
-    os << StringPrintf("0x%08x\n\n", oat_header.GetImageFileLocationOatChecksum());
+    os << "BOOT IMAGE CHECKSUM:\n";
+    os << StringPrintf("0x%08x\n\n", oat_header.GetBootImageChecksum());
 
     // Print the key-value store.
     {
@@ -1771,27 +1771,25 @@
 
     os << "IMAGE LOCATION: " << image_space_.GetImageLocation() << "\n\n";
 
-    os << "IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n\n";
+    os << "IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n";
+    os << "IMAGE SIZE: " << image_header_.GetImageSize() << "\n";
+    os << "IMAGE CHECKSUM: " << std::hex << image_header_.GetImageChecksum() << std::dec << "\n\n";
 
-    os << "IMAGE SIZE: " << image_header_.GetImageSize() << "\n\n";
+    os << "OAT CHECKSUM: " << StringPrintf("0x%08x\n\n", image_header_.GetOatChecksum()) << "\n";
+    os << "OAT FILE BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatFileBegin()) << "\n";
+    os << "OAT DATA BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatDataBegin()) << "\n";
+    os << "OAT DATA END:" << reinterpret_cast<void*>(image_header_.GetOatDataEnd()) << "\n";
+    os << "OAT FILE END:" << reinterpret_cast<void*>(image_header_.GetOatFileEnd()) << "\n\n";
+
+    os << "BOOT IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetBootImageBegin())
+        << "\n";
+    os << "BOOT IMAGE SIZE: " << image_header_.GetBootImageSize() << "\n\n";
 
     for (size_t i = 0; i < ImageHeader::kSectionCount; ++i) {
       auto section = static_cast<ImageHeader::ImageSections>(i);
       os << "IMAGE SECTION " << section << ": " << image_header_.GetImageSection(section) << "\n\n";
     }
 
-    os << "OAT CHECKSUM: " << StringPrintf("0x%08x\n\n", image_header_.GetOatChecksum());
-
-    os << "OAT FILE BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatFileBegin()) << "\n\n";
-
-    os << "OAT DATA BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatDataBegin()) << "\n\n";
-
-    os << "OAT DATA END:" << reinterpret_cast<void*>(image_header_.GetOatDataEnd()) << "\n\n";
-
-    os << "OAT FILE END:" << reinterpret_cast<void*>(image_header_.GetOatFileEnd()) << "\n\n";
-
-    os << "PATCH DELTA:" << image_header_.GetPatchDelta() << "\n\n";
-
     {
       os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots().Ptr()) << "\n";
       static_assert(arraysize(image_roots_descriptions_) ==
diff --git a/profman/profman.cc b/profman/profman.cc
index 734cdf4..2935a05 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -60,6 +60,7 @@
 
 static std::string CommandLine() {
   std::vector<std::string> command;
+  command.reserve(original_argc);
   for (int i = 0; i < original_argc; ++i) {
     command.push_back(original_argv[i]);
   }
diff --git a/runtime/art_field-inl.h b/runtime/art_field-inl.h
index c5fb7d5..53e4c11 100644
--- a/runtime/art_field-inl.h
+++ b/runtime/art_field-inl.h
@@ -399,6 +399,10 @@
   return FindFieldWithOffset<kExactOffset>(klass->GetSFields(), field_offset);
 }
 
+inline mirror::ClassLoader* ArtField::GetClassLoader() {
+  return GetDeclaringClass()->GetClassLoader();
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_ART_FIELD_INL_H_
diff --git a/runtime/art_field.h b/runtime/art_field.h
index 1cf7afa..99f2a1c 100644
--- a/runtime/art_field.h
+++ b/runtime/art_field.h
@@ -20,7 +20,6 @@
 #include <jni.h>
 
 #include "dex/dex_file_types.h"
-#include "dex/hidden_api_access_flags.h"
 #include "dex/modifiers.h"
 #include "dex/primitive.h"
 #include "gc_root.h"
@@ -35,6 +34,7 @@
 
 namespace mirror {
 class Class;
+class ClassLoader;
 class DexCache;
 class Object;
 class String;
@@ -45,6 +45,8 @@
   template<ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
   ObjPtr<mirror::Class> GetDeclaringClass() REQUIRES_SHARED(Locks::mutator_lock_);
 
+  mirror::ClassLoader* GetClassLoader() REQUIRES_SHARED(Locks::mutator_lock_);
+
   void SetDeclaringClass(ObjPtr<mirror::Class> new_declaring_class)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h
index 0c8fe58..e391a15 100644
--- a/runtime/base/mutex.h
+++ b/runtime/base/mutex.h
@@ -73,13 +73,17 @@
   // level lock, it is permitted to acquire a second one - with internal safeguards to ensure that
   // the second lock acquisition does not result in deadlock. This is implemented in the lock
   // order by treating the second acquisition of a kThreadWaitLock as a kThreadWaitWakeLock
-  // acquisition. Thus, acquiring kThreadWaitWakeLock requires holding kThreadWaitLock.
+  // acquisition. Thus, acquiring kThreadWaitWakeLock requires holding kThreadWaitLock. This entry
+  // is here near the bottom of the hierarchy because other locks should not be
+  // acquired while it is held. kThreadWaitLock cannot be moved here because GC
+  // activity acquires locks while holding the wait lock.
   kThreadWaitWakeLock,
-  kThreadWaitLock,
   kJdwpAdbStateLock,
   kJdwpSocketLock,
   kRegionSpaceRegionLock,
   kMarkSweepMarkStackLock,
+  // Can be held while GC related work is done, and thus must be above kMarkSweepMarkStackLock
+  kThreadWaitLock,
   kCHALock,
   kJitCodeCacheLock,
   kRosAllocGlobalLock,
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 7e01137..6703205 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -135,7 +135,7 @@
   ObjPtr<mirror::Class> resolved_type =
       referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedType(type_idx);
   if (UNLIKELY(resolved_type == nullptr)) {
-    resolved_type = DoResolveType(type_idx, referrer->GetDeclaringClass());
+    resolved_type = DoResolveType(type_idx, referrer);
   }
   return resolved_type;
 }
@@ -149,7 +149,7 @@
   ObjPtr<mirror::Class> resolved_type =
       referrer->GetDexCache<kWithoutReadBarrier>()->GetResolvedType(type_idx);
   if (UNLIKELY(resolved_type == nullptr)) {
-    resolved_type = DoResolveType(type_idx, referrer->GetDeclaringClass());
+    resolved_type = DoResolveType(type_idx, referrer);
   }
   return resolved_type;
 }
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index ce7dfaf..545754f 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -798,6 +798,8 @@
                FindSystemClass(self, "Ljava/lang/StackTraceElement;"));
   SetClassRoot(ClassRoot::kJavaLangStackTraceElementArrayClass,
                FindSystemClass(self, "[Ljava/lang/StackTraceElement;"));
+  SetClassRoot(ClassRoot::kJavaLangClassLoaderArrayClass,
+               FindSystemClass(self, "[Ljava/lang/ClassLoader;"));
 
   // Create conflict tables that depend on the class linker.
   runtime->FixupConflictTables();
@@ -1092,51 +1094,165 @@
   return false;
 }
 
-static bool FlattenPathClassLoader(ObjPtr<mirror::ClassLoader> class_loader,
-                                   std::list<ObjPtr<mirror::String>>* out_dex_file_names,
-                                   std::string* error_msg)
+static bool GetDexFileNames(ScopedObjectAccessUnchecked& soa,
+                            ObjPtr<mirror::ClassLoader> class_loader,
+                            /*out*/std::list<ObjPtr<mirror::String>>* dex_files,
+                            /*out*/std::string* error_msg)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  DCHECK(out_dex_file_names != nullptr);
-  DCHECK(error_msg != nullptr);
-  ScopedObjectAccessUnchecked soa(Thread::Current());
   StackHandleScope<1> hs(soa.Self());
   Handle<mirror::ClassLoader> handle(hs.NewHandle(class_loader));
-  while (!ClassLinker::IsBootClassLoader(soa, class_loader)) {
-    if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) !=
-        class_loader->GetClass()) {
-      *error_msg = StringPrintf("Unknown class loader type %s",
-                                class_loader->PrettyTypeOf().c_str());
-      // Unsupported class loader.
+  // Get element names. Sets error to true on failure.
+  auto add_element_names = [&](ObjPtr<mirror::Object> element, bool* error)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (element == nullptr) {
+      *error_msg = "Null dex element";
+      *error = true;  // Null element is a critical error.
+      return false;   // Had an error, stop the visit.
+    }
+    ObjPtr<mirror::String> name;
+    if (!GetDexPathListElementName(element, &name)) {
+      *error_msg = "Invalid dex path list element";
+      *error = true;   // Invalid element, make it a critical error.
+      return false;    // Stop the visit.
+    }
+    if (name != nullptr) {
+      dex_files->push_front(name);
+    }
+    return true;  // Continue with the next Element.
+  };
+  bool error = VisitClassLoaderDexElements(soa,
+                                           handle,
+                                           add_element_names,
+                                           /*defaultReturn=*/ false);
+  return !error;
+}
+
+static bool CompareClassLoaderTypes(ScopedObjectAccessUnchecked& soa,
+                                    ObjPtr<mirror::ClassLoader> image_class_loader,
+                                    ObjPtr<mirror::ClassLoader> class_loader,
+                                    std::string* error_msg)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (ClassLinker::IsBootClassLoader(soa, class_loader)) {
+    if (!ClassLinker::IsBootClassLoader(soa, image_class_loader)) {
+      *error_msg = "Hierarchies don't match";
       return false;
     }
-    // Get element names. Sets error to true on failure.
-    auto add_element_names = [&](ObjPtr<mirror::Object> element, bool* error)
-        REQUIRES_SHARED(Locks::mutator_lock_) {
-      if (element == nullptr) {
-        *error_msg = "Null dex element";
-        *error = true;  // Null element is a critical error.
-        return false;   // Had an error, stop the visit.
-      }
-      ObjPtr<mirror::String> name;
-      if (!GetDexPathListElementName(element, &name)) {
-        *error_msg = "Invalid dex path list element";
-        *error = false;  // Invalid element is not a critical error.
-        return false;    // Stop the visit.
-      }
-      if (name != nullptr) {
-        out_dex_file_names->push_front(name);
-      }
-      return true;  // Continue with the next Element.
-    };
-    bool error = VisitClassLoaderDexElements(soa,
-                                             handle,
-                                             add_element_names,
-                                             /* defaultReturn= */ false);
-    if (error) {
-      // An error occurred during DexPathList Element visiting.
+  } else if (ClassLinker::IsBootClassLoader(soa, image_class_loader)) {
+    *error_msg = "Hierarchies don't match";
+    return false;
+  } else if (class_loader->GetClass() != image_class_loader->GetClass()) {
+    *error_msg = StringPrintf("Class loader types don't match %s and %s",
+                              image_class_loader->PrettyTypeOf().c_str(),
+                              class_loader->PrettyTypeOf().c_str());
+    return false;
+  } else if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) !=
+      class_loader->GetClass()) {
+    *error_msg = StringPrintf("Unknown class loader type %s",
+                              class_loader->PrettyTypeOf().c_str());
+    // Unsupported class loader.
+    return false;
+  }
+  return true;
+}
+
+static bool CompareDexFiles(const std::list<ObjPtr<mirror::String>>& image_dex_files,
+                            const std::list<ObjPtr<mirror::String>>& loader_dex_files,
+                            std::string* error_msg)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  bool equal = (image_dex_files.size() == loader_dex_files.size()) &&
+      std::equal(image_dex_files.begin(),
+                 image_dex_files.end(),
+                 loader_dex_files.begin(),
+                 [](ObjPtr<mirror::String> lhs, ObjPtr<mirror::String> rhs)
+                     REQUIRES_SHARED(Locks::mutator_lock_) {
+                   return lhs->Equals(rhs);
+                 });
+  if (!equal) {
+    VLOG(image) << "Image dex files " << image_dex_files.size();
+    for (ObjPtr<mirror::String> name : image_dex_files) {
+      VLOG(image) << name->ToModifiedUtf8();
+    }
+    VLOG(image) << "Loader dex files " << loader_dex_files.size();
+    for (ObjPtr<mirror::String> name : loader_dex_files) {
+      VLOG(image) << name->ToModifiedUtf8();
+    }
+    *error_msg = "Mismatch in dex files";
+  }
+  return equal;
+}
+
+static bool CompareClassLoaders(ScopedObjectAccessUnchecked& soa,
+                                ObjPtr<mirror::ClassLoader> image_class_loader,
+                                ObjPtr<mirror::ClassLoader> class_loader,
+                                bool check_dex_file_names,
+                                std::string* error_msg)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (!CompareClassLoaderTypes(soa, image_class_loader, class_loader, error_msg)) {
+    return false;
+  }
+
+  if (ClassLinker::IsBootClassLoader(soa, class_loader)) {
+    // No need to check further.
+    return true;
+  }
+
+  if (check_dex_file_names) {
+    std::list<ObjPtr<mirror::String>> image_dex_files;
+    if (!GetDexFileNames(soa, image_class_loader, &image_dex_files, error_msg)) {
       return false;
     }
-    class_loader = class_loader->GetParent();
+
+    std::list<ObjPtr<mirror::String>> loader_dex_files;
+    if (!GetDexFileNames(soa, class_loader, &loader_dex_files, error_msg)) {
+      return false;
+    }
+
+    if (!CompareDexFiles(image_dex_files, loader_dex_files, error_msg)) {
+      return false;
+    }
+  }
+
+  ArtField* field =
+      jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+  ObjPtr<mirror::Object> shared_libraries_image_loader = field->GetObject(image_class_loader.Ptr());
+  ObjPtr<mirror::Object> shared_libraries_loader = field->GetObject(class_loader.Ptr());
+  if (shared_libraries_image_loader == nullptr) {
+    if (shared_libraries_loader != nullptr) {
+      *error_msg = "Mismatch in shared libraries";
+      return false;
+    }
+  } else if (shared_libraries_loader == nullptr) {
+    *error_msg = "Mismatch in shared libraries";
+    return false;
+  } else {
+    ObjPtr<mirror::ObjectArray<mirror::ClassLoader>> array1 =
+        shared_libraries_image_loader->AsObjectArray<mirror::ClassLoader>();
+    ObjPtr<mirror::ObjectArray<mirror::ClassLoader>> array2 =
+        shared_libraries_loader->AsObjectArray<mirror::ClassLoader>();
+    if (array1->GetLength() != array2->GetLength()) {
+      *error_msg = "Mismatch in number of shared libraries";
+      return false;
+    }
+
+    for (int32_t i = 0; i < array1->GetLength(); ++i) {
+      // Do a full comparison of the class loaders, including comparing their dex files.
+      if (!CompareClassLoaders(soa,
+                               array1->Get(i),
+                               array2->Get(i),
+                               /*check_dex_file_names=*/ true,
+                               error_msg)) {
+        return false;
+      }
+    }
+  }
+
+  // Do a full comparison of the class loaders, including comparing their dex files.
+  if (!CompareClassLoaders(soa,
+                           image_class_loader->GetParent(),
+                           class_loader->GetParent(),
+                           /*check_dex_file_names=*/ true,
+                           error_msg)) {
+    return false;
   }
   return true;
 }
@@ -1905,6 +2021,7 @@
 
   if (app_image) {
     ScopedObjectAccessUnchecked soa(Thread::Current());
+    ScopedAssertNoThreadSuspension sants("Checking app image", soa.Self());
     // Check that the class loader resolves the same way as the ones in the image.
     // Image class loader [A][B][C][image dex files]
     // Class loader = [???][dex_elements][image dex files]
@@ -1917,21 +2034,12 @@
       *error_msg = "Unexpected BootClassLoader in app image";
       return false;
     }
-    std::list<ObjPtr<mirror::String>> image_dex_file_names;
-    std::string temp_error_msg;
-    if (!FlattenPathClassLoader(image_class_loader.Get(), &image_dex_file_names, &temp_error_msg)) {
-      *error_msg = StringPrintf("Failed to flatten image class loader hierarchy '%s'",
-                                temp_error_msg.c_str());
-      return false;
-    }
-    std::list<ObjPtr<mirror::String>> loader_dex_file_names;
-    if (!FlattenPathClassLoader(class_loader.Get(), &loader_dex_file_names, &temp_error_msg)) {
-      *error_msg = StringPrintf("Failed to flatten class loader hierarchy '%s'",
-                                temp_error_msg.c_str());
-      return false;
-    }
-    // Add the temporary dex path list elements at the end.
+    // The dex files of `class_loader` are not setup yet, so we cannot do a full comparison
+    // of `class_loader` and `image_class_loader` in `CompareClassLoaders`. Therefore, we
+    // special case the comparison of dex files of the two class loaders, but then do full
+    // comparisons for their shared libraries and parent.
     auto elements = soa.Decode<mirror::ObjectArray<mirror::Object>>(dex_elements);
+    std::list<ObjPtr<mirror::String>> loader_dex_file_names;
     for (size_t i = 0, num_elems = elements->GetLength(); i < num_elems; ++i) {
       ObjPtr<mirror::Object> element = elements->GetWithoutChecks(i);
       if (element != nullptr) {
@@ -1942,31 +2050,29 @@
         }
       }
     }
-    // Ignore the number of image dex files since we are adding those to the class loader anyways.
-    CHECK_GE(static_cast<size_t>(image_dex_file_names.size()),
-             static_cast<size_t>(dex_caches->GetLength()));
-    size_t image_count = image_dex_file_names.size() - dex_caches->GetLength();
-    // Check that the dex file names match.
-    bool equal = image_count == loader_dex_file_names.size();
-    if (equal) {
-      auto it1 = image_dex_file_names.begin();
-      auto it2 = loader_dex_file_names.begin();
-      for (size_t i = 0; equal && i < image_count; ++i, ++it1, ++it2) {
-        equal = equal && (*it1)->Equals(*it2);
-      }
+    std::string temp_error_msg;
+    std::list<ObjPtr<mirror::String>> image_dex_file_names;
+    bool success = GetDexFileNames(
+        soa, image_class_loader.Get(), &image_dex_file_names, &temp_error_msg);
+    if (success) {
+      // Ignore the number of image dex files since we are adding those to the class loader anyways.
+      CHECK_GE(static_cast<size_t>(image_dex_file_names.size()),
+               static_cast<size_t>(dex_caches->GetLength()));
+      size_t image_count = image_dex_file_names.size() - dex_caches->GetLength();
+      image_dex_file_names.resize(image_count);
+      success = success && CompareDexFiles(image_dex_file_names,
+                                           loader_dex_file_names,
+                                           &temp_error_msg);
+      success = success && CompareClassLoaders(soa,
+                                               image_class_loader.Get(),
+                                               class_loader.Get(),
+                                               /*check_dex_file_names=*/ false,
+                                               &temp_error_msg);
     }
-    if (!equal) {
-      VLOG(image) << "Image dex files " << image_dex_file_names.size();
-      for (ObjPtr<mirror::String> name : image_dex_file_names) {
-        VLOG(image) << name->ToModifiedUtf8();
-      }
-      VLOG(image) << "Loader dex files " << loader_dex_file_names.size();
-      for (ObjPtr<mirror::String> name : loader_dex_file_names) {
-        VLOG(image) << name->ToModifiedUtf8();
-      }
-      *error_msg = "Rejecting application image due to class loader mismatch";
-      // Ignore class loader mismatch for now since these would just use possibly incorrect
-      // oat code anyways. The structural class check should be done in the parent.
+    if (!success) {
+      *error_msg = StringPrintf("Rejecting application image due to class loader mismatch: '%s'",
+                               temp_error_msg.c_str());
+      return false;
     }
   }
 
@@ -8074,14 +8180,22 @@
   return type;
 }
 
-ObjPtr<mirror::Class> ClassLinker::DoResolveType(dex::TypeIndex type_idx,
-                                                 ObjPtr<mirror::Class> referrer) {
+template <typename T>
+ObjPtr<mirror::Class> ClassLinker::DoResolveType(dex::TypeIndex type_idx, T referrer) {
   StackHandleScope<2> hs(Thread::Current());
   Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
   Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
   return DoResolveType(type_idx, dex_cache, class_loader);
 }
 
+// Instantiate the above.
+template ObjPtr<mirror::Class> ClassLinker::DoResolveType(dex::TypeIndex type_idx,
+                                                          ArtField* referrer);
+template ObjPtr<mirror::Class> ClassLinker::DoResolveType(dex::TypeIndex type_idx,
+                                                          ArtMethod* referrer);
+template ObjPtr<mirror::Class> ClassLinker::DoResolveType(dex::TypeIndex type_idx,
+                                                          ObjPtr<mirror::Class> referrer);
+
 ObjPtr<mirror::Class> ClassLinker::DoResolveType(dex::TypeIndex type_idx,
                                                  Handle<mirror::DexCache> dex_cache,
                                                  Handle<mirror::ClassLoader> class_loader) {
@@ -8994,21 +9108,14 @@
   CheckSystemClass(self, primitive_array_class, descriptor);
 }
 
-jobject ClassLinker::CreateWellKnownClassLoader(Thread* self,
-                                                const std::vector<const DexFile*>& dex_files,
-                                                jclass loader_class,
-                                                jobject parent_loader) {
-  CHECK(self->GetJniEnv()->IsSameObject(loader_class,
-                                        WellKnownClasses::dalvik_system_PathClassLoader) ||
-        self->GetJniEnv()->IsSameObject(loader_class,
-                                        WellKnownClasses::dalvik_system_DelegateLastClassLoader));
+ObjPtr<mirror::ClassLoader> ClassLinker::CreateWellKnownClassLoader(
+    Thread* self,
+    const std::vector<const DexFile*>& dex_files,
+    Handle<mirror::Class> loader_class,
+    Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries,
+    Handle<mirror::ClassLoader> parent_loader) {
 
-  // SOAAlreadyRunnable is protected, and we need something to add a global reference.
-  // We could move the jobject to the callers, but all call-sites do this...
-  ScopedObjectAccessUnchecked soa(self);
-
-  // For now, create a libcore-level DexFile for each ART DexFile. This "explodes" multidex.
-  StackHandleScope<6> hs(self);
+  StackHandleScope<5> hs(self);
 
   ArtField* dex_elements_field =
       jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements);
@@ -9098,8 +9205,8 @@
   }
 
   // Create the class loader..
-  Handle<mirror::Class> h_loader_class = hs.NewHandle(soa.Decode<mirror::Class>(loader_class));
-  Handle<mirror::Object> h_class_loader = hs.NewHandle(h_loader_class->AllocObject(self));
+  Handle<mirror::ClassLoader> h_class_loader = hs.NewHandle<mirror::ClassLoader>(
+      ObjPtr<mirror::ClassLoader>::DownCast(loader_class->AllocObject(self)));
   DCHECK(h_class_loader != nullptr);
   // Set DexPathList.
   ArtField* path_list_field =
@@ -9115,15 +9222,59 @@
                                "parent",
                                "Ljava/lang/ClassLoader;");
   DCHECK(parent_field != nullptr);
+  if (parent_loader.Get() == nullptr) {
+    ScopedObjectAccessUnchecked soa(self);
+    ObjPtr<mirror::Object> boot_loader(soa.Decode<mirror::Class>(
+        WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self));
+    parent_field->SetObject<false>(h_class_loader.Get(), boot_loader);
+  } else {
+    parent_field->SetObject<false>(h_class_loader.Get(), parent_loader.Get());
+  }
 
-  ObjPtr<mirror::Object> parent = (parent_loader != nullptr)
-      ? soa.Decode<mirror::ClassLoader>(parent_loader)
-      : soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader)->AllocObject(self);
-  parent_field->SetObject<false>(h_class_loader.Get(), parent);
+  ArtField* shared_libraries_field =
+      jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+  DCHECK(shared_libraries_field != nullptr);
+  shared_libraries_field->SetObject<false>(h_class_loader.Get(), shared_libraries.Get());
+
+  return h_class_loader.Get();
+}
+
+jobject ClassLinker::CreateWellKnownClassLoader(Thread* self,
+                                                const std::vector<const DexFile*>& dex_files,
+                                                jclass loader_class,
+                                                jobject parent_loader) {
+  CHECK(self->GetJniEnv()->IsSameObject(loader_class,
+                                        WellKnownClasses::dalvik_system_PathClassLoader) ||
+        self->GetJniEnv()->IsSameObject(loader_class,
+                                        WellKnownClasses::dalvik_system_DelegateLastClassLoader));
+
+  // SOAAlreadyRunnable is protected, and we need something to add a global reference.
+  // We could move the jobject to the callers, but all call-sites do this...
+  ScopedObjectAccessUnchecked soa(self);
+
+  // For now, create a libcore-level DexFile for each ART DexFile. This "explodes" multidex.
+  StackHandleScope<3> hs(self);
+
+  Handle<mirror::Class> h_loader_class =
+      hs.NewHandle<mirror::Class>(soa.Decode<mirror::Class>(loader_class));
+  Handle<mirror::ClassLoader> parent =
+      hs.NewHandle<mirror::ClassLoader>(ObjPtr<mirror::ClassLoader>::DownCast(
+          (parent_loader != nullptr)
+              ? soa.Decode<mirror::ClassLoader>(parent_loader)
+              : nullptr));
+  Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries =
+      hs.NewHandle<mirror::ObjectArray<mirror::ClassLoader>>(nullptr);
+
+  ObjPtr<mirror::ClassLoader> loader = CreateWellKnownClassLoader(
+      self,
+      dex_files,
+      h_loader_class,
+      shared_libraries,
+      parent);
 
   // Make it a global ref and return.
   ScopedLocalRef<jobject> local_ref(
-      soa.Env(), soa.Env()->AddLocalReference<jobject>(h_class_loader.Get()));
+      soa.Env(), soa.Env()->AddLocalReference<jobject>(loader));
   return soa.Env()->NewGlobalRef(local_ref.get());
 }
 
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index a65299a..15a7204 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -586,6 +586,16 @@
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_);
 
+  // Non-GlobalRef version of CreateWellKnownClassLoader
+  ObjPtr<mirror::ClassLoader> CreateWellKnownClassLoader(
+      Thread* self,
+      const std::vector<const DexFile*>& dex_files,
+      Handle<mirror::Class> loader_class,
+      Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries,
+      Handle<mirror::ClassLoader> parent_loader)
+          REQUIRES_SHARED(Locks::mutator_lock_)
+          REQUIRES(!Locks::dex_lock_);
+
   PointerSize GetImagePointerSize() const {
     return image_pointer_size_;
   }
@@ -921,8 +931,8 @@
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Implementation of ResolveType() called when the type was not found in the dex cache.
-  ObjPtr<mirror::Class> DoResolveType(dex::TypeIndex type_idx,
-                                      ObjPtr<mirror::Class> referrer)
+  template <typename T>
+  ObjPtr<mirror::Class> DoResolveType(dex::TypeIndex type_idx, T referrer)
       REQUIRES_SHARED(Locks::mutator_lock_)
       REQUIRES(!Locks::dex_lock_, !Roles::uninterruptible_);
   ObjPtr<mirror::Class> DoResolveType(dex::TypeIndex type_idx,
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index 0bae60a..029db7e 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -17,6 +17,7 @@
 #include "class_loader_context.h"
 
 #include <android-base/parseint.h>
+#include <android-base/strings.h>
 
 #include "art_field-inl.h"
 #include "base/casts.h"
@@ -24,11 +25,14 @@
 #include "base/stl_util.h"
 #include "class_linker.h"
 #include "class_loader_utils.h"
+#include "class_root.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/dex_file.h"
 #include "dex/dex_file_loader.h"
 #include "handle_scope-inl.h"
 #include "jni/jni_internal.h"
+#include "mirror/object_array-alloc-inl.h"
+#include "nativehelper/scoped_local_ref.h"
 #include "oat_file_assistant.h"
 #include "obj_ptr-inl.h"
 #include "runtime.h"
@@ -107,6 +111,39 @@
   }
 }
 
+static size_t FindMatchingSharedLibraryCloseMarker(const std::string& spec,
+                                                   size_t shared_library_open_index) {
+  // Counter of opened shared library marker we've encountered so far.
+  uint32_t counter = 1;
+  // The index at which we're operating in the loop.
+  uint32_t string_index = shared_library_open_index + 1;
+  size_t shared_library_close = std::string::npos;
+  while (counter != 0) {
+    shared_library_close =
+        spec.find_first_of(kClassLoaderSharedLibraryClosingMark, string_index);
+    size_t shared_library_open =
+        spec.find_first_of(kClassLoaderSharedLibraryOpeningMark, string_index);
+    if (shared_library_close == std::string::npos) {
+      // No matching closing marker. Return an error.
+      break;
+    }
+
+    if ((shared_library_open == std::string::npos) ||
+        (shared_library_close < shared_library_open)) {
+      // We have seen a closing marker. Decrement the counter.
+      --counter;
+      // Move the search index forward.
+      string_index = shared_library_close + 1;
+    } else {
+      // New nested opening marker. Increment the counter and move the search
+      // index after the marker.
+      ++counter;
+      string_index = shared_library_open + 1;
+    }
+  }
+  return shared_library_close;
+}
+
 // The expected format is:
 // "ClassLoaderType1[ClasspathElem1*Checksum1:ClasspathElem2*Checksum2...]{ClassLoaderType2[...]}".
 // The checksum part of the format is expected only if parse_cheksums is true.
@@ -160,7 +197,9 @@
     }
   }
 
-  if (class_loader_spec[class_loader_spec.length() - 1] == kClassLoaderSharedLibraryClosingMark) {
+  if ((class_loader_spec[class_loader_spec.length() - 1] == kClassLoaderSharedLibraryClosingMark) &&
+      (class_loader_spec[class_loader_spec.length() - 2] != kClassLoaderSharedLibraryOpeningMark)) {
+    // Non-empty list of shared libraries.
     size_t start_index = class_loader_spec.find_first_of(kClassLoaderSharedLibraryOpeningMark);
     if (start_index == std::string::npos) {
       return nullptr;
@@ -168,8 +207,43 @@
     std::string shared_libraries_spec =
         class_loader_spec.substr(start_index + 1, class_loader_spec.length() - start_index - 2);
     std::vector<std::string> shared_libraries;
-    Split(shared_libraries_spec, kClassLoaderSharedLibrarySeparator, &shared_libraries);
-    for (const std::string& shared_library_spec : shared_libraries) {
+    size_t cursor = 0;
+    while (cursor != shared_libraries_spec.length()) {
+      size_t shared_library_separator =
+          shared_libraries_spec.find_first_of(kClassLoaderSharedLibrarySeparator, cursor);
+      size_t shared_library_open =
+          shared_libraries_spec.find_first_of(kClassLoaderSharedLibraryOpeningMark, cursor);
+      std::string shared_library_spec;
+      if (shared_library_separator == std::string::npos) {
+        // Only one shared library, for example:
+        // PCL[...]
+        shared_library_spec =
+            shared_libraries_spec.substr(cursor, shared_libraries_spec.length() - cursor);
+        cursor = shared_libraries_spec.length();
+      } else if ((shared_library_open == std::string::npos) ||
+                 (shared_library_open > shared_library_separator)) {
+        // We found a shared library without nested shared libraries, for example:
+        // PCL[...]#PCL[...]{...}
+        shared_library_spec =
+            shared_libraries_spec.substr(cursor, shared_library_separator - cursor);
+        cursor = shared_library_separator + 1;
+      } else {
+        // The shared library contains nested shared libraries. Find the matching closing shared
+        // marker for it.
+        size_t closing_marker =
+            FindMatchingSharedLibraryCloseMarker(shared_libraries_spec, shared_library_open);
+        if (closing_marker == std::string::npos) {
+          // No matching closing marker, return an error.
+          return nullptr;
+        }
+        shared_library_spec = shared_libraries_spec.substr(cursor, closing_marker + 1 - cursor);
+        cursor = closing_marker + 1;
+        if (cursor != shared_libraries_spec.length() &&
+            shared_libraries_spec[cursor] == kClassLoaderSharedLibrarySeparator) {
+          // Pass the shared library separator marker.
+          ++cursor;
+        }
+      }
       std::unique_ptr<ClassLoaderInfo> shared_library(
           ParseInternal(shared_library_spec, parse_checksums));
       if (shared_library == nullptr) {
@@ -250,50 +324,24 @@
       // The class loader spec contains shared libraries. Find the matching closing
       // shared library marker for it.
 
-      // Counter of opened shared library marker we've encountered so far.
-      uint32_t counter = 1;
-      // The index at which we're operating in the loop.
-      uint32_t string_index = first_shared_library_open + 1;
-      while (counter != 0) {
-        size_t shared_library_close =
-            remaining.find_first_of(kClassLoaderSharedLibraryClosingMark, string_index);
-        size_t shared_library_open =
-            remaining.find_first_of(kClassLoaderSharedLibraryOpeningMark, string_index);
-        if (shared_library_close == std::string::npos) {
-          // No matching closing market. Return an error.
-          LOG(ERROR) << "Invalid class loader spec: " << class_loader_spec;
-          return nullptr;
-        }
+      uint32_t shared_library_close =
+          FindMatchingSharedLibraryCloseMarker(remaining, first_shared_library_open);
+      if (shared_library_close == std::string::npos) {
+        LOG(ERROR) << "Invalid class loader spec: " << class_loader_spec;
+        return nullptr;
+      }
+      class_loader_spec = remaining.substr(0, shared_library_close + 1);
 
-        if ((shared_library_open == std::string::npos) ||
-            (shared_library_close < shared_library_open)) {
-          // We have seen a closing marker. Decrement the counter.
-          --counter;
-          if (counter == 0) {
-            // Found the matching closing marker.
-            class_loader_spec = remaining.substr(0, shared_library_close + 1);
-
-            // Compute the remaining string to analyze.
-            if (remaining.size() == shared_library_close + 1) {
-              remaining = "";
-            } else if ((remaining.size() == shared_library_close + 2) ||
-                       (remaining.at(shared_library_close + 1) != kClassLoaderSeparator)) {
-              LOG(ERROR) << "Invalid class loader spec: " << class_loader_spec;
-              return nullptr;
-            } else {
-              remaining = remaining.substr(shared_library_close + 2,
-                                           remaining.size() - shared_library_close - 2);
-            }
-          } else {
-            // Move the search index forward.
-            string_index = shared_library_close + 1;
-          }
-        } else {
-          // New nested opening marker. Increment the counter and move the search
-          // index after the marker.
-          ++counter;
-          string_index = shared_library_open + 1;
-        }
+      // Compute the remaining string to analyze.
+      if (remaining.size() == shared_library_close + 1) {
+        remaining = "";
+      } else if ((remaining.size() == shared_library_close + 2) ||
+                 (remaining.at(shared_library_close + 1) != kClassLoaderSeparator)) {
+        LOG(ERROR) << "Invalid class loader spec: " << class_loader_spec;
+        return nullptr;
+      } else {
+        remaining = remaining.substr(shared_library_close + 2,
+                                     remaining.size() - shared_library_close - 2);
       }
     }
 
@@ -304,11 +352,11 @@
       return nullptr;
     }
     if (first == nullptr) {
-      first.reset(info.release());
+      first = std::move(info);
       previous_iteration = first.get();
     } else {
       CHECK(previous_iteration != nullptr);
-      previous_iteration->parent.reset(info.release());
+      previous_iteration->parent = std::move(info);
       previous_iteration = previous_iteration->parent.get();
     }
   }
@@ -571,21 +619,89 @@
   UNREACHABLE();
 }
 
-static jobject CreateClassLoaderInternal(Thread* self,
-                                         const ClassLoaderContext::ClassLoaderInfo& info)
+static std::string FlattenClasspath(const std::vector<std::string>& classpath) {
+  return android::base::Join(classpath, ':');
+}
+
+static ObjPtr<mirror::ClassLoader> CreateClassLoaderInternal(
+    Thread* self,
+    ScopedObjectAccess& soa,
+    const ClassLoaderContext::ClassLoaderInfo& info,
+    bool for_shared_library,
+    VariableSizedHandleScope& map_scope,
+    std::map<std::string, Handle<mirror::ClassLoader>>& canonicalized_libraries,
+    bool add_compilation_sources,
+    const std::vector<const DexFile*>& compilation_sources)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-  CHECK(info.shared_libraries.empty()) << "Class loader shared library not implemented yet";
-  jobject parent = nullptr;
+  if (for_shared_library) {
+    // Check if the shared library has already been created.
+    auto search = canonicalized_libraries.find(FlattenClasspath(info.classpath));
+    if (search != canonicalized_libraries.end()) {
+      return search->second.Get();
+    }
+  }
+
+  StackHandleScope<3> hs(self);
+  MutableHandle<mirror::ObjectArray<mirror::ClassLoader>> libraries(
+      hs.NewHandle<mirror::ObjectArray<mirror::ClassLoader>>(nullptr));
+
+  if (!info.shared_libraries.empty()) {
+    libraries.Assign(mirror::ObjectArray<mirror::ClassLoader>::Alloc(
+        self,
+        GetClassRoot<mirror::ObjectArray<mirror::ClassLoader>>(),
+        info.shared_libraries.size()));
+    for (uint32_t i = 0; i < info.shared_libraries.size(); ++i) {
+      // We should only add the compilation sources to the first class loader.
+      libraries->Set(i,
+                     CreateClassLoaderInternal(
+                         self,
+                         soa,
+                         *info.shared_libraries[i].get(),
+                         /* for_shared_library= */ true,
+                         map_scope,
+                         canonicalized_libraries,
+                         /* add_compilation_sources= */ false,
+                         compilation_sources));
+    }
+  }
+
+  MutableHandle<mirror::ClassLoader> parent = hs.NewHandle<mirror::ClassLoader>(nullptr);
   if (info.parent != nullptr) {
-    parent = CreateClassLoaderInternal(self, *info.parent.get());
+    // We should only add the compilation sources to the first class loader.
+    parent.Assign(CreateClassLoaderInternal(
+        self,
+        soa,
+        *info.parent.get(),
+        /* for_shared_library= */ false,
+        map_scope,
+        canonicalized_libraries,
+        /* add_compilation_sources= */ false,
+        compilation_sources));
   }
   std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(
       info.opened_dex_files);
-  return Runtime::Current()->GetClassLinker()->CreateWellKnownClassLoader(
-      self,
-      class_path_files,
-      GetClassLoaderClass(info.type),
-      parent);
+  if (add_compilation_sources) {
+    // For 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.
+    class_path_files.insert(class_path_files.end(),
+                            compilation_sources.begin(),
+                            compilation_sources.end());
+  }
+  Handle<mirror::Class> loader_class = hs.NewHandle<mirror::Class>(
+      soa.Decode<mirror::Class>(GetClassLoaderClass(info.type)));
+  ObjPtr<mirror::ClassLoader> loader =
+      Runtime::Current()->GetClassLinker()->CreateWellKnownClassLoader(
+          self,
+          class_path_files,
+          loader_class,
+          libraries,
+          parent);
+  if (for_shared_library) {
+    canonicalized_libraries[FlattenClasspath(info.classpath)] =
+        map_scope.NewHandle<mirror::ClassLoader>(loader);
+  }
+  return loader;
 }
 
 jobject ClassLoaderContext::CreateClassLoader(
@@ -598,30 +714,29 @@
   ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
 
   if (class_loader_chain_ == nullptr) {
+    CHECK(special_shared_library_);
     return class_linker->CreatePathClassLoader(self, compilation_sources);
   }
 
-  // Create the class loader of the parent.
-  jobject parent = nullptr;
-  if (class_loader_chain_->parent != nullptr) {
-    parent = CreateClassLoaderInternal(self, *class_loader_chain_->parent.get());
-  }
+  // Create a map of canonicalized shared libraries. As we're holding objects,
+  // we're creating a variable size handle scope to put handles in the map.
+  VariableSizedHandleScope map_scope(self);
+  std::map<std::string, Handle<mirror::ClassLoader>> canonicalized_libraries;
 
-  // 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_->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_->type),
-      parent);
+  // Create the class loader.
+  ObjPtr<mirror::ClassLoader> loader =
+      CreateClassLoaderInternal(self,
+                                soa,
+                                *class_loader_chain_.get(),
+                                /* for_shared_library= */ false,
+                                map_scope,
+                                canonicalized_libraries,
+                                /* add_compilation_sources= */ true,
+                                compilation_sources);
+  // Make it a global ref and return.
+  ScopedLocalRef<jobject> local_ref(
+      soa.Env(), soa.Env()->AddLocalReference<jobject>(loader));
+  return soa.Env()->NewGlobalRef(local_ref.get());
 }
 
 std::vector<const DexFile*> ClassLoaderContext::FlattenOpenedDexFiles() const {
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
index 37cef81..71f40ac 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -89,7 +89,10 @@
   // If the context is empty, this method only creates a single PathClassLoader with the
   // given compilation_sources.
   //
-  // Notes:
+  // Shared libraries found in the chain will be canonicalized based on the dex files they
+  // contain.
+  //
+  // Implementation 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;
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index f3e2ac0..0756982 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -19,12 +19,14 @@
 #include <gtest/gtest.h>
 
 #include "android-base/strings.h"
+#include "art_field-inl.h"
 #include "base/dchecked_vector.h"
 #include "base/stl_util.h"
 #include "class_linker.h"
 #include "common_runtime_test.h"
 #include "dex/dex_file.h"
 #include "handle_scope-inl.h"
+#include "jni/jni_internal.h"
 #include "mirror/class.h"
 #include "mirror/class_loader.h"
 #include "mirror/object-inl.h"
@@ -284,6 +286,25 @@
   VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "s1.dex");
 }
 
+TEST_F(ClassLoaderContextTest, ParseComplexSharedLibraries1) {
+  std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(
+      "PCL[]{PCL[s4.dex]{PCL[s5.dex]{PCL[s6.dex]}#PCL[s6.dex]}}");
+  VerifyContextSize(context.get(), 1);
+  VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "s4.dex");
+}
+
+TEST_F(ClassLoaderContextTest, ParseComplexSharedLibraries2) {
+  std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(
+      "PCL[]{PCL[s1.dex]{PCL[s2.dex]}#PCL[s2.dex]#"
+      "PCL[s3.dex]#PCL[s4.dex]{PCL[s5.dex]{PCL[s6.dex]}#PCL[s6.dex]}#PCL[s5.dex]{PCL[s6.dex]}}");
+  VerifyContextSize(context.get(), 1);
+  VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "s1.dex");
+  VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 1, "s2.dex");
+  VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 2, "s3.dex");
+  VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 3, "s4.dex");
+  VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 4, "s5.dex");
+}
+
 TEST_F(ClassLoaderContextTest, ParseValidEmptyContextDLC) {
   std::unique_ptr<ClassLoaderContext> context =
       ClassLoaderContext::Create("DLC[]");
@@ -316,6 +337,10 @@
   ASSERT_TRUE(nullptr == ClassLoaderContext::Create("DLC[s4.dex]}"));
   ASSERT_TRUE(nullptr == ClassLoaderContext::Create("DLC[s4.dex]{"));
   ASSERT_TRUE(nullptr == ClassLoaderContext::Create("DLC{DLC[s4.dex]}"));
+  ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{##}"));
+  ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{PCL[s4.dex]#}"));
+  ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{PCL[s4.dex]##}"));
+  ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{PCL[s4.dex]{PCL[s3.dex]}#}"));
 }
 
 TEST_F(ClassLoaderContextTest, OpenInvalidDexFiles) {
@@ -605,6 +630,292 @@
       soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
 }
 
+TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibraries) {
+  // 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<4> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle(
+      soa.Decode<mirror::ClassLoader>(jclass_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 shared libraries.
+  ArtField* field =
+      jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+  ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get());
+  ASSERT_TRUE(raw_shared_libraries != nullptr);
+
+  Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries(
+      hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>()));
+  ASSERT_EQ(shared_libraries->GetLength(), 2);
+
+  // Verify the first shared library.
+  Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0));
+  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);
+  raw_shared_libraries = field->GetObject(class_loader_2.Get());
+  ASSERT_TRUE(raw_shared_libraries == nullptr);
+
+  // Verify the second shared library.
+  Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(shared_libraries->Get(1));
+  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);
+  raw_shared_libraries = field->GetObject(class_loader_3.Get());
+  ASSERT_TRUE(raw_shared_libraries == nullptr);
+
+  // All class loaders should have the BootClassLoader as a parent.
+  ASSERT_TRUE(class_loader_1->GetParent()->GetClass() ==
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+  ASSERT_TRUE(class_loader_2->GetParent()->GetClass() ==
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+  ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+}
+
+TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibrariesInParentToo) {
+  // 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) + "]{" +
+      "PCL[" + CreateClassPath(classpath_dex_b) + "]};" +
+      "PCL[" + 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<6> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle(
+      soa.Decode<mirror::ClassLoader>(jclass_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 : 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 its shared library.
+  ArtField* field =
+      jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+  ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get());
+  ASSERT_TRUE(raw_shared_libraries != nullptr);
+
+  Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries(
+      hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>()));
+  ASSERT_EQ(shared_libraries->GetLength(), 1);
+
+  Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0));
+  std::vector<const DexFile*> class_loader_2_dex_files =
+      MakeNonOwningPointerVector(classpath_dex_b);
+  VerifyClassLoaderDexFiles(soa,
+                            class_loader_2,
+                            WellKnownClasses::dalvik_system_PathClassLoader,
+                            class_loader_2_dex_files);
+  raw_shared_libraries = field->GetObject(class_loader_2.Get());
+  ASSERT_TRUE(raw_shared_libraries == nullptr);
+
+  // Verify the parent.
+  Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(class_loader_1->GetParent());
+  std::vector<const DexFile*> class_loader_3_dex_files =
+      MakeNonOwningPointerVector(classpath_dex_c);
+  VerifyClassLoaderDexFiles(soa,
+                            class_loader_3,
+                            WellKnownClasses::dalvik_system_PathClassLoader,
+                            class_loader_3_dex_files);
+
+  // Verify its shared library.
+  raw_shared_libraries = field->GetObject(class_loader_3.Get());
+  ASSERT_TRUE(raw_shared_libraries != nullptr);
+
+  Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries_2(
+      hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>()));
+  ASSERT_EQ(shared_libraries->GetLength(), 1);
+
+  Handle<mirror::ClassLoader> class_loader_4 = hs.NewHandle(shared_libraries_2->Get(0));
+  std::vector<const DexFile*> class_loader_4_dex_files =
+      MakeNonOwningPointerVector(classpath_dex_d);
+  VerifyClassLoaderDexFiles(soa,
+                            class_loader_4,
+                            WellKnownClasses::dalvik_system_PathClassLoader,
+                            class_loader_4_dex_files);
+  raw_shared_libraries = field->GetObject(class_loader_4.Get());
+  ASSERT_TRUE(raw_shared_libraries == nullptr);
+
+  // Class loaders should have the BootClassLoader as a parent.
+  ASSERT_TRUE(class_loader_2->GetParent()->GetClass() ==
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+  ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+  ASSERT_TRUE(class_loader_4->GetParent()->GetClass() ==
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+}
+
+TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSharedLibrariesDependencies) {
+  // 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) + "]{" +
+      "PCL[" + CreateClassPath(classpath_dex_b) + "]{" +
+      "PCL[" + 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<6> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle(
+      soa.Decode<mirror::ClassLoader>(jclass_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 : 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 its shared library.
+  ArtField* field =
+      jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+  ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get());
+  ASSERT_TRUE(raw_shared_libraries != nullptr);
+
+  Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries(
+      hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>()));
+  ASSERT_EQ(shared_libraries->GetLength(), 1);
+
+  Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0));
+  std::vector<const DexFile*> class_loader_2_dex_files =
+      MakeNonOwningPointerVector(classpath_dex_b);
+  VerifyClassLoaderDexFiles(soa,
+                            class_loader_2,
+                            WellKnownClasses::dalvik_system_PathClassLoader,
+                            class_loader_2_dex_files);
+
+  // Verify the shared library dependency of the shared library.
+  raw_shared_libraries = field->GetObject(class_loader_2.Get());
+  ASSERT_TRUE(raw_shared_libraries != nullptr);
+
+  Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries_2(
+      hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>()));
+  ASSERT_EQ(shared_libraries_2->GetLength(), 1);
+
+  Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(shared_libraries_2->Get(0));
+  std::vector<const DexFile*> class_loader_3_dex_files =
+      MakeNonOwningPointerVector(classpath_dex_c);
+  VerifyClassLoaderDexFiles(soa,
+                            class_loader_3,
+                            WellKnownClasses::dalvik_system_PathClassLoader,
+                            class_loader_3_dex_files);
+  raw_shared_libraries = field->GetObject(class_loader_3.Get());
+  ASSERT_TRUE(raw_shared_libraries == nullptr);
+
+  // Verify the parent.
+  Handle<mirror::ClassLoader> class_loader_4 = hs.NewHandle(class_loader_1->GetParent());
+  std::vector<const DexFile*> class_loader_4_dex_files =
+      MakeNonOwningPointerVector(classpath_dex_d);
+  VerifyClassLoaderDexFiles(soa,
+                            class_loader_4,
+                            WellKnownClasses::dalvik_system_PathClassLoader,
+                            class_loader_4_dex_files);
+  raw_shared_libraries = field->GetObject(class_loader_4.Get());
+  ASSERT_TRUE(raw_shared_libraries == nullptr);
+
+  // Class loaders should have the BootClassLoader as a parent.
+  ASSERT_TRUE(class_loader_2->GetParent()->GetClass() ==
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+  ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+  ASSERT_TRUE(class_loader_4->GetParent()->GetClass() ==
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+}
 
 TEST_F(ClassLoaderContextTest, RemoveSourceLocations) {
   std::unique_ptr<ClassLoaderContext> context =
@@ -622,6 +933,91 @@
   VerifyClassLoaderPCL(context.get(), 0, "");
 }
 
+TEST_F(ClassLoaderContextTest, CreateClassLoaderWithSameSharedLibraries) {
+  // 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::string context_spec =
+      "PCL[" + CreateClassPath(classpath_dex_a) + "]{" +
+      "PCL[" + CreateClassPath(classpath_dex_b) + "]};" +
+      "PCL[" + CreateClassPath(classpath_dex_c) + "]{" +
+      "PCL[" + CreateClassPath(classpath_dex_b) + "]}";
+
+  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<6> hs(soa.Self());
+  Handle<mirror::ClassLoader> class_loader_1 = hs.NewHandle(
+      soa.Decode<mirror::ClassLoader>(jclass_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 : 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 its shared library.
+  ArtField* field =
+      jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_sharedLibraryLoaders);
+  ObjPtr<mirror::Object> raw_shared_libraries = field->GetObject(class_loader_1.Get());
+  ASSERT_TRUE(raw_shared_libraries != nullptr);
+
+  Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries(
+      hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>()));
+  ASSERT_EQ(shared_libraries->GetLength(), 1);
+
+  Handle<mirror::ClassLoader> class_loader_2 = hs.NewHandle(shared_libraries->Get(0));
+  std::vector<const DexFile*> class_loader_2_dex_files =
+      MakeNonOwningPointerVector(classpath_dex_b);
+  VerifyClassLoaderDexFiles(soa,
+                            class_loader_2,
+                            WellKnownClasses::dalvik_system_PathClassLoader,
+                            class_loader_2_dex_files);
+
+  // Verify the parent.
+  Handle<mirror::ClassLoader> class_loader_3 = hs.NewHandle(class_loader_1->GetParent());
+  std::vector<const DexFile*> class_loader_3_dex_files =
+      MakeNonOwningPointerVector(classpath_dex_c);
+  VerifyClassLoaderDexFiles(soa,
+                            class_loader_3,
+                            WellKnownClasses::dalvik_system_PathClassLoader,
+                            class_loader_3_dex_files);
+
+  // Verify its shared library is the same as the child.
+  raw_shared_libraries = field->GetObject(class_loader_3.Get());
+  ASSERT_TRUE(raw_shared_libraries != nullptr);
+  Handle<mirror::ObjectArray<mirror::ClassLoader>> shared_libraries_2(
+      hs.NewHandle(raw_shared_libraries->AsObjectArray<mirror::ClassLoader>()));
+  ASSERT_EQ(shared_libraries_2->GetLength(), 1);
+  ASSERT_EQ(shared_libraries_2->Get(0), class_loader_2.Get());
+
+  // Class loaders should have the BootClassLoader as a parent.
+  ASSERT_TRUE(class_loader_2->GetParent()->GetClass() ==
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+  ASSERT_TRUE(class_loader_3->GetParent()->GetClass() ==
+      soa.Decode<mirror::Class>(WellKnownClasses::java_lang_BootClassLoader));
+}
+
 TEST_F(ClassLoaderContextTest, EncodeInOatFile) {
   std::string dex1_name = GetTestDexFileName("Main");
   std::string dex2_name = GetTestDexFileName("MyClass");
diff --git a/runtime/class_root.h b/runtime/class_root.h
index 1cd135f..1ff4845 100644
--- a/runtime/class_root.h
+++ b/runtime/class_root.h
@@ -101,6 +101,7 @@
   M(kLongArrayClass,                        "[J",                                         mirror::PrimitiveArray<int64_t>)                          \
   M(kShortArrayClass,                       "[S",                                         mirror::PrimitiveArray<int16_t>)                          \
   M(kJavaLangStackTraceElementArrayClass,   "[Ljava/lang/StackTraceElement;",             mirror::ObjectArray<mirror::StackTraceElement>)           \
+  M(kJavaLangClassLoaderArrayClass,         "[Ljava/lang/ClassLoader;",                   mirror::ObjectArray<mirror::ClassLoader>)                 \
   M(kDalvikSystemClassExt,                  "Ldalvik/system/ClassExt;",                   mirror::ClassExt)
 
 // Well known mirror::Class roots accessed via ClassLinker::GetClassRoots().
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index b11e368..f52a0f9 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -140,13 +140,13 @@
                                                  &error_msg));
   ASSERT_TRUE(image_header != nullptr) << error_msg;
   const OatHeader& oat_header = odex_file->GetOatHeader();
-  uint32_t combined_checksum = image_header->GetOatChecksum();
+  uint32_t boot_image_checksum = image_header->GetImageChecksum();
 
   if (CompilerFilter::DependsOnImageChecksum(filter)) {
     if (with_alternate_image) {
-      EXPECT_NE(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
+      EXPECT_NE(boot_image_checksum, oat_header.GetBootImageChecksum());
     } else {
-      EXPECT_EQ(combined_checksum, oat_header.GetImageFileLocationOatChecksum());
+      EXPECT_EQ(boot_image_checksum, oat_header.GetBootImageChecksum());
     }
   }
 }
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index f451978..040a8c5 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#include <setjmp.h>
-
 #include <memory>
 
 #include "base/macros.h"
diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc
index 1ab0b0e..5c2830d 100644
--- a/runtime/fault_handler.cc
+++ b/runtime/fault_handler.cc
@@ -16,7 +16,6 @@
 
 #include "fault_handler.h"
 
-#include <setjmp.h>
 #include <string.h>
 #include <sys/mman.h>
 #include <sys/ucontext.h>
diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h
index 02eeefe..24ef0b1 100644
--- a/runtime/fault_handler.h
+++ b/runtime/fault_handler.h
@@ -18,7 +18,6 @@
 #ifndef ART_RUNTIME_FAULT_HANDLER_H_
 #define ART_RUNTIME_FAULT_HANDLER_H_
 
-#include <setjmp.h>
 #include <signal.h>
 #include <stdint.h>
 
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index aba1c5a..fefe9ab 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -95,6 +95,7 @@
       weak_ref_access_enabled_(true),
       copied_live_bytes_ratio_sum_(0.f),
       gc_count_(0),
+      reclaimed_bytes_ratio_sum_(0.f),
       young_gen_(young_gen),
       skipped_blocks_lock_("concurrent copying bytes blocks lock", kMarkSweepMarkStackLock),
       measure_read_barrier_slow_path_(measure_read_barrier_slow_path),
@@ -110,7 +111,8 @@
       force_evacuate_all_(false),
       gc_grays_immune_objects_(false),
       immune_gray_stack_lock_("concurrent copying immune gray stack lock",
-                              kMarkSweepMarkStackLock) {
+                              kMarkSweepMarkStackLock),
+      num_bytes_allocated_before_gc_(0) {
   static_assert(space::RegionSpace::kRegionSize == accounting::ReadBarrierTable::kRegionSize,
                 "The region space size and the read barrier table region size must match");
   CHECK(kEnableGenerationalConcurrentCopyingCollection || !young_gen_);
@@ -323,6 +325,7 @@
 
 void ConcurrentCopying::InitializePhase() {
   TimingLogger::ScopedTiming split("InitializePhase", GetTimings());
+  num_bytes_allocated_before_gc_ = static_cast<int64_t>(heap_->GetBytesAllocated());
   if (kVerboseMode) {
     LOG(INFO) << "GC InitializePhase";
     LOG(INFO) << "Region-space : " << reinterpret_cast<void*>(region_space_->Begin()) << "-"
@@ -2091,6 +2094,11 @@
 
   CheckEmptyMarkStack();
 
+  int64_t num_bytes_allocated_after_gc = static_cast<int64_t>(heap_->GetBytesAllocated());
+  int64_t diff = num_bytes_allocated_before_gc_ - num_bytes_allocated_after_gc;
+  auto ratio = static_cast<float>(diff) / num_bytes_allocated_before_gc_;
+  reclaimed_bytes_ratio_sum_ += ratio;
+
   if (kVerboseMode) {
     LOG(INFO) << "GC end of ReclaimPhase";
   }
@@ -3199,6 +3207,7 @@
 
 void ConcurrentCopying::DumpPerformanceInfo(std::ostream& os) {
   GarbageCollector::DumpPerformanceInfo(os);
+  size_t num_gc_cycles = GetCumulativeTimings().GetIterations();
   MutexLock mu(Thread::Current(), rb_slow_path_histogram_lock_);
   if (rb_slow_path_time_histogram_.SampleSize() > 0) {
     Histogram<uint64_t>::CumulativeData cumulative_data;
@@ -3211,15 +3220,15 @@
   if (rb_slow_path_count_gc_total_ > 0) {
     os << "GC slow path count " << rb_slow_path_count_gc_total_ << "\n";
   }
-  float average_ratio = copied_live_bytes_ratio_sum_ / gc_count_;
 
-  if (young_gen_) {
-    os << "Average minor GC copied live bytes ratio "
-       << average_ratio << " over " << gc_count_ << " minor GCs\n";
-  } else {
-    os << "Average major GC copied live bytes ratio "
-       << average_ratio << " over " << gc_count_ << " major GCs\n";
-  }
+  os << "Average " << (young_gen_ ? "minor" : "major") << " GC reclaim bytes ratio "
+     << (reclaimed_bytes_ratio_sum_ / num_gc_cycles) << " over " << num_gc_cycles
+     << " GC cycles\n";
+
+  os << "Average " << (young_gen_ ? "minor" : "major") << " GC copied live bytes ratio "
+     << (copied_live_bytes_ratio_sum_ / gc_count_) << " over " << gc_count_
+     << " " << (young_gen_ ? "minor" : "major") << " GCs\n";
+
   os << "Cumulative bytes moved "
      << cumulative_bytes_moved_.load(std::memory_order_relaxed) << "\n";
   os << "Cumulative objects moved "
diff --git a/runtime/gc/collector/concurrent_copying.h b/runtime/gc/collector/concurrent_copying.h
index cd086c4..6535b11 100644
--- a/runtime/gc/collector/concurrent_copying.h
+++ b/runtime/gc/collector/concurrent_copying.h
@@ -359,10 +359,12 @@
   Atomic<uint64_t> cumulative_bytes_moved_;
   Atomic<uint64_t> cumulative_objects_moved_;
 
-  // copied_live_bytes_ratio_sum_ and gc_count_ are read and written by CC per
-  // GC, in ReclaimPhase, and are read by DumpPerformanceInfo (potentially from
-  // another thread). However, at present, DumpPerformanceInfo is only called
-  // when the runtime shuts down, so no concurrent access.
+  // copied_live_bytes_ratio_sum_ is read and written by CC per GC, in
+  // ReclaimPhase, and is read by DumpPerformanceInfo (potentially from another
+  // thread). However, at present, DumpPerformanceInfo is only called when the
+  // runtime shuts down, so no concurrent access. The same reasoning goes for
+  // gc_count_ and reclaimed_bytes_ratio_sum_
+
   // The sum of of all copied live bytes ratio (to_bytes/from_bytes)
   float copied_live_bytes_ratio_sum_;
   // The number of GC counts, used to calculate the average above. (It doesn't
@@ -371,6 +373,9 @@
   // space.)
   size_t gc_count_;
 
+  // reclaimed_bytes_ratio = reclaimed_bytes/num_allocated_bytes per GC cycle
+  float reclaimed_bytes_ratio_sum_;
+
   // Generational "sticky", only trace through dirty objects in region space.
   const bool young_gen_;
   // If true, the GC thread is done scanning marked objects on dirty and aged
@@ -416,6 +421,9 @@
   // ConcurrentCopying::SweepArray).
   MemMap sweep_array_free_buffer_mem_map_;
 
+  // Use signed because after_gc may be larger than before_gc.
+  int64_t num_bytes_allocated_before_gc_;
+
   class ActivateReadBarrierEntrypointsCallback;
   class ActivateReadBarrierEntrypointsCheckpoint;
   class AssertToSpaceInvariantFieldVisitor;
diff --git a/runtime/gc/collector/immune_spaces_test.cc b/runtime/gc/collector/immune_spaces_test.cc
index c2a67bf..9f98f6c 100644
--- a/runtime/gc/collector/immune_spaces_test.cc
+++ b/runtime/gc/collector/immune_spaces_test.cc
@@ -113,22 +113,20 @@
     ImageSection sections[ImageHeader::kSectionCount];
     new (image_map.Begin()) ImageHeader(
         /*image_begin=*/ PointerToLowMemUInt32(image_map.Begin()),
-        /*image_size=*/ image_map.Size(),
+        /*image_size=*/ image_size,
         sections,
         /*image_roots=*/ PointerToLowMemUInt32(image_map.Begin()) + 1,
         /*oat_checksum=*/ 0u,
         // The oat file data in the header is always right after the image space.
         /*oat_file_begin=*/ PointerToLowMemUInt32(oat_map.Begin()),
-        /*oat_data_begin=*/PointerToLowMemUInt32(oat_map.Begin()),
-        /*oat_data_end=*/PointerToLowMemUInt32(oat_map.Begin() + oat_size),
-        /*oat_file_end=*/PointerToLowMemUInt32(oat_map.Begin() + oat_size),
-        /*boot_image_begin=*/0u,
-        /*boot_image_size=*/0u,
-        /*boot_oat_begin=*/0u,
-        /*boot_oat_size=*/0u,
-        /*pointer_size=*/sizeof(void*),
+        /*oat_data_begin=*/ PointerToLowMemUInt32(oat_map.Begin()),
+        /*oat_data_end=*/ PointerToLowMemUInt32(oat_map.Begin() + oat_size),
+        /*oat_file_end=*/ PointerToLowMemUInt32(oat_map.Begin() + oat_size),
+        /*boot_image_begin=*/ 0u,
+        /*boot_image_size=*/ 0u,
+        /*pointer_size=*/ sizeof(void*),
         ImageHeader::kStorageModeUncompressed,
-        /*data_size=*/0u);
+        /*data_size=*/ 0u);
     return new DummyImageSpace(std::move(image_map),
                                std::move(live_bitmap),
                                std::move(oat_file),
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index 19b1fc7..15e0711 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -736,7 +736,8 @@
 void SemiSpace::ProcessMarkStack() {
   TimingLogger::ScopedTiming t(__FUNCTION__, GetTimings());
   accounting::ContinuousSpaceBitmap* live_bitmap = nullptr;
-  if (collect_from_space_only_) {
+  const bool collect_from_space_only = collect_from_space_only_;
+  if (collect_from_space_only) {
     // If a bump pointer space only collection (and the promotion is
     // enabled,) we delay the live-bitmap marking of promoted objects
     // from MarkObject() until this function.
@@ -748,7 +749,7 @@
   }
   while (!mark_stack_->IsEmpty()) {
     Object* obj = mark_stack_->PopBack();
-    if (collect_from_space_only_ && promo_dest_space_->HasAddress(obj)) {
+    if (collect_from_space_only && promo_dest_space_->HasAddress(obj)) {
       // obj has just been promoted. Mark the live bitmap for it,
       // which is delayed from MarkObject().
       DCHECK(!live_bitmap->Test(obj));
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 71c2392..4359bea 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -1064,17 +1064,19 @@
 
 uint64_t Heap::GetTotalGcCpuTime() {
   uint64_t sum = 0;
-  sum += young_concurrent_copying_collector_->GetTotalCpuTime();
-  sum += concurrent_copying_collector_->GetTotalCpuTime();
+  for (auto* collector : garbage_collectors_) {
+    sum += collector->GetTotalCpuTime();
+  }
   return sum;
 }
+
 void Heap::DumpGcPerformanceInfo(std::ostream& os) {
   // Dump cumulative timings.
   os << "Dumping cumulative Gc timings\n";
   uint64_t total_duration = 0;
   // Dump cumulative loggers for each GC type.
   uint64_t total_paused_time = 0;
-  for (auto& collector : garbage_collectors_) {
+  for (auto* collector : garbage_collectors_) {
     total_duration += collector->GetCumulativeTimings().GetTotalNs();
     total_paused_time += collector->GetTotalPausedTimeNs();
     collector->DumpPerformanceInfo(os);
@@ -1134,7 +1136,7 @@
 }
 
 void Heap::ResetGcPerformanceInfo() {
-  for (auto& collector : garbage_collectors_) {
+  for (auto* collector : garbage_collectors_) {
     collector->ResetMeasurements();
   }
   total_bytes_freed_ever_ = 0;
@@ -1155,7 +1157,7 @@
 
 uint64_t Heap::GetGcCount() const {
   uint64_t gc_count = 0U;
-  for (auto& collector : garbage_collectors_) {
+  for (auto* collector : garbage_collectors_) {
     gc_count += collector->GetCumulativeTimings().GetIterations();
   }
   return gc_count;
@@ -1163,7 +1165,7 @@
 
 uint64_t Heap::GetGcTime() const {
   uint64_t gc_time = 0U;
-  for (auto& collector : garbage_collectors_) {
+  for (auto* collector : garbage_collectors_) {
     gc_time += collector->GetCumulativeTimings().GetTotalNs();
   }
   return gc_time;
@@ -3503,7 +3505,7 @@
 }
 
 collector::GarbageCollector* Heap::FindCollectorByGcType(collector::GcType gc_type) {
-  for (const auto& collector : garbage_collectors_) {
+  for (auto* collector : garbage_collectors_) {
     if (collector->GetCollectorType() == collector_type_ &&
         collector->GetGcType() == gc_type) {
       return collector;
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 0766999..bfb3746 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -383,20 +383,16 @@
  public:
   static std::unique_ptr<ImageSpace> InitAppImage(const char* image_filename,
                                                   const char* image_location,
-                                                  bool validate_oat_file,
                                                   const OatFile* oat_file,
                                                   /*inout*/MemMap* image_reservation,
-                                                  /*inout*/MemMap* oat_reservation,
                                                   /*out*/std::string* error_msg)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     TimingLogger logger(__PRETTY_FUNCTION__, /*precise=*/ true, VLOG_IS_ON(image));
     std::unique_ptr<ImageSpace> space = Init(image_filename,
                                              image_location,
-                                             validate_oat_file,
                                              oat_file,
                                              &logger,
                                              image_reservation,
-                                             oat_reservation,
                                              error_msg);
     if (space != nullptr) {
       TimingLogger::ScopedTiming timing("RelocateImage", &logger);
@@ -438,11 +434,9 @@
 
   static std::unique_ptr<ImageSpace> Init(const char* image_filename,
                                           const char* image_location,
-                                          bool validate_oat_file,
                                           const OatFile* oat_file,
                                           TimingLogger* logger,
                                           /*inout*/MemMap* image_reservation,
-                                          /*inout*/MemMap* oat_reservation,
                                           /*out*/std::string* error_msg)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     CHECK(image_filename != nullptr);
@@ -479,8 +473,8 @@
     }
 
     if (oat_file != nullptr) {
-      // If we have an oat file, check the oat file checksum. The oat file is only non-null for the
-      // app image case. Otherwise, we open the oat file after the image and check the checksum there.
+      // If we have an oat file (i.e. for app image), check the oat file checksum.
+      // Otherwise, we open the oat file after the image and check the checksum there.
       const uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum();
       const uint32_t image_oat_checksum = image_header->GetOatChecksum();
       if (oat_checksum != image_oat_checksum) {
@@ -517,15 +511,13 @@
       return nullptr;
     }
 
-    MemMap map;
-
     // GetImageBegin is the preferred address to map the image. If we manage to map the
     // image at the image begin, the amount of fixup work required is minimized.
     // If it is pic we will retry with error_msg for the failure case. Pass a null error_msg to
     // avoid reading proc maps for a mapping failure and slowing everything down.
     // For the boot image, we have already reserved the memory and we load the image
     // into the `image_reservation`.
-    map = LoadImageFile(
+    MemMap map = LoadImageFile(
         image_filename,
         image_location,
         *image_header,
@@ -583,33 +575,7 @@
                                                      std::move(map),
                                                      std::move(bitmap),
                                                      image_end));
-
-    // VerifyImageAllocations() will be called later in Runtime::Init()
-    // as some class roots like ArtMethod::java_lang_reflect_ArtMethod_
-    // and ArtField::java_lang_reflect_ArtField_, which are used from
-    // Object::SizeOf() which VerifyImageAllocations() calls, are not
-    // set yet at this point.
-    if (oat_file == nullptr) {
-      TimingLogger::ScopedTiming timing("OpenOatFile", logger);
-      space->oat_file_ = OpenOatFile(*space, image_filename, oat_reservation, error_msg);
-      if (space->oat_file_ == nullptr) {
-        DCHECK(!error_msg->empty());
-        return nullptr;
-      }
-      space->oat_file_non_owned_ = space->oat_file_.get();
-    } else {
-      space->oat_file_non_owned_ = oat_file;
-    }
-
-    if (validate_oat_file) {
-      TimingLogger::ScopedTiming timing("ValidateOatFile", logger);
-      CHECK(space->oat_file_ != nullptr);
-      if (!ImageSpace::ValidateOatFile(*space->oat_file_, error_msg)) {
-        DCHECK(!error_msg->empty());
-        return nullptr;
-      }
-    }
-
+    space->oat_file_non_owned_ = oat_file;
     return space;
   }
 
@@ -700,11 +666,9 @@
   class FixupVisitor : public ValueObject {
    public:
     FixupVisitor(const RelocationRange& boot_image,
-                 const RelocationRange& boot_oat,
                  const RelocationRange& app_image,
                  const RelocationRange& app_oat)
         : boot_image_(boot_image),
-          boot_oat_(boot_oat),
           app_image_(app_image),
           app_oat_(app_oat) {}
 
@@ -727,8 +691,8 @@
     // Return the relocated address of a code pointer (contained by an oat file).
     ALWAYS_INLINE const void* ForwardCode(const void* src) const {
       const uintptr_t uint_src = reinterpret_cast<uintptr_t>(src);
-      if (boot_oat_.InSource(uint_src)) {
-        return reinterpret_cast<const void*>(boot_oat_.ToDest(uint_src));
+      if (boot_image_.InSource(uint_src)) {
+        return reinterpret_cast<const void*>(boot_image_.ToDest(uint_src));
       }
       if (app_oat_.InSource(uint_src)) {
         return reinterpret_cast<const void*>(app_oat_.ToDest(uint_src));
@@ -745,7 +709,6 @@
    protected:
     // Source section.
     const RelocationRange boot_image_;
-    const RelocationRange boot_oat_;
     const RelocationRange app_image_;
     const RelocationRange app_oat_;
   };
@@ -893,7 +856,7 @@
       // We want to use our own class loader and not the one in the image.
       if (obj->IsClass<kVerifyNone>()) {
         mirror::Class* as_klass = obj->AsClass<kVerifyNone>();
-        FixupObjectAdapter visitor(boot_image_, boot_oat_, app_image_, app_oat_);
+        FixupObjectAdapter visitor(boot_image_, app_image_, app_oat_);
         as_klass->FixupNativePointers<kVerifyNone>(as_klass, pointer_size_, visitor);
         // Deal with the pointer arrays. Use the helper function since multiple classes can reference
         // the same arrays.
@@ -1025,10 +988,8 @@
       *error_msg = "Can not relocate app image without boot oat file";
       return false;
     }
-    const uint32_t boot_image_size = boot_image_end - boot_image_begin;
-    const uint32_t boot_oat_size = boot_oat_end - boot_oat_begin;
+    const uint32_t boot_image_size = boot_oat_end - boot_image_begin;
     const uint32_t image_header_boot_image_size = image_header.GetBootImageSize();
-    const uint32_t image_header_boot_oat_size = image_header.GetBootOatSize();
     if (boot_image_size != image_header_boot_image_size) {
       *error_msg = StringPrintf("Boot image size %" PRIu64 " does not match expected size %"
                                     PRIu64,
@@ -1036,20 +997,10 @@
                                 static_cast<uint64_t>(image_header_boot_image_size));
       return false;
     }
-    if (boot_oat_size != image_header_boot_oat_size) {
-      *error_msg = StringPrintf("Boot oat size %" PRIu64 " does not match expected size %"
-                                    PRIu64,
-                                static_cast<uint64_t>(boot_oat_size),
-                                static_cast<uint64_t>(image_header_boot_oat_size));
-      return false;
-    }
     TimingLogger logger(__FUNCTION__, true, false);
     RelocationRange boot_image(image_header.GetBootImageBegin(),
                                boot_image_begin,
                                boot_image_size);
-    RelocationRange boot_oat(image_header.GetBootOatBegin(),
-                             boot_oat_begin,
-                             boot_oat_size);
     RelocationRange app_image(reinterpret_cast<uintptr_t>(image_header.GetImageBegin()),
                               reinterpret_cast<uintptr_t>(target_base),
                               image_header.GetImageSize());
@@ -1061,11 +1012,9 @@
     VLOG(image) << "App image " << app_image;
     VLOG(image) << "App oat " << app_oat;
     VLOG(image) << "Boot image " << boot_image;
-    VLOG(image) << "Boot oat " << boot_oat;
-    // True if we need to fixup any heap pointers, otherwise only code pointers.
+    // True if we need to fixup any heap pointers.
     const bool fixup_image = boot_image.Delta() != 0 || app_image.Delta() != 0;
-    const bool fixup_code = boot_oat.Delta() != 0 || app_oat.Delta() != 0;
-    if (!fixup_image && !fixup_code) {
+    if (!fixup_image) {
       // Nothing to fix up.
       return true;
     }
@@ -1074,7 +1023,7 @@
     const ImageSection& objects_section = image_header.GetObjectsSection();
     uintptr_t objects_begin = reinterpret_cast<uintptr_t>(target_base + objects_section.Offset());
     uintptr_t objects_end = reinterpret_cast<uintptr_t>(target_base + objects_section.End());
-    FixupObjectAdapter fixup_adapter(boot_image, boot_oat, app_image, app_oat);
+    FixupObjectAdapter fixup_adapter(boot_image, app_image, app_oat);
     if (fixup_image) {
       // Two pass approach, fix up all classes first, then fix up non class-objects.
       // The visited bitmap is used to ensure that pointer arrays are not forwarded twice.
@@ -1085,7 +1034,6 @@
       FixupObjectVisitor fixup_object_visitor(visited_bitmap.get(),
                                               pointer_size,
                                               boot_image,
-                                              boot_oat,
                                               app_image,
                                               app_oat);
       TimingLogger::ScopedTiming timing("Fixup classes", &logger);
@@ -1191,7 +1139,6 @@
       FixupArtMethodVisitor method_visitor(fixup_image,
                                            pointer_size,
                                            boot_image,
-                                           boot_oat,
                                            app_image,
                                            app_oat);
       image_header.VisitPackedArtMethods(&method_visitor, target_base, pointer_size);
@@ -1200,7 +1147,7 @@
       {
         // Only touches objects in the app image, no need for mutator lock.
         TimingLogger::ScopedTiming timing("Fixup fields", &logger);
-        FixupArtFieldVisitor field_visitor(boot_image, boot_oat, app_image, app_oat);
+        FixupArtFieldVisitor field_visitor(boot_image, app_image, app_oat);
         image_header.VisitPackedArtFields(&field_visitor, target_base);
       }
       {
@@ -1222,7 +1169,7 @@
         WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
         ClassTable temp_table;
         temp_table.ReadFromMemory(target_base + class_table_section.Offset());
-        FixupRootVisitor root_visitor(boot_image, boot_oat, app_image, app_oat);
+        FixupRootVisitor root_visitor(boot_image, app_image, app_oat);
         temp_table.VisitRoots(root_visitor);
       }
       // Fix up the intern table.
@@ -1234,7 +1181,7 @@
         InternTable temp_intern_table;
         // Note that we require that ReadFromMemory does not make an internal copy of the elements
         // so that the VisitRoots() will update the memory directly rather than the copies.
-        FixupRootVisitor root_visitor(boot_image, boot_oat, app_image, app_oat);
+        FixupRootVisitor root_visitor(boot_image, app_image, app_oat);
         temp_intern_table.AddTableFromMemory(target_base + intern_table_section.Offset(),
                                              [&](InternTable::UnorderedSet& strings)
             REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1249,49 +1196,6 @@
     }
     return true;
   }
-
-  static std::unique_ptr<OatFile> OpenOatFile(const ImageSpace& image,
-                                              const char* image_path,
-                                              /*inout*/MemMap* oat_reservation,
-                                              std::string* error_msg) {
-    const ImageHeader& image_header = image.GetImageHeader();
-    std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(image_path);
-
-    CHECK(image_header.GetOatDataBegin() != nullptr);
-
-    uint8_t* oat_data_begin = image_header.GetOatDataBegin();
-    if (oat_reservation != nullptr) {
-      oat_data_begin += oat_reservation->Begin() - image_header.GetOatFileBegin();
-    }
-    std::unique_ptr<OatFile> oat_file(OatFile::Open(/*zip_fd=*/ -1,
-                                                    oat_filename,
-                                                    oat_filename,
-                                                    !Runtime::Current()->IsAotCompiler(),
-                                                    /*low_4gb=*/ false,
-                                                    /*abs_dex_location=*/ nullptr,
-                                                    oat_reservation,
-                                                    error_msg));
-    if (oat_file == nullptr) {
-      *error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s",
-                                oat_filename.c_str(),
-                                image.GetName(),
-                                error_msg->c_str());
-      return nullptr;
-    }
-    CHECK(oat_data_begin == oat_file->Begin());
-    uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum();
-    uint32_t image_oat_checksum = image_header.GetOatChecksum();
-    if (oat_checksum != image_oat_checksum) {
-      *error_msg = StringPrintf("Failed to match oat file checksum 0x%x to expected oat checksum 0x%x"
-                                " in image %s",
-                                oat_checksum,
-                                image_oat_checksum,
-                                image.GetName());
-      return nullptr;
-    }
-
-    return oat_file;
-  }
 };
 
 class ImageSpace::BootImageLoader {
@@ -1351,26 +1255,22 @@
     }
     uint32_t image_start;
     uint32_t image_end;
-    uint32_t oat_end;
-    if (!GetBootImageAddressRange(filename, &image_start, &image_end, &oat_end, error_msg)) {
+    if (!GetBootImageAddressRange(filename, &image_start, &image_end, error_msg)) {
       return false;
     }
     if (locations.size() > 1u) {
       std::string last_filename = GetSystemImageFilename(locations.back().c_str(), image_isa_);
       uint32_t dummy;
-      if (!GetBootImageAddressRange(last_filename, &dummy, &image_end, &oat_end, error_msg)) {
+      if (!GetBootImageAddressRange(last_filename, &dummy, &image_end, error_msg)) {
         return false;
       }
     }
     MemMap image_reservation;
-    MemMap oat_reservation;
     MemMap local_extra_reservation;
-    if (!ReserveBootImageMemory(image_start,
-                                image_end,
-                                oat_end,
+    if (!ReserveBootImageMemory(/*reservation_size=*/ image_end - image_start,
+                                image_start,
                                 extra_reservation_size,
                                 &image_reservation,
-                                &oat_reservation,
                                 &local_extra_reservation,
                                 error_msg)) {
       return false;
@@ -1380,28 +1280,29 @@
     spaces.reserve(locations.size());
     for (const std::string& location : locations) {
       filename = GetSystemImageFilename(location.c_str(), image_isa_);
-      spaces.push_back(Load(location,
-                            filename,
-                            /*validate_oat_file=*/ false,
-                            &logger,
-                            &image_reservation,
-                            &oat_reservation,
-                            error_msg));
+      spaces.push_back(Load(location, filename, &logger, &image_reservation, error_msg));
       if (spaces.back() == nullptr) {
         return false;
       }
     }
-    if (!CheckReservationsExhausted(image_reservation, oat_reservation, error_msg)) {
+    for (std::unique_ptr<ImageSpace>& space : spaces) {
+      static constexpr bool kValidateOatFile = false;
+      if (!OpenOatFile(space.get(), kValidateOatFile, &logger, &image_reservation, error_msg)) {
+        return false;
+      }
+    }
+    if (!CheckReservationExhausted(image_reservation, error_msg)) {
       return false;
     }
 
     MaybeRelocateSpaces(spaces, &logger);
     InitRuntimeMethods(spaces);
-    *extra_reservation = std::move(local_extra_reservation);
-    VLOG(image) << "ImageSpace::BootImageLoader::InitFromDalvikCache exiting " << *spaces.front();
     boot_image_spaces->swap(spaces);
+    *extra_reservation = std::move(local_extra_reservation);
 
     if (VLOG_IS_ON(image)) {
+      LOG(INFO) << "ImageSpace::BootImageLoader::LoadFromSystem exiting "
+          << boot_image_spaces->front();
       logger.Dump(LOG_STREAM(INFO));
     }
     return true;
@@ -1421,8 +1322,7 @@
     }
     uint32_t image_start;
     uint32_t image_end;
-    uint32_t oat_end;
-    if (!GetBootImageAddressRange(cache_filename_, &image_start, &image_end, &oat_end, error_msg)) {
+    if (!GetBootImageAddressRange(cache_filename_, &image_start, &image_end, error_msg)) {
       return false;
     }
     if (locations.size() > 1u) {
@@ -1434,19 +1334,16 @@
         return false;
       }
       uint32_t dummy;
-      if (!GetBootImageAddressRange(last_filename, &dummy, &image_end, &oat_end, error_msg)) {
+      if (!GetBootImageAddressRange(last_filename, &dummy, &image_end, error_msg)) {
         return false;
       }
     }
     MemMap image_reservation;
-    MemMap oat_reservation;
     MemMap local_extra_reservation;
-    if (!ReserveBootImageMemory(image_start,
-                                image_end,
-                                oat_end,
+    if (!ReserveBootImageMemory(/*reservation_size=*/ image_end - image_start,
+                                image_start,
                                 extra_reservation_size,
                                 &image_reservation,
-                                &oat_reservation,
                                 &local_extra_reservation,
                                 error_msg)) {
       return false;
@@ -1459,28 +1356,28 @@
       if (!GetDalvikCacheFilename(location.c_str(), dalvik_cache_.c_str(), &filename, error_msg)) {
         return false;
       }
-      spaces.push_back(Load(location,
-                            filename,
-                            validate_oat_file,
-                            &logger,
-                            &image_reservation,
-                            &oat_reservation,
-                            error_msg));
+      spaces.push_back(Load(location, filename, &logger, &image_reservation, error_msg));
       if (spaces.back() == nullptr) {
         return false;
       }
     }
-    if (!CheckReservationsExhausted(image_reservation, oat_reservation, error_msg)) {
+    for (std::unique_ptr<ImageSpace>& space : spaces) {
+      if (!OpenOatFile(space.get(), validate_oat_file, &logger, &image_reservation, error_msg)) {
+        return false;
+      }
+    }
+    if (!CheckReservationExhausted(image_reservation, error_msg)) {
       return false;
     }
 
     MaybeRelocateSpaces(spaces, &logger);
     InitRuntimeMethods(spaces);
-    *extra_reservation = std::move(local_extra_reservation);
     boot_image_spaces->swap(spaces);
+    *extra_reservation = std::move(local_extra_reservation);
 
-    VLOG(image) << "ImageSpace::BootImageLoader::InitFromDalvikCache exiting " << *spaces.front();
     if (VLOG_IS_ON(image)) {
+      LOG(INFO) << "ImageSpace::BootImageLoader::LoadFromDalvikCache exiting "
+          << boot_image_spaces->front();
       logger.Dump(LOG_STREAM(INFO));
     }
     return true;
@@ -2013,10 +1910,8 @@
 
   std::unique_ptr<ImageSpace> Load(const std::string& image_location,
                                    const std::string& image_filename,
-                                   bool validate_oat_file,
                                    TimingLogger* logger,
                                    /*inout*/MemMap* image_reservation,
-                                   /*inout*/MemMap* oat_reservation,
                                    /*out*/std::string* error_msg)
       REQUIRES_SHARED(Locks::mutator_lock_) {
     // Should this be a RDWR lock? This is only a defensive measure, as at
@@ -2045,14 +1940,80 @@
     // file name.
     return Loader::Init(image_filename.c_str(),
                         image_location.c_str(),
-                        validate_oat_file,
                         /*oat_file=*/ nullptr,
                         logger,
                         image_reservation,
-                        oat_reservation,
                         error_msg);
   }
 
+  bool OpenOatFile(ImageSpace* space,
+                   bool validate_oat_file,
+                   TimingLogger* logger,
+                   /*inout*/MemMap* image_reservation,
+                   /*out*/std::string* error_msg) {
+    // VerifyImageAllocations() will be called later in Runtime::Init()
+    // as some class roots like ArtMethod::java_lang_reflect_ArtMethod_
+    // and ArtField::java_lang_reflect_ArtField_, which are used from
+    // Object::SizeOf() which VerifyImageAllocations() calls, are not
+    // set yet at this point.
+    DCHECK(image_reservation != nullptr);
+    std::unique_ptr<OatFile> oat_file;
+    {
+      TimingLogger::ScopedTiming timing("OpenOatFile", logger);
+      std::string oat_filename =
+          ImageHeader::GetOatLocationFromImageLocation(space->GetImageFilename());
+
+      oat_file.reset(OatFile::Open(/*zip_fd=*/ -1,
+                                   oat_filename,
+                                   oat_filename,
+                                   !Runtime::Current()->IsAotCompiler(),
+                                   /*low_4gb=*/ false,
+                                   /*abs_dex_location=*/ nullptr,
+                                   image_reservation,
+                                   error_msg));
+      if (oat_file == nullptr) {
+        *error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s",
+                                  oat_filename.c_str(),
+                                  space->GetName(),
+                                  error_msg->c_str());
+        return false;
+      }
+      const ImageHeader& image_header = space->GetImageHeader();
+      uint32_t oat_checksum = oat_file->GetOatHeader().GetChecksum();
+      uint32_t image_oat_checksum = image_header.GetOatChecksum();
+      if (oat_checksum != image_oat_checksum) {
+        *error_msg = StringPrintf("Failed to match oat file checksum 0x%x to expected oat checksum"
+                                  " 0x%x in image %s",
+                                  oat_checksum,
+                                  image_oat_checksum,
+                                  space->GetName());
+        return false;
+      }
+      ptrdiff_t relocation_diff = space->Begin() - image_header.GetImageBegin();
+      CHECK(image_header.GetOatDataBegin() != nullptr);
+      uint8_t* oat_data_begin = image_header.GetOatDataBegin() + relocation_diff;
+      if (oat_file->Begin() != oat_data_begin) {
+        *error_msg = StringPrintf("Oat file '%s' referenced from image %s has unexpected begin"
+                                      " %p v. %p",
+                                  oat_filename.c_str(),
+                                  space->GetName(),
+                                  oat_file->Begin(),
+                                  oat_data_begin);
+        return false;
+      }
+    }
+    if (validate_oat_file) {
+      TimingLogger::ScopedTiming timing("ValidateOatFile", logger);
+      if (!ImageSpace::ValidateOatFile(*oat_file, error_msg)) {
+        DCHECK(!error_msg->empty());
+        return false;
+      }
+    }
+    space->oat_file_ = std::move(oat_file);
+    space->oat_file_non_owned_ = space->oat_file_.get();
+    return true;
+  }
+
   // Extract boot class path from oat file associated with `image_filename`
   // and list all associated image locations.
   static bool GetBootClassPathImageLocations(const std::string& image_location,
@@ -2087,7 +2048,6 @@
   bool GetBootImageAddressRange(const std::string& filename,
                                 /*out*/uint32_t* start,
                                 /*out*/uint32_t* end,
-                                /*out*/uint32_t* oat_end,
                                 /*out*/std::string* error_msg) {
     ImageHeader system_hdr;
     if (!ReadSpecificImageHeader(filename.c_str(), &system_hdr)) {
@@ -2096,22 +2056,19 @@
     }
     *start = reinterpret_cast32<uint32_t>(system_hdr.GetImageBegin());
     CHECK_ALIGNED(*start, kPageSize);
-    *end = RoundUp(*start + system_hdr.GetImageSize(), kPageSize);
-    *oat_end = RoundUp(reinterpret_cast32<uint32_t>(system_hdr.GetOatFileEnd()), kPageSize);
+    *end = RoundUp(reinterpret_cast32<uint32_t>(system_hdr.GetOatFileEnd()), kPageSize);
     return true;
   }
 
-  bool ReserveBootImageMemory(uint32_t image_start,
-                              uint32_t image_end,
-                              uint32_t oat_end,
+  bool ReserveBootImageMemory(uint32_t reservation_size,
+                              uint32_t image_start,
                               size_t extra_reservation_size,
                               /*out*/MemMap* image_reservation,
-                              /*out*/MemMap* oat_reservation,
                               /*out*/MemMap* extra_reservation,
                               /*out*/std::string* error_msg) {
     DCHECK(!image_reservation->IsValid());
-    size_t total_size =
-        dchecked_integral_cast<size_t>(oat_end - image_start) + extra_reservation_size;
+    DCHECK_LT(extra_reservation_size, std::numeric_limits<uint32_t>::max() - reservation_size);
+    size_t total_size = reservation_size + extra_reservation_size;
     bool relocate = Runtime::Current()->ShouldRelocate();
     // If relocating, choose a random address for ALSR.
     uint32_t addr = relocate ? ART_BASE_ADDRESS + ChooseRelocationOffsetDelta() : image_start;
@@ -2140,37 +2097,17 @@
         return false;
       }
     }
-    uint32_t diff = reinterpret_cast32<uint32_t>(image_reservation->Begin()) - image_start;
-    image_start += diff;
-    image_end += diff;
-    oat_end += diff;
-    DCHECK(!oat_reservation->IsValid());
-    *oat_reservation = image_reservation->RemapAtEnd(reinterpret_cast32<uint8_t*>(image_end),
-                                                     "Boot image oat reservation",
-                                                     PROT_NONE,
-                                                     error_msg);
-    if (!oat_reservation->IsValid()) {
-      return false;
-    }
 
     return true;
   }
 
-  bool CheckReservationsExhausted(const MemMap& image_reservation,
-                                  const MemMap& oat_reservation,
-                                  /*out*/std::string* error_msg) {
+  bool CheckReservationExhausted(const MemMap& image_reservation, /*out*/std::string* error_msg) {
     if (image_reservation.IsValid()) {
       *error_msg = StringPrintf("Excessive image reservation after loading boot image: %p-%p",
                                 image_reservation.Begin(),
                                 image_reservation.End());
       return false;
     }
-    if (oat_reservation.IsValid()) {
-      *error_msg = StringPrintf("Excessive oat reservation after loading boot image: %p-%p",
-                                image_reservation.Begin(),
-                                image_reservation.End());
-      return false;
-    }
     return true;
   }
 
@@ -2374,12 +2311,11 @@
 std::unique_ptr<ImageSpace> ImageSpace::CreateFromAppImage(const char* image,
                                                            const OatFile* oat_file,
                                                            std::string* error_msg) {
+  // Note: The oat file has already been validated.
   return Loader::InitAppImage(image,
                               image,
-                              /*validate_oat_file=*/ false,
                               oat_file,
                               /*image_reservation=*/ nullptr,
-                              /*oat_reservation=*/ nullptr,
                               error_msg);
 }
 
diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc
index 188c5f3..d3df7fd 100644
--- a/runtime/hidden_api.cc
+++ b/runtime/hidden_api.cc
@@ -21,7 +21,6 @@
 #include "art_field-inl.h"
 #include "art_method-inl.h"
 #include "base/dumpable.h"
-#include "base/sdk_version.h"
 #include "dex/class_accessor-inl.h"
 #include "scoped_thread_state_change.h"
 #include "thread-inl.h"
@@ -76,21 +75,6 @@
   kAccessDenied  = 1 << 1,
 };
 
-static SdkVersion GetMaxAllowedSdkVersionForApiList(ApiList api_list) {
-  switch (api_list) {
-    case ApiList::kWhitelist:
-    case ApiList::kLightGreylist:
-      return SdkVersion::kMax;
-    case ApiList::kDarkGreylist:
-      return SdkVersion::kO_MR1;
-    case ApiList::kBlacklist:
-      return SdkVersion::kMin;
-    case ApiList::kNoList:
-      LOG(FATAL) << "Unexpected value";
-      UNREACHABLE();
-  }
-}
-
 MemberSignature::MemberSignature(ArtField* field) {
   class_name_ = field->GetDeclaringClass()->GetDescriptor(&tmp_);
   member_name_ = field->GetName();
@@ -264,7 +248,7 @@
   }
 
   uint32_t flags = kInvalidDexFlags;
-  DCHECK(!AreValidFlags(flags));
+  DCHECK(!AreValidDexFlags(flags));
 
   ClassAccessor accessor(declaring_class->GetDexFile(),
                          *class_def,
@@ -277,7 +261,7 @@
   accessor.VisitFields(fn_visit, fn_visit);
 
   CHECK_NE(flags, kInvalidDexFlags) << "Could not find flags for field " << field->PrettyField();
-  DCHECK(AreValidFlags(flags));
+  DCHECK(AreValidDexFlags(flags));
   return flags;
 }
 
@@ -294,7 +278,7 @@
   }
 
   uint32_t flags = kInvalidDexFlags;
-  DCHECK(!AreValidFlags(flags));
+  DCHECK(!AreValidDexFlags(flags));
 
   ClassAccessor accessor(declaring_class->GetDexFile(),
                          *class_def,
@@ -307,7 +291,7 @@
   accessor.VisitMethods(fn_visit, fn_visit);
 
   CHECK_NE(flags, kInvalidDexFlags) << "Could not find flags for method " << method->PrettyMethod();
-  DCHECK(AreValidFlags(flags));
+  DCHECK(AreValidDexFlags(flags));
   return flags;
 }
 
@@ -323,7 +307,7 @@
   const bool deny_access =
       (policy == EnforcementPolicy::kEnabled) &&
       IsSdkVersionSetAndMoreThan(runtime->GetTargetSdkVersion(),
-                                 GetMaxAllowedSdkVersionForApiList(api_list));
+                                 api_list.GetMaxAllowedSdkVersion());
 
   MemberSignature member_signature(member);
 
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 32bae11..eea58e9 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -19,8 +19,8 @@
 
 #include "art_field.h"
 #include "art_method.h"
+#include "base/hiddenapi_flags.h"
 #include "base/mutex.h"
-#include "dex/hidden_api_access_flags.h"
 #include "intrinsics_enum.h"
 #include "mirror/class-inl.h"
 #include "reflection.h"
@@ -177,10 +177,10 @@
   uint32_t runtime_flags = 0u;
 
   uint32_t dex_flags = member.GetHiddenapiFlags();
-  DCHECK(AreValidFlags(dex_flags));
+  DCHECK(AreValidDexFlags(dex_flags));
 
-  ApiList api_list = static_cast<hiddenapi::ApiList>(dex_flags);
-  if (api_list == ApiList::kWhitelist) {
+  ApiList api_list = ApiList::FromDexFlags(dex_flags);
+  if (api_list == ApiList::Whitelist()) {
     runtime_flags |= kAccPublicApi;
   }
 
@@ -316,7 +316,7 @@
   // Decode hidden API access flags from the dex file.
   // This is an O(N) operation scaling with the number of fields/methods
   // in the class. Only do this on slow path and only do it once.
-  ApiList api_list = static_cast<hiddenapi::ApiList>(detail::GetDexFlags(member));
+  ApiList api_list = ApiList::FromDexFlags(detail::GetDexFlags(member));
 
   // Member is hidden and caller is not exempted. Enter slow path.
   return detail::ShouldDenyAccessToMemberImpl(member, api_list, access_method);
diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc
index 314d878..520dc6d 100644
--- a/runtime/hidden_api_test.cc
+++ b/runtime/hidden_api_test.cc
@@ -101,30 +101,32 @@
   ScopedObjectAccess soa(self_);
 
   runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kDisabled);
-  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
-  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
-  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), false);
-  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Whitelist()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Greylist()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxO()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Blacklist()), false);
 
   runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kJustWarn);
-  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
-  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
-  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), false);
-  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Whitelist()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Greylist()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxO()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Blacklist()), false);
 
   runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
-  runtime_->SetTargetSdkVersion(static_cast<uint32_t>(SdkVersion::kO_MR1));
-  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
-  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
-  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), false);
-  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), true);
+  runtime_->SetTargetSdkVersion(
+      static_cast<uint32_t>(hiddenapi::ApiList::GreylistMaxO().GetMaxAllowedSdkVersion()));
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Whitelist()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Greylist()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxO()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Blacklist()), true);
 
   runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
-  runtime_->SetTargetSdkVersion(static_cast<uint32_t>(SdkVersion::kP));
-  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kWhitelist), false);
-  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kLightGreylist), false);
-  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kDarkGreylist), true);
-  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::kBlacklist), true);
+  runtime_->SetTargetSdkVersion(
+      static_cast<uint32_t>(hiddenapi::ApiList::GreylistMaxO().GetMaxAllowedSdkVersion()) + 1);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Whitelist()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Greylist()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::GreylistMaxO()), true);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Blacklist()), true);
 }
 
 TEST_F(HiddenApiTest, CheckMembersRead) {
diff --git a/runtime/image.cc b/runtime/image.cc
index 59ac283..3023cef 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -26,7 +26,7 @@
 namespace art {
 
 const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '7', '\0' };  // Added CRC32 intrinsic
+const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '9', '\0' };  // Remove boot oat extents.
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
@@ -39,13 +39,12 @@
                          uint32_t oat_file_end,
                          uint32_t boot_image_begin,
                          uint32_t boot_image_size,
-                         uint32_t boot_oat_begin,
-                         uint32_t boot_oat_size,
                          uint32_t pointer_size,
                          StorageMode storage_mode,
                          size_t data_size)
   : image_begin_(image_begin),
     image_size_(image_size),
+    image_checksum_(0u),
     oat_checksum_(oat_checksum),
     oat_file_begin_(oat_file_begin),
     oat_data_begin_(oat_data_begin),
@@ -53,9 +52,6 @@
     oat_file_end_(oat_file_end),
     boot_image_begin_(boot_image_begin),
     boot_image_size_(boot_image_size),
-    boot_oat_begin_(boot_oat_begin),
-    boot_oat_size_(boot_oat_size),
-    patch_delta_(0),
     image_roots_(image_roots),
     pointer_size_(pointer_size),
     storage_mode_(storage_mode),
@@ -79,7 +75,6 @@
   oat_data_begin_ += delta;
   oat_data_end_ += delta;
   oat_file_end_ += delta;
-  patch_delta_ += delta;
   RelocateImageObjects(delta);
   RelocateImageMethods(delta);
 }
@@ -115,9 +110,6 @@
   if (oat_file_begin_ >= oat_data_begin_) {
     return false;
   }
-  if (!IsAligned<kPageSize>(patch_delta_)) {
-    return false;
-  }
   return true;
 }
 
diff --git a/runtime/image.h b/runtime/image.h
index d925956..f33b9b2 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -93,23 +93,7 @@
   };
   static constexpr StorageMode kDefaultStorageMode = kStorageModeUncompressed;
 
-  ImageHeader()
-      : image_begin_(0U),
-        image_size_(0U),
-        oat_checksum_(0U),
-        oat_file_begin_(0U),
-        oat_data_begin_(0U),
-        oat_data_end_(0U),
-        oat_file_end_(0U),
-        boot_image_begin_(0U),
-        boot_image_size_(0U),
-        boot_oat_begin_(0U),
-        boot_oat_size_(0U),
-        patch_delta_(0),
-        image_roots_(0U),
-        pointer_size_(0U),
-        storage_mode_(kDefaultStorageMode),
-        data_size_(0) {}
+  ImageHeader() {}
 
   ImageHeader(uint32_t image_begin,
               uint32_t image_size,
@@ -122,8 +106,6 @@
               uint32_t oat_file_end,
               uint32_t boot_image_begin,
               uint32_t boot_image_size,
-              uint32_t boot_oat_begin,
-              uint32_t boot_oat_size,
               uint32_t pointer_size,
               StorageMode storage_mode,
               size_t data_size);
@@ -136,7 +118,15 @@
   }
 
   size_t GetImageSize() const {
-    return static_cast<uint32_t>(image_size_);
+    return image_size_;
+  }
+
+  uint32_t GetImageChecksum() const {
+    return image_checksum_;
+  }
+
+  void SetImageChecksum(uint32_t image_checksum) {
+    image_checksum_ = image_checksum;
   }
 
   uint32_t GetOatChecksum() const {
@@ -171,14 +161,6 @@
     return pointer_size_;
   }
 
-  int32_t GetPatchDelta() const {
-    return patch_delta_;
-  }
-
-  void SetPatchDelta(int32_t patch_delta) {
-    patch_delta_ = patch_delta;
-  }
-
   static std::string GetOatLocationFromImageLocation(const std::string& image) {
     return GetLocationFromImageLocation(image, "oat");
   }
@@ -322,14 +304,6 @@
     return boot_image_size_;
   }
 
-  uint32_t GetBootOatBegin() const {
-    return boot_oat_begin_;
-  }
-
-  uint32_t GetBootOatSize() const {
-    return boot_oat_size_;
-  }
-
   StorageMode GetStorageMode() const {
     return storage_mode_;
   }
@@ -390,45 +364,39 @@
   uint8_t version_[4];
 
   // Required base address for mapping the image.
-  uint32_t image_begin_;
+  uint32_t image_begin_ = 0u;
 
   // Image size, not page aligned.
-  uint32_t image_size_;
+  uint32_t image_size_ = 0u;
+
+  // Image file checksum (calculated with the checksum field set to 0).
+  uint32_t image_checksum_ = 0u;
 
   // Checksum of the oat file we link to for load time sanity check.
-  uint32_t oat_checksum_;
+  uint32_t oat_checksum_ = 0u;
 
   // Start address for oat file. Will be before oat_data_begin_ for .so files.
-  uint32_t oat_file_begin_;
+  uint32_t oat_file_begin_ = 0u;
 
   // Required oat address expected by image Method::GetCode() pointers.
-  uint32_t oat_data_begin_;
+  uint32_t oat_data_begin_ = 0u;
 
   // End of oat data address range for this image file.
-  uint32_t oat_data_end_;
+  uint32_t oat_data_end_ = 0u;
 
   // End of oat file address range. will be after oat_data_end_ for
   // .so files. Used for positioning a following alloc spaces.
-  uint32_t oat_file_end_;
+  uint32_t oat_file_end_ = 0u;
 
   // Boot image begin and end (app image headers only).
-  uint32_t boot_image_begin_;
-  uint32_t boot_image_size_;
-
-  // Boot oat begin and end (app image headers only).
-  uint32_t boot_oat_begin_;
-  uint32_t boot_oat_size_;
-
-  // TODO: We should probably insert a boot image checksum for app images.
-
-  // The total delta that this image has been patched.
-  int32_t patch_delta_;
+  uint32_t boot_image_begin_ = 0u;
+  uint32_t boot_image_size_ = 0u;  // Includes heap (*.art) and code (.oat).
 
   // Absolute address of an Object[] of objects needed to reinitialize from an image.
-  uint32_t image_roots_;
+  uint32_t image_roots_ = 0u;
 
   // Pointer size, this affects the size of the ArtMethods.
-  uint32_t pointer_size_;
+  uint32_t pointer_size_ = 0u;
 
   // Image section sizes/offsets correspond to the uncompressed form.
   ImageSection sections_[kSectionCount];
@@ -437,11 +405,11 @@
   uint64_t image_methods_[kImageMethodsCount];
 
   // Storage method for the image, the image may be compressed.
-  StorageMode storage_mode_;
+  StorageMode storage_mode_ = kDefaultStorageMode;
 
   // Data size for the image data excluding the bitmap and the header. For compressed images, this
   // is the compressed size in the file.
-  uint32_t data_size_;
+  uint32_t data_size_ = 0u;
 
   friend class linker::ImageWriter;
 };
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 1701ca8..0bdb0c9 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -1806,8 +1806,10 @@
 
 void JitCodeCache::GetProfiledMethods(const std::set<std::string>& dex_base_locations,
                                       std::vector<ProfileMethodInfo>& methods) {
+  Thread* self = Thread::Current();
+  WaitUntilInlineCacheAccessible(self);
+  MutexLock mu(self, lock_);
   ScopedTrace trace(__FUNCTION__);
-  MutexLock mu(Thread::Current(), lock_);
   uint16_t jit_compile_threshold = Runtime::Current()->GetJITOptions()->GetCompileThreshold();
   for (const ProfilingInfo* info : profiling_infos_) {
     ArtMethod* method = info->GetMethod();
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 519eed7..e931b28 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -17,7 +17,6 @@
 #include "oat.h"
 
 #include <string.h>
-#include <zlib.h>
 
 #include "android-base/stringprintf.h"
 
@@ -68,7 +67,7 @@
                      const InstructionSetFeatures* instruction_set_features,
                      uint32_t dex_file_count,
                      const SafeMap<std::string, std::string>* variable_data)
-    : adler32_checksum_(adler32(0L, Z_NULL, 0)),
+    : oat_checksum_(0u),
       instruction_set_(instruction_set),
       instruction_set_features_bitmap_(instruction_set_features->AsBitmap()),
       dex_file_count_(dex_file_count),
@@ -81,7 +80,7 @@
       quick_imt_conflict_trampoline_offset_(0),
       quick_resolution_trampoline_offset_(0),
       quick_to_interpreter_bridge_offset_(0),
-      image_file_location_oat_checksum_(0) {
+      boot_image_checksum_(0) {
   // Don't want asserts in header as they would be checked in each file that includes it. But the
   // fields are private, so we check inside a method.
   static_assert(sizeof(magic_) == sizeof(kOatMagic),
@@ -143,47 +142,11 @@
 
 uint32_t OatHeader::GetChecksum() const {
   CHECK(IsValid());
-  return adler32_checksum_;
+  return oat_checksum_;
 }
 
-void OatHeader::UpdateChecksumWithHeaderData() {
-  UpdateChecksum(&instruction_set_, sizeof(instruction_set_));
-  UpdateChecksum(&instruction_set_features_bitmap_, sizeof(instruction_set_features_bitmap_));
-  UpdateChecksum(&dex_file_count_, sizeof(dex_file_count_));
-  UpdateChecksum(&image_file_location_oat_checksum_, sizeof(image_file_location_oat_checksum_));
-
-  // Update checksum for variable data size.
-  UpdateChecksum(&key_value_store_size_, sizeof(key_value_store_size_));
-
-  // Update for data, if existing.
-  if (key_value_store_size_ > 0U) {
-    UpdateChecksum(&key_value_store_, key_value_store_size_);
-  }
-
-  UpdateChecksum(&executable_offset_, sizeof(executable_offset_));
-  UpdateChecksum(&interpreter_to_interpreter_bridge_offset_,
-                 sizeof(interpreter_to_interpreter_bridge_offset_));
-  UpdateChecksum(&interpreter_to_compiled_code_bridge_offset_,
-                 sizeof(interpreter_to_compiled_code_bridge_offset_));
-  UpdateChecksum(&jni_dlsym_lookup_offset_, sizeof(jni_dlsym_lookup_offset_));
-  UpdateChecksum(&quick_generic_jni_trampoline_offset_,
-                 sizeof(quick_generic_jni_trampoline_offset_));
-  UpdateChecksum(&quick_imt_conflict_trampoline_offset_,
-                 sizeof(quick_imt_conflict_trampoline_offset_));
-  UpdateChecksum(&quick_resolution_trampoline_offset_,
-                 sizeof(quick_resolution_trampoline_offset_));
-  UpdateChecksum(&quick_to_interpreter_bridge_offset_,
-                 sizeof(quick_to_interpreter_bridge_offset_));
-}
-
-void OatHeader::UpdateChecksum(const void* data, size_t length) {
-  DCHECK(IsValid());
-  if (data != nullptr) {
-    const uint8_t* bytes = reinterpret_cast<const uint8_t*>(data);
-    adler32_checksum_ = adler32(adler32_checksum_, bytes, length);
-  } else {
-    DCHECK_EQ(0U, length);
-  }
+void OatHeader::SetChecksum(uint32_t oat_checksum) {
+  oat_checksum_ = oat_checksum;
 }
 
 InstructionSet OatHeader::GetInstructionSet() const {
@@ -353,14 +316,14 @@
   quick_to_interpreter_bridge_offset_ = offset;
 }
 
-uint32_t OatHeader::GetImageFileLocationOatChecksum() const {
+uint32_t OatHeader::GetBootImageChecksum() const {
   CHECK(IsValid());
-  return image_file_location_oat_checksum_;
+  return boot_image_checksum_;
 }
 
-void OatHeader::SetImageFileLocationOatChecksum(uint32_t image_file_location_oat_checksum) {
+void OatHeader::SetBootImageChecksum(uint32_t boot_image_checksum) {
   CHECK(IsValid());
-  image_file_location_oat_checksum_ = image_file_location_oat_checksum;
+  boot_image_checksum_ = boot_image_checksum;
 }
 
 uint32_t OatHeader::GetKeyValueStoreSize() const {
diff --git a/runtime/oat.h b/runtime/oat.h
index 3d6415e..ee46f42 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -31,8 +31,8 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  // Last oat version changed reason: Remove interpreter alt tables.
-  static constexpr uint8_t kOatVersion[] = { '1', '6', '3', '\0' };
+  // Last oat version changed reason: Image checksums.
+  static constexpr uint8_t kOatVersion[] = { '1', '6', '4', '\0' };
 
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
   static constexpr const char* kDebuggableKey = "debuggable";
@@ -56,8 +56,7 @@
   std::string GetValidationErrorMessage() const;
   const char* GetMagic() const;
   uint32_t GetChecksum() const;
-  void UpdateChecksumWithHeaderData();
-  void UpdateChecksum(const void* data, size_t length);
+  void SetChecksum(uint32_t checksum);
   uint32_t GetDexFileCount() const {
     DCHECK(IsValid());
     return dex_file_count_;
@@ -94,8 +93,8 @@
   InstructionSet GetInstructionSet() const;
   uint32_t GetInstructionSetFeaturesBitmap() const;
 
-  uint32_t GetImageFileLocationOatChecksum() const;
-  void SetImageFileLocationOatChecksum(uint32_t image_file_location_oat_checksum);
+  uint32_t GetBootImageChecksum() const;
+  void SetBootImageChecksum(uint32_t boot_image_checksum);
 
   uint32_t GetKeyValueStoreSize() const;
   const uint8_t* GetKeyValueStore() const;
@@ -123,7 +122,7 @@
 
   uint8_t magic_[4];
   uint8_t version_[4];
-  uint32_t adler32_checksum_;
+  uint32_t oat_checksum_;
 
   InstructionSet instruction_set_;
   uint32_t instruction_set_features_bitmap_;
@@ -138,7 +137,7 @@
   uint32_t quick_resolution_trampoline_offset_;
   uint32_t quick_to_interpreter_bridge_offset_;
 
-  uint32_t image_file_location_oat_checksum_;
+  uint32_t boot_image_checksum_;
 
   uint32_t key_value_store_size_;
   uint8_t key_value_store_[0];  // note variable width data at end
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index af87637..c1ccd33 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -33,6 +33,7 @@
 #include "android/dlext.h"
 #endif
 
+#include <android-base/logging.h>
 #include "android-base/stringprintf.h"
 
 #include "art_method.h"
@@ -467,6 +468,7 @@
       }
       prev_entry = &entry;
     }
+    CHECK(prev_entry != nullptr);
     CHECK_LT(prev_entry->GetIndex(index_bits), number_of_indexes);
   }
 }
@@ -1755,11 +1757,15 @@
   }
 }
 
-OatDexFile::OatDexFile(TypeLookupTable&& lookup_table) : lookup_table_(std::move(lookup_table)) {}
+OatDexFile::OatDexFile(TypeLookupTable&& lookup_table) : lookup_table_(std::move(lookup_table)) {
+  // Stripped-down OatDexFile only allowed in the compiler.
+  CHECK(Runtime::Current() == nullptr || Runtime::Current()->IsAotCompiler());
+}
 
 OatDexFile::~OatDexFile() {}
 
 size_t OatDexFile::FileSize() const {
+  DCHECK(dex_file_pointer_ != nullptr);
   return reinterpret_cast<const DexFile::Header*>(dex_file_pointer_)->file_size_;
 }
 
@@ -1779,6 +1785,7 @@
 }
 
 uint32_t OatDexFile::GetOatClassOffset(uint16_t class_def_index) const {
+  DCHECK(oat_class_offsets_pointer_ != nullptr);
   return oat_class_offsets_pointer_[class_def_index];
 }
 
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index a06be4c..6f32b98 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -419,7 +419,7 @@
       // starts up.
       LOG(WARNING) << "Dex location " << dex_location_ << " does not seem to include dex file. "
         << "Allow oat file use. This is potentially dangerous.";
-    } else if (file.GetOatHeader().GetImageFileLocationOatChecksum() != image_info->oat_checksum) {
+    } else if (file.GetOatHeader().GetBootImageChecksum() != image_info->boot_image_checksum) {
       VLOG(oat) << "Oat image checksum does not match image checksum.";
       return kOatBootImageOutOfDate;
     }
@@ -574,8 +574,7 @@
     return nullptr;
   }
 
-  info->oat_checksum = image_header->GetOatChecksum();
-  info->patch_delta = image_header->GetPatchDelta();
+  info->boot_image_checksum = image_header->GetImageChecksum();
   return info;
 }
 
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 590ae22..09c9d3b 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -246,8 +246,7 @@
 
  private:
   struct ImageInfo {
-    uint32_t oat_checksum = 0;
-    int32_t patch_delta = 0;
+    uint32_t boot_image_checksum = 0;
     std::string location;
 
     static std::unique_ptr<ImageInfo> GetRuntimeImageInfo(InstructionSet isa,
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 7ac1ab4..2c882ec 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -118,9 +118,10 @@
 }
 
 std::vector<const OatFile*> OatFileManager::GetBootOatFiles() const {
-  std::vector<const OatFile*> oat_files;
   std::vector<gc::space::ImageSpace*> image_spaces =
       Runtime::Current()->GetHeap()->GetBootImageSpaces();
+  std::vector<const OatFile*> oat_files;
+  oat_files.reserve(image_spaces.size());
   for (gc::space::ImageSpace* image_space : image_spaces) {
     oat_files.push_back(image_space->GetOatFile());
   }
@@ -153,6 +154,7 @@
 std::vector<const OatFile*> OatFileManager::RegisterImageOatFiles(
     const std::vector<gc::space::ImageSpace*>& spaces) {
   std::vector<const OatFile*> oat_files;
+  oat_files.reserve(spaces.size());
   for (gc::space::ImageSpace* space : spaces) {
     oat_files.push_back(RegisterOatFile(space->ReleaseOatFile()));
   }
@@ -290,10 +292,12 @@
 
   // Generate type index information for each dex file.
   std::vector<TypeIndexInfo> loaded_types;
+  loaded_types.reserve(dex_files_loaded.size());
   for (const DexFile* dex_file : dex_files_loaded) {
     loaded_types.push_back(TypeIndexInfo(dex_file));
   }
   std::vector<TypeIndexInfo> unloaded_types;
+  unloaded_types.reserve(dex_files_unloaded.size());
   for (const DexFile* dex_file : dex_files_unloaded) {
     unloaded_types.push_back(TypeIndexInfo(dex_file));
   }
diff --git a/runtime/thread.h b/runtime/thread.h
index b304cef..ccde236 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -17,8 +17,6 @@
 #ifndef ART_RUNTIME_THREAD_H_
 #define ART_RUNTIME_THREAD_H_
 
-#include <setjmp.h>
-
 #include <atomic>
 #include <bitset>
 #include <deque>
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index bd59e73..f24711a 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -31,7 +31,6 @@
 #include "dex/class_accessor-inl.h"
 #include "dex/dex_file.h"
 #include "dex/dex_file_loader.h"
-#include "dex/hidden_api_access_flags.h"
 #include "dex_to_dex_decompiler.h"
 #include "quicken_info.h"
 
diff --git a/test/411-checker-hdiv-hrem-pow2/src/DivTest.java b/test/411-checker-hdiv-hrem-pow2/src/DivTest.java
index a3882e7..1a086ef 100644
--- a/test/411-checker-hdiv-hrem-pow2/src/DivTest.java
+++ b/test/411-checker-hdiv-hrem-pow2/src/DivTest.java
@@ -94,6 +94,9 @@
   /// CHECK-START-ARM64: java.lang.Integer DivTest.$noinline$IntDivBy2(int) disassembly (after)
   /// CHECK:                 add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31
   /// CHECK:                 asr w{{\d+}}, w{{\d+}}, #1
+  /// CHECK-START-X86_64: java.lang.Integer DivTest.$noinline$IntDivBy2(int) disassembly (after)
+  /// CHECK-NOT:             cmovnl/geq
+  /// CHECK:                 add
   private static Integer $noinline$IntDivBy2(int v) {
     int r = v / 2;
     return r;
@@ -102,6 +105,9 @@
   /// CHECK-START-ARM64: java.lang.Integer DivTest.$noinline$IntDivByMinus2(int) disassembly (after)
   /// CHECK:                 add w{{\d+}}, w{{\d+}}, w{{\d+}}, lsr #31
   /// CHECK:                 neg w{{\d+}}, w{{\d+}}, asr #1
+  /// CHECK-START-X86_64: java.lang.Integer DivTest.$noinline$IntDivByMinus2(int) disassembly (after)
+  /// CHECK-NOT:             cmovnl/geq
+  /// CHECK:                 add
   private static Integer $noinline$IntDivByMinus2(int v) {
     int r = v / -2;
     return r;
@@ -205,6 +211,9 @@
   /// CHECK-START-ARM64: java.lang.Long DivTest.$noinline$LongDivBy2(long) disassembly (after)
   /// CHECK:                 add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63
   /// CHECK:                 asr x{{\d+}}, x{{\d+}}, #1
+  /// CHECK-START-X86_64: java.lang.Long DivTest.$noinline$LongDivBy2(long) disassembly (after)
+  /// CHECK-NOT:             cmovnl/geq
+  /// CHECK:                 addq
   private static Long $noinline$LongDivBy2(long v) {
     long r = v / 2;
     return r;
@@ -213,6 +222,9 @@
   /// CHECK-START-ARM64: java.lang.Long DivTest.$noinline$LongDivByMinus2(long) disassembly (after)
   /// CHECK:                 add x{{\d+}}, x{{\d+}}, x{{\d+}}, lsr #63
   /// CHECK:                 neg x{{\d+}}, x{{\d+}}, asr #1
+  /// CHECK-START-X86_64: java.lang.Long DivTest.$noinline$LongDivByMinus2(long) disassembly (after)
+  /// CHECK-NOT:             cmovnl/geq
+  /// CHECK:                 addq
   private static Long $noinline$LongDivByMinus2(long v) {
     long r = v / -2;
     return r;
diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc
index d11aa57..2464263 100644
--- a/test/674-hiddenapi/hiddenapi.cc
+++ b/test/674-hiddenapi/hiddenapi.cc
@@ -30,7 +30,8 @@
 extern "C" JNIEXPORT void JNICALL Java_Main_init(JNIEnv*, jclass) {
   Runtime* runtime = Runtime::Current();
   runtime->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
-  runtime->SetTargetSdkVersion(static_cast<uint32_t>(SdkVersion::kO_MR1));
+  runtime->SetTargetSdkVersion(
+      static_cast<uint32_t>(hiddenapi::ApiList::GreylistMaxO().GetMaxAllowedSdkVersion()));
   runtime->SetDedupeHiddenApiWarnings(false);
 }
 
diff --git a/tools/build_linux_bionic_tests.sh b/tools/build_linux_bionic_tests.sh
index 3ee7d50..2b178f2 100755
--- a/tools/build_linux_bionic_tests.sh
+++ b/tools/build_linux_bionic_tests.sh
@@ -29,6 +29,7 @@
 source build/envsetup.sh >&/dev/null # for get_build_var
 
 out_dir=$(get_build_var OUT_DIR)
+host_out=$(get_build_var HOST_OUT)
 
 # TODO(b/31559095) Figure out a better way to do this.
 #
@@ -80,8 +81,8 @@
   $soong_out/bin/hiddenapi
   $soong_out/bin/hprof-conv
   $soong_out/bin/timeout_dumper
-  $(find $ANDROID_HOST_OUT/lib64 -type f | sed "s:$ANDROID_HOST_OUT:$soong_out:g")
-  $(find $ANDROID_HOST_OUT/nativetest64 -type f | sed "s:$ANDROID_HOST_OUT:$soong_out:g"))
+  $(find $host_out/lib64 -type f | sed "s:$host_out:$soong_out:g")
+  $(find $host_out/nativetest64 -type f | sed "s:$host_out:$soong_out:g"))
 
 echo building ${bionic_targets[*]}
 
diff --git a/tools/hiddenapi/hiddenapi.cc b/tools/hiddenapi/hiddenapi.cc
index 65a4945..7f4c546 100644
--- a/tools/hiddenapi/hiddenapi.cc
+++ b/tools/hiddenapi/hiddenapi.cc
@@ -23,14 +23,14 @@
 #include "android-base/strings.h"
 
 #include "base/bit_utils.h"
-#include "base/stl_util.h"
+#include "base/hiddenapi_flags.h"
 #include "base/mem_map.h"
 #include "base/os.h"
+#include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
 #include "dex/art_dex_file_loader.h"
 #include "dex/class_accessor-inl.h"
 #include "dex/dex_file-inl.h"
-#include "dex/hidden_api_access_flags.h"
 
 namespace art {
 
@@ -39,6 +39,7 @@
 
 static std::string CommandLine() {
   std::vector<std::string> command;
+  command.reserve(original_argc);
   for (int i = 0; i < original_argc; ++i) {
     command.push_back(original_argv[i]);
   }
@@ -600,7 +601,7 @@
   // between BeginClassDef and EndClassDef in the order of appearance of
   // fields/methods in the class data stream.
   void WriteFlags(hiddenapi::ApiList flags) {
-    uint32_t uint_flags = static_cast<uint32_t>(flags);
+    uint32_t uint_flags = flags.GetIntValue();
     EncodeUnsignedLeb128(&data_, uint_flags);
     class_def_has_non_zero_flags_ |= (uint_flags != 0u);
   }
@@ -934,9 +935,9 @@
 
     // Load dex signatures.
     std::map<std::string, hiddenapi::ApiList> api_list;
-    OpenApiFile(light_greylist_path_, api_list, hiddenapi::ApiList::kLightGreylist);
-    OpenApiFile(dark_greylist_path_, api_list, hiddenapi::ApiList::kDarkGreylist);
-    OpenApiFile(blacklist_path_, api_list, hiddenapi::ApiList::kBlacklist);
+    OpenApiFile(light_greylist_path_, api_list, hiddenapi::ApiList::Greylist());
+    OpenApiFile(dark_greylist_path_, api_list, hiddenapi::ApiList::GreylistMaxO());
+    OpenApiFile(blacklist_path_, api_list, hiddenapi::ApiList::Blacklist());
 
     // Iterate over input dex files and insert HiddenapiClassData sections.
     for (size_t i = 0; i < boot_dex_paths_.size(); ++i) {
@@ -956,7 +957,7 @@
             // TODO: Load whitelist and CHECK that entry was found.
             auto it = api_list.find(boot_member.GetApiEntry());
             builder.WriteFlags(
-                (it == api_list.end()) ? hiddenapi::ApiList::kWhitelist : it->second);
+                (it == api_list.end()) ? hiddenapi::ApiList::Whitelist() : it->second);
           };
           auto fn_field = [&](const ClassAccessor::Field& boot_field) {
             fn_shared(DexMember(boot_class, boot_field));
@@ -987,7 +988,8 @@
 
     for (std::string line; std::getline(api_file, line);) {
       CHECK(api_list.find(line) == api_list.end())
-          << "Duplicate entry: " << line << " (" << api_list[line] << " and " << membership << ")";
+          << "Duplicate entry: " << line << " (" << api_list.find(line)->second
+          << " and " << membership << ")";
       api_list.emplace(line, membership);
     }
     api_file.close();
diff --git a/tools/hiddenapi/hiddenapi_test.cc b/tools/hiddenapi/hiddenapi_test.cc
index 0010b784..66ce2de 100644
--- a/tools/hiddenapi/hiddenapi_test.cc
+++ b/tools/hiddenapi/hiddenapi_test.cc
@@ -132,7 +132,7 @@
     CHECK(accessor.HasClassData()) << "Class " << accessor.GetDescriptor() << " has no data";
 
     if (!accessor.HasHiddenapiClassData()) {
-      return hiddenapi::ApiList::kWhitelist;
+      return hiddenapi::ApiList::Whitelist();
     }
 
     for (const ClassAccessor::Field& field : accessor.GetFields()) {
@@ -141,7 +141,7 @@
         const uint32_t actual_visibility = field.GetAccessFlags() & kAccVisibilityFlags;
         CHECK_EQ(actual_visibility, expected_visibility)
             << "Field " << name << " in class " << accessor.GetDescriptor();
-        return static_cast<hiddenapi::ApiList>(field.GetHiddenapiFlags());
+        return hiddenapi::ApiList::FromDexFlags(field.GetHiddenapiFlags());
       }
     }
 
@@ -159,7 +159,7 @@
     CHECK(accessor.HasClassData()) << "Class " << accessor.GetDescriptor() << " has no data";
 
     if (!accessor.HasHiddenapiClassData()) {
-      return hiddenapi::ApiList::kWhitelist;
+      return hiddenapi::ApiList::Whitelist();
     }
 
     for (const ClassAccessor::Method& method : accessor.GetMethods()) {
@@ -170,7 +170,7 @@
         const uint32_t actual_visibility = method.GetAccessFlags() & kAccVisibilityFlags;
         CHECK_EQ(actual_visibility, expected_visibility)
             << "Method " << name << " in class " << accessor.GetDescriptor();
-        return static_cast<hiddenapi::ApiList>(method.GetHiddenapiFlags());
+        return hiddenapi::ApiList::FromDexFlags(method.GetHiddenapiFlags());
       }
     }
 
@@ -224,7 +224,7 @@
   OpenStream(blacklist) << "LMain;->ifield:LBadType3;" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kWhitelist, GetIFieldHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetIFieldHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, InstanceFieldLightGreylistMatch) {
@@ -234,7 +234,7 @@
   OpenStream(blacklist) << "LMain;->ifield:LBadType3;" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kLightGreylist, GetIFieldHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetIFieldHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, InstanceFieldDarkGreylistMatch) {
@@ -244,7 +244,7 @@
   OpenStream(blacklist) << "LMain;->ifield:LBadType3;" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kDarkGreylist, GetIFieldHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetIFieldHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, InstanceFieldBlacklistMatch) {
@@ -254,7 +254,7 @@
   OpenStream(blacklist) << "LMain;->ifield:I" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kBlacklist, GetIFieldHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetIFieldHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, InstanceFieldTwoListsMatch1) {
@@ -291,7 +291,7 @@
   OpenStream(blacklist) << "LMain;->sfield:LBadType3;" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kWhitelist, GetSFieldHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSFieldHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, StaticFieldLightGreylistMatch) {
@@ -301,7 +301,7 @@
   OpenStream(blacklist) << "LMain;->sfield:LBadType3;" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kLightGreylist, GetSFieldHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSFieldHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, StaticFieldDarkGreylistMatch) {
@@ -311,7 +311,7 @@
   OpenStream(blacklist) << "LMain;->sfield:LBadType3;" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kDarkGreylist, GetSFieldHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSFieldHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, StaticFieldBlacklistMatch) {
@@ -321,7 +321,7 @@
   OpenStream(blacklist) << "LMain;->sfield:Ljava/lang/Object;" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kBlacklist, GetSFieldHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSFieldHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, StaticFieldTwoListsMatch1) {
@@ -358,7 +358,7 @@
   OpenStream(blacklist) << "LMain;->imethod(LBadType3;)V" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kWhitelist, GetIMethodHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetIMethodHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, InstanceMethodLightGreylistMatch) {
@@ -368,7 +368,7 @@
   OpenStream(blacklist) << "LMain;->imethod(LBadType3;)V" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kLightGreylist, GetIMethodHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetIMethodHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, InstanceMethodDarkGreylistMatch) {
@@ -378,7 +378,7 @@
   OpenStream(blacklist) << "LMain;->imethod(LBadType3;)V" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kDarkGreylist, GetIMethodHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetIMethodHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, InstanceMethodBlacklistMatch) {
@@ -388,7 +388,7 @@
   OpenStream(blacklist) << "LMain;->imethod(J)V" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kBlacklist, GetIMethodHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetIMethodHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, InstanceMethodTwoListsMatch1) {
@@ -425,7 +425,7 @@
   OpenStream(blacklist) << "LMain;->smethod(LBadType3;)V" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kWhitelist, GetSMethodHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSMethodHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, StaticMethodLightGreylistMatch) {
@@ -435,7 +435,7 @@
   OpenStream(blacklist) << "LMain;->smethod(LBadType3;)V" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kLightGreylist, GetSMethodHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSMethodHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, StaticMethodDarkGreylistMatch) {
@@ -445,7 +445,7 @@
   OpenStream(blacklist) << "LMain;->smethod(LBadType3;)V" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kDarkGreylist, GetSMethodHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSMethodHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, StaticMethodBlacklistMatch) {
@@ -455,7 +455,7 @@
   OpenStream(blacklist) << "LMain;->smethod(Ljava/lang/Object;)V" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kBlacklist, GetSMethodHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSMethodHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, StaticMethodTwoListsMatch1) {
@@ -492,7 +492,7 @@
   OpenStream(blacklist) << "LMain;->inmethod(LBadType3;)V" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kWhitelist, GetINMethodHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetINMethodHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, InstanceNativeMethodLightGreylistMatch) {
@@ -502,7 +502,7 @@
   OpenStream(blacklist) << "LMain;->inmethod(LBadType3;)V" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kLightGreylist, GetINMethodHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetINMethodHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, InstanceNativeMethodDarkGreylistMatch) {
@@ -512,7 +512,7 @@
   OpenStream(blacklist) << "LMain;->inmethod(LBadType3;)V" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kDarkGreylist, GetINMethodHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetINMethodHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, InstanceNativeMethodBlacklistMatch) {
@@ -522,7 +522,7 @@
   OpenStream(blacklist) << "LMain;->inmethod(C)V" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kBlacklist, GetINMethodHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetINMethodHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, InstanceNativeMethodTwoListsMatch1) {
@@ -559,7 +559,7 @@
   OpenStream(blacklist) << "LMain;->snmethod(LBadType3;)V" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kWhitelist, GetSNMethodHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::Whitelist(), GetSNMethodHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, StaticNativeMethodLightGreylistMatch) {
@@ -569,7 +569,7 @@
   OpenStream(blacklist) << "LMain;->snmethod(LBadType3;)V" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kLightGreylist, GetSNMethodHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::Greylist(), GetSNMethodHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, StaticNativeMethodDarkGreylistMatch) {
@@ -579,7 +579,7 @@
   OpenStream(blacklist) << "LMain;->snmethod(LBadType3;)V" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kDarkGreylist, GetSNMethodHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::GreylistMaxO(), GetSNMethodHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, StaticNativeMethodBlacklistMatch) {
@@ -589,7 +589,7 @@
   OpenStream(blacklist) << "LMain;->snmethod(Ljava/lang/Integer;)V" << std::endl;
   auto dex_file = RunHiddenApi(light_greylist, dark_greylist, blacklist, {}, &dex);
   ASSERT_NE(dex_file.get(), nullptr);
-  ASSERT_EQ(hiddenapi::ApiList::kBlacklist, GetSNMethodHiddenFlags(*dex_file));
+  ASSERT_EQ(hiddenapi::ApiList::Blacklist(), GetSNMethodHiddenFlags(*dex_file));
 }
 
 TEST_F(HiddenApiTest, StaticNativeMethodTwoListsMatch1) {
diff --git a/tools/timeout_dumper/Android.bp b/tools/timeout_dumper/Android.bp
index dfd5442..bb813d4 100644
--- a/tools/timeout_dumper/Android.bp
+++ b/tools/timeout_dumper/Android.bp
@@ -22,6 +22,11 @@
         darwin: {
             enabled: false,
         },
+        linux_bionic: {
+            sanitize: {
+                address: false,
+            },
+        },
     },
     device_supported: false,
 
diff --git a/tools/veridex/hidden_api.h b/tools/veridex/hidden_api.h
index da9f058..b58332b 100644
--- a/tools/veridex/hidden_api.h
+++ b/tools/veridex/hidden_api.h
@@ -17,7 +17,7 @@
 #ifndef ART_TOOLS_VERIDEX_HIDDEN_API_H_
 #define ART_TOOLS_VERIDEX_HIDDEN_API_H_
 
-#include "dex/hidden_api_access_flags.h"
+#include "base/hiddenapi_flags.h"
 #include "dex/method_reference.h"
 
 #include <ostream>
@@ -45,20 +45,20 @@
 
   hiddenapi::ApiList GetApiList(const std::string& name) const {
     if (IsInList(name, blacklist_)) {
-      return hiddenapi::ApiList::kBlacklist;
+      return hiddenapi::ApiList::Blacklist();
     } else if (IsInList(name, dark_greylist_)) {
-      return hiddenapi::ApiList::kDarkGreylist;
+      return hiddenapi::ApiList::GreylistMaxO();
     } else if (IsInList(name, light_greylist_)) {
-      return hiddenapi::ApiList::kLightGreylist;
+      return hiddenapi::ApiList::Greylist();
     } else if (IsInList(name, whitelist_)) {
-      return hiddenapi::ApiList::kWhitelist;
+      return hiddenapi::ApiList::Whitelist();
     } else {
-      return hiddenapi::ApiList::kNoList;
+      return hiddenapi::ApiList::Invalid();
     }
   }
 
   bool IsInAnyList(const std::string& name) const {
-    return GetApiList(name) != hiddenapi::ApiList::kNoList;
+    return GetApiList(name).IsValid();
   }
 
   static std::string GetApiMethodName(const DexFile& dex_file, uint32_t method_index);
@@ -92,7 +92,7 @@
   uint32_t count = 0;
   uint32_t reflection_count = 0;
   uint32_t linking_count = 0;
-  uint32_t api_counts[5] = { 0, 0, 0, 0, 0 };
+  uint32_t api_counts[hiddenapi::ApiList::kValueCount] = {};  // initialize all to zero
 };
 
 }  // namespace art
diff --git a/tools/veridex/hidden_api_finder.cc b/tools/veridex/hidden_api_finder.cc
index e24d151..3cd7c95 100644
--- a/tools/veridex/hidden_api_finder.cc
+++ b/tools/veridex/hidden_api_finder.cc
@@ -180,7 +180,7 @@
   for (const std::pair<const std::string,
                        std::vector<MethodReference>>& pair : method_locations_) {
     hiddenapi::ApiList api_list = hidden_api_.GetApiList(pair.first);
-    stats->api_counts[static_cast<unsigned>(api_list)]++;
+    stats->api_counts[api_list.GetIntValue()]++;
     os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):";
     os << std::endl;
     HiddenApiFinder::DumpReferences(os, pair.second);
@@ -191,7 +191,7 @@
   for (const std::pair<const std::string,
                        std::vector<MethodReference>>& pair : field_locations_) {
     hiddenapi::ApiList api_list = hidden_api_.GetApiList(pair.first);
-    stats->api_counts[static_cast<unsigned>(api_list)]++;
+    stats->api_counts[api_list.GetIntValue()]++;
     os << "#" << ++stats->count << ": Linking " << api_list << " " << pair.first << " use(s):";
     os << std::endl;
     HiddenApiFinder::DumpReferences(os, pair.second);
@@ -204,8 +204,8 @@
       for (const std::string& name : strings_) {
         std::string full_name = cls + "->" + name;
         hiddenapi::ApiList api_list = hidden_api_.GetApiList(full_name);
-        stats->api_counts[static_cast<unsigned>(api_list)]++;
-        if (api_list != hiddenapi::ApiList::kNoList) {
+        if (api_list.IsValid()) {
+          stats->api_counts[api_list.GetIntValue()]++;
           stats->reflection_count++;
           os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name
              << " potential use(s):";
diff --git a/tools/veridex/precise_hidden_api_finder.cc b/tools/veridex/precise_hidden_api_finder.cc
index 6aef89f..be99ed2 100644
--- a/tools/veridex/precise_hidden_api_finder.cc
+++ b/tools/veridex/precise_hidden_api_finder.cc
@@ -91,8 +91,7 @@
       std::string cls(info.cls.ToString());
       std::string name(info.name.ToString());
       std::string full_name = cls + "->" + name;
-      hiddenapi::ApiList api_list = hidden_api_.GetApiList(full_name);
-      if (api_list != hiddenapi::ApiList::kNoList) {
+      if (hidden_api_.IsInAnyList(full_name)) {
         named_uses[full_name].push_back(ref);
       }
     }
@@ -102,7 +101,7 @@
     ++stats->reflection_count;
     const std::string& full_name = it.first;
     hiddenapi::ApiList api_list = hidden_api_.GetApiList(full_name);
-    stats->api_counts[static_cast<unsigned>(api_list)]++;
+    stats->api_counts[api_list.GetIntValue()]++;
     os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name << " use(s):";
     os << std::endl;
     for (const MethodReference& ref : it.second) {
diff --git a/tools/veridex/veridex.cc b/tools/veridex/veridex.cc
index 179e391..2787950 100644
--- a/tools/veridex/veridex.cc
+++ b/tools/veridex/veridex.cc
@@ -271,17 +271,17 @@
                                const VeridexOptions& options) {
     static const char* kPrefix = "       ";
     if (options.only_report_sdk_uses) {
-      os << stats.api_counts[static_cast<unsigned>(hiddenapi::ApiList::kWhitelist)]
+      os << stats.api_counts[hiddenapi::ApiList::Whitelist().GetIntValue()]
          << " SDK API uses." << std::endl;
     } else {
       os << stats.count << " hidden API(s) used: "
          << stats.linking_count << " linked against, "
          << stats.reflection_count << " through reflection" << std::endl;
-      os << kPrefix << stats.api_counts[static_cast<unsigned>(hiddenapi::ApiList::kBlacklist)]
+      os << kPrefix << stats.api_counts[hiddenapi::ApiList::Blacklist().GetIntValue()]
          << " in blacklist" << std::endl;
-      os << kPrefix << stats.api_counts[static_cast<unsigned>(hiddenapi::ApiList::kDarkGreylist)]
+      os << kPrefix << stats.api_counts[hiddenapi::ApiList::GreylistMaxO().GetIntValue()]
          << " in dark greylist" << std::endl;
-      os << kPrefix << stats.api_counts[static_cast<unsigned>(hiddenapi::ApiList::kLightGreylist)]
+      os << kPrefix << stats.api_counts[hiddenapi::ApiList::Greylist().GetIntValue()]
          << " in light greylist" << std::endl;
     }
   }