Snap for 8580505 from 6898d018f6a48bbc2a8e471850e84e4611c7815c to main-cg-testing-release

Change-Id: Ic925903944f3777a1f514e3c30e4c1f0d6f49a73
diff --git a/TEST_MAPPING b/TEST_MAPPING
index d606d7c..91a19d2 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -401,6 +401,12 @@
       "name": "art-run-test-2042-checker-dce-always-throw[com.google.android.art.apex]"
     },
     {
+      "name": "art-run-test-2042-reference-processing[com.google.android.art.apex]"
+    },
+    {
+      "name": "art-run-test-2043-reference-pauses[com.google.android.art.apex]"
+    },
+    {
       "name": "art-run-test-2231-checker-heap-poisoning[com.google.android.art.apex]"
     },
     {
@@ -1699,6 +1705,12 @@
       "name": "art-run-test-2042-checker-dce-always-throw"
     },
     {
+      "name": "art-run-test-2042-reference-processing"
+    },
+    {
+      "name": "art-run-test-2043-reference-pauses"
+    },
+    {
       "name": "art-run-test-2231-checker-heap-poisoning"
     },
     {
@@ -2589,9 +2601,6 @@
   ],
   "hwasan-presubmit": [
     {
-      "name": "BootImageProfileTest"
-    },
-    {
       "name": "ArtServiceTests"
     },
     {
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index 0a1f017..d05324b 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -414,7 +414,7 @@
   CHECK(!caller->IsCriticalNative());
   CHECK(caller->IsSynchronized());
   ObjPtr<mirror::Object> lock;
-  if (self->GetManagedStack()->GetTopQuickFrameTag()) {
+  if (self->GetManagedStack()->GetTopQuickFrameGenericJniTag()) {
     // Generic JNI.
     lock = GetGenericJniSynchronizationObject(self, caller);
   } else if (caller->IsStatic()) {
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 6cb5021..b88ebaf 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -95,6 +95,13 @@
   const InstructionSetFeatures* instruction_set_features =
       compiler_options.GetInstructionSetFeatures();
 
+  // When  walking the stack the top frame doesn't have a pc associated with it. We then depend on
+  // the invariant that we don't have JITed code when AOT code is available. In debuggable runtimes
+  // this invariant doesn't hold. So we tag the SP for JITed code to indentify if we are executing
+  // JITed code or AOT code. Since tagging involves additional instructions we tag only in
+  // debuggable runtimes.
+  bool should_tag_sp = compiler_options.GetDebuggable() && compiler_options.IsJitCompiler();
+
   // i.e. if the method was annotated with @FastNative
   const bool is_fast_native = (access_flags & kAccFastNative) != 0u;
 
@@ -219,7 +226,7 @@
   //       because garbage collections are disabled within the execution of a
   //       @CriticalNative method.
   if (LIKELY(!is_critical_native)) {
-    __ StoreStackPointerToThread(Thread::TopOfManagedStackOffset<kPointerSize>());
+    __ StoreStackPointerToThread(Thread::TopOfManagedStackOffset<kPointerSize>(), should_tag_sp);
   }
 
   // 2. Lock the object (if synchronized) and transition out of Runnable (if normal native).
@@ -605,7 +612,7 @@
     if (reference_return) {
       // Suspend check entry point overwrites top of managed stack and leaves it clobbered.
       // We need to restore the top for subsequent runtime call to `JniDecodeReferenceResult()`.
-      __ StoreStackPointerToThread(Thread::TopOfManagedStackOffset<kPointerSize>());
+      __ StoreStackPointerToThread(Thread::TopOfManagedStackOffset<kPointerSize>(), should_tag_sp);
     }
     if (reference_return && main_out_arg_size != 0) {
       __ IncreaseFrameSize(main_out_arg_size);
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index 6e6d40d..61151fe 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -428,8 +428,15 @@
   asm_.StoreToOffset(kStoreWord, scratch, tr, thr_offs.Int32Value());
 }
 
-void ArmVIXLJNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) {
-  asm_.StoreToOffset(kStoreWord, sp, tr, thr_offs.Int32Value());
+void ArmVIXLJNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs, bool tag_sp) {
+  if (tag_sp) {
+    UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
+    vixl32::Register reg = temps.Acquire();
+    ___ Orr(reg, sp, 0x2);
+    asm_.StoreToOffset(kStoreWord, reg, tr, thr_offs.Int32Value());
+  } else {
+    asm_.StoreToOffset(kStoreWord, sp, tr, thr_offs.Int32Value());
+  }
 }
 
 void ArmVIXLJNIMacroAssembler::SignExtend(ManagedRegister mreg ATTRIBUTE_UNUSED,
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.h b/compiler/utils/arm/jni_macro_assembler_arm_vixl.h
index ed453ae..980de41 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.h
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.h
@@ -70,7 +70,7 @@
 
   void StoreStackOffsetToThread(ThreadOffset32 thr_offs, FrameOffset fr_offs) override;
 
-  void StoreStackPointerToThread(ThreadOffset32 thr_offs) override;
+  void StoreStackPointerToThread(ThreadOffset32 thr_offs, bool tag_sp) override;
 
   void StoreSpanning(FrameOffset dest, ManagedRegister src, FrameOffset in_off) override;
 
diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.cc b/compiler/utils/arm64/jni_macro_assembler_arm64.cc
index 50ca468..323a01e 100644
--- a/compiler/utils/arm64/jni_macro_assembler_arm64.cc
+++ b/compiler/utils/arm64/jni_macro_assembler_arm64.cc
@@ -218,10 +218,13 @@
   ___ Str(scratch, MEM_OP(reg_x(TR), tr_offs.Int32Value()));
 }
 
-void Arm64JNIMacroAssembler::StoreStackPointerToThread(ThreadOffset64 tr_offs) {
+void Arm64JNIMacroAssembler::StoreStackPointerToThread(ThreadOffset64 tr_offs, bool tag_sp) {
   UseScratchRegisterScope temps(asm_.GetVIXLAssembler());
   Register scratch = temps.AcquireX();
   ___ Mov(scratch, reg_x(SP));
+  if (tag_sp) {
+    ___ Orr(scratch, scratch, 0x2);
+  }
   ___ Str(scratch, MEM_OP(reg_x(TR), tr_offs.Int32Value()));
 }
 
diff --git a/compiler/utils/arm64/jni_macro_assembler_arm64.h b/compiler/utils/arm64/jni_macro_assembler_arm64.h
index 2c04184..daea95d 100644
--- a/compiler/utils/arm64/jni_macro_assembler_arm64.h
+++ b/compiler/utils/arm64/jni_macro_assembler_arm64.h
@@ -72,7 +72,7 @@
   void StoreRawPtr(FrameOffset dest, ManagedRegister src) override;
   void StoreImmediateToFrame(FrameOffset dest, uint32_t imm) override;
   void StoreStackOffsetToThread(ThreadOffset64 thr_offs, FrameOffset fr_offs) override;
-  void StoreStackPointerToThread(ThreadOffset64 thr_offs) override;
+  void StoreStackPointerToThread(ThreadOffset64 thr_offs, bool tag_sp) override;
   void StoreSpanning(FrameOffset dest, ManagedRegister src, FrameOffset in_off) override;
 
   // Load routines.
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index b2d4dcd..f867a05 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -159,7 +159,8 @@
   __ StoreRef(FrameOffset(48), scratch_register);
   __ StoreSpanning(FrameOffset(48), method_register, FrameOffset(48));
   __ StoreStackOffsetToThread(ThreadOffset32(512), FrameOffset(4096));
-  __ StoreStackPointerToThread(ThreadOffset32(512));
+  __ StoreStackPointerToThread(ThreadOffset32(512), false);
+  __ StoreStackPointerToThread(ThreadOffset32(512), true);
 
   // Other
   __ Call(method_register, FrameOffset(48));
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 1775014..dac21ae 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -41,46 +41,46 @@
   "      7e: 0d f5 80 5c   add.w r12, sp, #4096\n"
   "      82: c9 f8 00 c2   str.w r12, [r9, #512]\n"
   "      86: c9 f8 00 d2   str.w sp, [r9, #512]\n"
-  "      8a: d0 f8 30 e0   ldr.w lr, [r0, #48]\n"
-  "      8e: f0 47         blx lr\n"
-  "      90: dd f8 2c c0   ldr.w r12, [sp, #44]\n"
-  "      94: cd f8 30 c0   str.w r12, [sp, #48]\n"
-  "      98: d9 f8 00 c2   ldr.w r12, [r9, #512]\n"
-  "      9c: cd f8 2c c0   str.w r12, [sp, #44]\n"
-  "      a0: dd f8 2c c0   ldr.w r12, [sp, #44]\n"
-  "      a4: cd f8 30 c0   str.w r12, [sp, #48]\n"
-  "      a8: 48 46         mov r0, r9\n"
-  "      aa: cd f8 30 90   str.w r9, [sp, #48]\n"
-  "      ae: 04 46         mov r4, r0\n"
-  "      b0: 0d f1 30 0c   add.w r12, sp, #48\n"
-  "      b4: bb f1 00 0f   cmp.w r11, #0\n"
-  "      b8: 18 bf         it ne\n"
-  "      ba: e3 46         movne r11, r12\n"
-  "      bc: 0d f1 30 0b   add.w r11, sp, #48\n"
-  "      c0: 5f ea 0b 00   movs.w r0, r11\n"
-  "      c4: 18 bf         it ne\n"
-  "      c6: 0c a8         addne r0, sp, #48\n"
-  "      c8: dd f8 40 c0   ldr.w r12, [sp, #64]\n"
-  "      cc: bc f1 00 0f   cmp.w r12, #0\n"
-  "      d0: 18 bf         it ne\n"
-  "      d2: 0d f1 40 0c   addne.w r12, sp, #64\n"
-  "      d6: cd f8 30 c0   str.w r12, [sp, #48]\n"
-  "      da: 5f ea 0b 00   movs.w r0, r11\n"
-  "      de: 18 bf         it ne\n"
-  "      e0: 00 a8         addne r0, sp, #0\n"
-  "      e2: 0d f2 04 40   addw r0, sp, #1028\n"
-  "      e6: bb f1 00 0f   cmp.w r11, #0\n"
-  "      ea: 08 bf         it eq\n"
-  "      ec: 58 46         moveq r0, r11\n"
-  "      ee: 0d f2 04 4c   addw r12, sp, #1028\n"
-  "      f2: bb f1 00 0f   cmp.w r11, #0\n"
-  "      f6: 18 bf         it ne\n"
-  "      f8: e3 46         movne r11, r12\n"
-  "      fa: d9 f8 9c c0   ldr.w r12, [r9, #156]\n"
-  "      fe: bc f1 00 0f   cmp.w r12, #0\n"
-  "     102: 71 d1         bne 0x1e8     @ imm = #226\n"
-  "     104: cd f8 ff c7   str.w r12, [sp, #2047]\n"
-  "     108: cd f8 ff c7   str.w r12, [sp, #2047]\n"
+  "      8a: 4d f0 02 0c   orr r12, sp, #2\n"
+  "      8e: c9 f8 00 c2   str.w r12, [r9, #512]\n"
+  "      92: d0 f8 30 e0   ldr.w lr, [r0, #48]\n"
+  "      96: f0 47         blx lr\n"
+  "      98: dd f8 2c c0   ldr.w r12, [sp, #44]\n"
+  "      9c: cd f8 30 c0   str.w r12, [sp, #48]\n"
+  "      a0: d9 f8 00 c2   ldr.w r12, [r9, #512]\n"
+  "      a4: cd f8 2c c0   str.w r12, [sp, #44]\n"
+  "      a8: dd f8 2c c0   ldr.w r12, [sp, #44]\n"
+  "      ac: cd f8 30 c0   str.w r12, [sp, #48]\n"
+  "      b0: 48 46         mov r0, r9\n"
+  "      b2: cd f8 30 90   str.w r9, [sp, #48]\n"
+  "      b6: 04 46         mov r4, r0\n"
+  "      b8: 0d f1 30 0c   add.w r12, sp, #48\n"
+  "      bc: bb f1 00 0f   cmp.w r11, #0\n"
+  "      c0: 18 bf         it ne\n"
+  "      c2: e3 46         movne r11, r12\n"
+  "      c4: 0d f1 30 0b   add.w r11, sp, #48\n"
+  "      c8: 5f ea 0b 00   movs.w r0, r11\n"
+  "      cc: 18 bf         it ne\n"
+  "      ce: 0c a8         addne r0, sp, #48\n"
+  "      d0: dd f8 40 c0   ldr.w r12, [sp, #64]\n"
+  "      d4: bc f1 00 0f   cmp.w r12, #0\n"
+  "      d8: 18 bf         it ne\n"
+  "      da: 0d f1 40 0c   addne.w r12, sp, #64\n"
+  "      de: cd f8 30 c0   str.w r12, [sp, #48]\n"
+  "      e2: 5f ea 0b 00   movs.w r0, r11\n"
+  "      e6: 18 bf         it ne\n"
+  "      e8: 00 a8         addne r0, sp, #0\n"
+  "      ea: 0d f2 04 40   addw r0, sp, #1028\n"
+  "      ee: bb f1 00 0f   cmp.w r11, #0\n"
+  "      f2: 08 bf         it eq\n"
+  "      f4: 58 46         moveq r0, r11\n"
+  "      f6: 0d f2 04 4c   addw r12, sp, #1028\n"
+  "      fa: bb f1 00 0f   cmp.w r11, #0\n"
+  "      fe: 18 bf         it ne\n"
+  "     100: e3 46         movne r11, r12\n"
+  "     102: d9 f8 9c c0   ldr.w r12, [r9, #156]\n"
+  "     106: bc f1 00 0f   cmp.w r12, #0\n"
+  "     10a: 71 d1         bne 0x1f0     @ imm = #226\n"
   "     10c: cd f8 ff c7   str.w r12, [sp, #2047]\n"
   "     110: cd f8 ff c7   str.w r12, [sp, #2047]\n"
   "     114: cd f8 ff c7   str.w r12, [sp, #2047]\n"
@@ -135,26 +135,28 @@
   "     1d8: cd f8 ff c7   str.w r12, [sp, #2047]\n"
   "     1dc: cd f8 ff c7   str.w r12, [sp, #2047]\n"
   "     1e0: cd f8 ff c7   str.w r12, [sp, #2047]\n"
-  "     1e4: 00 f0 02 b8   b.w 0x1ec     @ imm = #4\n"
-  "     1e8: 00 f0 1b b8   b.w 0x222     @ imm = #54\n"
-  "     1ec: cd f8 ff c7   str.w r12, [sp, #2047]\n"
-  "     1f0: cd f8 ff c7   str.w r12, [sp, #2047]\n"
+  "     1e4: cd f8 ff c7   str.w r12, [sp, #2047]\n"
+  "     1e8: cd f8 ff c7   str.w r12, [sp, #2047]\n"
+  "     1ec: 00 f0 02 b8   b.w 0x1f4     @ imm = #4\n"
+  "     1f0: 00 f0 1b b8   b.w 0x22a     @ imm = #54\n"
   "     1f4: cd f8 ff c7   str.w r12, [sp, #2047]\n"
   "     1f8: cd f8 ff c7   str.w r12, [sp, #2047]\n"
   "     1fc: cd f8 ff c7   str.w r12, [sp, #2047]\n"
   "     200: cd f8 ff c7   str.w r12, [sp, #2047]\n"
   "     204: cd f8 ff c7   str.w r12, [sp, #2047]\n"
   "     208: cd f8 ff c7   str.w r12, [sp, #2047]\n"
-  "     20c: 0d f5 80 5d   add.w sp, sp, #4096\n"
-  "     210: 08 b0         add sp, #32\n"
-  "     212: 01 b0         add sp, #4\n"
-  "     214: bd ec 10 8a   vpop {s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31}\n"
-  "     218: bd e8 e0 4d   pop.w {r5, r6, r7, r8, r10, r11, lr}\n"
-  "     21c: d9 f8 24 80   ldr.w r8, [r9, #36]\n"
-  "     220: 70 47         bx lr\n"
-  "     222: d9 f8 9c 00   ldr.w r0, [r9, #156]\n"
-  "     226: d9 f8 d0 e2   ldr.w lr, [r9, #720]\n"
-  "     22a: f0 47         blx lr\n"
+  "     20c: cd f8 ff c7   str.w r12, [sp, #2047]\n"
+  "     210: cd f8 ff c7   str.w r12, [sp, #2047]\n"
+  "     214: 0d f5 80 5d   add.w sp, sp, #4096\n"
+  "     218: 08 b0         add sp, #32\n"
+  "     21a: 01 b0         add sp, #4\n"
+  "     21c: bd ec 10 8a   vpop {s16, s17, s18, s19, s20, s21, s22, s23, s24, s25, s26, s27, s28, s29, s30, s31}\n"
+  "     220: bd e8 e0 4d   pop.w {r5, r6, r7, r8, r10, r11, lr}\n"
+  "     224: d9 f8 24 80   ldr.w r8, [r9, #36]\n"
+  "     228: 70 47         bx lr\n"
+  "     22a: d9 f8 9c 00   ldr.w r0, [r9, #156]\n"
+  "     22e: d9 f8 d0 e2   ldr.w lr, [r9, #720]\n"
+  "     232: f0 47         blx lr\n"
 };
 
 const char* const VixlLoadFromOffsetResults = {
diff --git a/compiler/utils/jni_macro_assembler.h b/compiler/utils/jni_macro_assembler.h
index 7022e3d..c8c713a 100644
--- a/compiler/utils/jni_macro_assembler.h
+++ b/compiler/utils/jni_macro_assembler.h
@@ -126,7 +126,11 @@
   virtual void StoreStackOffsetToThread(ThreadOffset<kPointerSize> thr_offs,
                                         FrameOffset fr_offs) = 0;
 
-  virtual void StoreStackPointerToThread(ThreadOffset<kPointerSize> thr_offs) = 0;
+  // Stores stack pointer by tagging it if required so we can walk the stack. In debuggable runtimes
+  // we use tag to tell if we are using JITed code or AOT code. In non-debuggable runtimes we never
+  // use JITed code when AOT code is present. So checking for AOT code is sufficient to detect which
+  // code is being executed. We avoid tagging in non-debuggable runtimes to reduce instructions.
+  virtual void StoreStackPointerToThread(ThreadOffset<kPointerSize> thr_offs, bool tag_sp) = 0;
 
   virtual void StoreSpanning(FrameOffset dest,
                              ManagedRegister src,
diff --git a/compiler/utils/x86/jni_macro_assembler_x86.cc b/compiler/utils/x86/jni_macro_assembler_x86.cc
index 685f5f1..55d5428 100644
--- a/compiler/utils/x86/jni_macro_assembler_x86.cc
+++ b/compiler/utils/x86/jni_macro_assembler_x86.cc
@@ -187,8 +187,18 @@
   __ fs()->movl(Address::Absolute(thr_offs), scratch);
 }
 
-void X86JNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) {
-  __ fs()->movl(Address::Absolute(thr_offs), ESP);
+void X86JNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs, bool tag_sp) {
+  if (tag_sp) {
+    // There is no free register, store contents onto stack and restore back later.
+    Register scratch = ECX;
+    __ movl(Address(ESP, -32), scratch);
+    __ movl(scratch, ESP);
+    __ orl(scratch, Immediate(0x2));
+    __ fs()->movl(Address::Absolute(thr_offs), scratch);
+    __ movl(scratch, Address(ESP, -32));
+  } else {
+    __ fs()->movl(Address::Absolute(thr_offs), ESP);
+  }
 }
 
 void X86JNIMacroAssembler::StoreSpanning(FrameOffset /*dst*/,
diff --git a/compiler/utils/x86/jni_macro_assembler_x86.h b/compiler/utils/x86/jni_macro_assembler_x86.h
index 29fccfd..f8ce38b 100644
--- a/compiler/utils/x86/jni_macro_assembler_x86.h
+++ b/compiler/utils/x86/jni_macro_assembler_x86.h
@@ -66,7 +66,7 @@
 
   void StoreStackOffsetToThread(ThreadOffset32 thr_offs, FrameOffset fr_offs) override;
 
-  void StoreStackPointerToThread(ThreadOffset32 thr_offs) override;
+  void StoreStackPointerToThread(ThreadOffset32 thr_offs, bool tag_sp) override;
 
   void StoreSpanning(FrameOffset dest, ManagedRegister src, FrameOffset in_off) override;
 
diff --git a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
index d5d1bba..adc431f 100644
--- a/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
+++ b/compiler/utils/x86_64/jni_macro_assembler_x86_64.cc
@@ -217,8 +217,15 @@
   __ gs()->movq(Address::Absolute(thr_offs, true), scratch);
 }
 
-void X86_64JNIMacroAssembler::StoreStackPointerToThread(ThreadOffset64 thr_offs) {
-  __ gs()->movq(Address::Absolute(thr_offs, true), CpuRegister(RSP));
+void X86_64JNIMacroAssembler::StoreStackPointerToThread(ThreadOffset64 thr_offs, bool tag_sp) {
+  if (tag_sp) {
+    CpuRegister reg = GetScratchRegister();
+    __ movq(reg, CpuRegister(RSP));
+    __ orq(reg, Immediate(0x2));
+    __ gs()->movq(Address::Absolute(thr_offs, true), reg);
+  } else {
+    __ gs()->movq(Address::Absolute(thr_offs, true), CpuRegister(RSP));
+  }
 }
 
 void X86_64JNIMacroAssembler::StoreSpanning(FrameOffset /*dst*/,
diff --git a/compiler/utils/x86_64/jni_macro_assembler_x86_64.h b/compiler/utils/x86_64/jni_macro_assembler_x86_64.h
index e080f0b..feaf27e 100644
--- a/compiler/utils/x86_64/jni_macro_assembler_x86_64.h
+++ b/compiler/utils/x86_64/jni_macro_assembler_x86_64.h
@@ -67,7 +67,7 @@
 
   void StoreStackOffsetToThread(ThreadOffset64 thr_offs, FrameOffset fr_offs) override;
 
-  void StoreStackPointerToThread(ThreadOffset64 thr_offs) override;
+  void StoreStackPointerToThread(ThreadOffset64 thr_offs, bool tag_sp) override;
 
   void StoreSpanning(FrameOffset dest, ManagedRegister src, FrameOffset in_off) override;
 
diff --git a/libartbase/base/hiddenapi_flags.h b/libartbase/base/hiddenapi_flags.h
index 7415b93..9d0a18e 100644
--- a/libartbase/base/hiddenapi_flags.h
+++ b/libartbase/base/hiddenapi_flags.h
@@ -98,11 +98,12 @@
     kMaxTargetP = 4,
     kMaxTargetQ = 5,
     kMaxTargetR = 6,
+    kMaxTargetS = 7,
 
     // Special values
     kInvalid =      (static_cast<uint32_t>(-1) & kValueBitMask),
     kMin =          kSdk,
-    kMax =          kMaxTargetR,
+    kMax =          kMaxTargetS,
   };
 
   // Additional bit flags after the first kValueBitSize bits in dex flags.
@@ -139,6 +140,7 @@
     "max-target-p",
     "max-target-q",
     "max-target-r",
+    "max-target-s",
   };
 
   // A magic marker used by tests to mimic a hiddenapi list which doesn't exist
@@ -160,6 +162,7 @@
     /* max-target-p */ SdkVersion::kP,
     /* max-target-q */ SdkVersion::kQ,
     /* max-target-r */ SdkVersion::kR,
+    /* max-target-s */ SdkVersion::kS,
   };
 
   explicit ApiList(Value val, uint32_t domain_apis = 0u)
@@ -204,6 +207,7 @@
   static ApiList MaxTargetP() { return ApiList(Value::kMaxTargetP); }
   static ApiList MaxTargetQ() { return ApiList(Value::kMaxTargetQ); }
   static ApiList MaxTargetR() { return ApiList(Value::kMaxTargetR); }
+  static ApiList MaxTargetS() { return ApiList(Value::kMaxTargetS); }
   static ApiList CorePlatformApi() { return ApiList(DomainApi::kCorePlatformApi); }
   static ApiList TestApi() { return ApiList(DomainApi::kTestApi); }
 
diff --git a/libartservice/Android.bp b/libartservice/Android.bp
index 985a2eb..b9632ea 100644
--- a/libartservice/Android.bp
+++ b/libartservice/Android.bp
@@ -68,27 +68,9 @@
         "com.android.art",
         "com.android.art.debug",
     ],
-    sdk_version: "core_platform",
+    sdk_version: "system_server_current",
     min_sdk_version: "31",
 
-    public: {
-        // Override the setting of "module_current" from the defaults as that is
-        // not available in all manifests where this needs to be built.
-        sdk_version: "core_current",
-    },
-
-    system_server: {
-        // Override the setting of "module_current" from the defaults as that is
-        // not available in all manifests where this needs to be built.
-        sdk_version: "core_current",
-    },
-
-    // The API elements are the ones annotated with
-    //   libcore.api.CorePlatformApi(status=libcore.api.CorePlatformApi.Status.STABLE)
-    droiddoc_options: [
-        "--show-single-annotation libcore.api.CorePlatformApi\\(status=libcore.api.CorePlatformApi.Status.STABLE\\)",
-    ],
-
     // Temporarily disable compatibility with previous released APIs.
     // TODO - remove once prototype has stabilized
     //   running "m update-api" will give instructions on what to do next
@@ -100,11 +82,10 @@
     compile_dex: true,
 
     srcs: [
-        "service/java/com/android/server/art/ArtManagerLocal.java",
+        "service/java/**/*.java",
     ],
 
     libs: [
-        "art.module.api.annotations.for.system.modules",
     ],
 
     plugins: ["java_api_finder"],
diff --git a/libartservice/api/current.txt b/libartservice/api/current.txt
index c7844e0..d802177 100644
--- a/libartservice/api/current.txt
+++ b/libartservice/api/current.txt
@@ -1,9 +1 @@
 // Signature format: 2.0
-package com.android.server.art {
-
-  public final class ArtManagerLocal {
-    ctor public ArtManagerLocal();
-  }
-
-}
-
diff --git a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
index 04629cb..64aec7b 100644
--- a/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
+++ b/libartservice/service/java/com/android/server/art/ArtManagerLocal.java
@@ -16,13 +16,16 @@
 
 package com.android.server.art;
 
+import android.annotation.SystemApi;
+
 /**
- * This class provides a system API for functionality provided by the ART
- * module.
+ * This class provides a system API for functionality provided by the ART module.
+ *
+ * @hide
  */
-@libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
+@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
 public final class ArtManagerLocal {
-    static final String LOG_TAG = "ArtService";
+    private static final String TAG = "ArtService";
 
     public ArtManagerLocal() {}
 }
diff --git a/libartservice/tests/Android.bp b/libartservice/tests/Android.bp
index dc110a1..1b18cc5 100644
--- a/libartservice/tests/Android.bp
+++ b/libartservice/tests/Android.bp
@@ -27,7 +27,7 @@
     default_applicable_licenses: ["art_license"],
 }
 
