Change well known `Thread` fields to `ArtField*`.

Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Bug: 253570082
Change-Id: Ie2d3075ba75e8513aa91b9e4d950e71616fc1cc1
diff --git a/adbconnection/adbconnection.cc b/adbconnection/adbconnection.cc
index f9ebe40..d0dde29 100644
--- a/adbconnection/adbconnection.cc
+++ b/adbconnection/adbconnection.cc
@@ -23,6 +23,7 @@
 #include "adbconnection/client.h"
 #include "android-base/endian.h"
 #include "android-base/stringprintf.h"
+#include "art_field-inl.h"
 #include "base/file_utils.h"
 #include "base/globals.h"
 #include "base/logging.h"
@@ -180,15 +181,18 @@
   }
 }
 
-static jobject CreateAdbConnectionThread(art::Thread* thr) {
-  JNIEnv* env = thr->GetJniEnv();
-  // Move to native state to talk with the jnienv api.
-  art::ScopedThreadStateChange stsc(thr, art::ThreadState::kNative);
+static jobject CreateAdbConnectionThread(art::ScopedObjectAccess& soa)
+    REQUIRES_SHARED(art::Locks::mutator_lock_) {
+  JNIEnv* env = soa.Self()->GetJniEnv();
   ScopedLocalRef<jstring> thr_name(env, env->NewStringUTF(kAdbConnectionThreadName));
-  ScopedLocalRef<jobject> thr_group(
-      env,
-      env->GetStaticObjectField(art::WellKnownClasses::java_lang_ThreadGroup,
-                                art::WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup));
+  art::ArtField* system_thread_group_field =
+      art::WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup;
+  // Avoid using `ArtField::GetObject` as it requires linking against `libdexfile` for
+  // `operator<<(std::ostream&, Primitive::Type)`.
+  art::ObjPtr<art::mirror::Object> system_thread_group =
+      system_thread_group_field->GetDeclaringClass()->GetFieldObject<art::mirror::Object>(
+          system_thread_group_field->GetOffset());
+  ScopedLocalRef<jobject> thr_group(env, soa.AddLocalReference<jobject>(system_thread_group));
   return env->NewObject(art::WellKnownClasses::java_lang_Thread,
                         art::WellKnownClasses::java_lang_Thread_init,
                         thr_group.get(),
@@ -274,7 +278,7 @@
     }
     runtime->StartThreadBirth();
   }
-  ScopedLocalRef<jobject> thr(soa.Env(), CreateAdbConnectionThread(soa.Self()));
+  ScopedLocalRef<jobject> thr(soa.Env(), CreateAdbConnectionThread(soa));
   // Note: Using pthreads instead of std::thread to not abort when the thread cannot be
   //       created (exception support required).
   std::unique_ptr<CallbackData> data(new CallbackData { this, soa.Env()->NewGlobalRef(thr.get()) });
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index 82165d0..55fe9ef 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -447,9 +447,8 @@
     if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kMonitorWait)) {
       art::Thread* self = art::Thread::Current();
       art::JNIEnvExt* jnienv = self->GetJniEnv();
-      art::ArtField* parkBlockerField = art::jni::DecodeArtField(
-          art::WellKnownClasses::java_lang_Thread_parkBlocker);
-      art::ObjPtr<art::mirror::Object> blocker_obj = parkBlockerField->GetObj(self->GetPeer());
+      art::ObjPtr<art::mirror::Object> blocker_obj =
+          art::WellKnownClasses::java_lang_Thread_parkBlocker->GetObj(self->GetPeer());
       if (blocker_obj.IsNull()) {
         blocker_obj = self->GetPeer();
       }
@@ -505,9 +504,8 @@
     if (handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kMonitorWaited)) {
       art::Thread* self = art::Thread::Current();
       art::JNIEnvExt* jnienv = self->GetJniEnv();
-      art::ArtField* parkBlockerField = art::jni::DecodeArtField(
-          art::WellKnownClasses::java_lang_Thread_parkBlocker);
-      art::ObjPtr<art::mirror::Object> blocker_obj = parkBlockerField->GetObj(self->GetPeer());
+      art::ObjPtr<art::mirror::Object> blocker_obj =
+          art::WellKnownClasses::java_lang_Thread_parkBlocker->GetObj(self->GetPeer());
       if (blocker_obj.IsNull()) {
         blocker_obj = self->GetPeer();
       }
diff --git a/openjdkjvmti/ti_thread.cc b/openjdkjvmti/ti_thread.cc
index 7fb789d..a138c69 100644
--- a/openjdkjvmti/ti_thread.cc
+++ b/openjdkjvmti/ti_thread.cc
@@ -297,7 +297,7 @@
 
     // ThreadGroup.
     if (peer != nullptr) {
-      art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group);
+      art::ArtField* f = art::WellKnownClasses::java_lang_Thread_group;
       CHECK(f != nullptr);
       art::ObjPtr<art::mirror::Object> group = f->GetObject(peer);
       info_ptr->thread_group = group == nullptr
@@ -322,7 +322,7 @@
 
     // Name.
     {
-      art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_name);
+      art::ArtField* f = art::WellKnownClasses::java_lang_Thread_name;
       CHECK(f != nullptr);
       art::ObjPtr<art::mirror::Object> name = f->GetObject(peer);
       std::string name_cpp;
@@ -343,21 +343,21 @@
 
     // Priority.
     {
-      art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_priority);
+      art::ArtField* f = art::WellKnownClasses::java_lang_Thread_priority;
       CHECK(f != nullptr);
       info_ptr->priority = static_cast<jint>(f->GetInt(peer));
     }
 
     // Daemon.
     {
-      art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_daemon);
+      art::ArtField* f = art::WellKnownClasses::java_lang_Thread_daemon;
       CHECK(f != nullptr);
       info_ptr->is_daemon = f->GetBoolean(peer) == 0 ? JNI_FALSE : JNI_TRUE;
     }
 
     // ThreadGroup.
     {
-      art::ArtField* f = art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group);
+      art::ArtField* f = art::WellKnownClasses::java_lang_Thread_group;
       CHECK(f != nullptr);
       art::ObjPtr<art::mirror::Object> group = f->GetObject(peer);
       info_ptr->thread_group = group == nullptr
