Merge "Handle multidex in LocationIsOnSystemFramework."
diff --git a/Android.mk b/Android.mk
index 64b9400..d6472be 100644
--- a/Android.mk
+++ b/Android.mk
@@ -110,22 +110,33 @@
 
 # Sync test files to the target, depends upon all things that must be pushed to the target.
 .PHONY: test-art-target-sync
-# Check if we need to sync. In case ART_TEST_ANDROID_ROOT is not empty,
-# the code below uses 'adb push' instead of 'adb sync', which does not
-# check if the files on the device have changed.
+# Check if we need to sync. In case ART_TEST_CHROOT or ART_TEST_ANDROID_ROOT
+# is not empty, the code below uses 'adb push' instead of 'adb sync',
+# which does not check if the files on the device have changed.
+# TODO: Remove support for ART_TEST_ANDROID_ROOT when it is no longer needed.
 ifneq ($(ART_TEST_NO_SYNC),true)
+# Sync system and data partitions.
 ifeq ($(ART_TEST_ANDROID_ROOT),)
+ifeq ($(ART_TEST_CHROOT),)
 test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS)
 	$(TEST_ART_ADB_ROOT_AND_REMOUNT)
 	adb sync system && adb sync data
 else
 test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS)
 	$(TEST_ART_ADB_ROOT_AND_REMOUNT)
-	adb wait-for-device push $(PRODUCT_OUT)/system $(ART_TEST_ANDROID_ROOT)
-# Push the contents of the `data` dir into `/data` on the device.  If
-# `/data` already exists on the device, it is not overwritten, but its
-# contents are updated.
-	adb push $(PRODUCT_OUT)/data /
+	adb wait-for-device
+	adb push $(PRODUCT_OUT)/system $(ART_TEST_CHROOT)/
+	adb push $(PRODUCT_OUT)/data $(ART_TEST_CHROOT)/
+endif
+else
+test-art-target-sync: $(TEST_ART_TARGET_SYNC_DEPS)
+	$(TEST_ART_ADB_ROOT_AND_REMOUNT)
+	adb wait-for-device
+	adb push $(PRODUCT_OUT)/system $(ART_TEST_CHROOT)$(ART_TEST_ANDROID_ROOT)
+# Push the contents of the `data` dir into `$(ART_TEST_CHROOT)/data` on the device (note
+# that $(ART_TEST_CHROOT) can be empty).  If `$(ART_TEST_CHROOT)/data` already exists on
+# the device, it is not overwritten, but its content is updated.
+	adb push $(PRODUCT_OUT)/data $(ART_TEST_CHROOT)/
 endif
 endif
 
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index b46f677..3daaf01 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -472,17 +472,32 @@
 
 $$(gtest_rule) valgrind-$$(gtest_rule): PRIVATE_TARGET_EXE := $$(gtest_target_exe)
 
+ifeq ($(ART_TEST_CHROOT),)
+# Non-chroot configuration.
+maybe_art_test_chroot :=
+maybe_chroot_command :=
+else
+# Chroot configuration.
+maybe_art_test_chroot := $(ART_TEST_CHROOT)/
+maybe_chroot_command := chroot $(ART_TEST_CHROOT)
+endif
+
+# File witnessing the success of the gtest, the presence of which means the gtest's success.
+gtest_witness := \
+  $(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$(gtest_rule)-$$$$PPID
+
+$$(gtest_rule): GTEST_WITNESS := $$(gtest_witness)
+
 .PHONY: $$(gtest_rule)
 $$(gtest_rule): test-art-target-sync
-	$(hide) adb shell touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID
-	$(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID
-	$(hide) adb shell chmod 755 $$(PRIVATE_TARGET_EXE)
+	$(hide) adb shell touch $$(GTEST_WITNESS)
+	$(hide) adb shell rm $$(GTEST_WITNESS)
+	$(hide) adb shell chmod 755 $(maybe_art_test_chroot)$$(PRIVATE_TARGET_EXE)
 	$(hide) $$(call ART_TEST_SKIP,$$@) && \
-	  (adb shell "env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \
+	  (adb shell "$(maybe_chroot_command) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \
 	       ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) $$(PRIVATE_TARGET_EXE) \
-	     && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID" \
-	   && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID /tmp/ \
-	       && $$(call ART_TEST_PASSED,$$@)) \
+	     && touch $$(GTEST_WITNESS)" \
+	   && (adb pull $$(GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \
 	   || $$(call ART_TEST_FAILED,$$@))
 	$(hide) rm -f /tmp/$$@-$$$$PPID
 
@@ -490,21 +505,27 @@
   ART_TEST_TARGET_GTEST_RULES += $$(gtest_rule)
   ART_TEST_TARGET_GTEST_$(1)_RULES += $$(gtest_rule)
 
+# File witnessing the success of the Valgrind gtest, the presence of which means the gtest's
+# success.
+valgrind_gtest_witness := \
+  $(maybe_art_test_chroot)$(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/valgrind-$$(gtest_rule)-$$$$PPID
+
+valgrind-$$(gtest_rule): VALGRIND_GTEST_WITNESS := $$(valgrind_gtest_witness)
+
 .PHONY: valgrind-$$(gtest_rule)
 valgrind-$$(gtest_rule): $(ART_VALGRIND_TARGET_DEPENDENCIES) test-art-target-sync
-	$(hide) adb shell touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID
-	$(hide) adb shell rm $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID
-	$(hide) adb shell chmod 755 $$(PRIVATE_TARGET_EXE)
+	$(hide) adb shell touch $$(VALGRIND_GTEST_WITNESS)
+	$(hide) adb shell rm $$(VALGRIND_GTEST_WITNESS)
+	$(hide) adb shell chmod 755 $(maybe_art_test_chroot)$$(PRIVATE_TARGET_EXE)
 	$(hide) $$(call ART_TEST_SKIP,$$@) && \
-	  (adb shell "env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \
+	  (adb shell "$(maybe_chroot_command) env $(GCOV_ENV) LD_LIBRARY_PATH=$(4) \
 	       ANDROID_ROOT=$(ART_GTEST_TARGET_ANDROID_ROOT) \
-	       $$$$ANDROID_ROOT/bin/valgrind \
+	       $(ART_GTEST_TARGET_ANDROID_ROOT)/bin/valgrind \
 	       --leak-check=full --error-exitcode=1 --workaround-gcc296-bugs=yes \
 	       --suppressions=$(ART_TARGET_TEST_DIR)/valgrind-target-suppressions.txt \
 	       --num-callers=50 --show-mismatched-frees=no $$(PRIVATE_TARGET_EXE) \
-	     && touch $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID" \
-	   && (adb pull $(ART_TARGET_TEST_DIR)/$(TARGET_$(3)ARCH)/$$@-$$$$PPID /tmp/ \
-	       && $$(call ART_TEST_PASSED,$$@)) \
+	     && touch $$(VALGRIND_GTEST_WITNESS)" \
+	   && (adb pull $$(VALGRIND_GTEST_WITNESS) /tmp/ && $$(call ART_TEST_PASSED,$$@)) \
 	   || $$(call ART_TEST_FAILED,$$@))
 	$(hide) rm -f /tmp/$$@-$$$$PPID
 
@@ -514,10 +535,13 @@
   ART_TEST_TARGET_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule)
 
   # Clear locally defined variables.
-  valgrind_gtest_rule :=
-  gtest_rule :=
-  gtest_exe :=
+  valgrind_gtest_witness :=
+  gtest_witness :=
+  maybe_chroot_command :=
+  maybe_art_test_chroot :=
   gtest_target_exe :=
+  gtest_exe :=
+  gtest_rule :=
 endef  # define-art-gtest-rule-target
 
 ART_VALGRIND_DEPENDENCIES := \
@@ -595,10 +619,9 @@
   ART_TEST_HOST_VALGRIND_GTEST_$(1)_RULES += valgrind-$$(gtest_rule)
 
   # Clear locally defined variables.
-  valgrind_gtest_rule :=
-  gtest_rule :=
-  gtest_exe :=
   gtest_deps :=
+  gtest_exe :=
+  gtest_rule :=
 endef  # define-art-gtest-rule-host
 
 # Define the rules to build and run host and target gtests.
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 231017f..1e44311 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -736,6 +736,26 @@
   }
 }
 
+void CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(
+    HLoadMethodType* method_type,
+    Location runtime_proto_index_location,
+    Location runtime_return_location) {
+  DCHECK_EQ(method_type->InputCount(), 1u);
+  LocationSummary* locations =
+      new (method_type->GetBlock()->GetGraph()->GetAllocator()) LocationSummary(
+          method_type, LocationSummary::kCallOnMainOnly);
+  locations->SetInAt(0, Location::NoLocation());
+  locations->AddTemp(runtime_proto_index_location);
+  locations->SetOut(runtime_return_location);
+}
+
+void CodeGenerator::GenerateLoadMethodTypeRuntimeCall(HLoadMethodType* method_type) {
+  LocationSummary* locations = method_type->GetLocations();
+  MoveConstant(locations->GetTemp(0), method_type->GetProtoIndex());
+  CheckEntrypointTypes<kQuickResolveMethodType, void*, uint32_t>();
+  InvokeRuntime(kQuickResolveMethodType, method_type, method_type->GetDexPc());
+}
+
 static uint32_t GetBootImageOffsetImpl(const void* object, ImageHeader::ImageSections section) {
   Runtime* runtime = Runtime::Current();
   DCHECK(runtime->IsAotCompiler());
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index f0c4ee0..7e84a44 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -564,6 +564,11 @@
                                                         Location runtime_return_location);
   void GenerateLoadClassRuntimeCall(HLoadClass* cls);
 
+  static void CreateLoadMethodTypeRuntimeCallLocationSummary(HLoadMethodType* method_type,
+                                                             Location runtime_type_index_location,
+                                                             Location runtime_return_location);
+  void GenerateLoadMethodTypeRuntimeCall(HLoadMethodType* method_type);
+
   uint32_t GetBootImageOffset(HLoadClass* load_class);
   uint32_t GetBootImageOffset(HLoadString* load_string);
   uint32_t GetBootImageOffset(HInvokeStaticOrDirect* invoke);
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index d4cfab8..0601d2d 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -5144,6 +5144,16 @@
   }
 }
 
+void LocationsBuilderARM64::VisitLoadMethodType(HLoadMethodType* load) {
+  InvokeRuntimeCallingConvention calling_convention;
+  Location location = LocationFrom(calling_convention.GetRegisterAt(0));
+  CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location);
+}
+
+void InstructionCodeGeneratorARM64::VisitLoadMethodType(HLoadMethodType* load) {
+  codegen_->GenerateLoadMethodTypeRuntimeCall(load);
+}
+
 static MemOperand GetExceptionTlsAddress() {
   return MemOperand(tr, Thread::ExceptionOffset<kArm64PointerSize>().Int32Value());
 }
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 58ce9aa..33304c6 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -7527,6 +7527,16 @@
   }
 }
 
+void LocationsBuilderARMVIXL::VisitLoadMethodType(HLoadMethodType* load) {
+  InvokeRuntimeCallingConventionARMVIXL calling_convention;
+  Location location = LocationFrom(calling_convention.GetRegisterAt(0));
+  CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location);
+}
+
+void InstructionCodeGeneratorARMVIXL::VisitLoadMethodType(HLoadMethodType* load) {
+  codegen_->GenerateLoadMethodTypeRuntimeCall(load);
+}
+
 void LocationsBuilderARMVIXL::VisitClinitCheck(HClinitCheck* check) {
   LocationSummary* locations =
       new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 25e2edd..3a3fcff 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -8226,6 +8226,16 @@
   }
 }
 
+void LocationsBuilderMIPS::VisitLoadMethodType(HLoadMethodType* load) {
+  InvokeRuntimeCallingConvention calling_convention;
+  Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
+  CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, loc, loc);
+}
+
+void InstructionCodeGeneratorMIPS::VisitLoadMethodType(HLoadMethodType* load) {
+  codegen_->GenerateLoadMethodTypeRuntimeCall(load);
+}
+
 static int32_t GetExceptionTlsOffset() {
   return Thread::ExceptionOffset<kMipsPointerSize>().Int32Value();
 }
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 5b07b55..d6fc9a1 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -6262,6 +6262,16 @@
   }
 }
 
+void LocationsBuilderMIPS64::VisitLoadMethodType(HLoadMethodType* load) {
+  InvokeRuntimeCallingConvention calling_convention;
+  Location loc = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
+  CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, loc, loc);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitLoadMethodType(HLoadMethodType* load) {
+  codegen_->GenerateLoadMethodTypeRuntimeCall(load);
+}
+
 static int32_t GetExceptionTlsOffset() {
   return Thread::ExceptionOffset<kMips64PointerSize>().Int32Value();
 }
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 82d1fda..d18a750 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -6539,6 +6539,16 @@
   }
 }
 