-java_test {
+android_test {
     name: "ArtServiceTests",
 
     // Include all test java files.
@@ -36,9 +36,15 @@
     ],
 
     static_libs: [
+        "androidx.test.ext.junit",
+        "androidx.test.ext.truth",
         "androidx.test.runner",
+        "mockito-target-minus-junit4",
         "service-art.impl",
     ],
 
+    sdk_version: "system_server_current",
+    min_sdk_version: "31",
+
     test_suites: ["general-tests"],
 }
diff --git a/libartservice/tests/AndroidManifest.xml b/libartservice/tests/AndroidManifest.xml
new file mode 100644
index 0000000..921bde9
--- /dev/null
+++ b/libartservice/tests/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.server.art.tests">
+
+    <application android:label="ArtServiceTests">
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.server.art.tests"
+        android:label="Tests for ART Serices" />
+</manifest>
diff --git a/libartservice/tests/src/com/android/server/art/ArtManagerLocalTests.java b/libartservice/tests/src/com/android/server/art/ArtManagerLocalTests.java
index 515849b..b0323c4 100644
--- a/libartservice/tests/src/com/android/server/art/ArtManagerLocalTests.java
+++ b/libartservice/tests/src/com/android/server/art/ArtManagerLocalTests.java
@@ -16,22 +16,29 @@
 
 package com.android.server.art;
 
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
 
 import com.android.server.art.ArtManagerLocal;
 