@@ -846,15 +846,16 @@
   data->java_vm = art::Runtime::Current()->GetJavaVM();
   data->jvmti_env = jvmti_env;
   data->priority = priority;
-  ScopedLocalRef<jstring> s(
-      env,
-      reinterpret_cast<jstring>(
-          env->GetObjectField(thread, art::WellKnownClasses::java_lang_Thread_name)));
-  if (s == nullptr) {
-    data->name = "JVMTI Agent Thread";
-  } else {
-    ScopedUtfChars name(env, s.get());
-    data->name = name.c_str();
+  {
+    art::ScopedObjectAccess soa(env);
+    art::ObjPtr<art::mirror::Object> name =
+        art::WellKnownClasses::java_lang_Thread_name->GetObject(
+            soa.Decode<art::mirror::Object>(thread));
+    if (name == nullptr) {
+      data->name = "JVMTI Agent Thread";
+    } else {
+      data->name = name->AsString()->ToModifiedUtf8();
+    }
   }
 
   pthread_t pthread;
diff --git a/openjdkjvmti/ti_threadgroup.cc b/openjdkjvmti/ti_threadgroup.cc
index bc912cf..e7d91ef 100644
--- a/openjdkjvmti/ti_threadgroup.cc
+++ b/openjdkjvmti/ti_threadgroup.cc
@@ -106,8 +106,7 @@
 
   // Do the name first. It's the only thing that can fail.
   {
-    art::ArtField* name_field =
-        art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_name);
+    art::ArtField* name_field = art::WellKnownClasses::java_lang_ThreadGroup_name;
     CHECK(name_field != nullptr);
     art::ObjPtr<art::mirror::String> name_obj =
         art::ObjPtr<art::mirror::String>::DownCast(name_field->GetObject(obj.Get()));
@@ -129,8 +128,7 @@
 
   // Parent.
   {
-    art::ArtField* parent_field =
-        art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_parent);
+    art::ArtField* parent_field = art::WellKnownClasses::java_lang_ThreadGroup_parent;
     CHECK(parent_field != nullptr);
     art::ObjPtr<art::mirror::Object> parent_group = parent_field->GetObject(obj.Get());
     info_ptr->parent = parent_group == nullptr
@@ -161,8 +159,7 @@
     REQUIRES_SHARED(art::Locks::mutator_lock_) {
   CHECK(desired_thread_group != nullptr);
 
-  art::ArtField* thread_group_field =
-      art::jni::DecodeArtField(art::WellKnownClasses::java_lang_Thread_group);
+  art::ArtField* thread_group_field = art::WellKnownClasses::java_lang_Thread_group;
   DCHECK(thread_group_field != nullptr);
   art::ObjPtr<art::mirror::Object> group = thread_group_field->GetObject(peer);
   return (group == desired_thread_group.Get());
@@ -194,8 +191,7 @@
   CHECK(thread_group != nullptr);
 
   // Get the ThreadGroup[] "groups" out of this thread group...
-  art::ArtField* groups_field =
-      art::jni::DecodeArtField(art::WellKnownClasses::java_lang_ThreadGroup_groups);
+  art::ArtField* groups_field = art::WellKnownClasses::java_lang_ThreadGroup_groups;
   art::ObjPtr<art::mirror::Object> groups_array = groups_field->GetObject(thread_group.Get());
 
   if (groups_array == nullptr) {
diff --git a/runtime/native/jdk_internal_misc_Unsafe.cc b/runtime/native/jdk_internal_misc_Unsafe.cc
index e708732..7ab9152 100644
--- a/runtime/native/jdk_internal_misc_Unsafe.cc
+++ b/runtime/native/jdk_internal_misc_Unsafe.cc
@@ -487,8 +487,7 @@
     // or the thread has already terminated. Setting the field to true will be
     // respected when the thread does start, and is harmless if the thread has
     // already terminated.
-    ArtField* unparked =
-        jni::DecodeArtField(WellKnownClasses::java_lang_Thread_unparkedBeforeStart);
+    ArtField* unparked = WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
     // JNI must use non transactional mode.
     unparked->SetBoolean<false>(soa.Decode<mirror::Object>(jthread), JNI_TRUE);
   }
diff --git a/runtime/native/sun_misc_Unsafe.cc b/runtime/native/sun_misc_Unsafe.cc
index 1781a29..da28489 100644
--- a/runtime/native/sun_misc_Unsafe.cc
+++ b/runtime/native/sun_misc_Unsafe.cc
@@ -534,8 +534,7 @@
     // or the thread has already terminated. Setting the field to true will be
     // respected when the thread does start, and is harmless if the thread has
     // already terminated.
-    ArtField* unparked =
-        jni::DecodeArtField(WellKnownClasses::java_lang_Thread_unparkedBeforeStart);
+    ArtField* unparked = WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
     // JNI must use non transactional mode.
     unparked->SetBoolean<false>(soa.Decode<mirror::Object>(jthread), JNI_TRUE);
   }
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 46429e8..d3c268b 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -2246,17 +2246,15 @@
 }
 
 void Runtime::InitThreadGroups(Thread* self) {
-  JNIEnvExt* env = self->GetJniEnv();
-  ScopedJniEnvLocalRefState env_state(env);
+  ScopedObjectAccess soa(self);
+  ArtField* main_thread_group_field = WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup;
+  ArtField* system_thread_group_field = WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup;
+  ObjPtr<mirror::Class> thread_group_class = main_thread_group_field->GetDeclaringClass();
   main_thread_group_ =
-      env->NewGlobalRef(env->GetStaticObjectField(
-          WellKnownClasses::java_lang_ThreadGroup,
-          WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup));
+      soa.Vm()->AddGlobalRef(self, main_thread_group_field->GetObject(thread_group_class));
   CHECK_IMPLIES(main_thread_group_ == nullptr, IsAotCompiler());
   system_thread_group_ =
-      env->NewGlobalRef(env->GetStaticObjectField(
-          WellKnownClasses::java_lang_ThreadGroup,
-          WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup));
+      soa.Vm()->AddGlobalRef(self, system_thread_group_field->GetObject(thread_group_class));
   CHECK_IMPLIES(system_thread_group_ == nullptr, IsAotCompiler());
 }
 
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 58f1dc1..5ce9d17 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -641,7 +641,7 @@
     self->DeleteJPeer(self->GetJniEnv());
     self->SetThreadName(self->GetThreadName()->ToModifiedUtf8().c_str());
 