+void LocationsBuilderX86::VisitLoadMethodType(HLoadMethodType* load) {
+  InvokeRuntimeCallingConvention calling_convention;
+  Location location = Location::RegisterLocation(calling_convention.GetRegisterAt(0));
+  CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location);
+}
+
+void InstructionCodeGeneratorX86::VisitLoadMethodType(HLoadMethodType* load) {
+  codegen_->GenerateLoadMethodTypeRuntimeCall(load);
+}
+
 void LocationsBuilderX86::VisitClinitCheck(HClinitCheck* check) {
   LocationSummary* locations =
       new (GetGraph()->GetAllocator()) LocationSummary(check, LocationSummary::kCallOnSlowPath);
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 322b0cf..450c857 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -5908,6 +5908,16 @@
   }
 }
 
+void LocationsBuilderX86_64::VisitLoadMethodType(HLoadMethodType* load) {
+  // Custom calling convention: RAX serves as both input and output.
+  Location location = Location::RegisterLocation(RAX);
+  CodeGenerator::CreateLoadMethodTypeRuntimeCallLocationSummary(load, location, location);
+}
+
+void InstructionCodeGeneratorX86_64::VisitLoadMethodType(HLoadMethodType* load) {
+  codegen_->GenerateLoadMethodTypeRuntimeCall(load);
+}
+
 void InstructionCodeGeneratorX86_64::VisitClinitCheck(HClinitCheck* check) {
   // We assume the class to not be null.
   SlowPathCode* slow_path = new (codegen_->GetScopedAllocator()) LoadClassSlowPathX86_64(
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 54d4644..87ce1f0 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -386,6 +386,13 @@
         << load_class->NeedsAccessCheck() << std::noboolalpha;
   }
 
+  void VisitLoadMethodType(HLoadMethodType* load_method_type) OVERRIDE {
+    StartAttributeStream("load_kind") << "RuntimeCall";
+    const DexFile& dex_file = load_method_type->GetDexFile();
+    const DexFile::ProtoId& proto_id = dex_file.GetProtoId(load_method_type->GetProtoIndex());
+    StartAttributeStream("method_type") << dex_file.GetProtoSignature(proto_id);
+  }
+
   void VisitLoadString(HLoadString* load_string) OVERRIDE {
     StartAttributeStream("load_kind") << load_string->GetLoadKind();
   }
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 9647dd5..61730a8 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -1896,6 +1896,13 @@
   }
 }
 
+void HInstructionBuilder::BuildLoadMethodType(uint16_t proto_idx, uint32_t dex_pc) {
+  const DexFile& dex_file = *dex_compilation_unit_->GetDexFile();
+  HLoadMethodType* load_method_type =
+      new (allocator_) HLoadMethodType(graph_->GetCurrentMethod(), proto_idx, dex_file, dex_pc);
+  AppendInstruction(load_method_type);
+}
+
 void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction,
                                          uint8_t destination,
                                          uint8_t reference,
@@ -2927,6 +2934,13 @@
       break;
     }
 
+    case Instruction::CONST_METHOD_TYPE: {
+      uint16_t proto_idx = instruction.VRegB_21c();
+      BuildLoadMethodType(proto_idx, dex_pc);
+      UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
+      break;
+    }
+
     case Instruction::MOVE_EXCEPTION: {
       AppendInstruction(new (allocator_) HLoadException(dex_pc));
       UpdateLocal(instruction.VRegA_11x(), current_block_->GetLastInstruction());
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index f788292..3fde54c 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -45,6 +45,7 @@
 
 namespace mirror {
 class Class;
+class MethodType;
 }  // namespace mirror
 
 class HInstructionBuilder : public ValueObject {
@@ -239,6 +240,9 @@
   bool LoadClassNeedsAccessCheck(Handle<mirror::Class> klass)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  // Builds a `HLoadMethodType` loading the given `proto_index`.
+  void BuildLoadMethodType(uint16_t proto_idx, uint32_t dex_pc);
+
   // Returns the outer-most compiling method's class.
   ObjPtr<mirror::Class> GetOutermostCompilingClass() const;
 
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index ae1e606..54882ff 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -41,6 +41,7 @@
 #include "intrinsics_enum.h"
 #include "locations.h"
 #include "mirror/class.h"
+#include "mirror/method_type.h"
 #include "offsets.h"
 #include "utils/intrusive_forward_list.h"
 
@@ -1382,6 +1383,7 @@
   M(LessThanOrEqual, Condition)                                         \
   M(LoadClass, Instruction)                                             \
   M(LoadException, Instruction)                                         \
+  M(LoadMethodType, Instruction)                                        \
   M(LoadString, Instruction)                                            \
   M(LongConstant, Constant)                                             \
   M(Max, Instruction)                                                   \
@@ -6498,6 +6500,50 @@
   special_input->AddUseAt(this, 0);
 }
 
+class HLoadMethodType FINAL : public HInstruction {
+ public:
+  HLoadMethodType(HCurrentMethod* current_method,
+                  uint16_t proto_idx,
+                  const DexFile& dex_file,
+                  uint32_t dex_pc)
+      : HInstruction(kLoadMethodType,
+                     DataType::Type::kReference,
+                     SideEffectsForArchRuntimeCalls(),
+                     dex_pc),
+        special_input_(HUserRecord<HInstruction*>(current_method)),
+        proto_idx_(proto_idx),
+        dex_file_(dex_file) {
+  }
+
+  using HInstruction::GetInputRecords;  // Keep the const version visible.
+  ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL {
+    return ArrayRef<HUserRecord<HInstruction*>>(
+        &special_input_, (special_input_.GetInstruction() != nullptr) ? 1u : 0u);
+  }
+
+  bool IsClonable() const OVERRIDE { return true; }
+
+  uint16_t GetProtoIndex() const { return proto_idx_; }
+
+  const DexFile& GetDexFile() const { return dex_file_; }
+
+  static SideEffects SideEffectsForArchRuntimeCalls() {
+    return SideEffects::CanTriggerGC();
+  }
+
+  DECLARE_INSTRUCTION(LoadMethodType);
+
+ protected:
+  DEFAULT_COPY_CONSTRUCTOR(LoadMethodType);
+
+ private:
+  // The special input is the HCurrentMethod for kRuntimeCall.
+  HUserRecord<HInstruction*> special_input_;
+
+  uint16_t proto_idx_;
+  const DexFile& dex_file_;
+};
+
 /**
  * Performs an initialization check on its Class object input.
  */
@@ -7747,11 +7793,13 @@
   return instruction->IsConstant() && instruction->AsConstant()->IsZeroBitPattern();
 }
 
+// Implement HInstruction::Is##type() for concrete instructions.
 #define INSTRUCTION_TYPE_CHECK(type, super)                                    \
   inline bool HInstruction::Is##type() const { return GetKind() == k##type; }
   FOR_EACH_CONCRETE_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
 #undef INSTRUCTION_TYPE_CHECK
 
+// Implement HInstruction::Is##type() for abstract instructions.
 #define INSTRUCTION_TYPE_CHECK_RESULT(type, super)                             \
   std::is_base_of<BaseType, H##type>::value,
 #define INSTRUCTION_TYPE_CHECK(type, super)                                    \
@@ -7766,7 +7814,7 @@
 
   FOR_EACH_ABSTRACT_INSTRUCTION(INSTRUCTION_TYPE_CHECK)
 #undef INSTRUCTION_TYPE_CHECK
-#undef INSTRUCTION_TYPE_CHECK_CASE
+#undef INSTRUCTION_TYPE_CHECK_RESULT
 
 #define INSTRUCTION_TYPE_CAST(type, super)                                     \
   inline const H##type* HInstruction::As##type() const {                       \
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index c47c69a..b15a0ea 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -59,6 +59,12 @@
   return GetRootHandle(handles_, ClassLinker::kJavaLangClass, &class_class_handle_);
 }
 
+ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetMethodTypeClassHandle() {
+  return GetRootHandle(handles_,
+                       ClassLinker::kJavaLangInvokeMethodType,
+                       &method_type_class_handle_);
+}
+
 ReferenceTypeInfo::TypeHandle ReferenceTypePropagation::HandleCache::GetStringClassHandle() {
   return GetRootHandle(handles_, ClassLinker::kJavaLangString, &string_class_handle_);
 }
@@ -89,6 +95,7 @@
   void VisitLoadClass(HLoadClass* load_class) OVERRIDE;
   void VisitInstanceOf(HInstanceOf* load_class) OVERRIDE;
   void VisitClinitCheck(HClinitCheck* clinit_check) OVERRIDE;
+  void VisitLoadMethodType(HLoadMethodType* instr) OVERRIDE;
   void VisitLoadString(HLoadString* instr) OVERRIDE;
   void VisitLoadException(HLoadException* instr) OVERRIDE;
   void VisitNewArray(HNewArray* instr) OVERRIDE;
@@ -668,6 +675,11 @@
   instr->SetReferenceTypeInfo(instr->InputAt(0)->GetReferenceTypeInfo());
 }
 
+void ReferenceTypePropagation::RTPVisitor::VisitLoadMethodType(HLoadMethodType* instr) {
+  instr->SetReferenceTypeInfo(
+      ReferenceTypeInfo::Create(handle_cache_->GetMethodTypeClassHandle(), /* is_exact */ true));
+}
+
 void ReferenceTypePropagation::RTPVisitor::VisitLoadString(HLoadString* instr) {
   instr->SetReferenceTypeInfo(
       ReferenceTypeInfo::Create(handle_cache_->GetStringClassHandle(), /* is_exact */ true));
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 400852f..da2193d 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -75,6 +75,7 @@
 
     ReferenceTypeInfo::TypeHandle GetObjectClassHandle();
     ReferenceTypeInfo::TypeHandle GetClassClassHandle();
+    ReferenceTypeInfo::TypeHandle GetMethodTypeClassHandle();
     ReferenceTypeInfo::TypeHandle GetStringClassHandle();
     ReferenceTypeInfo::TypeHandle GetThrowableClassHandle();
 
@@ -83,6 +84,7 @@
 
     ReferenceTypeInfo::TypeHandle object_class_handle_;
     ReferenceTypeInfo::TypeHandle class_class_handle_;
+    ReferenceTypeInfo::TypeHandle method_type_class_handle_;
     ReferenceTypeInfo::TypeHandle string_class_handle_;
     ReferenceTypeInfo::TypeHandle throwable_class_handle_;
   };
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 674dc9a..ff3e1ba 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -153,7 +153,7 @@
   " 21c:	f8d9 8034 	ldr.w	r8, [r9, #52]	; 0x34\n",
   " 220:	4770      	bx	lr\n",
   " 222:	4660      	mov	r0, ip\n",