-import junit.framework.TestCase;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
 
-public class ArtManagerLocalTests extends TestCase {
+@SmallTest
+@RunWith(MockitoJUnitRunner.class)
+public class ArtManagerLocalTests {
     private ArtManagerLocal mArtManagerLocal;
 
-    public void setup() {
+    @Before
+    public void setUp() {
         mArtManagerLocal = new ArtManagerLocal();
     }
 
+    @Test
     public void testScaffolding() {
-        assertTrue(true);
+        assertThat(true).isTrue();
     }
-}
\ No newline at end of file
+}
diff --git a/odrefresh/CacheInfo.xsd b/odrefresh/CacheInfo.xsd
index 1cbef8a..196caf7 100644
--- a/odrefresh/CacheInfo.xsd
+++ b/odrefresh/CacheInfo.xsd
@@ -27,6 +27,7 @@
     <!-- True if the cache info is generated in the Compilation OS. -->
     <xs:attribute name="compilationOsMode" type="xs:boolean" />
     <xs:sequence>
+      <xs:element name="systemProperties" minOccurs="1" maxOccurs="1" type="t:keyValuePairList" />
       <xs:element name="artModuleInfo" minOccurs="1" maxOccurs="1" type="t:moduleInfo" />
       <xs:element name="moduleInfoList" minOccurs="1" maxOccurs="1" type="t:moduleInfoList" />
       <xs:element name="bootClasspath" minOccurs="1" maxOccurs="1" type="t:classpath" />
@@ -36,6 +37,19 @@
     </xs:complexType>
   </xs:element>
 
+  <!-- List of key-value pairs. -->
+  <xs:complexType name="keyValuePairList">
+    <xs:sequence>
+      <xs:element name="item" type="t:keyValuePair" />
+    </xs:sequence>
+  </xs:complexType>
+
+  <!-- A key-value pair. -->
+  <xs:complexType name="keyValuePair">
+    <xs:attribute name="k" type="xs:string" use="required" />
+    <xs:attribute name="v" type="xs:string" use="required" />
+  </xs:complexType>
+
   <!-- List of modules. -->
   <xs:complexType name="moduleInfoList">
     <xs:sequence>
diff --git a/odrefresh/odr_config.h b/odrefresh/odr_config.h
index d95ab96..6a4646a 100644
--- a/odrefresh/odr_config.h
+++ b/odrefresh/odr_config.h
@@ -19,9 +19,11 @@
 
 #include <optional>
 #include <string>
+#include <unordered_map>
 #include <vector>
 
 #include "android-base/file.h"
+#include "android-base/no_destructor.h"
 #include "arch/instruction_set.h"
 #include "base/file_utils.h"
 #include "base/globals.h"
@@ -32,6 +34,17 @@
 namespace art {
 namespace odrefresh {
 
+struct SystemPropertyConfig {
+  const char* name;
+  const char* default_value;
+};
+
+// The system properties that odrefresh keeps track of. Odrefresh will recompile everything if any
+// property changes.
+const android::base::NoDestructor<std::vector<SystemPropertyConfig>> kSystemProperties{
+    {SystemPropertyConfig{.name = "persist.device_config.runtime_native_boot.enable_uffd_gc",
+                          .default_value = "false"}}};
+
 // An enumeration of the possible zygote configurations on Android.
 enum class ZygoteKind : uint8_t {
   // 32-bit primary zygote, no secondary zygote.
@@ -66,6 +79,9 @@
   bool compilation_os_mode_ = false;
   bool minimal_ = false;
 
+  // The current values of system properties listed in `kSystemProperties`.
+  std::unordered_map<std::string, std::string> system_properties_;
+
   // Staging directory for artifacts. The directory must exist and will be automatically removed
   // after compilation. If empty, use the default directory.
   std::string staging_dir_;
@@ -145,6 +161,9 @@
   }
   bool GetCompilationOsMode() const { return compilation_os_mode_; }
   bool GetMinimal() const { return minimal_; }
+  const std::unordered_map<std::string, std::string>& GetSystemProperties() const {
+    return system_properties_;
+  }
 
   void SetApexInfoListFile(const std::string& file_path) { apex_info_list_file_ = file_path; }
   void SetArtBinDir(const std::string& art_bin_dir) { art_bin_dir_ = art_bin_dir; }
@@ -196,6 +215,10 @@
 
   void SetMinimal(bool value) { minimal_ = value; }
 
+  std::unordered_map<std::string, std::string>* MutableSystemProperties() {
+    return &system_properties_;
+  }
+
  private:
   // Returns a pair for the possible instruction sets for the configured instruction set
   // architecture. The first item is the 32-bit architecture and the second item is the 64-bit
diff --git a/odrefresh/odrefresh.cc b/odrefresh/odrefresh.cc
index 1d12b94..70cf48e 100644
--- a/odrefresh/odrefresh.cc
+++ b/odrefresh/odrefresh.cc
@@ -648,6 +648,11 @@
     return Errorf("Could not create directory {}", QuotePath(dir_name));
   }
 
+  std::vector<art_apex::KeyValuePair> system_properties;
+  for (const auto& [key, value] : config_.GetSystemProperties()) {
+    system_properties.emplace_back(key, value);
+  }
+
   std::optional<std::vector<apex::ApexInfo>> apex_info_list = GetApexInfoList();
   if (!apex_info_list.has_value()) {
     return Errorf("Could not update {}: no APEX info", QuotePath(cache_info_filename_));
@@ -685,15 +690,16 @@
     return Errorf("Cannot open {} for writing.", QuotePath(cache_info_filename_));
   }
 
-  art_apex::CacheInfo info(
+  std::unique_ptr<art_apex::CacheInfo> info(new art_apex::CacheInfo(
+      {art_apex::KeyValuePairList(system_properties)},
       {art_module_info},
       {art_apex::ModuleInfoList(module_info_list)},
       {art_apex::Classpath(bcp_components.value())},
       {art_apex::Classpath(bcp_compilable_components.value())},
       {art_apex::SystemServerComponents(system_server_components.value())},
-      config_.GetCompilationOsMode() ? std::make_optional(true) : std::nullopt);
+      config_.GetCompilationOsMode() ? std::make_optional(true) : std::nullopt));
 
-  art_apex::write(out, info);
+  art_apex::write(out, *info);
   out.close();
   if (out.fail()) {
     return Errorf("Cannot write to {}", QuotePath(cache_info_filename_));
@@ -836,16 +842,106 @@
   return jars_missing_artifacts->empty();
 }
 
+WARN_UNUSED bool OnDeviceRefresh::CheckSystemPropertiesAreDefault() const {
+  const std::unordered_map<std::string, std::string>& system_properties =
+      config_.GetSystemProperties();
+
+  for (const SystemPropertyConfig& system_property_config : *kSystemProperties.get()) {
+    auto property = system_properties.find(system_property_config.name);
+    DCHECK(property != system_properties.end());
+
+    if (property->second != system_property_config.default_value) {
+      LOG(INFO) << "System property " << system_property_config.name << " has a non-default value ("
+                << property->second << ").";
+      return false;
+    }
+  }
+
+  return true;
+}
+
+WARN_UNUSED bool OnDeviceRefresh::CheckSystemPropertiesHaveNotChanged(
+    const art_apex::CacheInfo& cache_info) const {
+  std::unordered_map<std::string, std::string> cached_system_properties;
+  const art_apex::KeyValuePairList* list = cache_info.getFirstSystemProperties();
+  if (list == nullptr) {
+    // This should never happen. We have already checked the ART module version, and the cache
+    // info is generated by the latest version of the ART module if it exists.
+    LOG(ERROR) << "Missing system properties from cache-info.";
+    return false;
+  }
+
+  for (const art_apex::KeyValuePair& pair : list->getItem()) {
+    cached_system_properties[pair.getK()] = pair.getV();
+  }
+
+  const std::unordered_map<std::string, std::string>& system_properties =
+      config_.GetSystemProperties();
+
+  for (const SystemPropertyConfig& system_property_config : *kSystemProperties.get()) {
+    auto property = system_properties.find(system_property_config.name);
+    DCHECK(property != system_properties.end());
+
+    auto cached_property = cached_system_properties.find(system_property_config.name);
+    if (cached_property == cached_system_properties.end()) {
+      // This should never happen. We have already checked the ART module version, and the cache
+      // info is generated by the latest version of the ART module if it exists.
+      LOG(ERROR) << "Missing system property from cache-info (" << system_property_config.name
+                 << ")";
+      return false;
+    }
+
+    if (property->second != cached_property->second) {
+      LOG(INFO) << "System property " << system_property_config.name
+                << " value changed (before: " << cached_property->second
+                << ", now: " << property->second << ").";
+      return false;
+    }
+  }
+
+  return true;
+}
+
+WARN_UNUSED bool OnDeviceRefresh::BootClasspathArtifactsOnSystemUsable(
+    const apex::ApexInfo& art_apex_info) const {
+  if (!art_apex_info.getIsFactory()) {
+    return false;
+  }
+  LOG(INFO) << "Factory ART APEX mounted.";
+
+  if (!CheckSystemPropertiesAreDefault()) {
+    return false;
+  }
+  LOG(INFO) << "System properties are set to default values.";
+
+  return true;
+}
+
+WARN_UNUSED bool OnDeviceRefresh::SystemServerArtifactsOnSystemUsable(
+    const std::vector<apex::ApexInfo>& apex_info_list) const {
+  if (std::any_of(apex_info_list.begin(),
+                  apex_info_list.end(),
+                  [](const apex::ApexInfo& apex_info) { return !apex_info.getIsFactory(); })) {
+    return false;
+  }
+  LOG(INFO) << "Factory APEXes mounted.";
+
+  if (!CheckSystemPropertiesAreDefault()) {
+    return false;
+  }
+  LOG(INFO) << "System properties are set to default values.";
+
+  return true;
+}
+
 WARN_UNUSED bool OnDeviceRefresh::CheckBootClasspathArtifactsAreUpToDate(
     OdrMetrics& metrics,
     const InstructionSet isa,
     const apex::ApexInfo& art_apex_info,
     const std::optional<art_apex::CacheInfo>& cache_info,
     /*out*/ std::vector<std::string>* checked_artifacts) const {
-  if (art_apex_info.getIsFactory()) {
-    LOG(INFO) << "Factory ART APEX mounted.";
-
-    // ART is not updated, so we can use the artifacts on /system. Check if they exist.
+  if (BootClasspathArtifactsOnSystemUsable(art_apex_info)) {
+    // We can use the artifacts on /system. Check if they exist.
     std::string error_msg;
     if (BootClasspathArtifactsExist(/*on_system=*/true, /*minimal=*/false, isa, &error_msg)) {
       return true;
@@ -900,6 +996,14 @@
     return false;
   }
 
+  if (!CheckSystemPropertiesHaveNotChanged(cache_info.value())) {
+    // We don't have a trigger kind for system property changes. For now, we reuse
+    // `kApexVersionMismatch` as it implies the expected behavior: re-compile regardless of the last
+    // compilation attempt.
+    metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
+    return false;
+  }
+
   // Check boot class components.
   //
   // This checks the size and checksums of odrefresh compilable files on the DEX2OATBOOTCLASSPATH
@@ -961,12 +1065,8 @@
   std::set<std::string> jars_missing_artifacts_on_system;
   bool artifacts_on_system_up_to_date = false;
 
-  if (std::all_of(apex_info_list.begin(),
-                  apex_info_list.end(),
-                  [](const apex::ApexInfo& apex_info) { return apex_info.getIsFactory(); })) {
-    LOG(INFO) << "Factory APEXes mounted.";
-
-    // APEXes are not updated, so we can use the artifacts on /system. Check if they exist.
+  if (SystemServerArtifactsOnSystemUsable(apex_info_list)) {
+    // We can use the artifacts on /system. Check if they exist.
     std::string error_msg;
     if (SystemServerArtifactsExist(
             /*on_system=*/true, &error_msg, &jars_missing_artifacts_on_system)) {
@@ -1052,6 +1152,14 @@
     }
   }
 
+  if (!CheckSystemPropertiesHaveNotChanged(cache_info.value())) {
+    // We don't have a trigger kind for system property changes. For now, we reuse
+    // `kApexVersionMismatch` as it implies the expected behavior: re-compile regardless of the last
+    // compilation attempt.
+    metrics.SetTrigger(OdrMetrics::Trigger::kApexVersionMismatch);
+    return false;
+  }
+
   // Check system server components.
   //
   // This checks the size and checksums of odrefresh compilable files on the
diff --git a/odrefresh/odrefresh.h b/odrefresh/odrefresh.h
index 58da57a..9dd4009 100644
--- a/odrefresh/odrefresh.h
+++ b/odrefresh/odrefresh.h
@@ -140,6 +140,25 @@
       /*out*/ std::set<std::string>* jars_missing_artifacts,
       /*out*/ std::vector<std::string>* checked_artifacts = nullptr) const;
 
+  // Returns true if all of the system properties listed in `kSystemProperties` are set to the
+  // default values.
+  WARN_UNUSED bool CheckSystemPropertiesAreDefault() const;
+
+  // Returns true if none of the system properties listed in `kSystemProperties` has changed since
+  // the last compilation.
+  WARN_UNUSED bool CheckSystemPropertiesHaveNotChanged(
+      const com::android::art::CacheInfo& cache_info) const;
+
+  // Returns true if boot classpath artifacts on /system are usable if they exist. Note that this
+  // function does not check file existence.
+  WARN_UNUSED bool BootClasspathArtifactsOnSystemUsable(
+      const com::android::apex::ApexInfo& art_apex_info) const;
+
+  // Returns true if system_server artifacts on /system are usable if they exist. Note that this
+  // function does not check file existence.
+  WARN_UNUSED bool SystemServerArtifactsOnSystemUsable(
+      const std::vector<com::android::apex::ApexInfo>& apex_info_list) const;
+
   // Checks whether all boot classpath artifacts are up to date. Returns true if all are present,
   // false otherwise.
   // If `checked_artifacts` is present, adds checked artifacts to `checked_artifacts`.
diff --git a/odrefresh/odrefresh_main.cc b/odrefresh/odrefresh_main.cc
index c2298f0..01299e0 100644
--- a/odrefresh/odrefresh_main.cc
+++ b/odrefresh/odrefresh_main.cc
@@ -18,6 +18,7 @@
 
 #include <string>
 #include <string_view>
+#include <unordered_map>
 
 #include "android-base/properties.h"
 #include "android-base/stringprintf.h"
@@ -34,14 +35,17 @@
 
 namespace {
 
+using ::android::base::GetProperty;
 using ::art::odrefresh::CompilationOptions;
 using ::art::odrefresh::ExitCode;
+using ::art::odrefresh::kSystemProperties;
 using ::art::odrefresh::OdrCompilationLog;
 using ::art::odrefresh::OdrConfig;
 using ::art::odrefresh::OdrMetrics;
 using ::art::odrefresh::OnDeviceRefresh;
 using ::art::odrefresh::QuotePath;
 using ::art::odrefresh::ShouldDisableRefresh;
+using ::art::odrefresh::SystemPropertyConfig;
 using ::art::odrefresh::ZygoteKind;
 
 void UsageMsgV(const char* fmt, va_list ap) {
@@ -155,7 +159,7 @@
 
   if (zygote.empty()) {
     // Use ro.zygote by default, if not overridden by --zygote-arch flag.
-    zygote = android::base::GetProperty("ro.zygote", {});
+    zygote = GetProperty("ro.zygote", {});
   }
   ZygoteKind zygote_kind;
   if (!ParseZygoteKind(zygote.c_str(), &zygote_kind)) {
@@ -164,19 +168,24 @@
   config->SetZygoteKind(zygote_kind);
 
   if (config->GetSystemServerCompilerFilter().empty()) {
-    std::string filter =
-        android::base::GetProperty("dalvik.vm.systemservercompilerfilter", "speed");
+    std::string filter = GetProperty("dalvik.vm.systemservercompilerfilter", "speed");
     config->SetSystemServerCompilerFilter(filter);
   }
 
-  if (ShouldDisableRefresh(
-          android::base::GetProperty("ro.build.version.sdk", /*default_value=*/""))) {
+  if (ShouldDisableRefresh(GetProperty("ro.build.version.sdk", /*default_value=*/""))) {
     config->SetRefresh(false);
   }
 
   return n;
 }
 
+void GetSystemProperties(std::unordered_map<std::string, std::string>* system_properties) {
+  for (const SystemPropertyConfig& system_property_config : *kSystemProperties.get()) {
+    (*system_properties)[system_property_config.name] =
+        GetProperty(system_property_config.name, system_property_config.default_value);
+  }
+}
+
 NO_RETURN void UsageHelp(const char* argv0) {
   std::string name(android::base::Basename(argv0));
   UsageMsg("Usage: %s [OPTION...] ACTION", name.c_str());
@@ -229,6 +238,7 @@
   if (argc != 1) {
     ArgumentError("Expected 1 argument, but have %d.", argc);
   }
+  GetSystemProperties(config.MutableSystemProperties());
 
   OdrMetrics metrics(config.GetArtifactDirectory());
   OnDeviceRefresh odr(config);
@@ -268,6 +278,6 @@
   } else if (action == "--help") {
     UsageHelp(argv[0]);
   } else {
-    ArgumentError("Unknown argument: ", action);
+    ArgumentError("Unknown argument: %s", action.data());
   }
 }
diff --git a/odrefresh/schema/current.txt b/odrefresh/schema/current.txt
index 958f2a0..c45870b 100644
--- a/odrefresh/schema/current.txt
+++ b/odrefresh/schema/current.txt
@@ -8,12 +8,14 @@
     method public boolean getCompilationOsMode();
     method public com.android.art.Classpath getDex2oatBootClasspath();
     method public com.android.art.ModuleInfoList getModuleInfoList();
+    method public com.android.art.KeyValuePairList getSystemProperties();
     method public com.android.art.SystemServerComponents getSystemServerComponents();
     method public void setArtModuleInfo(com.android.art.ModuleInfo);
     method public void setBootClasspath(com.android.art.Classpath);
     method public void setCompilationOsMode(boolean);
     method public void setDex2oatBootClasspath(com.android.art.Classpath);
     method public void setModuleInfoList(com.android.art.ModuleInfoList);
+    method public void setSystemProperties(com.android.art.KeyValuePairList);
     method public void setSystemServerComponents(com.android.art.SystemServerComponents);
   }
 
@@ -33,6 +35,20 @@
     method public void setSize(java.math.BigInteger);
   }
 
+  public class KeyValuePair {
+    ctor public KeyValuePair();
+    method public String getK();
+    method public String getV();
+    method public void setK(String);
+    method public void setV(String);
+  }
+
+  public class KeyValuePairList {
+    ctor public KeyValuePairList();
+    method public com.android.art.KeyValuePair getItem();
+    method public void setItem(com.android.art.KeyValuePair);
+  }
+
   public class ModuleInfo {
     ctor public ModuleInfo();
     method public long getLastUpdateMillis();
diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S
index 61a6865..134c327 100644
--- a/runtime/arch/arm64/quick_entrypoints_arm64.S
+++ b/runtime/arch/arm64/quick_entrypoints_arm64.S
@@ -1083,6 +1083,11 @@
 
 ENTRY art_quick_aput_obj
     cbz x2, .Laput_obj_null
+#if defined(USE_READ_BARRIER) && !defined(USE_BAKER_READ_BARRIER)
+    READ_BARRIER_SLOW x3, w3, x0, MIRROR_OBJECT_CLASS_OFFSET
+    READ_BARRIER_SLOW x3, w3, x3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET
+    READ_BARRIER_SLOW x4, w4, x2, MIRROR_OBJECT_CLASS_OFFSET
+#else  // !defined(USE_READ_BARRIER) || defined(USE_BAKER_READ_BARRIER)
 #ifdef USE_READ_BARRIER
     cbnz wMR, .Laput_obj_gc_marking
 #endif  // USE_READ_BARRIER
@@ -1092,6 +1097,7 @@
     UNPOISON_HEAP_REF w3
     ldr w4, [x2, #MIRROR_OBJECT_CLASS_OFFSET]          // Heap reference = 32b; zero-extends to x4.
     UNPOISON_HEAP_REF w4
+#endif  // !defined(USE_READ_BARRIER) || defined(USE_BAKER_READ_BARRIER)
     cmp w3, w4  // value's type == array's component type - trivial assignability
     bne .Laput_obj_check_assignability
 .Laput_obj_store:
@@ -1139,20 +1145,18 @@
     RESTORE_TWO_REGS x2, xLR, 16
     RESTORE_TWO_REGS_DECREASE_FRAME x0, x1, 32
 
-#ifdef USE_READ_BARRIER
+#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
     .cfi_remember_state
-#endif  // USE_READ_BARRIER
+#endif  // defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
     SETUP_SAVE_ALL_CALLEE_SAVES_FRAME
     mov x1, x2                      // Pass value.
     mov x2, xSELF                   // Pass Thread::Current.
     bl artThrowArrayStoreException  // (Object*, Object*, Thread*).
     brk 0                           // Unreachable.
 
-#ifdef USE_READ_BARRIER
+#if defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
     CFI_RESTORE_STATE_AND_DEF_CFA sp, 0
 .Laput_obj_gc_marking:
-
-#ifdef USE_BAKER_READ_BARRIER
     BAKER_RB_CHECK_GRAY_BIT_AND_LOAD \
         w3, x0, MIRROR_OBJECT_CLASS_OFFSET, .Laput_obj_mark_array_class
 .Laput_obj_mark_array_class_continue:
@@ -1162,17 +1166,10 @@
     BAKER_RB_CHECK_GRAY_BIT_AND_LOAD \
         w4, x2, MIRROR_OBJECT_CLASS_OFFSET, .Laput_obj_mark_object_class
 .Laput_obj_mark_object_class_continue:
-#else  // USE_BAKER_READ_BARRIER
-    READ_BARRIER_SLOW x3, w3, x0, MIRROR_OBJECT_CLASS_OFFSET
-    READ_BARRIER_SLOW x3, w3, x3, MIRROR_CLASS_COMPONENT_TYPE_OFFSET
-    READ_BARRIER_SLOW x4, w4, x2, MIRROR_OBJECT_CLASS_OFFSET
-#endif  // USE_BAKER_READ_BARRIER
-
     cmp w3, w4  // value's type == array's component type - trivial assignability
     bne .Laput_obj_check_assignability
     b   .Laput_obj_store
 
-#ifdef USE_BAKER_READ_BARRIER
 .Laput_obj_mark_array_class:
     BAKER_RB_LOAD_AND_MARK w3, x0, MIRROR_OBJECT_CLASS_OFFSET, art_quick_read_barrier_mark_reg03
     b .Laput_obj_mark_array_class_continue
@@ -1185,8 +1182,7 @@
 .Laput_obj_mark_object_class:
     BAKER_RB_LOAD_AND_MARK w4, x2, MIRROR_OBJECT_CLASS_OFFSET, art_quick_read_barrier_mark_reg04
     b .Laput_obj_mark_object_class_continue
-#endif  // USE_BAKER_READ_BARRIER
-#endif  // USE_READ_BARRIER
+#endif  // defined(USE_READ_BARRIER) && defined(USE_BAKER_READ_BARRIER)
 END art_quick_aput_obj
 
 // Macro to facilitate adding new allocation entrypoints.
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 867f75c..40b7a7b 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -617,6 +617,15 @@
   }
 
   OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromEntryPoint(oat_entry_point);
+  // We could have existing Oat code for native methods but we may not use it if the runtime is java
+  // debuggable or when profiling boot class path. There is no easy way to check if the pc
+  // corresponds to QuickGenericJniStub. Since we have eliminated all the other cases, if the pc
+  // doesn't correspond to the AOT code then we must be running QuickGenericJniStub.
+  if (IsNative() && !method_header->Contains(pc)) {
+    DCHECK_NE(pc, 0u) << "PC 0 for " << PrettyMethod();
+    return nullptr;
+  }
+
   DCHECK(method_header->Contains(pc))
       << PrettyMethod()
       << " " << std::hex << pc << " " << oat_entry_point
diff --git a/runtime/art_method.h b/runtime/art_method.h
index d8bd380..c2de718 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -259,7 +259,9 @@
   }
 
   void SetMemorySharedMethod() REQUIRES_SHARED(Locks::mutator_lock_) {
-    if (!IsIntrinsic() && !IsAbstract()) {
+    // Disable until we make sure critical code is AOTed.
+    static constexpr bool kEnabledMemorySharedMethod = false;
+    if (kEnabledMemorySharedMethod && !IsIntrinsic() && !IsAbstract()) {
       AddAccessFlags(kAccMemorySharedMethod);
       SetHotCounter();
     }
diff --git a/runtime/base/locks.h b/runtime/base/locks.h
index 829adff..c15e5de 100644
--- a/runtime/base/locks.h
+++ b/runtime/base/locks.h
@@ -68,12 +68,12 @@
   // Can be held while GC related work is done, and thus must be above kMarkSweepMarkStackLock
   kThreadWaitLock,
   kCHALock,
-  kJitCodeCacheLock,
   kRosAllocGlobalLock,
   kRosAllocBracketLock,
   kRosAllocBulkFreeLock,
   kAllocSpaceLock,
   kTaggingLockLevel,
+  kJitCodeCacheLock,
   kTransactionLogLock,
   kCustomTlsLock,
   kJniFunctionTableLock,
diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc
index c78b604..80eb89f 100644
--- a/runtime/entrypoints/jni/jni_entrypoints.cc
+++ b/runtime/entrypoints/jni/jni_entrypoints.cc
@@ -87,11 +87,11 @@
     }
 
     // Replace the runtime method on the stack with the target method.
-    DCHECK(!self->GetManagedStack()->GetTopQuickFrameTag());
+    DCHECK(!self->GetManagedStack()->GetTopQuickFrameGenericJniTag());
     ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrameKnownNotTagged();
     DCHECK(*sp == Runtime::Current()->GetCalleeSaveMethod(CalleeSaveType::kSaveRefsAndArgs));
     *sp = target_method;
-    self->SetTopOfStackTagged(sp);  // Fake GenericJNI frame.
+    self->SetTopOfStackGenericJniTagged(sp);  // Fake GenericJNI frame.
 
     // Continue with the target method.
     method = target_method;
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index b15ce69..31471d6 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1034,18 +1034,7 @@
   // This will get the entry point either from the oat file, the JIT or the appropriate bridge
   // method if none of those can be found.
   result = instrumentation->GetCodeForInvoke(method);
-  jit::Jit* jit = Runtime::Current()->GetJit();
   DCHECK_NE(result, GetQuickInstrumentationEntryPoint()) << method->PrettyMethod();
-  DCHECK(jit == nullptr ||
-         // Native methods come through here in Interpreter entrypoints. We might not have
-         // disabled jit-gc but that is fine since we won't return jit-code for native methods.
-         method->IsNative() ||
-         !jit->GetCodeCache()->GetGarbageCollectCode());
-  DCHECK(!method->IsNative() ||
-         jit == nullptr ||
-         !jit->GetCodeCache()->ContainsPc(result))
-      << method->PrettyMethod() << " code will jump to possibly cleaned up jit code!";
-
   bool interpreter_entry = Runtime::Current()->GetClassLinker()->IsQuickToInterpreterBridge(result);
   bool is_static = method->IsStatic();
   uint32_t shorty_len;
@@ -2096,7 +2085,7 @@
   }
 
   // Fix up managed-stack things in Thread. After this we can walk the stack.
-  self->SetTopOfStackTagged(managed_sp);
+  self->SetTopOfStackGenericJniTagged(managed_sp);
 
   self->VerifyStack();
 
@@ -2190,7 +2179,7 @@
   // anything that requires a mutator lock before that would cause problems as GC may have the
   // exclusive mutator lock and may be moving objects, etc.
   ArtMethod** sp = self->GetManagedStack()->GetTopQuickFrame();
-  DCHECK(self->GetManagedStack()->GetTopQuickFrameTag());
+  DCHECK(self->GetManagedStack()->GetTopQuickFrameGenericJniTag());
   uint32_t* sp32 = reinterpret_cast<uint32_t*>(sp);
   ArtMethod* called = *sp;
   uint32_t cookie = *(sp32 - 1);
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 6cbf178..0de62fe 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -1080,7 +1080,35 @@
       REQUIRES_SHARED(Locks::heap_bitmap_lock_) {
     DCHECK_EQ(collector_->RegionSpace()->RegionIdxForRef(obj), obj_region_idx_);
     DCHECK(kHandleInterRegionRefs || collector_->immune_spaces_.ContainsObject(obj));
-    CheckReference(obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(offset));
+    mirror::Object* ref =
+            obj->GetFieldObject<mirror::Object, kVerifyNone, kWithoutReadBarrier>(offset);
+    // TODO(lokeshgidra): Remove the following condition once b/173676071 is fixed.
+    if (UNLIKELY(ref == nullptr && offset == mirror::Object::ClassOffset())) {
+      // It has been verified as a race condition (see b/173676071)! After a small
+      // wait when we reload the class pointer, it turns out to be a valid class
+      // object. So as a workaround, we can continue execution and log an error
+      // that this happened.
+      for (size_t i = 0; i < 1000; i++) {
+        // Wait for 1ms at a time. Don't wait for more than 1 second in total.
+        usleep(1000);
+        ref = obj->GetClass<kVerifyNone, kWithoutReadBarrier>();
+        if (ref != nullptr) {
+          LOG(ERROR) << "klass pointer for obj: "
+                     << obj << " (" << mirror::Object::PrettyTypeOf(obj)
+                     << ") found to be null first. Reloading after a small wait fetched klass: "
+                     << ref << " (" << mirror::Object::PrettyTypeOf(ref) << ")";
+          break;
+        }
+      }
+
+      if (UNLIKELY(ref == nullptr)) {
+        // It must be heap corruption. Remove memory protection and dump data.
+        collector_->region_space_->Unprotect();
+        LOG(FATAL_WITHOUT_ABORT) << "klass pointer for ref: " << obj << " found to be null.";
+        collector_->heap_->GetVerification()->LogHeapCorruption(obj, offset, ref, /* fatal */ true);
+      }
+    }
+    CheckReference(ref);
   }
 
   void operator()(ObjPtr<mirror::Class> klass, ObjPtr<mirror::Reference> ref) const
diff --git a/runtime/hidden_api_test.cc b/runtime/hidden_api_test.cc
index f5cd15e..e204c57 100644
--- a/runtime/hidden_api_test.cc
+++ b/runtime/hidden_api_test.cc
@@ -235,6 +235,7 @@
   runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kJustWarn);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Sdk()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Unsupported()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetS()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetR()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetQ()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetP()), false);
@@ -248,6 +249,7 @@
   SetChangeIdState(kHideMaxtargetsdkQHiddenApis, false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Sdk()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Unsupported()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetS()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetR()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetQ()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetP()), false);
@@ -261,6 +263,7 @@
   SetChangeIdState(kHideMaxtargetsdkQHiddenApis, false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Sdk()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Unsupported()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetS()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetR()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetQ()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetP()), false);