-    ArtField* priorityField = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_priority);
+    ArtField* priorityField = WellKnownClasses::java_lang_Thread_priority;
     self->SetNativePriority(priorityField->GetInt(self->tlsPtr_.opeer));
 
     runtime->GetRuntimeCallbacks()->ThreadStart(self);
@@ -649,8 +649,7 @@
     // Unpark ourselves if the java peer was unparked before it started (see
     // b/28845097#comment49 for more information)
 
-    ArtField* unparkedField = jni::DecodeArtField(
-        WellKnownClasses::java_lang_Thread_unparkedBeforeStart);
+    ArtField* unparkedField = WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
     bool should_unpark = false;
     {
       // Hold the lock here, so that if another thread calls unpark before the thread starts
@@ -676,7 +675,7 @@
 
 Thread* Thread::FromManagedThread(const ScopedObjectAccessAlreadyRunnable& soa,
                                   ObjPtr<mirror::Object> thread_peer) {
-  ArtField* f = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_nativePeer);
+  ArtField* f = WellKnownClasses::java_lang_Thread_nativePeer;
   Thread* result = reinterpret_cast64<Thread*>(f->GetLong(thread_peer));
   // Check that if we have a result it is either suspended or we hold the thread_list_lock_
   // to stop it from going away.
@@ -834,6 +833,22 @@
   madvise(pregion, unwanted_size, MADV_DONTNEED);
 }
 