-  " 224:	f8d9 c2c4 	ldr.w	ip, [r9, #708]	; 0x2c4\n",
+  " 224:	f8d9 c2c8 	ldr.w	ip, [r9, #712]	; 0x2c8\n",
   " 228:	47e0      	blx	ip\n",
   nullptr
 };
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 63518be..6b65aca 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1257,10 +1257,10 @@
         if (stored_class_loader_context_ == nullptr) {
           Usage("Option --stored-class-loader-context has an incorrect format: %s",
                 stored_context_arg.c_str());
-        } else if (!class_loader_context_->VerifyClassLoaderContextMatch(
+        } else if (class_loader_context_->VerifyClassLoaderContextMatch(
             stored_context_arg,
             /*verify_names*/ false,
-            /*verify_checksums*/ false)) {
+            /*verify_checksums*/ false) != ClassLoaderContext::VerificationResult::kVerifies) {
           Usage(
               "Option --stored-class-loader-context '%s' mismatches --class-loader-context '%s'",
               stored_context_arg.c_str(),
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index 1699153..7bd9f0f 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -497,7 +497,7 @@
   EXPECT_EQ(76U, sizeof(OatHeader));
   EXPECT_EQ(4U, sizeof(OatMethodOffsets));
   EXPECT_EQ(24U, sizeof(OatQuickMethodHeader));
-  EXPECT_EQ(162 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
+  EXPECT_EQ(163 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
             sizeof(QuickEntryPoints));
 }
 
diff --git a/libdexfile/dex/modifiers.h b/libdexfile/dex/modifiers.h
index 2425a58..be82fff 100644
--- a/libdexfile/dex/modifiers.h
+++ b/libdexfile/dex/modifiers.h
@@ -58,6 +58,7 @@
 static constexpr uint32_t kAccSkipAccessChecks =      0x00080000;  // method (runtime, not native)
 // Used by a class to denote that the verifier has attempted to check it at least once.
 static constexpr uint32_t kAccVerificationAttempted = 0x00080000;  // class (runtime)
+static constexpr uint32_t kAccSkipHiddenApiChecks =   0x00100000;  // class (runtime)
 // This is set by the class linker during LinkInterfaceMethods. It is used by a method to represent
 // that it was copied from its declaring class into another class. All methods marked kAccMiranda
 // and kAccDefaultConflict will have this bit set. Any kAccDefault method contained in the methods_
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 116453b..64e6796 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -422,6 +422,7 @@
     srcs: [
         "arch/instruction_set.h",
         "base/mutex.h",
+        "class_loader_context.h",
         "class_status.h",
         "debugger.h",
         "gc_root.h",
diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S
index a930cc4..1a1f4ed 100644
--- a/runtime/arch/arm/quick_entrypoints_arm.S
+++ b/runtime/arch/arm/quick_entrypoints_arm.S
@@ -1015,7 +1015,10 @@
 END \name
 .endm
 
-// Macro for string and type resolution and initialization.
+    /*
+     * Macro for resolution and initialization of indexed DEX file
+     * constants such as classes and strings.
+     */
 .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET
     .extern \entrypoint
 ENTRY \name
@@ -1040,6 +1043,7 @@
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
 
 // Note: Functions `art{Get,Set}<Kind>{Static,Instance}FromCompiledCode` are
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 9ff5ebe..9919e98 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1533,7 +1533,10 @@
 END \name
 .endm
 
-// Macro for string and type resolution and initialization.
+    /*
+     * Macro for resolution and initialization of indexed DEX file
+     * constants such as classes and strings.
+     */
 .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET
     .extern \entrypoint
 ENTRY \name
@@ -1577,6 +1580,7 @@
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
 
 // Note: Functions `art{Get,Set}<Kind>{Static,Instance}FromCompiledCode` are
diff --git a/runtime/arch/mips/entrypoints_init_mips.cc b/runtime/arch/mips/entrypoints_init_mips.cc
index 9418caf..58b9d48 100644
--- a/runtime/arch/mips/entrypoints_init_mips.cc
+++ b/runtime/arch/mips/entrypoints_init_mips.cc
@@ -203,6 +203,8 @@
   static_assert(!IsDirectEntrypoint(kQuickInitializeType), "Non-direct C stub marked direct.");
   qpoints->pResolveString = art_quick_resolve_string;
   static_assert(!IsDirectEntrypoint(kQuickResolveString), "Non-direct C stub marked direct.");
+  qpoints->pResolveMethodType = art_quick_resolve_method_type;
+  static_assert(!IsDirectEntrypoint(kQuickResolveMethodType), "Non-direct C stub marked direct.");
 
   // Field
   qpoints->pSet8Instance = art_quick_set8_instance;
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index d8fe480..104a4ca 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -2027,8 +2027,11 @@
 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32
 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64
 
-// Macro for string and type resolution and initialization.
-// $a0 is both input and output.
+    /*
+     * Macro for resolution and initialization of indexed DEX file
+     * constants such as classes and strings. $a0 is both input and
+     * output.
+     */
 .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET
     .extern \entrypoint
 ENTRY_NO_GP \name
@@ -2053,6 +2056,12 @@
 .endm
 
     /*
+     * Entry from managed code to resolve a method type. On entry, A0 holds the method type index.
+     * On success the MethodType is returned, otherwise an exception is raised.
+     */
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode
+
+    /*
      * Entry from managed code to resolve a string, this stub will allocate a String and deliver an
      * exception on error. On success the String is returned. A0 holds the string index. The fast
      * path check for hit in strings cache has already been performed.
diff --git a/runtime/arch/mips64/quick_entrypoints_mips64.S b/runtime/arch/mips64/quick_entrypoints_mips64.S
index 8d2a7bd..1e94e07 100644
--- a/runtime/arch/mips64/quick_entrypoints_mips64.S
+++ b/runtime/arch/mips64/quick_entrypoints_mips64.S
@@ -1930,8 +1930,11 @@
 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved32_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_32
 GENERATE_ALLOC_ARRAY_TLAB art_quick_alloc_array_resolved64_tlab, artAllocArrayFromCodeResolvedTLAB, COMPUTE_ARRAY_SIZE_64
 
-// Macro for string and type resolution and initialization.
-// $a0 is both input and output.
+    /*
+     * Macro for resolution and initialization of indexed DEX file
+     * constants such as classes and strings. $a0 is both input and
+     * output.
+     */
 .macro ONE_ARG_SAVE_EVERYTHING_DOWNCALL name, entrypoint, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET
     .extern \entrypoint
 ENTRY_NO_GP \name
@@ -1953,6 +1956,12 @@
 .endm
 
     /*
+     * Entry from managed code to resolve a method type. On entry, A0 holds the method type index.
+     * On success the MethodType is returned, otherwise an exception is raised.
+     */
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode
+
+    /*
      * Entry from managed code to resolve a string, this stub will allocate a String and deliver an
      * exception on error. On success the String is returned. A0 holds the string index. The fast
      * path check for hit in strings cache has already been performed.
diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S
index df43aef..0ae6914 100644
--- a/runtime/arch/x86/quick_entrypoints_x86.S
+++ b/runtime/arch/x86/quick_entrypoints_x86.S
@@ -923,7 +923,10 @@
     END_FUNCTION VAR(c_name)
 END_MACRO
 
-// Macro for string and type resolution and initialization.
+    /*
+     * Macro for resolution and initialization of indexed DEX file
+     * constants such as classes and strings.
+     */
 MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET)
     DEFINE_FUNCTION VAR(c_name)
     SETUP_SAVE_EVERYTHING_FRAME ebx, ebx, \runtime_method_offset  // save ref containing registers for GC
@@ -932,7 +935,7 @@
     CFI_ADJUST_CFA_OFFSET(8)
     pushl %fs:THREAD_SELF_OFFSET                      // pass Thread::Current()
     CFI_ADJUST_CFA_OFFSET(4)
-    PUSH eax                                          // pass arg1
+    PUSH eax                                          // pass the index of the constant as arg1
     call CALLVAR(cxx_name)                            // cxx_name(arg1, Thread*)
     addl MACRO_LITERAL(16), %esp                      // pop arguments
     CFI_ADJUST_CFA_OFFSET(-16)
@@ -1278,6 +1281,7 @@
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
 
 TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO
diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
index 4f941e1..8fef802 100644
--- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S
+++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S
@@ -951,12 +951,15 @@
     END_FUNCTION VAR(c_name)
 END_MACRO
 
-// Macro for string and type resolution and initialization.
+    /*
+     * Macro for resolution and initialization of indexed DEX file
+     * constants such as classes and strings.
+     */
 MACRO3(ONE_ARG_SAVE_EVERYTHING_DOWNCALL, c_name, cxx_name, runtime_method_offset = RUNTIME_SAVE_EVERYTHING_METHOD_OFFSET)
     DEFINE_FUNCTION VAR(c_name)
     SETUP_SAVE_EVERYTHING_FRAME \runtime_method_offset  // save everything for GC
     // Outgoing argument set up
-    movl %eax, %edi                               // pass string index
+    movl %eax, %edi                               // pass the index of the constant as arg0
     movq %gs:THREAD_SELF_OFFSET, %rsi             // pass Thread::Current()
     call CALLVAR(cxx_name)                        // cxx_name(arg0, Thread*)
     testl %eax, %eax                              // If result is null, deliver the OOME.
@@ -1298,6 +1301,7 @@
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_static_storage, artInitializeStaticStorageFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL_FOR_CLINIT art_quick_initialize_type, artInitializeTypeFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_initialize_type_and_verify_access, artInitializeTypeAndVerifyAccessFromCode
+ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_method_type, artResolveMethodTypeFromCode
 ONE_ARG_SAVE_EVERYTHING_DOWNCALL art_quick_resolve_string, artResolveStringFromCode
 
 TWO_ARG_REF_DOWNCALL art_quick_handle_fill_data, artHandleFillArrayDataFromCode, RETURN_IF_EAX_ZERO
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 2f7d6ab..705e1ff 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -73,7 +73,7 @@
 
 // Offset of field Thread::tlsPtr_.mterp_current_ibase.
 #define THREAD_CURRENT_IBASE_OFFSET \
-    (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 162) * __SIZEOF_POINTER__)
+    (THREAD_LOCAL_OBJECTS_OFFSET + __SIZEOF_SIZE_T__ + (1 + 163) * __SIZEOF_POINTER__)
 ADD_TEST_EQ(THREAD_CURRENT_IBASE_OFFSET,
             art::Thread::MterpCurrentIBaseOffset<POINTER_SIZE>().Int32Value())
 // Offset of field Thread::tlsPtr_.mterp_default_ibase.
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index 9817414..2bd5411 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -672,9 +672,10 @@
   return !location.empty() && location[0] == '/';
 }
 
-bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec,
-                                                       bool verify_names,
-                                                       bool verify_checksums) const {
+ClassLoaderContext::VerificationResult ClassLoaderContext::VerifyClassLoaderContextMatch(
+    const std::string& context_spec,
+    bool verify_names,
+    bool verify_checksums) const {
   if (verify_names || verify_checksums) {
     DCHECK(dex_files_open_attempted_);
     DCHECK(dex_files_open_result_);
@@ -683,15 +684,21 @@
   ClassLoaderContext expected_context;
   if (!expected_context.Parse(context_spec, verify_checksums)) {
     LOG(WARNING) << "Invalid class loader context: " << context_spec;
-    return false;
+    return VerificationResult::kMismatch;
   }
 
   // Special shared library contexts always match. They essentially instruct the runtime
   // to ignore the class path check because the oat file is known to be loaded in different
   // contexts. OatFileManager will further verify if the oat file can be loaded based on the
   // collision check.
-  if (special_shared_library_ || expected_context.special_shared_library_) {
-    return true;
+  if (expected_context.special_shared_library_) {
+    // Special case where we are the only entry in the class path.
+    if (class_loader_chain_.size() == 1 && class_loader_chain_[0].classpath.size() == 0) {
+      return VerificationResult::kVerifies;
+    }
+    return VerificationResult::kForcedToSkipChecks;
+  } else if (special_shared_library_) {
+    return VerificationResult::kForcedToSkipChecks;
   }
 
   if (expected_context.class_loader_chain_.size() != class_loader_chain_.size()) {
@@ -699,7 +706,7 @@
         << expected_context.class_loader_chain_.size()
         << ", actual=" << class_loader_chain_.size()
         << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
-    return false;
+    return VerificationResult::kMismatch;
   }
 
   for (size_t i = 0; i < class_loader_chain_.size(); i++) {
@@ -710,14 +717,14 @@
           << ". expected=" << GetClassLoaderTypeName(expected_info.type)
           << ", found=" << GetClassLoaderTypeName(info.type)
           << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
-      return false;
+      return VerificationResult::kMismatch;
     }
     if (info.classpath.size() != expected_info.classpath.size()) {
       LOG(WARNING) << "ClassLoaderContext classpath size mismatch for position " << i
             << ". expected=" << expected_info.classpath.size()
             << ", found=" << info.classpath.size()
             << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
-      return false;
+      return VerificationResult::kMismatch;
     }
 
     if (verify_checksums) {
@@ -772,7 +779,7 @@
             << ". expected=" << expected_info.classpath[k]
             << ", found=" << info.classpath[k]
             << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
-        return false;
+        return VerificationResult::kMismatch;
       }
 
       // Compare the checksums.
@@ -781,11 +788,11 @@
                      << ". expected=" << expected_info.checksums[k]
                      << ", found=" << info.checksums[k]
                      << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
-        return false;
+        return VerificationResult::kMismatch;
       }
     }
   }
-  return true;
+  return VerificationResult::kVerifies;
 }
 
 jclass ClassLoaderContext::GetClassLoaderClass(ClassLoaderType type) {
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
index 1c83007..a4268aa 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -22,8 +22,10 @@
 
 #include "arch/instruction_set.h"
 #include "base/dchecked_vector.h"
+#include "dex/dex_file.h"
 #include "handle_scope.h"
 #include "mirror/class_loader.h"
+#include "oat_file.h"
 #include "scoped_thread_state_change.h"
 
 namespace art {
@@ -34,6 +36,18 @@
 // Utility class which holds the class loader context used during compilation/verification.
 class ClassLoaderContext {
  public:
+  enum class VerificationResult {
+    kVerifies,
+    kForcedToSkipChecks,
+    kMismatch,
+  };
+
+  enum ClassLoaderType {
+    kInvalidClassLoader = 0,
+    kPathClassLoader = 1,
+    kDelegateLastClassLoader = 2
+  };
+
   ~ClassLoaderContext();
 
   // Opens requested class path files and appends them to ClassLoaderInfo::opened_dex_files.
@@ -109,7 +123,7 @@
   // This should be called after OpenDexFiles().
   // Names are only verified if verify_names is true.
   // Checksums are only verified if verify_checksums is true.
-  bool VerifyClassLoaderContextMatch(const std::string& context_spec,
+  VerificationResult VerifyClassLoaderContextMatch(const std::string& context_spec,
                                      bool verify_names = true,
                                      bool verify_checksums = true) const;
 
@@ -141,12 +155,6 @@
   static std::unique_ptr<ClassLoaderContext> Default();
 
  private:
-  enum ClassLoaderType {
-    kInvalidClassLoader = 0,
-    kPathClassLoader = 1,
-    kDelegateLastClassLoader = 2
-  };
-
   struct ClassLoaderInfo {
     // The type of this class loader.
     ClassLoaderType type;
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index 4689ae4..5e3f48c 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -608,6 +608,17 @@
   VerifyClassLoaderPCLFromTestDex(context.get(), 3, "ForClassLoaderA");
 }
 
+
+TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextFirstElement) {
+  std::string context_spec = "PCL[]";
+  std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
+  ASSERT_TRUE(context != nullptr);
+  PretendContextOpenedDexFiles(context.get());
+  // Ensure that the special shared library marks as verified for the first thing in the class path.
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(OatFile::kSpecialSharedLibrary),
+            ClassLoaderContext::VerificationResult::kVerifies);
+}
+
 TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) {
   std::string context_spec = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890]";
   std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
@@ -619,28 +630,36 @@
   VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex");
   VerifyClassLoaderDLC(context.get(), 1, "c.dex");
 
-  ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_spec));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec),
+            ClassLoaderContext::VerificationResult::kVerifies);
 
   std::string wrong_class_loader_type = "PCL[a.dex*123:b.dex*456];PCL[c.dex*890]";