@@ -269,6 +272,8 @@
   SetChangeIdState(kHideMaxtargetsdkQHiddenApis, true);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Sdk()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Unsupported()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetS()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetR()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetQ()), true);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetP()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetO()), true);
@@ -281,6 +286,7 @@
   SetChangeIdState(kHideMaxtargetsdkQHiddenApis, false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Sdk()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Unsupported()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetS()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetR()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetQ()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetP()), true);
@@ -294,6 +300,7 @@
   SetChangeIdState(kHideMaxtargetsdkQHiddenApis, true);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Sdk()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Unsupported()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetS()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetR()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetQ()), true);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetP()), true);
@@ -307,6 +314,19 @@
   SetChangeIdState(kHideMaxtargetsdkQHiddenApis, true);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Sdk()), false);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Unsupported()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetS()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetR()), true);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetQ()), true);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetP()), true);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetO()), true);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Blocked()), true);
+
+  runtime_->SetHiddenApiEnforcementPolicy(hiddenapi::EnforcementPolicy::kEnabled);
+  runtime_->SetTargetSdkVersion(
+      static_cast<uint32_t>(hiddenapi::ApiList::MaxTargetS().GetMaxAllowedSdkVersion()) + 1);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Sdk()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::Unsupported()), false);
+  ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetS()), true);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetR()), true);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetQ()), true);
   ASSERT_EQ(ShouldDenyAccess(hiddenapi::ApiList::MaxTargetP()), true);
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 4b3b318..b4dc9f6 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -230,32 +230,9 @@
       method->GetDeclaringClass()->DescriptorEquals("Ljava/lang/reflect/Proxy;");
 }
 