+template <bool kSupportTransaction>
+static void SetNativePeer(ObjPtr<mirror::Object> java_peer, Thread* thread)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  ArtField* field = WellKnownClasses::java_lang_Thread_nativePeer;
+  if (kSupportTransaction && Runtime::Current()->IsActiveTransaction()) {
+    field->SetLong</*kTransactionActive=*/ true>(java_peer, reinterpret_cast<jlong>(thread));
+  } else {
+    field->SetLong</*kTransactionActive=*/ false>(java_peer, reinterpret_cast<jlong>(thread));
+  }
+}
+
+static void SetNativePeer(JNIEnv* env, jobject java_peer, Thread* thread) {
+  ScopedObjectAccess soa(env);
+  SetNativePeer</*kSupportTransaction=*/ false>(soa.Decode<mirror::Object>(java_peer), thread);
+}
+
 void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) {
   CHECK(java_peer != nullptr);
   Thread* self = static_cast<JNIEnvExt*>(env)->GetSelf();
@@ -841,7 +856,7 @@
   if (VLOG_IS_ON(threads)) {
     ScopedObjectAccess soa(env);
 
-    ArtField* f = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_name);
+    ArtField* f = WellKnownClasses::java_lang_Thread_name;
     ObjPtr<mirror::String> java_name =
         f->GetObject(soa.Decode<mirror::Object>(java_peer))->AsString();
     std::string thread_name;
@@ -880,8 +895,7 @@
 
   // Thread.start is synchronized, so we know that nativePeer is 0, and know that we're not racing
   // to assign it.
-  env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer,
-                    reinterpret_cast<jlong>(child_thread));
+  SetNativePeer(env, java_peer, child_thread);
 
   // Try to allocate a JNIEnvExt for the thread. We do this here as we might be out of memory and
   // do not have a good way to report this on the child's side.
@@ -925,7 +939,7 @@
   delete child_thread;
   child_thread = nullptr;
   // TODO: remove from thread group?
-  env->SetLongField(java_peer, WellKnownClasses::java_lang_Thread_nativePeer, 0);
+  SetNativePeer(env, java_peer, nullptr);
   {
     std::string msg(child_jni_env_ext.get() == nullptr ?
         StringPrintf("Could not allocate JNI Env: %s", error_msg.c_str()) :
@@ -1093,14 +1107,11 @@
 Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_peer) {
   auto set_peer_action = [&](Thread* self) {
     // Install the given peer.
-    {
-      DCHECK(self == Thread::Current());
-      ScopedObjectAccess soa(self);
-      self->tlsPtr_.opeer = soa.Decode<mirror::Object>(thread_peer).Ptr();
-    }
-    self->GetJniEnv()->SetLongField(thread_peer,
-                                    WellKnownClasses::java_lang_Thread_nativePeer,
-                                    reinterpret_cast64<jlong>(self));
+    DCHECK(self == Thread::Current());
+    ScopedObjectAccess soa(self);
+    ObjPtr<mirror::Object> peer = soa.Decode<mirror::Object>(thread_peer);
+    self->tlsPtr_.opeer = peer.Ptr();
+    SetNativePeer</*kSupportTransaction=*/ false>(peer, self);
     return true;
   };
   return Attach(thread_name, as_daemon, set_peer_action, /* should_run_callbacks= */ true);
@@ -1142,11 +1153,9 @@
 
   Thread* self = this;
   DCHECK_EQ(self, Thread::Current());
-  env->SetLongField(peer.get(),
-                    WellKnownClasses::java_lang_Thread_nativePeer,
-                    reinterpret_cast64<jlong>(self));
-
   ScopedObjectAccess soa(self);
+  SetNativePeer</*kSupportTransaction=*/ false>(soa.Decode<mirror::Object>(peer.get()), self);
+
   StackHandleScope<1> hs(self);
   MutableHandle<mirror::String> peer_thread_name(hs.NewHandle(GetThreadName()));
   if (peer_thread_name == nullptr) {
@@ -1235,14 +1244,13 @@
                       jobject thread_group,
                       jobject thread_name,
                       jint thread_priority) {
-  jni::DecodeArtField(WellKnownClasses::java_lang_Thread_daemon)->
+  WellKnownClasses::java_lang_Thread_daemon->
       SetBoolean<kTransactionActive>(peer, thread_is_daemon);
-  jni::DecodeArtField(WellKnownClasses::java_lang_Thread_group)->
+  WellKnownClasses::java_lang_Thread_group->
       SetObject<kTransactionActive>(peer, soa.Decode<mirror::Object>(thread_group));
-  jni::DecodeArtField(WellKnownClasses::java_lang_Thread_name)->
+  WellKnownClasses::java_lang_Thread_name->
       SetObject<kTransactionActive>(peer, soa.Decode<mirror::Object>(thread_name));
-  jni::DecodeArtField(WellKnownClasses::java_lang_Thread_priority)->
-      SetInt<kTransactionActive>(peer, thread_priority);
+  WellKnownClasses::java_lang_Thread_priority->SetInt<kTransactionActive>(peer, thread_priority);
 }
 
 void Thread::SetCachedThreadName(const char* name) {
@@ -1417,11 +1425,10 @@
 }
 
 ObjPtr<mirror::String> Thread::GetThreadName() const {
-  ArtField* f = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_name);
   if (tlsPtr_.opeer == nullptr) {
     return nullptr;
   }
-  ObjPtr<mirror::Object> name = f->GetObject(tlsPtr_.opeer);
+  ObjPtr<mirror::Object> name = WellKnownClasses::java_lang_Thread_name->GetObject(tlsPtr_.opeer);
   return name == nullptr ? nullptr : name->AsString();
 }
 
@@ -1959,20 +1966,15 @@
   // cause ScopedObjectAccessUnchecked to deadlock.
   if (gAborting == 0 && self != nullptr && thread != nullptr && thread->tlsPtr_.opeer != nullptr) {
     ScopedObjectAccessUnchecked soa(self);
-    priority = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_priority)
-        ->GetInt(thread->tlsPtr_.opeer);
-    is_daemon = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_daemon)
-        ->GetBoolean(thread->tlsPtr_.opeer);
+    priority = WellKnownClasses::java_lang_Thread_priority->GetInt(thread->tlsPtr_.opeer);
+    is_daemon = WellKnownClasses::java_lang_Thread_daemon->GetBoolean(thread->tlsPtr_.opeer);
 
     ObjPtr<mirror::Object> thread_group =