-  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_type));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_type),
+            ClassLoaderContext::VerificationResult::kMismatch);
 
   std::string wrong_class_loader_order = "DLC[c.dex*890];PCL[a.dex*123:b.dex*456]";
-  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_order));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_order),
+            ClassLoaderContext::VerificationResult::kMismatch);
 
   std::string wrong_classpath_order = "PCL[b.dex*456:a.dex*123];DLC[c.dex*890]";
-  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_classpath_order));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_classpath_order),
+            ClassLoaderContext::VerificationResult::kMismatch);
 
   std::string wrong_checksum = "PCL[a.dex*999:b.dex*456];DLC[c.dex*890]";
-  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_checksum));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_checksum),
+            ClassLoaderContext::VerificationResult::kMismatch);
 
   std::string wrong_extra_class_loader = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890];PCL[d.dex*321]";
-  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader),
+            ClassLoaderContext::VerificationResult::kMismatch);
 
   std::string wrong_extra_classpath = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890:d.dex*321]";
-  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_classpath));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_classpath),
+            ClassLoaderContext::VerificationResult::kMismatch);
 
   std::string wrong_spec = "PCL[a.dex*999:b.dex*456];DLC[";
-  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_spec));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_spec),
+            ClassLoaderContext::VerificationResult::kMismatch);
 }
 
 TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) {
@@ -652,7 +671,8 @@
   std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d);
 
   std::string context_with_no_base_dir = context->EncodeContextForOatFile("");
-  ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_no_base_dir));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_no_base_dir),
+            ClassLoaderContext::VerificationResult::kVerifies);
 
   std::string dex_location = GetTestDexFileName("ForClassLoaderA");
   size_t pos = dex_location.rfind('/');
@@ -661,7 +681,8 @@
 
   std::string context_with_base_dir = context->EncodeContextForOatFile(parent);
   ASSERT_NE(context_with_base_dir, context_with_no_base_dir);
-  ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_with_base_dir));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_with_base_dir),
+            ClassLoaderContext::VerificationResult::kVerifies);
 }
 
 TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncodingMultidex) {
@@ -669,7 +690,8 @@
 
   std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader);
 
-  ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")));
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")),
+            ClassLoaderContext::VerificationResult::kVerifies);
 }
 
 }  // namespace art
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index 246c703..df184bc 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -260,4 +260,19 @@
   return DoGetCalleeSaveMethodOuterCallerAndPc(sp, type).first;
 }
 
+ObjPtr<mirror::MethodType> ResolveMethodTypeFromCode(ArtMethod* referrer,
+                                                     uint32_t proto_idx) {
+  Thread::PoisonObjectPointersIfDebug();
+  ObjPtr<mirror::MethodType> method_type =
+      referrer->GetDexCache()->GetResolvedMethodType(proto_idx);
+  if (UNLIKELY(method_type == nullptr)) {
+    StackHandleScope<2> hs(Thread::Current());
+    Handle<mirror::DexCache> dex_cache(hs.NewHandle(referrer->GetDexCache()));
+    Handle<mirror::ClassLoader> class_loader(hs.NewHandle(referrer->GetClassLoader()));
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    method_type = class_linker->ResolveMethodType(hs.Self(), proto_idx, dex_cache, class_loader);
+  }
+  return method_type;
+}
+
 }  // namespace art
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index eb32153..203ff3d 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -34,6 +34,7 @@
 namespace mirror {
 class Array;
 class Class;
+class MethodType;
 class Object;
 class String;
 }  // namespace mirror
@@ -151,6 +152,10 @@
     REQUIRES_SHARED(Locks::mutator_lock_)
     REQUIRES(!Roles::uninterruptible_);
 
+ObjPtr<mirror::MethodType> ResolveMethodTypeFromCode(ArtMethod* referrer, uint32_t proto_idx)
+    REQUIRES_SHARED(Locks::mutator_lock_)
+    REQUIRES(!Roles::uninterruptible_);
+
 inline ObjPtr<mirror::String> ResolveStringFromCode(ArtMethod* referrer,
                                                     dex::StringIndex string_idx)
     REQUIRES_SHARED(Locks::mutator_lock_)
diff --git a/runtime/entrypoints/quick/quick_default_externs.h b/runtime/entrypoints/quick/quick_default_externs.h
index 2d0932a..d934a53 100644
--- a/runtime/entrypoints/quick/quick_default_externs.h
+++ b/runtime/entrypoints/quick/quick_default_externs.h
@@ -37,6 +37,7 @@
 extern "C" void* art_quick_initialize_static_storage(uint32_t);
 extern "C" void* art_quick_initialize_type(uint32_t);
 extern "C" void* art_quick_initialize_type_and_verify_access(uint32_t);
+extern "C" void* art_quick_resolve_method_type(uint32_t);
 extern "C" void* art_quick_resolve_string(uint32_t);
 
 // Field entrypoints.
diff --git a/runtime/entrypoints/quick/quick_default_init_entrypoints.h b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
index 8c90800..a4572f6 100644
--- a/runtime/entrypoints/quick/quick_default_init_entrypoints.h
+++ b/runtime/entrypoints/quick/quick_default_init_entrypoints.h
@@ -37,6 +37,7 @@
   qpoints->pInitializeStaticStorage = art_quick_initialize_static_storage;
   qpoints->pInitializeTypeAndVerifyAccess = art_quick_initialize_type_and_verify_access;
   qpoints->pInitializeType = art_quick_initialize_type;
+  qpoints->pResolveMethodType = art_quick_resolve_method_type;
   qpoints->pResolveString = art_quick_resolve_string;
 
   // Field
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index cfb427f..09cbfff 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -183,6 +183,16 @@
   return result.Ptr();
 }
 
+extern "C" mirror::MethodType* artResolveMethodTypeFromCode(uint32_t proto_idx, Thread* self)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ScopedQuickEntrypointChecks sqec(self);
+  auto caller_and_outer = GetCalleeSaveMethodCallerAndOuterMethod(self,
+                                                                  CalleeSaveType::kSaveEverything);
+  ArtMethod* caller = caller_and_outer.caller;
+  ObjPtr<mirror::MethodType> result = ResolveMethodTypeFromCode(caller, proto_idx);
+  return result.Ptr();
+}
+
 extern "C" mirror::String* artResolveStringFromCode(int32_t string_idx, Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   ScopedQuickEntrypointChecks sqec(self);
diff --git a/runtime/entrypoints/quick/quick_entrypoints_list.h b/runtime/entrypoints/quick/quick_entrypoints_list.h
index 48a56f2..39dcd39 100644
--- a/runtime/entrypoints/quick/quick_entrypoints_list.h
+++ b/runtime/entrypoints/quick/quick_entrypoints_list.h
@@ -38,6 +38,7 @@
   V(InitializeStaticStorage, void*, uint32_t) \
   V(InitializeTypeAndVerifyAccess, void*, uint32_t) \
   V(InitializeType, void*, uint32_t) \
+  V(ResolveMethodType, void*, uint32_t) \
   V(ResolveString, void*, uint32_t) \
 \
   V(Set8Instance, int, uint32_t, void*, int8_t) \
diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc
index 1fdf439..b0689f6 100644
--- a/runtime/entrypoints_order_test.cc
+++ b/runtime/entrypoints_order_test.cc
@@ -183,7 +183,8 @@
                          sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeTypeAndVerifyAccess, pInitializeType,
                          sizeof(void*));
-    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeType, pResolveString, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pInitializeType, pResolveMethodType, sizeof(void*));
+    EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveMethodType, pResolveString, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pResolveString, pSet8Instance, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet8Instance, pSet8Static, sizeof(void*));
     EXPECT_OFFSET_DIFFNP(QuickEntryPoints, pSet8Static, pSet16Instance, sizeof(void*));
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index 65c6406..8e21fd3 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -143,31 +143,45 @@
 // Returns true if the caller is either loaded by the boot strap class loader or comes from
 // a dex file located in ${ANDROID_ROOT}/framework/.
 ALWAYS_INLINE
-inline bool IsCallerInPlatformDex(ObjPtr<mirror::ClassLoader> caller_class_loader,
-                                  ObjPtr<mirror::DexCache> caller_dex_cache)
+inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller,
+                            ObjPtr<mirror::ClassLoader> caller_class_loader,
+                            ObjPtr<mirror::DexCache> caller_dex_cache)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   if (caller_class_loader.IsNull()) {
+    // Boot class loader.
     return true;
-  } else if (caller_dex_cache.IsNull()) {
-    return false;
-  } else {
-    const DexFile* caller_dex_file = caller_dex_cache->GetDexFile();
-    return caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile();
   }
+
+  if (!caller_dex_cache.IsNull()) {
+    const DexFile* caller_dex_file = caller_dex_cache->GetDexFile();
+    if (caller_dex_file != nullptr && caller_dex_file->IsPlatformDexFile()) {
+      // Caller is in a platform dex file.
+      return true;
+    }
+  }
+
+  if (!caller.IsNull() &&
+      caller->ShouldSkipHiddenApiChecks() &&
+      Runtime::Current()->IsJavaDebuggable()) {
+    // We are in debuggable mode and this caller has been marked trusted.
+    return true;
+  }
+
+  return false;
 }
 
 }  // namespace detail
 
 // Returns true if access to `member` should be denied to the caller of the
-// reflective query. The decision is based on whether the caller is in the
-// platform or not. Because different users of this function determine this
-// in a different way, `fn_caller_in_platform(self)` is called and should
-// return true if the caller is located in the platform.
+// reflective query. The decision is based on whether the caller is trusted or
+// not. Because different users of this function determine this in a different
+// way, `fn_caller_is_trusted(self)` is called and should return true if the
+// caller is allowed to access the platform.
 // This function might print warnings into the log if the member is hidden.
 template<typename T>
 inline Action GetMemberAction(T* member,
                               Thread* self,
-                              std::function<bool(Thread*)> fn_caller_in_platform,
+                              std::function<bool(Thread*)> fn_caller_is_trusted,
                               AccessMethod access_method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK(member != nullptr);
@@ -188,8 +202,8 @@
 
   // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access.
   // This can be *very* expensive. Save it for last.
-  if (fn_caller_in_platform(self)) {
-    // Caller in the platform. Exit.
+  if (fn_caller_is_trusted(self)) {
+    // Caller is trusted. Exit.
     return kAllow;
   }
 
@@ -197,10 +211,9 @@
   return detail::GetMemberActionImpl(member, api_list, action, access_method);
 }
 
-inline bool IsCallerInPlatformDex(ObjPtr<mirror::Class> caller)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
+inline bool IsCallerTrusted(ObjPtr<mirror::Class> caller) REQUIRES_SHARED(Locks::mutator_lock_) {
   return !caller.IsNull() &&
-      detail::IsCallerInPlatformDex(caller->GetClassLoader(), caller->GetDexCache());
+      detail::IsCallerTrusted(caller, caller->GetClassLoader(), caller->GetDexCache());
 }
 
 // Returns true if access to `member` should be denied to a caller loaded with
@@ -212,10 +225,11 @@
                               ObjPtr<mirror::DexCache> caller_dex_cache,
                               AccessMethod access_method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  bool caller_in_platform = detail::IsCallerInPlatformDex(caller_class_loader, caller_dex_cache);
+  bool is_caller_trusted =
+      detail::IsCallerTrusted(/* caller */ nullptr, caller_class_loader, caller_dex_cache);
   return GetMemberAction(member,
                          /* thread */ nullptr,
-                         [caller_in_platform] (Thread*) { return caller_in_platform; },
+                         [is_caller_trusted] (Thread*) { return is_caller_trusted; },
                          access_method);
 }
 
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 249a8b0..d8aa00c 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -563,7 +563,7 @@
   }
 }
 