-static void UpdateEntryPoints(ArtMethod* method, const void* quick_code)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  if (kIsDebugBuild) {
-    if (NeedsClinitCheckBeforeCall(method) &&
-        !method->GetDeclaringClass()->IsVisiblyInitialized()) {
-      CHECK(CanHandleInitializationCheck(quick_code));
-    }
-    jit::Jit* jit = Runtime::Current()->GetJit();
-    if (jit != nullptr && jit->GetCodeCache()->ContainsPc(quick_code)) {
-      // Ensure we always have the thumb entrypoint for JIT on arm32.
-      if (kRuntimeISA == InstructionSet::kArm) {
-        CHECK_EQ(reinterpret_cast<uintptr_t>(quick_code) & 1, 1u);
-      }
-    }
-    if (IsProxyInit(method)) {
-      CHECK_NE(quick_code, GetQuickInstrumentationEntryPoint());
-    }
-  }
-  // If the method is from a boot image, don't dirty it if the entrypoint
-  // doesn't change.
-  if (method->GetEntryPointFromQuickCompiledCode() != quick_code) {
-    method->SetEntryPointFromQuickCompiledCode(quick_code);
-  }
-}
-
-bool Instrumentation::CodeNeedsEntryExitStub(const void* code, ArtMethod* method)
+// Returns true if we need entry exit stub to call entry hooks. JITed code
+// directly call entry / exit hooks and don't need the stub.
+static bool CodeNeedsEntryExitStub(const void* code, ArtMethod* method)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   // Proxy.init should never have entry/exit stubs.
   if (IsProxyInit(method)) {
@@ -294,6 +271,36 @@
   return true;
 }
 