-        jni::DecodeArtField(WellKnownClasses::java_lang_Thread_group)
-            ->GetObject(thread->tlsPtr_.opeer);
+        WellKnownClasses::java_lang_Thread_group->GetObject(thread->tlsPtr_.opeer);
 
     if (thread_group != nullptr) {
-      ArtField* group_name_field =
-          jni::DecodeArtField(WellKnownClasses::java_lang_ThreadGroup_name);
       ObjPtr<mirror::String> group_name_string =
-          group_name_field->GetObject(thread_group)->AsString();
+          WellKnownClasses::java_lang_ThreadGroup_name->GetObject(thread_group)->AsString();
       group_name = (group_name_string != nullptr) ? group_name_string->ToModifiedUtf8() : "<null>";
     }
   } else if (thread != nullptr) {
@@ -2434,9 +2436,9 @@
   jobject thread_group_jobject = thread_group;
   if (thread_group == nullptr || kIsDebugBuild) {
     // There is always a group set. Retrieve it.
-    thread_group_jobject_scoped.reset(
-        soa.Env()->GetObjectField(thread_jobject.get(),
-                                  WellKnownClasses::java_lang_Thread_group));
+    thread_group_jobject_scoped.reset(soa.AddLocalReference<jobject>(
+        WellKnownClasses::java_lang_Thread_group->GetObject(
+            soa.Decode<mirror::Object>(thread_jobject.get()))));
     thread_group_jobject = thread_group_jobject_scoped.get();
     if (kIsDebugBuild && thread_group != nullptr) {
       CHECK(soa.Env()->IsSameObject(thread_group, thread_group_jobject));
@@ -2578,18 +2580,12 @@
     }
 
     // this.nativePeer = 0;
-    if (Runtime::Current()->IsActiveTransaction()) {
-      jni::DecodeArtField(WellKnownClasses::java_lang_Thread_nativePeer)
-          ->SetLong<true>(tlsPtr_.opeer, 0);
-    } else {
-      jni::DecodeArtField(WellKnownClasses::java_lang_Thread_nativePeer)
-          ->SetLong<false>(tlsPtr_.opeer, 0);
-    }
+    SetNativePeer</*kSupportTransaction=*/ true>(tlsPtr_.opeer, nullptr);
 
     // Thread.join() is implemented as an Object.wait() on the Thread.lock object. Signal anyone
     // who is waiting.
     ObjPtr<mirror::Object> lock =
-        jni::DecodeArtField(WellKnownClasses::java_lang_Thread_lock)->GetObject(tlsPtr_.opeer);
+        WellKnownClasses::java_lang_Thread_lock->GetObject(tlsPtr_.opeer);
     // (This conditional is only needed for tests, where Thread.lock won't have been set.)
     if (lock != nullptr) {
       StackHandleScope<1> hs(self);
@@ -2679,8 +2675,8 @@
 void Thread::RemoveFromThreadGroup(ScopedObjectAccessAlreadyRunnable& soa) {
   // this.group.removeThread(this);
   // group can be null if we're in the compiler or a test.
-  ObjPtr<mirror::Object> ogroup = jni::DecodeArtField(WellKnownClasses::java_lang_Thread_group)
-      ->GetObject(tlsPtr_.opeer);
+  ObjPtr<mirror::Object> ogroup =
+      WellKnownClasses::java_lang_Thread_group->GetObject(tlsPtr_.opeer);
   if (ogroup != nullptr) {
     ScopedLocalRef<jobject> group(soa.Env(), soa.AddLocalReference<jobject>(ogroup));
     ScopedLocalRef<jobject> peer(soa.Env(), soa.AddLocalReference<jobject>(tlsPtr_.opeer));
@@ -4800,8 +4796,7 @@
   if (GetPeer() == nullptr) {
     return false;
   }
-  return jni::DecodeArtField(
-      WellKnownClasses::java_lang_Thread_systemDaemon)->GetBoolean(GetPeer());
+  return WellKnownClasses::java_lang_Thread_systemDaemon->GetBoolean(GetPeer());
 }
 
 std::string Thread::StateAndFlagsAsHexString() const {
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index d2614a6..8fa7bde 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -30,6 +30,7 @@
 #include "nativehelper/scoped_utf_chars.h"
 #include "unwindstack/AndroidUnwinder.h"
 
+#include "art_field-inl.h"
 #include "base/aborting.h"
 #include "base/histogram-inl.h"
 #include "base/mutex-inl.h"
@@ -44,8 +45,10 @@
 #include "gc_root.h"
 #include "jni/jni_internal.h"
 #include "lock_word.h"
+#include "mirror/string.h"
 #include "monitor.h"
 #include "native_stack_dump.h"
+#include "obj_ptr-inl.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread.h"
 #include "trace.h"
@@ -862,20 +865,16 @@
   return true;
 }
 
-static void ThreadSuspendByPeerWarning(Thread* self,
+static void ThreadSuspendByPeerWarning(ScopedObjectAccess& soa,
                                        LogSeverity severity,
                                        const char* message,
-                                       jobject peer) {
-  JNIEnvExt* env = self->GetJniEnv();
-  ScopedLocalRef<jstring>
-      scoped_name_string(env, static_cast<jstring>(env->GetObjectField(
-          peer, WellKnownClasses::java_lang_Thread_name)));
-  ScopedUtfChars scoped_name_chars(env, scoped_name_string.get());
-  if (scoped_name_chars.c_str() == nullptr) {
-      LOG(severity) << message << ": " << peer;
-      env->ExceptionClear();
+                                       jobject peer) REQUIRES_SHARED(Locks::mutator_lock_) {
+  ObjPtr<mirror::Object> name =
+      WellKnownClasses::java_lang_Thread_name->GetObject(soa.Decode<mirror::Object>(peer));
+  if (name == nullptr) {
+    LOG(severity) << message << ": " << peer;
   } else {
-      LOG(severity) << message << ": " << peer << ":" << scoped_name_chars.c_str();
+    LOG(severity) << message << ": " << peer << ":" << name->AsString()->ToModifiedUtf8();
   }
 }
 
@@ -913,7 +912,7 @@
                                                               reason);
           DCHECK(updated);
         }
-        ThreadSuspendByPeerWarning(self,
+        ThreadSuspendByPeerWarning(soa,
                                    ::android::base::WARNING,
                                     "No such thread for suspend",
                                     peer);
@@ -966,7 +965,7 @@
         const uint64_t total_delay = NanoTime() - start_time;
         if (total_delay >= thread_suspend_timeout_ns_) {
           if (suspended_thread == nullptr) {
-            ThreadSuspendByPeerWarning(self,
+            ThreadSuspendByPeerWarning(soa,
                                        ::android::base::FATAL,
                                        "Failed to issue suspend request",
                                        peer);
@@ -978,7 +977,7 @@
             // Explicitly release thread_suspend_count_lock_; we haven't held it for long, so
             // seeing threads blocked on it is not informative.
             Locks::thread_suspend_count_lock_->Unlock(self);
-            ThreadSuspendByPeerWarning(self,
+            ThreadSuspendByPeerWarning(soa,
                                        ::android::base::FATAL,
                                        "Thread suspension timed out",
                                        peer);
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 308e013..52a9f6b 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -143,21 +143,21 @@
 ArtField* WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
 ArtField* WellKnownClasses::java_io_FileDescriptor_descriptor;
 ArtField* WellKnownClasses::java_lang_ClassLoader_parent;
-jfieldID WellKnownClasses::java_lang_Thread_parkBlocker;
-jfieldID WellKnownClasses::java_lang_Thread_daemon;
-jfieldID WellKnownClasses::java_lang_Thread_group;
-jfieldID WellKnownClasses::java_lang_Thread_lock;
-jfieldID WellKnownClasses::java_lang_Thread_name;
-jfieldID WellKnownClasses::java_lang_Thread_priority;
-jfieldID WellKnownClasses::java_lang_Thread_nativePeer;
-jfieldID WellKnownClasses::java_lang_Thread_systemDaemon;
-jfieldID WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
-jfieldID WellKnownClasses::java_lang_ThreadGroup_groups;
-jfieldID WellKnownClasses::java_lang_ThreadGroup_ngroups;
-jfieldID WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup;
-jfieldID WellKnownClasses::java_lang_ThreadGroup_name;
-jfieldID WellKnownClasses::java_lang_ThreadGroup_parent;
-jfieldID WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup;
+ArtField* WellKnownClasses::java_lang_Thread_parkBlocker;
+ArtField* WellKnownClasses::java_lang_Thread_daemon;
+ArtField* WellKnownClasses::java_lang_Thread_group;
+ArtField* WellKnownClasses::java_lang_Thread_lock;
+ArtField* WellKnownClasses::java_lang_Thread_name;
+ArtField* WellKnownClasses::java_lang_Thread_priority;
+ArtField* WellKnownClasses::java_lang_Thread_nativePeer;
+ArtField* WellKnownClasses::java_lang_Thread_systemDaemon;
+ArtField* WellKnownClasses::java_lang_Thread_unparkedBeforeStart;
+ArtField* WellKnownClasses::java_lang_ThreadGroup_groups;
+ArtField* WellKnownClasses::java_lang_ThreadGroup_ngroups;
+ArtField* WellKnownClasses::java_lang_ThreadGroup_mainThreadGroup;
+ArtField* WellKnownClasses::java_lang_ThreadGroup_name;
+ArtField* WellKnownClasses::java_lang_ThreadGroup_parent;
+ArtField* WellKnownClasses::java_lang_ThreadGroup_systemThreadGroup;
 jfieldID WellKnownClasses::java_lang_Throwable_cause;
 jfieldID WellKnownClasses::java_lang_Throwable_detailMessage;
 jfieldID WellKnownClasses::java_lang_Throwable_stackTrace;
@@ -502,29 +502,36 @@
   java_lang_ClassLoader_parent = CacheField(
       j_l_cl, /*is_static=*/ false, "parent", "Ljava/lang/ClassLoader;");
 
+  ObjPtr<mirror::Class> j_l_Thread = soa.Decode<mirror::Class>(java_lang_Thread);
   java_lang_Thread_parkBlocker =
-      CacheField(env, java_lang_Thread, false, "parkBlocker", "Ljava/lang/Object;");
-  java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z");
+      CacheField(j_l_Thread, /*is_static=*/ false, "parkBlocker", "Ljava/lang/Object;");
+  java_lang_Thread_daemon = CacheField(j_l_Thread, /*is_static=*/ false, "daemon", "Z");
   java_lang_Thread_group =
-      CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;");
-  java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;");
-  java_lang_Thread_name = CacheField(env, java_lang_Thread, false, "name", "Ljava/lang/String;");
-  java_lang_Thread_priority = CacheField(env, java_lang_Thread, false, "priority", "I");
-  java_lang_Thread_nativePeer = CacheField(env, java_lang_Thread, false, "nativePeer", "J");
-  java_lang_Thread_systemDaemon = CacheField(env, java_lang_Thread, false, "systemDaemon", "Z");
+      CacheField(j_l_Thread, /*is_static=*/ false, "group", "Ljava/lang/ThreadGroup;");
+  java_lang_Thread_lock =
+      CacheField(j_l_Thread, /*is_static=*/ false, "lock", "Ljava/lang/Object;");
+  java_lang_Thread_name =
+      CacheField(j_l_Thread, /*is_static=*/ false, "name", "Ljava/lang/String;");
+  java_lang_Thread_priority = CacheField(j_l_Thread, /*is_static=*/ false, "priority", "I");
+  java_lang_Thread_nativePeer = CacheField(j_l_Thread, /*is_static=*/ false, "nativePeer", "J");
+  java_lang_Thread_systemDaemon =
+      CacheField(j_l_Thread, /*is_static=*/ false, "systemDaemon", "Z");
   java_lang_Thread_unparkedBeforeStart =
-      CacheField(env, java_lang_Thread, false, "unparkedBeforeStart", "Z");
+      CacheField(j_l_Thread, /*is_static=*/ false, "unparkedBeforeStart", "Z");
+
+  ObjPtr<mirror::Class> j_l_tg = soa.Decode<mirror::Class>(java_lang_ThreadGroup);
   java_lang_ThreadGroup_groups =
-      CacheField(env, java_lang_ThreadGroup, false, "groups", "[Ljava/lang/ThreadGroup;");
-  java_lang_ThreadGroup_ngroups = CacheField(env, java_lang_ThreadGroup, false, "ngroups", "I");
+      CacheField(j_l_tg, /*is_static=*/ false, "groups", "[Ljava/lang/ThreadGroup;");
+  java_lang_ThreadGroup_ngroups = CacheField(j_l_tg, /*is_static=*/ false, "ngroups", "I");
   java_lang_ThreadGroup_mainThreadGroup =
-      CacheField(env, java_lang_ThreadGroup, true, "mainThreadGroup", "Ljava/lang/ThreadGroup;");
+      CacheField(j_l_tg, /*is_static=*/ true, "mainThreadGroup", "Ljava/lang/ThreadGroup;");
   java_lang_ThreadGroup_name =
-      CacheField(env, java_lang_ThreadGroup, false, "name", "Ljava/lang/String;");
+      CacheField(j_l_tg, /*is_static=*/ false, "name", "Ljava/lang/String;");
   java_lang_ThreadGroup_parent =
-      CacheField(env, java_lang_ThreadGroup, false, "parent", "Ljava/lang/ThreadGroup;");
+      CacheField(j_l_tg, /*is_static=*/ false, "parent", "Ljava/lang/ThreadGroup;");
   java_lang_ThreadGroup_systemThreadGroup =
-      CacheField(env, java_lang_ThreadGroup, true, "systemThreadGroup", "Ljava/lang/ThreadGroup;");
+      CacheField(j_l_tg, /*is_static=*/ true, "systemThreadGroup", "Ljava/lang/ThreadGroup;");
+
   java_lang_Throwable_cause =
       CacheField(env, java_lang_Throwable, false, "cause", "Ljava/lang/Throwable;");
   java_lang_Throwable_detailMessage =
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index c6a0197..6ffca8a 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -156,21 +156,21 @@
   static ArtField* dalvik_system_VMRuntime_nonSdkApiUsageConsumer;
   static ArtField* java_io_FileDescriptor_descriptor;
   static ArtField* java_lang_ClassLoader_parent;
-  static jfieldID java_lang_Thread_parkBlocker;
-  static jfieldID java_lang_Thread_daemon;
-  static jfieldID java_lang_Thread_group;
-  static jfieldID java_lang_Thread_lock;
-  static jfieldID java_lang_Thread_name;
-  static jfieldID java_lang_Thread_priority;
-  static jfieldID java_lang_Thread_nativePeer;
-  static jfieldID java_lang_Thread_systemDaemon;
-  static jfieldID java_lang_Thread_unparkedBeforeStart;
-  static jfieldID java_lang_ThreadGroup_groups;
-  static jfieldID java_lang_ThreadGroup_ngroups;
-  static jfieldID java_lang_ThreadGroup_mainThreadGroup;
-  static jfieldID java_lang_ThreadGroup_name;
-  static jfieldID java_lang_ThreadGroup_parent;
-  static jfieldID java_lang_ThreadGroup_systemThreadGroup;
+  static ArtField* java_lang_Thread_parkBlocker;
+  static ArtField* java_lang_Thread_daemon;
+  static ArtField* java_lang_Thread_group;
+  static ArtField* java_lang_Thread_lock;
+  static ArtField* java_lang_Thread_name;
+  static ArtField* java_lang_Thread_priority;
+  static ArtField* java_lang_Thread_nativePeer;
+  static ArtField* java_lang_Thread_systemDaemon;
+  static ArtField* java_lang_Thread_unparkedBeforeStart;
+  static ArtField* java_lang_ThreadGroup_groups;
+  static ArtField* java_lang_ThreadGroup_ngroups;
+  static ArtField* java_lang_ThreadGroup_mainThreadGroup;
+  static ArtField* java_lang_ThreadGroup_name;
+  static ArtField* java_lang_ThreadGroup_parent;
+  static ArtField* java_lang_ThreadGroup_systemThreadGroup;
   static jfieldID java_lang_Throwable_cause;
   static jfieldID java_lang_Throwable_detailMessage;
   static jfieldID java_lang_Throwable_stackTrace;