-void JitCodeCache::FreeCode(const void* code_ptr) {
+void JitCodeCache::FreeCodeAndData(const void* code_ptr) {
   uintptr_t allocation = FromCodeToAllocation(code_ptr);
   // Notify native debugger that we are about to remove the code.
   // It does nothing if we are not using native debugger.
@@ -590,7 +590,7 @@
   MutexLock mu(Thread::Current(), lock_);
   ScopedCodeCacheWrite scc(this);
   for (const OatQuickMethodHeader* method_header : method_headers) {
-    FreeCode(method_header->GetCode());
+    FreeCodeAndData(method_header->GetCode());
   }
 }
 
@@ -916,7 +916,7 @@
       in_cache = true;
       if (it->second.GetMethods().empty()) {
         if (release_memory) {
-          FreeCode(it->second.GetCode());
+          FreeCodeAndData(it->second.GetCode());
         }
         jni_stubs_map_.erase(it);
       } else {
@@ -928,7 +928,7 @@
       if (it->second == method) {
         in_cache = true;
         if (release_memory) {
-          FreeCode(it->first);
+          FreeCodeAndData(it->first);
         }
         it = method_code_map_.erase(it);
       } else {
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index b10f57e..958e8e8 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -317,8 +317,8 @@
       REQUIRES(lock_)
       REQUIRES(Locks::mutator_lock_);
 
-  // Free in the mspace allocations for `code_ptr`.
-  void FreeCode(const void* code_ptr) REQUIRES(lock_);
+  // Free code and data allocations for `code_ptr`.
+  void FreeCodeAndData(const void* code_ptr) REQUIRES(lock_);
 
   // Number of bytes allocated in the code cache.
   size_t CodeCacheSizeLocked() REQUIRES(lock_);
@@ -357,10 +357,10 @@
       REQUIRES(lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  void FreeCode(uint8_t* code) REQUIRES(lock_);
   uint8_t* AllocateCode(size_t code_size) REQUIRES(lock_);
-  void FreeData(uint8_t* data) REQUIRES(lock_);
+  void FreeCode(uint8_t* code) REQUIRES(lock_);
   uint8_t* AllocateData(size_t data_size) REQUIRES(lock_);
+  void FreeData(uint8_t* data) REQUIRES(lock_);
 
   bool IsWeakAccessEnabled(Thread* self) const;
   void WaitUntilInlineCacheAccessible(Thread* self)
diff --git a/runtime/jni/jni_internal.cc b/runtime/jni/jni_internal.cc
index 9dbcded..cd66a60 100644
--- a/runtime/jni/jni_internal.cc
+++ b/runtime/jni/jni_internal.cc
@@ -80,15 +80,15 @@
 // things not rendering correctly. E.g. b/16858794
 static constexpr bool kWarnJniAbort = false;
 
-static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
-  return hiddenapi::IsCallerInPlatformDex(GetCallingClass(self, /* num_frames */ 1));
+static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
+  return hiddenapi::IsCallerTrusted(GetCallingClass(self, /* num_frames */ 1));
 }
 
 template<typename T>
 ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   hiddenapi::Action action = hiddenapi::GetMemberAction(
-      member, self, IsCallerInPlatformDex, hiddenapi::kJNI);
+      member, self, IsCallerTrusted, hiddenapi::kJNI);
   if (action != hiddenapi::kAllow) {
     hiddenapi::NotifyHiddenApiListener(member);
   }
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 51d1376..98e25eb 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -210,6 +210,15 @@
     return (GetAccessFlags() & kAccClassIsFinalizable) != 0;
   }
 
+  ALWAYS_INLINE bool ShouldSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) {
+    return (GetAccessFlags() & kAccSkipHiddenApiChecks) != 0;
+  }
+
+  ALWAYS_INLINE void SetSkipHiddenApiChecks() REQUIRES_SHARED(Locks::mutator_lock_) {
+    uint32_t flags = GetAccessFlags();
+    SetAccessFlags(flags | kAccSkipHiddenApiChecks);
+  }
+
   ALWAYS_INLINE void SetRecursivelyInitialized() REQUIRES_SHARED(Locks::mutator_lock_) {
     DCHECK_EQ(GetLockOwnerThreadId(), Thread::Current()->GetThreadId());
     uint32_t flags = GetField32(OFFSET_OF_OBJECT_MEMBER(Class, access_flags_));
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 5c2ca24..cdba6b2 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -816,6 +816,28 @@
   return static_cast<jlong>(file_size);
 }
 
+static void DexFile_setTrusted(JNIEnv* env, jclass, jobject j_cookie) {
+  Runtime* runtime = Runtime::Current();
+  ScopedObjectAccess soa(env);
+
+  // Currently only allow this for debuggable apps.
+  if (!runtime->IsJavaDebuggable()) {
+    ThrowSecurityException("Can't exempt class, process is not debuggable.");
+    return;
+  }
+
+  std::vector<const DexFile*> dex_files;
+  const OatFile* oat_file;
+  if (!ConvertJavaArrayToDexFiles(env, j_cookie, dex_files, oat_file)) {
+    Thread::Current()->AssertPendingException();
+    return;
+  }
+
+  for (const DexFile* dex_file : dex_files) {
+    const_cast<DexFile*>(dex_file)->SetIsPlatformDexFile();
+  }
+}
+
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"),
   NATIVE_METHOD(DexFile,
@@ -854,7 +876,8 @@
                 "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
   NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J"),
   NATIVE_METHOD(DexFile, getDexFileOptimizationStatus,
-                "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;")
+                "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
+  NATIVE_METHOD(DexFile, setTrusted, "(Ljava/lang/Object;)V")
 };
 
 void register_dalvik_system_DexFile(JNIEnv* env) {
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 6aaafc2..f1e267b 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -588,6 +588,25 @@
   Runtime::Current()->AttachAgent(env, filename, classloader);
 }
 
+static void VMDebug_allowHiddenApiReflectionFrom(JNIEnv* env, jclass, jclass j_caller) {
+  Runtime* runtime = Runtime::Current();
+  ScopedObjectAccess soa(env);
+
+  if (!runtime->IsJavaDebuggable()) {
+    ThrowSecurityException("Can't exempt class, process is not debuggable.");
+    return;
+  }
+
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::Class> h_caller(hs.NewHandle(soa.Decode<mirror::Class>(j_caller)));
+  if (h_caller.IsNull()) {
+    ThrowNullPointerException("argument is null");
+    return;
+  }
+
+  h_caller->SetSkipHiddenApiChecks();
+}
+
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"),
   NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"),
@@ -623,6 +642,7 @@
   NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"),
   NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"),
   NATIVE_METHOD(VMDebug, nativeAttachAgent, "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"),
+  NATIVE_METHOD(VMDebug, allowHiddenApiReflectionFrom, "(Ljava/lang/Class;)V"),
 };
 
 void register_dalvik_system_VMDebug(JNIEnv* env) {
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 2625c0a..68024cd 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -52,7 +52,7 @@
 
 // Returns true if the first caller outside of the Class class or java.lang.invoke package
 // is in a platform DEX file.
-static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
+static bool IsCallerTrusted(Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) {
   // Walk the stack and find the first frame not from java.lang.Class and not from java.lang.invoke.
   // This is very expensive. Save this till the last.
   struct FirstExternalCallerVisitor : public StackVisitor {
@@ -99,7 +99,7 @@
   FirstExternalCallerVisitor visitor(self);
   visitor.WalkStack();
   return visitor.caller != nullptr &&
-         hiddenapi::IsCallerInPlatformDex(visitor.caller->GetDeclaringClass());
+         hiddenapi::IsCallerTrusted(visitor.caller->GetDeclaringClass());
 }
 
 // Returns true if the first non-ClassClass caller up the stack is not allowed to
@@ -107,7 +107,7 @@
 ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   hiddenapi::EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy();
-  return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerInPlatformDex(self);
+  return policy != hiddenapi::EnforcementPolicy::kNoChecks && !IsCallerTrusted(self);
 }
 
 // Returns true if the first non-ClassClass caller up the stack should not be
@@ -116,7 +116,7 @@
 ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   hiddenapi::Action action = hiddenapi::GetMemberAction(
-      member, self, IsCallerInPlatformDex, hiddenapi::kReflection);
+      member, self, IsCallerTrusted, hiddenapi::kReflection);
   if (action != hiddenapi::kAllow) {
     hiddenapi::NotifyHiddenApiListener(member);
   }
diff --git a/runtime/oat.h b/runtime/oat.h
index 0318606..9a58ded 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  // Last oat version changed reason: Use rMR as temp in Baker RB introspection marking.
-  static constexpr uint8_t kOatVersion[] = { '1', '4', '1', '\0' };
+  // Last oat version changed reason: compiler support const-method-type
+  static constexpr uint8_t kOatVersion[] = { '1', '4', '2', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 9c8b651..241102e 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -1217,7 +1217,9 @@
     return false;
   }
 
-  bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext());
+
+  bool result = context->VerifyClassLoaderContextMatch(file->GetClassLoaderContext()) ==
+      ClassLoaderContext::VerificationResult::kVerifies;
   if (!result) {
     VLOG(oat) << "ClassLoaderContext check failed. Context was "
               << file->GetClassLoaderContext()
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 16e6cf3..59a1045 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -276,9 +276,19 @@
   }
 }
 