+static void UpdateEntryPoints(ArtMethod* method, const void* quick_code)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  if (kIsDebugBuild) {
+    if (NeedsClinitCheckBeforeCall(method) &&
+        !method->GetDeclaringClass()->IsVisiblyInitialized()) {
+      CHECK(CanHandleInitializationCheck(quick_code));
+    }
+    jit::Jit* jit = Runtime::Current()->GetJit();
+    if (jit != nullptr && jit->GetCodeCache()->ContainsPc(quick_code)) {
+      // Ensure we always have the thumb entrypoint for JIT on arm32.
+      if (kRuntimeISA == InstructionSet::kArm) {
+        CHECK_EQ(reinterpret_cast<uintptr_t>(quick_code) & 1, 1u);
+      }
+    }
+    if (IsProxyInit(method)) {
+      CHECK_NE(quick_code, GetQuickInstrumentationEntryPoint());
+    }
+    const Instrumentation* instr = Runtime::Current()->GetInstrumentation();
+    if (instr->EntryExitStubsInstalled()) {
+      DCHECK(quick_code == GetQuickInstrumentationEntryPoint() ||
+             !CodeNeedsEntryExitStub(quick_code, method));
+    }
+  }
+  // If the method is from a boot image, don't dirty it if the entrypoint
+  // doesn't change.
+  if (method->GetEntryPointFromQuickCompiledCode() != quick_code) {
+    method->SetEntryPointFromQuickCompiledCode(quick_code);
+  }
+}
+
 bool Instrumentation::InterpretOnly(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_) {
   if (method->IsNative()) {
     return false;
@@ -303,16 +310,11 @@
          Runtime::Current()->GetRuntimeCallbacks()->IsMethodBeingInspected(method);
 }
 
-static bool CanUseAotCode(ArtMethod* method, const void* quick_code)
+static bool CanUseAotCode(const void* quick_code)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   if (quick_code == nullptr) {
     return false;
   }
-  if (method->IsNative()) {
-    // AOT code for native methods can always be used.
-    return true;
-  }
-
   Runtime* runtime = Runtime::Current();
   // For simplicity, we never use AOT code for debuggable.
   if (runtime->IsJavaDebuggable()) {
@@ -348,7 +350,7 @@
   // In debuggable mode, we can only use AOT code for native methods.
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   const void* aot_code = method->GetOatMethodQuickCode(class_linker->GetImagePointerSize());
-  if (CanUseAotCode(method, aot_code)) {
+  if (CanUseAotCode(aot_code)) {
     return aot_code;
   }
 
@@ -407,7 +409,7 @@
   }
 
   // Use the provided AOT code if possible.
-  if (CanUseAotCode(method, aot_code)) {
+  if (CanUseAotCode(aot_code)) {
     UpdateEntryPoints(method, aot_code);
     return;
   }
@@ -1204,7 +1206,11 @@
     UpdateEntryPoints(method, GetQuickToInterpreterBridge());
   } else if (NeedsClinitCheckBeforeCall(method) &&
              !method->GetDeclaringClass()->IsVisiblyInitialized()) {
-    UpdateEntryPoints(method, GetQuickResolutionStub());
+    if (EntryExitStubsInstalled()) {
+      UpdateEntryPoints(method, GetQuickInstrumentationEntryPoint());
+    } else {
+      UpdateEntryPoints(method, GetQuickResolutionStub());
+    }
   } else {
     UpdateEntryPoints(method, GetMaybeInstrumentedCodeForInvoke(method));
   }
diff --git a/runtime/instrumentation.h b/runtime/instrumentation.h
index c63e73e..33ff8ff 100644
--- a/runtime/instrumentation.h
+++ b/runtime/instrumentation.h
@@ -580,11 +580,6 @@
   // False otherwise.
   bool RequiresInstrumentationInstallation(InstrumentationLevel new_level) const;
 
-  // Returns true if we need entry exit stub to call entry hooks. JITed code
-  // directly call entry / exit hooks and don't need the stub.
-  static bool CodeNeedsEntryExitStub(const void* code, ArtMethod* method)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
   // Update the current instrumentation_level_.
   void UpdateInstrumentationLevel(InstrumentationLevel level);
 
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index ae3575a..facf66a 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -208,7 +208,6 @@
   // Jit GC for now (b/147208992).
   if (code_cache->GetGarbageCollectCode()) {
     code_cache->SetGarbageCollectCode(!jit_compiler_->GenerateDebugInfo() &&
-        !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled() &&
         !jit->JitAtFirstUse());
   }
 
@@ -1662,7 +1661,6 @@
   // Jit GC for now (b/147208992).
   code_cache_->SetGarbageCollectCode(
       !jit_compiler_->GenerateDebugInfo() &&
-      !runtime->GetInstrumentation()->AreExitStubsInstalled() &&
       !JitAtFirstUse());
 
   if (is_system_server && runtime->HasImageWithProfile()) {
diff --git a/runtime/managed_stack.h b/runtime/managed_stack.h
index 04a27fe..2184f47 100644
--- a/runtime/managed_stack.h
+++ b/runtime/managed_stack.h
@@ -75,8 +75,12 @@
     return tagged_top_quick_frame_.GetSp();
   }
 
-  bool GetTopQuickFrameTag() const {
-    return tagged_top_quick_frame_.GetTag();
+  bool GetTopQuickFrameGenericJniTag() const {
+    return tagged_top_quick_frame_.GetGenericJniTag();
+  }
+
+  bool GetTopQuickFrameJitJniTag() const {
+    return tagged_top_quick_frame_.GetJitJniTag();
   }
 
   bool HasTopQuickFrame() const {
@@ -89,10 +93,10 @@
     tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateNotTagged(top);
   }
 
-  void SetTopQuickFrameTagged(ArtMethod** top) {
+  void SetTopQuickFrameGenericJniTagged(ArtMethod** top) {
     DCHECK(top_shadow_frame_ == nullptr);
     DCHECK_ALIGNED(top, 4u);
-    tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateTagged(top);
+    tagged_top_quick_frame_ = TaggedTopQuickFrame::CreateGenericJniTagged(top);
   }
 
   static constexpr size_t TaggedTopQuickFrameOffset() {
@@ -129,26 +133,30 @@
       return TaggedTopQuickFrame(reinterpret_cast<uintptr_t>(sp));
     }
 
-    static TaggedTopQuickFrame CreateTagged(ArtMethod** sp) {
+    static TaggedTopQuickFrame CreateGenericJniTagged(ArtMethod** sp) {
       DCHECK_ALIGNED(sp, 4u);
       return TaggedTopQuickFrame(reinterpret_cast<uintptr_t>(sp) | 1u);
     }
 
     // Get SP known to be not tagged and non-null.
     ArtMethod** GetSpKnownNotTagged() const {
-      DCHECK(!GetTag());
+      DCHECK(!GetGenericJniTag() && !GetJitJniTag());
       DCHECK_NE(tagged_sp_, 0u);
       return reinterpret_cast<ArtMethod**>(tagged_sp_);
     }
 
     ArtMethod** GetSp() const {
-      return reinterpret_cast<ArtMethod**>(tagged_sp_ & ~static_cast<uintptr_t>(1u));
+      return reinterpret_cast<ArtMethod**>(tagged_sp_ & ~static_cast<uintptr_t>(3u));
     }
 
-    bool GetTag() const {
+    bool GetGenericJniTag() const {
       return (tagged_sp_ & 1u) != 0u;
     }
 
+    bool GetJitJniTag() const {
+      return (tagged_sp_ & 2u) != 0u;
+    }
+
     uintptr_t GetTaggedSp() const {
       return tagged_sp_;
     }
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 9a6c8a5..d2eb3bd 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -3147,15 +3147,19 @@
     auto pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
     for (auto& m : klass->GetMethods(pointer_size)) {
       const void* code = m.GetEntryPointFromQuickCompiledCode();
+      // For java debuggable runtimes we also deoptimize native methods. For other cases (boot
+      // image profiling) we don't need to deoptimize native methods. If this changes also
+      // update Instrumentation::CanUseAotCode.
+      bool deoptimize_native_methods = Runtime::Current()->IsJavaDebuggable();
       if (Runtime::Current()->GetHeap()->IsInBootImageOatFile(code) &&
-          !m.IsNative() &&
+          (!m.IsNative() || deoptimize_native_methods) &&
           !m.IsProxyMethod()) {
         instrumentation_->InitializeMethodsCode(&m, /*aot_code=*/ nullptr);
       }
 
       if (Runtime::Current()->GetJit() != nullptr &&
           Runtime::Current()->GetJit()->GetCodeCache()->IsInZygoteExecSpace(code) &&
-          !m.IsNative()) {
+          (!m.IsNative() || deoptimize_native_methods)) {
         DCHECK(!m.IsProxyMethod());
         instrumentation_->InitializeMethodsCode(&m, /*aot_code=*/ nullptr);
       }
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 50a96d0..33d3668 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -800,10 +800,20 @@
         // between GenericJNI frame and JIT-compiled JNI stub; the entrypoint may have
         // changed since the frame was entered. The top quick frame tag indicates
         // GenericJNI here, otherwise it's either AOT-compiled or JNI-compiled JNI stub.
-        if (UNLIKELY(current_fragment->GetTopQuickFrameTag())) {
+        if (UNLIKELY(current_fragment->GetTopQuickFrameGenericJniTag())) {
           // The generic JNI does not have any method header.
           cur_oat_quick_method_header_ = nullptr;
+        } else if (UNLIKELY(current_fragment->GetTopQuickFrameJitJniTag())) {
+          // Should be JITed code.
+          Runtime* runtime = Runtime::Current();
+          const void* code = runtime->GetJit()->GetCodeCache()->GetJniStubCode(method);
+          CHECK(code != nullptr) << method->PrettyMethod();
+          cur_oat_quick_method_header_ = OatQuickMethodHeader::FromCodePointer(code);
         } else {
+          // We are sure we are not running GenericJni here. Though the entry point could still be
+          // GenericJnistub. The entry point is usually JITed, AOT or instrumentation stub when
+          // instrumentation is enabled. It could be lso a resolution stub if the class isn't
+          // visibly initialized yet.
           const void* existing_entry_point = method->GetEntryPointFromQuickCompiledCode();
           CHECK(existing_entry_point != nullptr);
           Runtime* runtime = Runtime::Current();
@@ -819,7 +829,11 @@
             if (code != nullptr) {
               cur_oat_quick_method_header_ = OatQuickMethodHeader::FromEntryPoint(code);
             } else {
-              // This must be a JITted JNI stub frame.
+              // This must be a JITted JNI stub frame. For non-debuggable runtimes we only generate
+              // JIT stubs if there are no AOT stubs for native methods. Since we checked for AOT
+              // code earlier, we must be running JITed code. For debuggable runtimes we might have
+              // JIT code even when AOT code is present but we tag SP in JITed JNI stubs
+              // in debuggable runtimes. This case is handled earlier.
               CHECK(runtime->GetJit() != nullptr);
               code = runtime->GetJit()->GetCodeCache()->GetJniStubCode(method);
               CHECK(code != nullptr) << method->PrettyMethod();
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 97cfb7a..e629b4e 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1981,6 +1981,9 @@
     if (thread->IsStillStarting()) {
       os << " (still starting up)";
     }
+    if (thread->tls32_.disable_thread_flip_count != 0) {
+      os << " DisableFlipCount = " << thread->tls32_.disable_thread_flip_count;
+    }
     os << "\n";
   } else {
     os << '"' << ::art::GetThreadName(tid) << '"'
diff --git a/runtime/thread.h b/runtime/thread.h
index b32e3c2..7ac4007 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -573,8 +573,8 @@
     tlsPtr_.managed_stack.SetTopQuickFrame(top_method);
   }
 
-  void SetTopOfStackTagged(ArtMethod** top_method) {
-    tlsPtr_.managed_stack.SetTopQuickFrameTagged(top_method);
+  void SetTopOfStackGenericJniTagged(ArtMethod** top_method) {
+    tlsPtr_.managed_stack.SetTopQuickFrameGenericJniTagged(top_method);
   }
 
   void SetTopOfShadowStack(ShadowFrame* top) {
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 6482e72..bfde711 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -1291,6 +1291,10 @@
   DCHECK_EQ(self, Thread::Current());
   CHECK_NE(self->GetState(), ThreadState::kRunnable);
   Locks::mutator_lock_->AssertNotHeld(self);
+  if (self->tls32_.disable_thread_flip_count != 0) {
+    LOG(FATAL) << "Incomplete PrimitiveArrayCritical section at exit: " << *self << "count = "
+               << self->tls32_.disable_thread_flip_count;
+  }
 
   VLOG(threads) << "ThreadList::Unregister() " << *self;
 
diff --git a/test/2042-reference-processing/Android.bp b/test/2042-reference-processing/Android.bp
new file mode 100644
index 0000000..a73b8d0
--- /dev/null
+++ b/test/2042-reference-processing/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2042-reference-processing`.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "art_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+    name: "art-run-test-2042-reference-processing",
+    defaults: ["art-run-test-defaults"],
+    test_config_template: ":art-run-test-target-template",
+    srcs: ["src/**/*.java"],
+    data: [
+        ":art-run-test-2042-reference-processing-expected-stdout",
+        ":art-run-test-2042-reference-processing-expected-stderr",
+    ],
+}
+
+// Test's expected standard output.
+genrule {
+    name: "art-run-test-2042-reference-processing-expected-stdout",
+    out: ["art-run-test-2042-reference-processing-expected-stdout.txt"],
+    srcs: ["expected-stdout.txt"],
+    cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+    name: "art-run-test-2042-reference-processing-expected-stderr",
+    out: ["art-run-test-2042-reference-processing-expected-stderr.txt"],
+    srcs: ["expected-stderr.txt"],
+    cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/2042-reference-processing/expected-stderr.txt b/test/2042-reference-processing/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/2042-reference-processing/expected-stderr.txt
diff --git a/test/2042-reference-processing/expected-stdout.txt b/test/2042-reference-processing/expected-stdout.txt
new file mode 100644
index 0000000..8b3e832
--- /dev/null
+++ b/test/2042-reference-processing/expected-stdout.txt
@@ -0,0 +1,2 @@
+Starting
+Finished
diff --git a/test/2042-reference-processing/info.txt b/test/2042-reference-processing/info.txt
new file mode 100644
index 0000000..e2d7a99
--- /dev/null
+++ b/test/2042-reference-processing/info.txt
@@ -0,0 +1,6 @@
+A test for reference processing correctness.
+
+The emphasis here is on fundamental properties. In particular, references to
+unreachable referents should be enqueued, and this should ensure that uncleared
+References don't point to objects for which References were enqueued. We also
+check various other ordering properties for java.lang.ref.References.
diff --git a/test/2042-reference-processing/src/Main.java b/test/2042-reference-processing/src/Main.java
new file mode 100644
index 0000000..ed67052
--- /dev/null
+++ b/test/2042-reference-processing/src/Main.java
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2022 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.ref.PhantomReference;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.math.BigInteger;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.HashMap;
+import java.util.TreeMap;
+
+/**
+ * Test that objects get finalized and their references cleared in the right order.
+ *
+ * We maintain a list of nominally MAX_LIVE_OBJS numbered finalizable objects.
+ * We then alternately drop the last 50, and add 50 more. When we see an object finalized
+ * or its reference cleared, we make sure that the preceding objects in its group of 50
+ * have also had their references cleared. We also perform a number of other more
+ * straightforward checks, such as ensuring that all references are eventually cleared,
+ * and all objects are finalized.
+ */
+public class Main {
+    // TODO(b/216481630) Enable CHECK_PHANTOM_REFS. This currently occasionally reports a few
+    // PhantomReferences as not enqueued. If this report is correct, this needs to be tracked
+    // down and fixed.
+    static final boolean CHECK_PHANTOM_REFS = false;
+
+    static final int MAX_LIVE_OBJS = 150;
+    static final int DROP_OBJS = 50;  // Number of linked objects dropped in each batch.
+    static final int MIN_LIVE_OBJS = MAX_LIVE_OBJS - DROP_OBJS;
+    static final int TOTAL_OBJS = 200_000;  // Allocate this many finalizable objects in total.
+    static final boolean REPORT_DROPS = false;
+    static volatile boolean pleaseStop;
+
+    AtomicInteger totalFinalized = new AtomicInteger(0);
+    Object phantomRefsLock = new Object();
+    int maxDropped = 0;
+    int liveObjects = 0;
+
+    // Number of next finalizable object to be allocated.
+    int nextAllocated = 0;
+
+    // List of finalizable objects in descending order. We add to the front and drop
+    // from the rear.
+    FinalizableObject listHead;
+
+    // A possibly incomplete list of FinalizableObject indices that were finalized, but
+    // have yet to be checked for consistency with reference processing.
+    ArrayBlockingQueue<Integer> finalized = new ArrayBlockingQueue<>(20_000);
+
+    // Maps from object number to Reference; Cleared references are deleted when queues are
+    // processed.
+    TreeMap<Integer, MyWeakReference> weakRefs = new TreeMap<>();
+    HashMap<Integer, MyPhantomReference> phantomRefs = new HashMap<>();
+
+    class FinalizableObject {
+        int n;
+        FinalizableObject next;
+        FinalizableObject(int num, FinalizableObject nextObj) {
+            n = num;
+            next = nextObj;
+        }
+        protected void finalize() {
+            if (!inPhantomRefs(n)) {
+                System.out.println("PhantomRef enqueued before finalizer ran");
+            }
+            totalFinalized.incrementAndGet();
+            if (!finalized.offer(n) && REPORT_DROPS) {
+                System.out.println("Dropped finalization of " + n);
+            }
+        }
+    }
+    ReferenceQueue<FinalizableObject> refQueue = new ReferenceQueue<>();
+    class MyWeakReference extends WeakReference<FinalizableObject> {
+        int n;
+        MyWeakReference(FinalizableObject obj) {
+            super(obj, refQueue);
+            n = obj.n;
+        }
+    };
+    class MyPhantomReference extends PhantomReference<FinalizableObject> {
+        int n;
+        MyPhantomReference(FinalizableObject obj) {
+            super(obj, refQueue);
+            n = obj.n;
+        }
+    }
+    boolean inPhantomRefs(int n) {
+        synchronized(phantomRefsLock) {
+            MyPhantomReference ref = phantomRefs.get(n);
+            if (ref == null) {
+                return false;
+            }
+            if (ref.n != n) {
+                System.out.println("phantomRef retrieval failed");
+            }
+            return true;
+        }
+    }
+
+    void CheckOKToClearWeak(int num) {
+        if (num > maxDropped) {
+            System.out.println("WeakRef to live object " + num + " was cleared/enqueued.");
+        }
+        int batchEnd = (num / DROP_OBJS + 1) * DROP_OBJS;
+        for (MyWeakReference wr : weakRefs.subMap(num + 1, batchEnd).values()) {
+            if (wr.n <= num || wr.n / DROP_OBJS != num / DROP_OBJS) {
+                throw new AssertionError("MyWeakReference logic error!");
+            }
+            // wr referent was dropped in same batch and precedes it in list.
+            if (wr.get() != null) {
+                // This violates the WeakReference spec, and can result in strong references
+                // to objects that have been cleaned.
+                System.out.println("WeakReference to " + wr.n
+                    + " was erroneously cleared after " + num);
+            }
+        }
+    }
+
+    void CheckOKToClearPhantom(int num) {
+        if (num > maxDropped) {
+            System.out.println("PhantomRef to live object " + num + " was enqueued.");
+        }
+        MyWeakReference wr = weakRefs.get(num);
+        if (wr != null && wr.get() != null) {
+            System.out.println("PhantomRef cleared before WeakRef for " + num);
+        }
+    }
+
+    void emptyAndCheckQueues() {
+        // Check recently finalized objects for consistency with cleared references.
+        while (true) {
+            Integer num = finalized.poll();
+            if (num == null) {
+                break;
+            }
+            MyWeakReference wr = weakRefs.get(num);
+            if (wr != null) {
+                if (wr.n != num) {
+                    System.out.println("Finalization logic error!");
+                }
+                if (wr.get() != null) {
+                    System.out.println("Finalizing object with uncleared reference");
+                }
+            }
+            CheckOKToClearWeak(num);
+        }
+        // Check recently enqueued references for consistency.
+        while (true) {
+            Reference<FinalizableObject> ref = (Reference<FinalizableObject>) refQueue.poll();
+            if (ref == null) {
+                break;
+            }
+            if (ref instanceof MyWeakReference) {
+                MyWeakReference wr = (MyWeakReference) ref;
+                if (wr.get() != null) {
+                    System.out.println("WeakRef " + wr.n + " enqueued but not cleared");
+                }
+                CheckOKToClearWeak(wr.n);
+                if (weakRefs.remove(Integer.valueOf(wr.n)) != ref) {
+                    System.out.println("Missing WeakReference: " + wr.n);
+                }
+            } else if (ref instanceof MyPhantomReference) {
+                MyPhantomReference pr = (MyPhantomReference) ref;
+                CheckOKToClearPhantom(pr.n);
+                if (phantomRefs.remove(Integer.valueOf(pr.n)) != ref) {
+                    System.out.println("Missing PhantomReference: " + pr.n);
+                }
+            } else {
+                System.out.println("Found unrecognized reference in queue");
+            }
+        }
+    }
+
+
+    /**
+     * Add n objects to the head of the list. These will be assigned the next n consecutive
+     * numbers after the current head of the list.
+     */
+    void addObjects(int n) {
+        for (int i = 0; i < n; ++i) {
+            int me = nextAllocated++;
+            listHead = new FinalizableObject(me, listHead);
+            weakRefs.put(me, new MyWeakReference(listHead));
+            synchronized(phantomRefsLock) {
+                phantomRefs.put(me, new MyPhantomReference(listHead));
+            }
+        }
+        liveObjects += n;
+    }
+
+    /**
+     * Drop n finalizable objects from the tail of the list. These are the lowest-numbered objects
+     * in the list.
+     */
+    void dropObjects(int n) {
+        FinalizableObject list = listHead;
+        FinalizableObject last = null;
+        if (n > liveObjects) {
+            System.out.println("Removing too many elements");
+        }
+        if (liveObjects == n) {
+            maxDropped = list.n;
+            listHead = null;
+        } else {
+            final int skip = liveObjects - n;
+            for (int i = 0; i < skip; ++i) {
+                last = list;
+                list = list.next;
+            }
+            int expected = nextAllocated - skip - 1;
+            if (list.n != expected) {
+                System.out.println("dropObjects found " + list.n + " but expected " + expected);
+            }
+            maxDropped = expected;
+            last.next = null;
+        }
+        liveObjects -= n;
+    }
+
+    void testLoop() {
+        System.out.println("Starting");
+        addObjects(MIN_LIVE_OBJS);
+        final int ITERS = (TOTAL_OBJS - MIN_LIVE_OBJS) / DROP_OBJS;
+        for (int i = 0; i < ITERS; ++i) {
+            addObjects(DROP_OBJS);
+            if (liveObjects != MAX_LIVE_OBJS) {
+                System.out.println("Unexpected live object count");
+            }
+            dropObjects(DROP_OBJS);
+            emptyAndCheckQueues();
+        }
+        dropObjects(MIN_LIVE_OBJS);
+        if (liveObjects != 0 || listHead != null) {
+            System.out.println("Unexpected live objecs at end");
+        }
+        if (maxDropped != TOTAL_OBJS - 1) {
+            System.out.println("Unexpected dropped object count: " + maxDropped);
+        }
+        for (int i = 0; i < 2; ++i) {
+            Runtime.getRuntime().gc();
+            System.runFinalization();
+            emptyAndCheckQueues();
+        }
+        if (!weakRefs.isEmpty()) {
+            System.out.println("Weak Reference map nonempty size = " + weakRefs.size());
+        }
+        if (CHECK_PHANTOM_REFS && !phantomRefs.isEmpty()) {
+            try {
+                Thread.sleep(500);
+            } catch (InterruptedException e) {
+                System.out.println("Unexpected interrupt");
+            }
+            if (!phantomRefs.isEmpty()) {
+                System.out.println("Phantom Reference map nonempty size = " + phantomRefs.size());
+                System.out.print("First elements:");
+                int i = 0;
+                for (MyPhantomReference pr : phantomRefs.values()) {
+                    System.out.print(" " + pr.n);
+                    if (++i > 10) {
+                        break;
+                    }
+                }
+                System.out.println("");
+            }
+        }
+        if (totalFinalized.get() != TOTAL_OBJS) {
+            System.out.println("Finalized only " + totalFinalized + " objects");
+        }
+    }
+
+    static Runnable causeGCs = new Runnable() {
+        public void run() {
+            // Allocate a lot.
+            BigInteger counter = BigInteger.ZERO;
+            while (!pleaseStop) {
+                counter = counter.add(BigInteger.TEN);
+            }
+            // Look at counter to reduce chance of optimizing out the allocation.
+            if (counter.longValue() % 10 != 0) {
+                 System.out.println("Bad causeGCs counter value: " + counter);
+            }
+        }
+    };
+
+    public static void main(String[] args) throws Exception {
+        Main theTest = new Main();
+        Thread gcThread = new Thread(causeGCs);
+        gcThread.setDaemon(true);  // Terminate if main thread dies.
+        gcThread.start();
+        theTest.testLoop();
+        pleaseStop = true;
+        gcThread.join();
+        System.out.println("Finished");
+    }
+}
diff --git a/test/2043-reference-pauses/Android.bp b/test/2043-reference-pauses/Android.bp
new file mode 100644
index 0000000..a84aea2
--- /dev/null
+++ b/test/2043-reference-pauses/Android.bp
@@ -0,0 +1,40 @@
+// Generated by `regen-test-files`. Do not edit manually.
+
+// Build rules for ART run-test `2043-reference-pauses`.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "art_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["art_license"],
+}
+
+// Test's Dex code.
+java_test {
+    name: "art-run-test-2043-reference-pauses",
+    defaults: ["art-run-test-defaults"],
+    test_config_template: ":art-run-test-target-template",
+    srcs: ["src/**/*.java"],
+    data: [
+        ":art-run-test-2043-reference-pauses-expected-stdout",
+        ":art-run-test-2043-reference-pauses-expected-stderr",
+    ],
+}
+
+// Test's expected standard output.
+genrule {
+    name: "art-run-test-2043-reference-pauses-expected-stdout",
+    out: ["art-run-test-2043-reference-pauses-expected-stdout.txt"],
+    srcs: ["expected-stdout.txt"],
+    cmd: "cp -f $(in) $(out)",
+}
+
+// Test's expected standard error.
+genrule {
+    name: "art-run-test-2043-reference-pauses-expected-stderr",
+    out: ["art-run-test-2043-reference-pauses-expected-stderr.txt"],
+    srcs: ["expected-stderr.txt"],
+    cmd: "cp -f $(in) $(out)",
+}
diff --git a/test/2043-reference-pauses/expected-stderr.txt b/test/2043-reference-pauses/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/2043-reference-pauses/expected-stderr.txt
diff --git a/test/2043-reference-pauses/expected-stdout.txt b/test/2043-reference-pauses/expected-stdout.txt
new file mode 100644
index 0000000..8b3e832
--- /dev/null
+++ b/test/2043-reference-pauses/expected-stdout.txt
@@ -0,0 +1,2 @@
+Starting
+Finished
diff --git a/test/2043-reference-pauses/info.txt b/test/2043-reference-pauses/info.txt
new file mode 100644
index 0000000..f76fa32
--- /dev/null
+++ b/test/2043-reference-pauses/info.txt
@@ -0,0 +1,5 @@
+Tests WeakReference processing and retention of objects needed by finalizers.
+
+Can be used as Reference.get() blocking benchmark by setting PRINT_TIMES to
+true. This will print maximum observed latencies for Reference.get() when
+significant memory is only reachable from SoftReferences and Finalizers.
diff --git a/test/2043-reference-pauses/src/Main.java b/test/2043-reference-pauses/src/Main.java
new file mode 100644
index 0000000..dc64d9a
--- /dev/null
+++ b/test/2043-reference-pauses/src/Main.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2022 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.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.lang.ref.SoftReference;
+import java.math.BigInteger;
+import java.util.ArrayList;
+
+/**
+ * Basic test of WeakReferences with large amounts of memory that's only reachable through
+ * finalizers. Also makes sure that finalizer-reachable data is not collected.
+ * Can easily be modified to time Reference.get() blocking.
+ */
+public class Main {
+    static final boolean PRINT_TIMES = false;  // true will cause benchmark failure.
+    // Data structures repeatedly allocated in background to trigger GC.
+    // Size of finalizer reachable trees.
+    static final int TREE_HEIGHT = 15;  // Trees contain 2^TREE_HEIGHT -1 allocated objects.
+    // Number of finalizable tree-owning objects that exist at one point.
+    static final int N_RESURRECTING_OBJECTS = 10;
+    // Number of short-lived, not finalizer-reachable, objects allocated between trees.
+    static final int N_PLAIN_OBJECTS = 20_000;
+    // Number of SoftReferences to CBTs we allocate.
+    static final int N_SOFTREFS = 10;
+
+    static final boolean BACKGROUND_GC_THREAD = true;
+    static final int NBATCHES = 10;
+    static final int NREFS = PRINT_TIMES ? 1_000_000 : 300_000;  // Multiple of NBATCHES.
+    static final int REFS_PER_BATCH = NREFS / NBATCHES;
+
+    static volatile boolean pleaseStop = false;
+
+    // Large array of WeakReferences filled and accessed by tests below.
+    ArrayList<WeakReference<Integer>> weakRefs = new ArrayList<>(NREFS);
+
+    /**
+     * Complete binary tree data structure. make(n) takes O(2^n) space.
+     */
+    static class CBT {
+        CBT left;
+        CBT right;
+        CBT(CBT l, CBT r) {
+            left = l;
+            right = r;
+        }
+        static CBT make(int n) {
+            if (n == 0) {
+                return null;
+            }
+            return new CBT(make(n - 1), make(n - 1));
+        }
+        /**
+         * Check that path described by bit-vector path has the correct length.
+         */
+        void check(int n, int path) {
+            CBT current = this;
+            for (int i = 0; i < n; i++, path = path >>> 1) {
+                // Unexpectedly short paths result in NPE.
+                if ((path & 1) == 0) {
+                    current = current.left;
+                } else {
+                    current = current.right;
+                }
+            }
+            if (current != null) {
+                System.out.println("Complete binary tree path too long");
+            }
+        }
+    }
+
+
+    /**
+     * A finalizable object that refers to O(2^TREE_HEIGHT) otherwise unreachable memory.
+     * When finalized, it creates a new identical object, making sure that one always stays
+     * around.
+     */
+    static class ResurrectingObject {
+        CBT stuff;
+        ResurrectingObject() {
+            stuff = CBT.make(TREE_HEIGHT);
+        }
+        static ResurrectingObject a[] = new ResurrectingObject[2];
+        static int i = 0;
+        static synchronized void allocOne() {
+            a[(++i) % 2] = new ResurrectingObject();
+            // Check the previous one to make it hard to optimize anything out.
+            if (i > 1) {
+                a[(i + 1) % 2].stuff.check(TREE_HEIGHT, i /* weirdly interpreted as path */);
+            }
+        }
+        protected void finalize() {
+            stuff.check(TREE_HEIGHT, 42 /* Some path descriptor */);
+            // Allocate a new one to replace this one.
+            allocOne();
+        }
+    }
+
+    void fillWeakRefs() {
+        for (int i = 0; i < NREFS; ++i) {
+             weakRefs.add(null);
+        }
+    }
+
+    /*
+     * Return maximum observed time in nanos to dereference a WeakReference to an unreachable
+     * object. weakRefs is presumed to be pre-filled to have the correct size.
+     */
+    long timeUnreachableInner() {
+        long maxNanos = 0;
+        // Fill weakRefs with WeakReferences to unreachable integers, a batch at a time.
+        // Then time and test .get() calls on carefully sampled array entries, some of which
+        // will have been cleared.
+        for (int i = 0; i < NBATCHES; ++i) {
+            for (int j = 0; j < REFS_PER_BATCH; ++j) {
+                weakRefs.set(i * REFS_PER_BATCH + j,
+                        new WeakReference(new Integer(i * REFS_PER_BATCH + j)));
+            }
+            try {
+                Thread.sleep(50);
+            } catch (InterruptedException e) {
+                System.out.println("Unexpected exception");
+            }
+            // Iterate over the filled-in section of weakRefs, but look only at a subset of the
+            // elements, making sure the subsets for different top-level iterations are disjoint.
+            // Otherwise the get() calls here will extend the lifetimes of the referents, and we
+            // may never see any cleared WeakReferences.
+            for (int j = (i + 1) * REFS_PER_BATCH - i - 1; j >= 0; j -= NBATCHES) {
+                WeakReference<Integer> wr = weakRefs.get(j);
+                if (wr != null) {
+                    long startNanos = System.nanoTime();
+                    Integer referent = wr.get();
+                    long totalNanos = System.nanoTime() - startNanos;
+                    if (referent == null) {
+                        // Optimization to reduce max space use and scanning time.
+                        weakRefs.set(j, null);
+                    }
+                    maxNanos = Math.max(maxNanos, totalNanos);
+                    if (referent != null && referent.intValue() != j) {
+                        System.out.println("Unexpected referent; expected " + j + " got "
+                                + referent.intValue());
+                    }
+                }
+            }
+        }
+        return maxNanos;
+    }
+
+    /*
+     * Wrapper for the above that also checks that references were reclaimed.
+     * We do this separately to make sure any stack references from the core of the
+     * test are gone. Empirically, we otherwise sometimes see the zeroth WeakReference
+     * not reclaimed.
+     */
+    long timeUnreachable() {
+        long maxNanos = timeUnreachableInner();
+        Runtime.getRuntime().gc();
+        System.runFinalization();  // Presumed to wait for reference clearing.
+        for (int i = 0; i < NREFS; ++i) {
+            if (weakRefs.get(i) != null && weakRefs.get(i).get() != null) {
+                System.out.println("WeakReference to " + i + " wasn't cleared");
+            }
+        }
+        return maxNanos;
+    }
+
+    /**
+     * Return maximum observed time in nanos to dereference a WeakReference to a reachable
+     * object. Overwrites weakRefs, which is presumed to have NREFS entries already.
+    */
+    long timeReachable() {
+        long maxNanos = 0;
+        // Similar to the above, but we use WeakReferences to otherwise reachable objects,
+        // which should thus not get cleared.
+        Integer[] strongRefs = new Integer[NREFS];
+        for (int i = 0; i < NBATCHES; ++i) {
+            for (int j = i * REFS_PER_BATCH; j < (i + 1) * NREFS / NBATCHES; ++j) {
+                Integer newObj = new Integer(j);
+                strongRefs[j] = newObj;
+                weakRefs.set(j, new WeakReference(newObj));
+            }
+            for (int j = (i + 1) * REFS_PER_BATCH - 1; j >= 0; --j) {
+                WeakReference<Integer> wr = weakRefs.get(j);
+                long startNanos = System.nanoTime();
+                Integer referent = wr.get();
+                long totalNanos = System.nanoTime() - startNanos;
+                maxNanos = Math.max(maxNanos, totalNanos);
+                if (referent == null) {
+                    System.out.println("Unexpectedly cleared referent at " + j);
+                } else if (referent.intValue() != j) {
+                    System.out.println("Unexpected reachable referent; expected " + j + " got "
+                            + referent.intValue());
+                }
+            }
+        }
+        Reference.reachabilityFence(strongRefs);
+        return maxNanos;
+    }
+
+    void runTest() {
+        System.out.println("Starting");
+        fillWeakRefs();
+        long unreachableNanos = timeUnreachable();
+        if (PRINT_TIMES) {
+            System.out.println("Finished timeUnrechable()");
+        }
+        long reachableNanos = timeReachable();
+        String unreachableMillis =
+                String. format("%,.3f", ((double) unreachableNanos) / 1_000_000);
+        String reachableMillis =
+                String. format("%,.3f", ((double) reachableNanos) / 1_000_000);
+        if (PRINT_TIMES) {
+            System.out.println(
+                    "Max time for WeakReference.get (unreachable): " + unreachableMillis);
+            System.out.println(
+                    "Max time for WeakReference.get (reachable): " + reachableMillis);
+        }
+        // Only report extremely egregious pauses to avoid spurious failures.
+        if (unreachableNanos > 10_000_000_000L) {
+            System.out.println("WeakReference.get (unreachable) time unreasonably long");
+        }
+        if (reachableNanos > 10_000_000_000L) {
+            System.out.println("WeakReference.get (reachable) time unreasonably long");
+        }
+    }
+
+    /**
+     * Allocate and GC a lot, while keeping significant amounts of finalizer and
+     * SoftReference-reachable memory around.
+     */
+    static Runnable allocFinalizable = new Runnable() {
+        public void run() {
+            // Allocate and drop some finalizable objects that take a long time
+            // to mark. Designed to be hard to optimize away. Each of these objects will
+            // build a new one in its finalizer before really going away.
+            ArrayList<SoftReference<CBT>> softRefs = new ArrayList<>(N_SOFTREFS);
+            for (int i = 0; i < N_SOFTREFS; ++i) {
+                // These should not normally get reclaimed, since we shouldn't run out of
+                // memory. They do increase tracing time.
+                softRefs.add(new SoftReference(CBT.make(TREE_HEIGHT)));
+            }
+            for (int i = 0; i < N_RESURRECTING_OBJECTS; ++i) {
+                ResurrectingObject.allocOne();
+            }
+            BigInteger counter = BigInteger.ZERO;
+            for (int i = 1; !pleaseStop; ++i) {
+                // Allocate a lot of short-lived objects, using BigIntegers to minimize the chance
+                // of the allocation getting optimized out. This makes things slightly more
+                // realistic, since not all objects will be finalizer reachable.
+                for (int j = 0; j < N_PLAIN_OBJECTS / 2; ++j) {
+                    counter = counter.add(BigInteger.TEN);
+                }
+                // Look at counter to reduce chance of optimizing out the allocation.
+                if (counter.longValue() % 10 != 0) {
+                    System.out.println("Bad allocFinalizable counter value: " + counter);
+                }
+                // Explicitly collect here, mostly to prevent heap growth. Otherwise we get
+                // ahead of the GC and eventually block on it.
+                Runtime.getRuntime().gc();
+                if (PRINT_TIMES && i % 100 == 0) {
+                    System.out.println("Collected " + i + " times");
+                }
+            }
+            // To be safe, access softRefs.
+            final CBT sample = softRefs.get(N_SOFTREFS / 2).get();
+            if (sample != null) {
+              sample.check(TREE_HEIGHT, 47 /* some path descriptor */);
+            }
+        }
+    };
+
+    public static void main(String[] args) throws Exception {
+        Main theTest = new Main();
+        Thread allocThread = null;
+        if (BACKGROUND_GC_THREAD) {
+            allocThread = new Thread(allocFinalizable);
+            allocThread.setDaemon(true);  // Terminate if main thread dies.
+            allocThread.start();
+        }
+        theTest.runTest();
+        if (BACKGROUND_GC_THREAD) {
+            pleaseStop = true;
+            allocThread.join();
+        }
+        System.out.println("Finished");
+    }
+}
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
index 28a737d..98ea906 100644
--- a/test/913-heaps/heaps.cc
+++ b/test/913-heaps/heaps.cc
@@ -191,6 +191,12 @@
         return 0;
       }
 
+      // Ignore system classes, which may come from the JIT compiling a method
+      // in these classes.
+      if (reference_kind == JVMTI_HEAP_REFERENCE_SYSTEM_CLASS) {
+        return 0;
+      }
+
       // Only check tagged objects.
       if (tag == 0) {
         return JVMTI_VISIT_OBJECTS;
diff --git a/test/common/runtime_state.cc b/test/common/runtime_state.cc
index 0b72612..f39bca1 100644
--- a/test/common/runtime_state.cc
+++ b/test/common/runtime_state.cc
@@ -276,7 +276,20 @@
     // this before checking if we will execute JIT code in case the request
     // is for an 'optimized' compilation.
     jit->CompileMethod(method, self, kind, /*prejit=*/ false);
-  } while (!code_cache->ContainsPc(method->GetEntryPointFromQuickCompiledCode()));
+    const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
+    if (code_cache->ContainsPc(entry_point)) {
+      // If we're running baseline or not requesting optimized, we're good to go.
+      if (jit->GetJitCompiler()->IsBaselineCompiler() || kind != CompilationKind::kOptimized) {
+        break;
+      }
+      // If we're requesting optimized, check that we did get the method
+      // compiled optimized.
+      OatQuickMethodHeader* method_header = OatQuickMethodHeader::FromEntryPoint(entry_point);
+      if (!CodeInfo::IsBaseline(method_header->GetOptimizedCodeInfoPtr())) {
+        break;
+      }
+    }
+  } while (true);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_ensureMethodJitCompiled(JNIEnv*, jclass, jobject meth) {
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 3e485f6..083ff80 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -1358,8 +1358,13 @@
         "description": "Interpreting BigInteger.add() is too slow (timeouts)"
     },
     {
-        "tests": ["2029-contended-monitors"],
+        "tests": ["2029-contended-monitors", "2043-reference-pauses"],
         "variant": "interpreter | interp-ac | gcstress | trace",
+        "description": ["Slow tests. Prone to timeouts."]
+    },
+    {
+        "tests": ["2042-reference-processing"],
+        "variant": "interpreter | interp-ac | gcstress | trace | debuggable",
         "description": ["Slow test. Prone to timeouts."]
     },
     {
diff --git a/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java b/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java
index d27902f..de497c5 100644
--- a/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java
+++ b/test/odsign/test-src/com/android/tests/odsign/OdrefreshHostTest.java
@@ -144,6 +144,63 @@
     }
 
     @Test
+    public void verifyEnableUffdGcChangeTriggersCompilation() throws Exception {
+        try {
+            // Disable phenotype flag syncing. Potentially, we can set
+            // `set_sync_disabled_for_tests` to `until_reboot`, but setting it to
+            // `persistent` prevents unrelated system crashes/restarts from affecting the
+            // test. `set_sync_disabled_for_tests` is reset in the `finally` block anyway.
+            getDevice().executeShellV2Command(
+                    "device_config set_sync_disabled_for_tests persistent");
+
+            // Simulate that the phenotype flag is set to the default value.
+            getDevice().executeShellV2Command(
+                    "device_config put runtime_native_boot enable_uffd_gc false");
+
+            long timeMs = mTestUtils.getCurrentTimeMs();
+            getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+
+            // Artifacts should not be re-compiled.
+            assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
+            assertArtifactsNotModifiedAfter(getSystemServerArtifacts(), timeMs);
+
+            // Simulate that the phenotype flag is set to true.
+            getDevice().executeShellV2Command(
+                    "device_config put runtime_native_boot enable_uffd_gc true");
+
+            timeMs = mTestUtils.getCurrentTimeMs();
+            getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+
+            // Artifacts should be re-compiled.
+            assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
+            assertArtifactsModifiedAfter(getSystemServerArtifacts(), timeMs);
+
+            // Run odrefresh again with the flag unchanged.
+            timeMs = mTestUtils.getCurrentTimeMs();
+            getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+
+            // Artifacts should not be re-compiled.
+            assertArtifactsNotModifiedAfter(getZygoteArtifacts(), timeMs);
+            assertArtifactsNotModifiedAfter(getSystemServerArtifacts(), timeMs);
+
+            // Simulate that the phenotype flag is set to false.
+            getDevice().executeShellV2Command(
+                    "device_config put runtime_native_boot enable_uffd_gc false");
+
+            timeMs = mTestUtils.getCurrentTimeMs();
+            getDevice().executeShellV2Command(ODREFRESH_COMMAND);
+
+            // Artifacts should be re-compiled.
+            assertArtifactsModifiedAfter(getZygoteArtifacts(), timeMs);
+            assertArtifactsModifiedAfter(getSystemServerArtifacts(), timeMs);
+        } finally {
+            getDevice().executeShellV2Command("device_config set_sync_disabled_for_tests none");
+            getDevice().executeShellV2Command(
+                    "device_config delete runtime_native_boot enable_uffd_gc");
+        }
+    }
+
+    @Test
     public void verifyNoCompilationWhenCacheIsGood() throws Exception {
         mTestUtils.removeCompilationLogToAvoidBackoff();
         long timeMs = mTestUtils.getCurrentTimeMs();
diff --git a/test/utils/regen-test-files b/test/utils/regen-test-files
index 37cc177..eae2324 100755
--- a/test/utils/regen-test-files
+++ b/test/utils/regen-test-files
@@ -209,6 +209,7 @@
   "CtsJdwpTestCases", # times out
   "art_standalone_compiler_tests", # b/230392320
   "art_standalone_dex2oat_tests",  # b/228881278
+  "BootImageProfileTest", # b/232012605
 ])
 
 # ART gtests that do not need root access to the device.
diff --git a/tools/libcore_gcstress_debug_failures.txt b/tools/libcore_gcstress_debug_failures.txt
index 4718947..ccd8db5 100644
--- a/tools/libcore_gcstress_debug_failures.txt
+++ b/tools/libcore_gcstress_debug_failures.txt
@@ -51,7 +51,13 @@
           "org.apache.harmony.tests.java.lang.ref.ReferenceQueueTest#test_removeJ",
           "org.apache.harmony.tests.java.lang.ProcessManagerTest#testSleep",
           "org.apache.harmony.tests.java.util.TimerTest#testOverdueTaskExecutesImmediately",
-          "org.apache.harmony.tests.java.util.WeakHashMapTest#test_keySet_hasNext"
+          "org.apache.harmony.tests.java.util.WeakHashMapTest#test_keySet_hasNext",
+          "test.java.lang.StrictMath.HypotTests_testAgainstTranslit_shard1",
+          "test.java.lang.StrictMath.HypotTests_testAgainstTranslit_shard2",
+          "test.java.lang.StrictMath.HypotTests_testAgainstTranslit_shard3",
+          "test.java.lang.StrictMath.HypotTests_testAgainstTranslit_shard4",
+          "test.java.math.BigDecimal",
+          "test.java.math.BigInteger_testConstructor"
   ]
 },
 {