-static bool CollisionCheck(std::vector<const DexFile*>& dex_files_loaded,
-                           std::vector<const DexFile*>& dex_files_unloaded,
-                           std::string* error_msg /*out*/) {
+static bool CheckClassCollision(const OatFile* oat_file,
+    const ClassLoaderContext* context,
+    std::string* error_msg /*out*/) {
+  std::vector<const DexFile*> dex_files_loaded = context->FlattenOpenedDexFiles();
+
+  // Vector that holds the newly opened dex files live, this is done to prevent leaks.
+  std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
+
+  ScopedTrace st("Collision check");
+  // Add dex files from the oat file to check.
+  std::vector<const DexFile*> dex_files_unloaded;
+  AddDexFilesFromOat(oat_file, &dex_files_unloaded, &opened_dex_files);
+
   // Generate type index information for each dex file.
   std::vector<TypeIndexInfo> loaded_types;
   for (const DexFile* dex_file : dex_files_loaded) {
@@ -355,9 +365,10 @@
 // against the following top element. If the descriptor is the same, it is now checked whether
 // the two elements agree on whether their dex file was from an already-loaded oat-file or the
 // new oat file. Any disagreement indicates a collision.
-bool OatFileManager::HasCollisions(const OatFile* oat_file,
-                                   const ClassLoaderContext* context,
-                                   std::string* error_msg /*out*/) const {
+OatFileManager::CheckCollisionResult OatFileManager::CheckCollision(
+    const OatFile* oat_file,
+    const ClassLoaderContext* context,
+    /*out*/ std::string* error_msg) const {
   DCHECK(oat_file != nullptr);
   DCHECK(error_msg != nullptr);
 
@@ -367,28 +378,59 @@
   // Note that this has correctness implications as we cannot guarantee that the class resolution
   // used during compilation is OK (b/37777332).
   if (context == nullptr) {
-      LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader";
-      return false;
+    LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader";
+    return CheckCollisionResult::kSkippedUnsupportedClassLoader;
   }
 
-  // If the pat file loading context matches the context used during compilation then we accept
+  // If the oat file loading context matches the context used during compilation then we accept
   // the oat file without addition checks
-  if (context->VerifyClassLoaderContextMatch(oat_file->GetClassLoaderContext())) {
-    return false;
+  ClassLoaderContext::VerificationResult result = context->VerifyClassLoaderContextMatch(
+      oat_file->GetClassLoaderContext(),
+      /*verify_names*/ true,
+      /*verify_checksums*/ true);
+  switch (result) {
+    case ClassLoaderContext::VerificationResult::kForcedToSkipChecks:
+      return CheckCollisionResult::kSkippedClassLoaderContextSharedLibrary;
+    case ClassLoaderContext::VerificationResult::kMismatch:
+      // Mismatched context, do the actual collision check.
+      break;
+    case ClassLoaderContext::VerificationResult::kVerifies:
+      return CheckCollisionResult::kNoCollisions;
   }
 
   // The class loader context does not match. Perform a full duplicate classes check.
+  return CheckClassCollision(oat_file, context, error_msg)
+      ? CheckCollisionResult::kPerformedHasCollisions : CheckCollisionResult::kNoCollisions;
+}
 
-  std::vector<const DexFile*> dex_files_loaded = context->FlattenOpenedDexFiles();
+bool OatFileManager::AcceptOatFile(CheckCollisionResult result) const {
+  // Take the file only if it has no collisions, or we must take it because of preopting.
+  // Also accept oat files for shared libraries and unsupported class loaders.
+  return result != CheckCollisionResult::kPerformedHasCollisions;
+}
 
-  // Vector that holds the newly opened dex files live, this is done to prevent leaks.
-  std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
-
-  ScopedTrace st("Collision check");
-  // Add dex files from the oat file to check.
-  std::vector<const DexFile*> dex_files_unloaded;
-  AddDexFilesFromOat(oat_file, &dex_files_unloaded, &opened_dex_files);
-  return CollisionCheck(dex_files_loaded, dex_files_unloaded, error_msg);
+bool OatFileManager::ShouldLoadAppImage(CheckCollisionResult check_collision_result,
+                                        const OatFile* source_oat_file,
+                                        ClassLoaderContext* context,
+                                        std::string* error_msg) {
+  Runtime* const runtime = Runtime::Current();
+  if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) {
+    // If we verified the class loader context (skipping due to the special marker doesn't
+    // count), then also avoid the collision check.
+    bool load_image = check_collision_result == CheckCollisionResult::kNoCollisions;
+    // If we skipped the collision check, we need to reverify to be sure its OK to load the
+    // image.
+    if (!load_image &&
+        check_collision_result ==
+            CheckCollisionResult::kSkippedClassLoaderContextSharedLibrary) {
+      // We can load the app image only if there are no collisions. If we know the
+      // class loader but didn't do the full collision check in HasCollisions(),
+      // do it now. b/77342775
+      load_image = !CheckClassCollision(source_oat_file, context, error_msg);
+    }
+    return load_image;
+  }
+  return false;
 }
 
 std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
@@ -473,16 +515,17 @@
             << reinterpret_cast<uintptr_t>(oat_file.get())
             << " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")";
 
+  CheckCollisionResult check_collision_result = CheckCollisionResult::kPerformedHasCollisions;
   if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) {
     // Prevent oat files from being loaded if no class_loader or dex_elements are provided.
     // This can happen when the deprecated DexFile.<init>(String) is called directly, and it
     // could load oat files without checking the classpath, which would be incorrect.
     // Take the file only if it has no collisions, or we must take it because of preopting.
-    bool accept_oat_file =
-        !HasCollisions(oat_file.get(), context.get(), /*out*/ &error_msg);
+    check_collision_result = CheckCollision(oat_file.get(), context.get(), /*out*/ &error_msg);
+    bool accept_oat_file = AcceptOatFile(check_collision_result);
     if (!accept_oat_file) {
       // Failed the collision check. Print warning.
-      if (Runtime::Current()->IsDexFileFallbackEnabled()) {
+      if (runtime->IsDexFileFallbackEnabled()) {
         if (!oat_file_assistant.HasOriginalDexFiles()) {
           // We need to fallback but don't have original dex files. We have to
           // fallback to opening the existing oat file. This is potentially
@@ -529,10 +572,11 @@
       // We need to throw away the image space if we are debuggable but the oat-file source of the
       // image is not otherwise we might get classes with inlined methods or other such things.
       std::unique_ptr<gc::space::ImageSpace> image_space;
-      if (kEnableAppImage && (!runtime->IsJavaDebuggable() || source_oat_file->IsDebuggable())) {
+      if (ShouldLoadAppImage(check_collision_result,
+                             source_oat_file,
+                             context.get(),
+                             &error_msg)) {
         image_space = oat_file_assistant.OpenImageSpace(source_oat_file);
-      } else {
-        image_space = nullptr;
       }
       if (image_space != nullptr) {
         ScopedObjectAccess soa(self);
diff --git a/runtime/oat_file_manager.h b/runtime/oat_file_manager.h
index 038474e..80456e9 100644
--- a/runtime/oat_file_manager.h
+++ b/runtime/oat_file_manager.h
@@ -108,23 +108,39 @@
   void SetOnlyUseSystemOatFiles();
 
  private:
+  enum class CheckCollisionResult {
+    kSkippedUnsupportedClassLoader,
+    kSkippedClassLoaderContextSharedLibrary,
+    kNoCollisions,
+    kPerformedHasCollisions,
+  };
+
   // Check that the class loader context of the given oat file matches the given context.
   // This will perform a check that all class loaders in the chain have the same type and
   // classpath.
   // If the context is null (which means the initial class loader was null or unsupported)
-  // this returns false.
+  // this returns kSkippedUnsupportedClassLoader.
   // If the context does not validate the method will check for duplicate class definitions of
   // the given oat file against the oat files (either from the class loaders if possible or all
   // non-boot oat files otherwise).
-  // Return true if there are any class definition collisions in the oat_file.
-  bool HasCollisions(const OatFile* oat_file,
-                     const ClassLoaderContext* context,
-                     /*out*/ std::string* error_msg) const
+  // Return kPerformedHasCollisions if there are any class definition collisions in the oat_file.
+  CheckCollisionResult CheckCollision(const OatFile* oat_file,
+                                      const ClassLoaderContext* context,
+                                      /*out*/ std::string* error_msg) const
       REQUIRES(!Locks::oat_file_manager_lock_);
 
   const OatFile* FindOpenedOatFileFromOatLocationLocked(const std::string& oat_location) const
       REQUIRES(Locks::oat_file_manager_lock_);
 
+  // Return true if we should accept the oat file.
+  bool AcceptOatFile(CheckCollisionResult result) const;
+
+  // Return true if we should attempt to load the app image.
+  bool ShouldLoadAppImage(CheckCollisionResult check_collision_result,
+                          const OatFile* source_oat_file,
+                          ClassLoaderContext* context,
+                          std::string* error_msg);
+
   std::set<std::unique_ptr<const OatFile>> oat_files_ GUARDED_BY(Locks::oat_file_manager_lock_);
   bool have_non_pic_oat_file_;
 
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index bc29aac..5cd6091 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -2293,8 +2293,6 @@
     case Instruction::CONST_METHOD_TYPE:
       work_line_->SetRegisterType<LockOp::kClear>(
           this, inst->VRegA_21c(), reg_types_.JavaLangInvokeMethodType());
-      // TODO: add compiler support for const-method-{handle,type} (b/66890674)
-      Fail(VERIFY_ERROR_FORCE_INTERPRETER);
       break;
     case Instruction::MONITOR_ENTER:
       work_line_->PushMonitor(this, inst->VRegA_11x(), work_insn_idx_);
diff --git a/test/172-app-image-twice/check b/test/172-app-image-twice/check
new file mode 100755
index 0000000..26a97a4
--- /dev/null
+++ b/test/172-app-image-twice/check
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+# Remove all lines not containing "passed".
+grep "^passed" "$2" | diff --strip-trailing-cr -q "$1" - >/dev/null
diff --git a/test/172-app-image-twice/debug_print_class.cc b/test/172-app-image-twice/debug_print_class.cc
new file mode 100644
index 0000000..6c3de20
--- /dev/null
+++ b/test/172-app-image-twice/debug_print_class.cc
@@ -0,0 +1,33 @@
+/*
+ * 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 "debug_print.h"
+#include "dex/dex_file.h"
+#include "mirror/class-inl.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-current-inl.h"
+
+namespace art {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_debugPrintClass(JNIEnv*, jclass, jclass cls) {
+  ScopedObjectAccess soa(Thread::Current());
+  ObjPtr<mirror::Class> klass = soa.Decode<mirror::Class>(cls);
+  LOG(ERROR) << "klass: " << klass.Ptr() << " dex_file: " << klass->GetDexFile().GetLocation()
+      << "/" << static_cast<const void*>(&klass->GetDexFile())
+      << " " << DescribeSpace(klass);
+}
+
+}  // namespace art
diff --git a/test/172-app-image-twice/expected.txt b/test/172-app-image-twice/expected.txt
new file mode 100644
index 0000000..b0aad4d
--- /dev/null
+++ b/test/172-app-image-twice/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/172-app-image-twice/info.txt b/test/172-app-image-twice/info.txt
new file mode 100644
index 0000000..028046e
--- /dev/null
+++ b/test/172-app-image-twice/info.txt
@@ -0,0 +1 @@
+Regression test for loading the same app image twice.
diff --git a/test/172-app-image-twice/profile b/test/172-app-image-twice/profile
new file mode 100644
index 0000000..70cb2ef
--- /dev/null
+++ b/test/172-app-image-twice/profile
@@ -0,0 +1 @@
+LTestClass;
diff --git a/test/172-app-image-twice/run b/test/172-app-image-twice/run
new file mode 100644
index 0000000..aa28190
--- /dev/null
+++ b/test/172-app-image-twice/run
@@ -0,0 +1,28 @@
+#!/bin/bash
+#
+# 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.
+
+# Build an app image with TestClass (specified by profile) and class loader
+# context that skips the duplicate class checks.
+
+# Target and host use a different shell, and we need to special case the
+# passing of the class loader context marker.
+if [[ "$@" = *" --host "* ]]; then
+  ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \
+      -Xcompiler-option --class-loader-context=\&
+else
+  ${RUN} $@ --profile -Xcompiler-option --compiler-filter=speed-profile \
+      -Xcompiler-option '--class-loader-context=\&'
+fi
diff --git a/test/172-app-image-twice/src/Main.java b/test/172-app-image-twice/src/Main.java
new file mode 100644
index 0000000..a1c151a
--- /dev/null
+++ b/test/172-app-image-twice/src/Main.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+import java.lang.reflect.Method;
+
+public class Main {
+    private static String TEST_NAME = "172-app-image-twice";
+
+    public static void main(String args[]) throws Exception {
+        System.loadLibrary(args[0]);
+
+        Class<?> tc1 = Class.forName("TestClass");
+
+        String dexPath = System.getenv("DEX_LOCATION") + "/" + TEST_NAME + ".jar";
+        Class<?> bdcl = Class.forName("dalvik.system.BaseDexClassLoader");
+        Method addDexPathMethod = bdcl.getDeclaredMethod("addDexPath", String.class);
+        addDexPathMethod.invoke(Main.class.getClassLoader(), dexPath);
+
+        Class<?> tc2 = Class.forName("TestClass");
+
+        // Add extra logging to simulate libcore logging, this logging should not be compared
+        // against.
+        System.out.println("Extra logging");
+
+        if (tc1 != tc2) {
+            System.out.println("Class mismatch!");
+            debugPrintClass(tc1);
+            debugPrintClass(tc2);
+        } else {
+            System.out.println("passed");
+        }
+    }
+
+    public static native void debugPrintClass(Class<?> cls);
+}
diff --git a/test/172-app-image-twice/src/TestClass.java b/test/172-app-image-twice/src/TestClass.java
new file mode 100644
index 0000000..5381718
--- /dev/null
+++ b/test/172-app-image-twice/src/TestClass.java
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+public class TestClass {
+}
diff --git a/test/979-const-method-handle/expected.txt b/test/979-const-method-handle/expected.txt
index bc943e3..2d169b9 100644
--- a/test/979-const-method-handle/expected.txt
+++ b/test/979-const-method-handle/expected.txt
@@ -1,4 +1,6 @@
 (int,Integer,System)String
+repeatConstMethodType0((int,Integer,System)String)
+repeatConstMethodType1((LocalClass)void)
 Hello World! And Hello Zog
 Hello World! And Hello Zorba
 name is HoverFly
diff --git a/test/979-const-method-handle/src/Main.java b/test/979-const-method-handle/src/Main.java
index 663814f..79be913 100644
--- a/test/979-const-method-handle/src/Main.java
+++ b/test/979-const-method-handle/src/Main.java
@@ -26,59 +26,100 @@
         throw new Error("Unreachable");
     }
 
+    private static class LocalClass {
+        public LocalClass() {}
+
+        private int field;
+    }
+
     @ConstantMethodType(
-        returnType = String.class,
-        parameterTypes = {int.class, Integer.class, System.class}
-    )
+            returnType = String.class,
+            parameterTypes = {int.class, Integer.class, System.class})
     private static MethodType methodType0() {
         unreachable();
         return null;
     }
 
+    @ConstantMethodType(
+            returnType = void.class,
+            parameterTypes = {LocalClass.class})
+    private static MethodType methodType1() {
+        unreachable();
+        return null;
+    }
+
+    private static void repeatConstMethodType0(MethodType expected) {
+        System.out.print("repeatConstMethodType0(");
+        System.out.print(expected);
+        System.out.println(")");
+        for (int i = 0; i < 12000; ++i) {
+            MethodType actual = methodType0();
+            if (!actual.equals(expected)) {
+                System.out.print("Expected: ");
+                System.out.println(expected);
+                System.out.print("Actual: ");
+                System.out.println(actual);
+                unreachable();
+            }
+        }
+    }
+
+    private static void repeatConstMethodType1(MethodType expected) {
+        System.out.print("repeatConstMethodType1(");
+        System.out.print(expected);
+        System.out.println(")");
+        for (int i = 0; i < 12000; ++i) {
+            MethodType actual = methodType1();
+            if (!actual.equals(expected)) {
+                System.out.print("Expected: ");
+                System.out.println(expected);
+                System.out.print("Actual: ");
+                System.out.println(actual);
+                unreachable();
+            }
+        }
+    }
+
     static void helloWorld(String who) {
         System.out.print("Hello World! And Hello ");
         System.out.println(who);
     }
 
     @ConstantMethodHandle(
-        kind = ConstantMethodHandle.INVOKE_STATIC,
-        owner = "Main",
-        fieldOrMethodName = "helloWorld",
-        descriptor = "(Ljava/lang/String;)V"
-    )
+            kind = ConstantMethodHandle.INVOKE_STATIC,
+            owner = "Main",
+            fieldOrMethodName = "helloWorld",
+            descriptor = "(Ljava/lang/String;)V")
     private static MethodHandle printHelloHandle() {
         unreachable();
         return null;
     }
 
     @ConstantMethodHandle(
-        kind = ConstantMethodHandle.STATIC_PUT,
-        owner = "Main",
-        fieldOrMethodName = "name",
-        descriptor = "Ljava/lang/String;"
-    )
+            kind = ConstantMethodHandle.STATIC_PUT,
+            owner = "Main",
+            fieldOrMethodName = "name",
+            descriptor = "Ljava/lang/String;")
     private static MethodHandle setNameHandle() {
         unreachable();
         return null;
     }
 
     @ConstantMethodHandle(
-        kind = ConstantMethodHandle.STATIC_GET,
-        owner = "java/lang/Math",
-        fieldOrMethodName = "E",
-        descriptor = "D"
-    )
+            kind = ConstantMethodHandle.STATIC_GET,
+            owner = "java/lang/Math",
+            fieldOrMethodName = "E",
+            descriptor = "D")
     private static MethodHandle getMathE() {
         unreachable();
         return null;
     }
 
     @ConstantMethodHandle(
-        kind = ConstantMethodHandle.STATIC_PUT,
-        owner = "java/lang/Math",
-        fieldOrMethodName = "E",
-        descriptor = "D"
-    )
+            kind = ConstantMethodHandle.STATIC_PUT,
+            owner = "java/lang/Math",
+            fieldOrMethodName = "E",
+            descriptor = "D")
     private static MethodHandle putMathE() {
         unreachable();
         return null;
@@ -86,6 +127,9 @@
 
     public static void main(String[] args) throws Throwable {
         System.out.println(methodType0());
+        repeatConstMethodType0(
+                MethodType.methodType(String.class, int.class, Integer.class, System.class));
+        repeatConstMethodType1(MethodType.methodType(void.class, LocalClass.class));
         printHelloHandle().invokeExact("Zog");
         printHelloHandle().invokeExact("Zorba");
         setNameHandle().invokeExact("HoverFly");
diff --git a/test/Android.bp b/test/Android.bp
index 0c6b449..76189f6 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -423,6 +423,7 @@
         "154-gc-loop/heap_interface.cc",
         "167-visit-locks/visit_locks.cc",
         "169-threadgroup-jni/jni_daemon_thread.cc",
+        "172-app-image-twice/debug_print_class.cc",
         "1945-proxy-method-arguments/get_args.cc",
         "203-multi-checkpoint/multi_checkpoint.cc",
         "305-other-fault-handler/fault_handler.cc",
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 81e77be..fad8011 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -13,6 +13,7 @@
 ARCHITECTURES_64="(arm64|x86_64|mips64|none)"
 ARCHITECTURES_PATTERN="${ARCHITECTURES_32}"
 BOOT_IMAGE=""
+CHROOT=
 COMPILE_FLAGS=""
 DALVIKVM="dalvikvm32"
 DEBUGGER="n"
@@ -299,6 +300,10 @@
     elif [ "x$1" = "x--no-optimize" ]; then
         OPTIMIZE="n"
         shift
+    elif [ "x$1" = "x--chroot" ]; then
+        shift
+        CHROOT="$1"
+        shift
     elif [ "x$1" = "x--android-root" ]; then
         shift
         ANDROID_ROOT="$1"
@@ -367,6 +372,9 @@
     fi
 done
 
+# The DEX_LOCATION with the chroot prefix, if any.
+CHROOT_DEX_LOCATION="$CHROOT$DEX_LOCATION"
+
 if [ "$USE_JVM" = "n" ]; then
     FLAGS="${FLAGS} ${ANDROID_FLAGS}"
     for feature in ${EXPERIMENTAL}; do
@@ -817,28 +825,28 @@
     adb root > /dev/null
     adb wait-for-device
     if [ "$QUIET" = "n" ]; then
-      adb shell rm -rf $DEX_LOCATION
-      adb shell mkdir -p $DEX_LOCATION
-      adb push $TEST_NAME.jar $DEX_LOCATION
-      adb push $TEST_NAME-ex.jar $DEX_LOCATION
+      adb shell rm -rf $CHROOT_DEX_LOCATION
+      adb shell mkdir -p $CHROOT_DEX_LOCATION
+      adb push $TEST_NAME.jar $CHROOT_DEX_LOCATION
+      adb push $TEST_NAME-ex.jar $CHROOT_DEX_LOCATION
       if [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
-        adb push profile $DEX_LOCATION
+        adb push profile $CHROOT_DEX_LOCATION
       fi
       # Copy resource folder
       if [ -d res ]; then
-        adb push res $DEX_LOCATION
+        adb push res $CHROOT_DEX_LOCATION
       fi
     else
-      adb shell rm -r $DEX_LOCATION >/dev/null 2>&1
-      adb shell mkdir -p $DEX_LOCATION >/dev/null 2>&1
-      adb push $TEST_NAME.jar $DEX_LOCATION >/dev/null 2>&1
-      adb push $TEST_NAME-ex.jar $DEX_LOCATION >/dev/null 2>&1
+      adb shell rm -rf $CHROOT_DEX_LOCATION >/dev/null 2>&1
+      adb shell mkdir -p $CHROOT_DEX_LOCATION >/dev/null 2>&1
+      adb push $TEST_NAME.jar $CHROOT_DEX_LOCATION >/dev/null 2>&1
+      adb push $TEST_NAME-ex.jar $CHROOT_DEX_LOCATION >/dev/null 2>&1
       if [ "$PROFILE" = "y" ] || [ "$RANDOM_PROFILE" = "y" ]; then
-        adb push profile $DEX_LOCATION >/dev/null 2>&1
+        adb push profile $CHROOT_DEX_LOCATION >/dev/null 2>&1
       fi
       # Copy resource folder
       if [ -d res ]; then
-        adb push res $DEX_LOCATION >/dev/null 2>&1
+        adb push res $CHROOT_DEX_LOCATION >/dev/null 2>&1
       fi
     fi
 
@@ -847,7 +855,7 @@
       # Current default installation is dalvikvm 64bits and dex2oat 32bits,
       # so we can only use LD_LIBRARY_PATH when testing on a local
       # installation.
-      LD_LIBRARY_PATH=$ANDROID_ROOT/$LIBRARY_DIRECTORY:$LD_LIBRARY_PATH
+      LD_LIBRARY_PATH="$ANDROID_ROOT/$LIBRARY_DIRECTORY:$LD_LIBRARY_PATH"
     fi
 
     # System libraries needed by libarttestd.so
@@ -889,14 +897,18 @@
     fi
 
     if [ "$QUIET" = "n" ]; then
-      adb push $cmdfile $DEX_LOCATION/cmdline.sh
+      adb push $cmdfile $CHROOT_DEX_LOCATION/cmdline.sh
     else
-      adb push $cmdfile $DEX_LOCATION/cmdline.sh > /dev/null 2>&1
+      adb push $cmdfile $CHROOT_DEX_LOCATION/cmdline.sh >/dev/null 2>&1
     fi
 
     exit_status=0
     if [ "$DRY_RUN" != "y" ]; then
-      adb shell sh $DEX_LOCATION/cmdline.sh
+      if [ -n "$CHROOT" ]; then
+        adb shell chroot "$CHROOT" sh $DEX_LOCATION/cmdline.sh
+      else
+        adb shell sh $DEX_LOCATION/cmdline.sh
+      fi
       exit_status=$?
     fi
 
diff --git a/test/knownfailures.json b/test/knownfailures.json
index f313758..f473a99 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -734,6 +734,7 @@
           "164-resolution-trampoline-dex-cache",
           "167-visit-locks",
           "168-vmstack-annotated",
+          "172-app-image-twice",
           "201-built-in-except-detail-messages",
           "203-multi-checkpoint",
           "304-method-tracing",
diff --git a/test/run-test b/test/run-test
index 5f85b08..be0a88d 100755
--- a/test/run-test
+++ b/test/run-test
@@ -121,6 +121,8 @@
   export HIDDENAPI="${ANDROID_HOST_OUT}/bin/hiddenapi"
 fi
 
+chroot=
+
 info="info.txt"
 build="build"
 run="run"
@@ -380,6 +382,16 @@
             break
         fi
         shift
+    elif [ "x$1" = "x--chroot" ]; then
+        shift
+        if [ "x$1" = "x" ]; then
+            echo "$0 missing argument to --chroot" 1>&2
+            usage="yes"
+            break
+        fi
+        chroot="$1"
+        run_args="${run_args} --chroot $1"
+        shift
     elif [ "x$1" = "x--android-root" ]; then
         shift
         if [ "x$1" = "x" ]; then
@@ -449,6 +461,9 @@
     fi
 done
 
+# The DEX_LOCATION with the chroot prefix, if any.
+chroot_dex_location="$chroot$DEX_LOCATION"
+
 run_args="${run_args} ${image_args}"
 # Allocate file descriptor real_stderr and redirect it to the shell's error
 # output (fd 2).
@@ -476,7 +491,7 @@
 # tmp_dir may be relative, resolve.
 #
 # Cannot use realpath, as it does not exist on Mac.
-# Cannot us a simple "cd", as the path might not be created yet.
+# Cannot use a simple "cd", as the path might not be created yet.
 # Cannot use readlink -m, as it does not exist on Mac.
 # Fallback to nuclear option:
 noncanonical_tmp_dir=$tmp_dir
@@ -550,7 +565,13 @@
     if [ "$runtime" = "jvm" ]; then
         if [ "$prebuild_mode" = "yes" ]; then
             err_echo "--prebuild with --jvm is unsupported"
-            exit 1;
+            exit 1
+        fi
+    else
+        # ART/Dalvik host mode.
+        if [ -n "$chroot" ]; then
+            err_echo "--chroot with --host is unsupported"
+            exit 1
         fi
     fi
 fi
@@ -628,6 +649,12 @@
     usage="yes"
 fi
 
+# TODO: Chroot-based bisection search is not supported yet (see below); implement it.
+if [ "$bisection_search" = "yes" -a -n "$chroot" ]; then
+  err_echo "--chroot with --bisection-search is unsupported"
+  exit 1
+fi
+
 if [ "$usage" = "no" ]; then
     if [ "x$1" = "x" -o "x$1" = "x-" ]; then
         test_dir=`basename "$oldwd"`
@@ -732,6 +759,7 @@
         echo "                          Run with jvmti method redefinition stress testing"
         echo "    --always-clean        Delete the test files even if the test fails."
         echo "    --never-clean         Keep the test files even if the test succeeds."
+        echo "    --chroot [newroot]    Run with root directory set to newroot."
         echo "    --android-root [path] The path on target for the android root. (/system by default)."
         echo "    --dex2oat-swap        Use a dex2oat swap file."
         echo "    --instruction-set-features [string]"
@@ -866,7 +894,7 @@
         if [ "$run_exit" = "0" ]; then
             if [ "$run_checker" = "yes" ]; then
                 if [ "$target_mode" = "yes" ]; then
-                  adb pull $cfg_output_dir/$cfg_output &> /dev/null
+                  adb pull "$chroot/$cfg_output_dir/$cfg_output" &> /dev/null
                 fi
                 "$checker" $checker_args "$cfg_output" "$tmp_dir" 2>&1
                 checker_exit="$?"
@@ -888,7 +916,7 @@
         "./${run}" $run_args "$@" >"$output" 2>&1
         if [ "$run_checker" = "yes" ]; then
           if [ "$target_mode" = "yes" ]; then
-            adb pull $cfg_output_dir/$cfg_output &> /dev/null
+            adb pull "$chroot/$cfg_output_dir/$cfg_output" &> /dev/null
           fi
           "$checker" -q $checker_args "$cfg_output" "$tmp_dir" >> "$output" 2>&1
         fi
@@ -926,7 +954,7 @@
             good_run="no"
         elif [ "$run_checker" = "yes" ]; then
             if [ "$target_mode" = "yes" ]; then
-              adb pull $cfg_output_dir/$cfg_output &> /dev/null
+              adb pull "$chroot/$cfg_output_dir/$cfg_output" &> /dev/null
             fi
             "$checker" -q $checker_args "$cfg_output" "$tmp_dir" >> "$output" 2>&1
             checker_exit="$?"
@@ -986,6 +1014,7 @@
 ) 2>&${real_stderr} 1>&2
 
 # Attempt bisection only if the test failed.
+# TODO: Implement support for chroot-based bisection search.
 if [ "$bisection_search" = "yes" -a "$good" != "yes" ]; then
     # Bisecting works by skipping different optimization passes which breaks checker assertions.
     if [ "$run_checker" == "yes" ]; then
@@ -997,17 +1026,18 @@
       maybe_device_mode=""
       raw_cmd=""
       if [ "$target_mode" = "yes" ]; then
-        # Produce cmdline.sh in $DEX_LOCATION. "$@" is passed as a runtime option
+        # Produce cmdline.sh in $chroot_dex_location. "$@" is passed as a runtime option
         # so that cmdline.sh forwards its arguments to dalvikvm. invoke-with is set
         # to exec in order to preserve pid when calling dalvikvm. This is required
         # for bisection search to correctly retrieve logs from device.
         "./${run}" $run_args --runtime-option '"$@"' --invoke-with exec --dry-run "$@" &> /dev/null
-        adb shell chmod u+x "$DEX_LOCATION/cmdline.sh"
+        adb shell chmod u+x "$chroot_dex_location/cmdline.sh"
         maybe_device_mode="--device"
         raw_cmd="$DEX_LOCATION/cmdline.sh"
       else
         raw_cmd="$cwd/${run} --external-log-tags $run_args $@"
       fi
+      # TODO: Pass a `--chroot` option to the bisection_search.py script and use it there.
       $ANDROID_BUILD_TOP/art/tools/bisection_search/bisection_search.py \
         $maybe_device_mode \
         --raw-cmd="$raw_cmd" \
@@ -1023,7 +1053,7 @@
     cd "$oldwd"
     rm -rf "$tmp_dir"
     if [ "$target_mode" = "yes" -a "$build_exit" = "0" ]; then
-        adb shell rm -rf $DEX_LOCATION
+        adb shell rm -rf $chroot_dex_location
     fi
     if [ "$good" = "yes" ]; then
         exit 0
@@ -1040,7 +1070,7 @@
     else
         echo "${TEST_NAME} files left in ${tmp_dir} on host"
         if [ "$target_mode" == "yes" ]; then
-            echo "and in ${DEX_LOCATION} on target"
+            echo "and in ${chroot_dex_location} on target"
         fi
     fi
 
diff --git a/test/testrunner/env.py b/test/testrunner/env.py
index 7564f5a..0c1c308 100644
--- a/test/testrunner/env.py
+++ b/test/testrunner/env.py
@@ -91,6 +91,8 @@
 HOST_2ND_ARCH_PREFIX_DEX2OAT_HOST_INSTRUCTION_SET_FEATURES = _env.get(
   HOST_2ND_ARCH_PREFIX + 'DEX2OAT_HOST_INSTRUCTION_SET_FEATURES')
 
+ART_TEST_CHROOT = _env.get('ART_TEST_CHROOT')
+
 ART_TEST_ANDROID_ROOT = _env.get('ART_TEST_ANDROID_ROOT')
 
 ART_TEST_WITH_STRACE = _getEnvBoolean('ART_TEST_DEBUG_GC', False)
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index 88b509d..254ffc9 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -320,6 +320,9 @@
   if env.ART_TEST_BISECTION:
     options_all += ' --bisection-search'
 
+  if env.ART_TEST_CHROOT:
+    options_all += ' --chroot ' + env.ART_TEST_CHROOT
+
   if env.ART_TEST_ANDROID_ROOT:
     options_all += ' --android-root ' + env.ART_TEST_ANDROID_ROOT
 
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index 31bddd5..10eb936 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -83,6 +83,10 @@
   make_command+=" debuggerd su"
   make_command+=" ${out_dir}/host/linux-x86/bin/adb libstdc++ "
   make_command+=" ${out_dir}/target/product/${TARGET_PRODUCT}/system/etc/public.libraries.txt"
+  if [[ -n "$ART_TEST_CHROOT" ]]; then
+    # These targets are needed for the chroot environment.
+    make_command+=" crash_dump event-log-tags"
+  fi
   mode_suffix="-target"
 fi
 
diff --git a/tools/cleanup-buildbot-device.sh b/tools/cleanup-buildbot-device.sh
new file mode 100755
index 0000000..53072ae
--- /dev/null
+++ b/tools/cleanup-buildbot-device.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+green='\033[0;32m'
+nc='\033[0m'
+
+# Setup as root, as device cleanup requires it.
+adb root
+adb wait-for-device
+
+if [[ -n "$ART_TEST_CHROOT" ]]; then
+  # Check that ART_TEST_CHROOT is correctly defined.
+  if [[ "x$ART_TEST_CHROOT" != x/* ]]; then
+    echo "$ART_TEST_CHROOT is not an absolute path"
+    exit 1
+  fi
+
+  echo -e "${green}Clean up /system in chroot${nc}"
+  # Remove all files under /system except the potential property_contexts file.
+  #
+  # The current ART Buildbot set-up runs the "setup device" step
+  # (performed by script tools/setup-buildbot-device.sh) before the
+  # "device cleanup" step (implemented by this script). As
+  # property_contexts file aliases are created during the former step,
+  # we need this exception to prevent the property_contexts file under
+  # /system in the chroot from being removed by the latter step.
+  #
+  # TODO: Reorder ART Buildbot steps so that "device cleanup" happens
+  # before "setup device" and remove this special case.
+  #
+  # TODO: Also consider adding a "tear down device" step on the ART
+  # Buildbot (at the very end of a build) undoing (some of) the work
+  # done in the "device setup" step.
+  adb shell find "$ART_TEST_CHROOT/system" \
+    ! -path "$ART_TEST_CHROOT/system/etc/selinux/plat_property_contexts" \
+    ! -type d \
+    -exec rm -f \{\} +
+
+  echo -e "${green}Clean up some subdirs in /data in chroot${nc}"
+  adb shell rm -rf \
+    "$ART_TEST_CHROOT/data/local/tmp/*" \
+    "$ART_TEST_CHROOT/data/art-test" \
+    "$ART_TEST_CHROOT/data/nativetest" \
+    "$ART_TEST_CHROOT/data/nativetest64" \
+    "$ART_TEST_CHROOT/data/run-test" \
+    "$ART_TEST_CHROOT/data/dalvik-cache/*" \
+    "$ART_TEST_CHROOT/data/misc/trace/*"
+else
+  adb shell rm -rf \
+    /data/local/tmp /data/art-test /data/nativetest /data/nativetest64 '/data/misc/trace/*'
+fi
diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh
index 56d412b..d376cad 100755
--- a/tools/run-jdwp-tests.sh
+++ b/tools/run-jdwp-tests.sh
@@ -68,6 +68,8 @@
 mode="target"
 # Use JIT compiling by default.
 use_jit=true
+# Don't use chroot by default.
+use_chroot=false
 variant_cmdline_parameter="--variant=X32"
 dump_command="/bin/true"
 # Timeout of JDWP test in ms.
@@ -110,6 +112,15 @@
     # We don't care about jit with the RI
     use_jit=false
     shift
+  elif [[ "$1" == "--chroot" ]]; then
+    use_chroot=true
+    # Adjust settings for chroot environment.
+    art="/system/bin/art"
+    art_debugee="sh /system/bin/art"
+    vm_command="--vm-command=$art"
+    device_dir="--device-dir=/tmp"
+    # Shift the "--chroot" flag and its argument.
+    shift 2
   elif [[ $1 == --test-timeout-ms ]]; then
     # Remove the --test-timeout-ms from the arguments.
     args=${args/$1}
@@ -191,6 +202,12 @@
   fi
 done
 
+if $use_chroot && [[ $mode == "host" ]]; then
+  # Chroot-based testing is not supported on host.
+  echo "Cannot use --chroot with --mode=host"
+  exit 1
+fi
+
 if [[ $has_gdb = "yes" ]]; then
   if [[ $explicit_debug = "no" ]]; then
     debug="yes"
diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh
index 26b5c0a..3537c1b 100755
--- a/tools/run-libcore-tests.sh
+++ b/tools/run-libcore-tests.sh
@@ -104,10 +104,14 @@
 gcstress=false
 debug=false
 
+# Don't use device mode by default.
+device_mode=false
+# Don't use chroot by default.
+use_chroot=false
+
 while true; do
   if [[ "$1" == "--mode=device" ]]; then
-    vogar_args="$vogar_args --device-dir=/data/local/tmp"
-    vogar_args="$vogar_args --vm-command=$android_root/bin/art"
+    device_mode=true
     vogar_args="$vogar_args --vm-arg -Ximage:/data/art-test/core.art"
     shift
   elif [[ "$1" == "--mode=host" ]]; then
@@ -131,6 +135,10 @@
   elif [[ "$1" == "-Xgc:gcstress" ]]; then
     gcstress=true
     shift
+  elif [[ "$1" == "--chroot" ]]; then
+    use_chroot=true
+    # Shift the "--chroot" flag and its argument.
+    shift 2
   elif [[ "$1" == "" ]]; then
     break
   else
@@ -138,6 +146,23 @@
   fi
 done
 
+if $device_mode; then
+  if $use_chroot; then
+    vogar_args="$vogar_args --device-dir=/tmp"
+    vogar_args="$vogar_args --vm-command=/system/bin/art"
+  else
+    vogar_args="$vogar_args --device-dir=/data/local/tmp"
+    vogar_args="$vogar_args --vm-command=$android_root/bin/art"
+  fi
+else
+  # Host mode.
+  if $use_chroot; then
+    # Chroot-based testing is not supported on host.
+    echo "Cannot use --chroot with --mode=host"
+    exit 1
+  fi
+fi
+
 # Increase the timeout, as vogar cannot set individual test
 # timeout when being asked to run packages, and some tests go above
 # the default timeout.
diff --git a/tools/setup-buildbot-device.sh b/tools/setup-buildbot-device.sh
index 5ce7f52..f71d973 100755
--- a/tools/setup-buildbot-device.sh
+++ b/tools/setup-buildbot-device.sh
@@ -17,8 +17,7 @@
 green='\033[0;32m'
 nc='\033[0m'
 
-# Setup as root, as the next buildbot step (device cleanup) requires it.
-# This is also required to set the date, if needed.
+# Setup as root, as some actions performed here (e.g. setting the date) requires it.
 adb root
 adb wait-for-device
 
@@ -100,3 +99,58 @@
   processes=$(adb shell "ps" | grep dalvikvm | awk '{print $2}')
   for i in $processes; do adb shell kill -9 $i; done
 fi
+
+if [[ -n "$ART_TEST_CHROOT" ]]; then
+  # Prepare the chroot dir.
+  echo -e "${green}Prepare the chroot dir in $ART_TEST_CHROOT${nc}"
+
+  # Check that ART_TEST_CHROOT is correctly defined.
+  [[ "x$ART_TEST_CHROOT" = x/* ]] || { echo "$ART_TEST_CHROOT is not an absolute path"; exit 1; }
+
+  # Create chroot.
+  adb shell mkdir -p "$ART_TEST_CHROOT"
+
+  # Provide property_contexts file(s) in chroot.
+  # This is required to have Android system properties work from the chroot.
+  # Notes:
+  # - In Android N, only '/property_contexts' is expected.
+  # - In Android O, property_context files are expected under /system and /vendor.
+  # (See bionic/libc/bionic/system_properties.cpp for more information.)
+  property_context_files="/property_contexts \
+    /system/etc/selinux/plat_property_contexts \
+    /vendor/etc/selinux/nonplat_property_context \
+    /plat_property_contexts \
+    /nonplat_property_contexts"
+  for f in $property_context_files; do
+    adb shell test -f "$f" \
+      "&&" mkdir -p "$ART_TEST_CHROOT$(dirname $f)" \
+      "&&" cp -f "$f" "$ART_TEST_CHROOT$f"
+  done
+
+  # Create directories required for ART testing in chroot.
+  adb shell mkdir -p "$ART_TEST_CHROOT/tmp"
+  adb shell mkdir -p "$ART_TEST_CHROOT/data/dalvik-cache"
+  adb shell mkdir -p "$ART_TEST_CHROOT/data/local/tmp"
+
+  # Populate /etc in chroot with required files.
+  adb shell mkdir -p "$ART_TEST_CHROOT/system/etc"
+  adb shell "cd $ART_TEST_CHROOT && ln -s system/etc etc"
+
+  # Provide /proc in chroot.
+  adb shell mkdir -p "$ART_TEST_CHROOT/proc"
+  adb shell mount | grep -q "^proc on $ART_TEST_CHROOT/proc type proc " \
+    || adb shell mount -t proc proc "$ART_TEST_CHROOT/proc"
+
+  # Provide /sys in chroot.
+  adb shell mkdir -p "$ART_TEST_CHROOT/sys"
+  adb shell mount | grep -q "^sysfs on $ART_TEST_CHROOT/sys type sysfs " \
+    || adb shell mount -t sysfs sysfs "$ART_TEST_CHROOT/sys"
+  # Provide /sys/kernel/debug in chroot.
+  adb shell mount | grep -q "^debugfs on $ART_TEST_CHROOT/sys/kernel/debug type debugfs " \
+    || adb shell mount -t debugfs debugfs "$ART_TEST_CHROOT/sys/kernel/debug"
+
+  # Provide /dev in chroot.
+  adb shell mkdir -p "$ART_TEST_CHROOT/dev"
+  adb shell mount | grep -q "^tmpfs on $ART_TEST_CHROOT/dev type tmpfs " \
+    || adb shell mount -o bind /dev "$ART_TEST_CHROOT/dev"
+fi