Global lock levels.

Introduce the notion of the mutators/GC being a shared-exclusive (aka
reader-writer) lock. Introduce globally ordered locks, analysable by
annotalysis, statically at compile time. Add locking attributes to
methods.

More subtly, remove the heap_lock_ and split between various locks that
are held for smaller periods (where work doesn't get blocked). Remove
buggy Dalvik style thread transitions. Make GC use CMS in all cases when
concurrent is enabled. Fix bug where suspend counts rather than debug
suspend counts were sent to JDWP. Move the PathClassLoader to
WellKnownClasses. In debugger refactor calls to send request and
possibly suspend. Break apart different VmWait thread states. Move
identity hash code to a shared method.

Change-Id: Icdbfc3ce3fcccd14341860ac7305d8e97b51f5c6
diff --git a/src/native/dalvik_system_DexFile.cc b/src/native/dalvik_system_DexFile.cc
index 3e749e5..30f411c 100644
--- a/src/native/dalvik_system_DexFile.cc
+++ b/src/native/dalvik_system_DexFile.cc
@@ -24,7 +24,7 @@
 #include "logging.h"
 #include "os.h"
 #include "runtime.h"
-#include "scoped_jni_thread_state.h"
+#include "scoped_thread_state_change.h"
 #include "ScopedLocalRef.h"
 #include "ScopedUtfChars.h"
 #include "space.h"
@@ -89,12 +89,14 @@
   if (env->ExceptionCheck()) {
     return 0;
   }
+  ScopedObjectAccess soa(env);
   const DexFile* dex_file;
   if (outputName.c_str() == NULL) {
     dex_file = Runtime::Current()->GetClassLinker()->FindDexFileInOatFileFromDexLocation(source);
   } else {
     std::string output(outputName.c_str());
-    dex_file = Runtime::Current()->GetClassLinker()->FindOrCreateOatFileForDexLocation(source, output);
+    dex_file =
+        Runtime::Current()->GetClassLinker()->FindOrCreateOatFileForDexLocation(source, output);
   }
   if (dex_file == NULL) {
     LOG(WARNING) << "Failed to open dex file: " << source;
@@ -105,7 +107,8 @@
   return static_cast<jint>(reinterpret_cast<uintptr_t>(dex_file));
 }
 
-static const DexFile* toDexFile(int dex_file_address) {
+static const DexFile* toDexFile(int dex_file_address)
+    SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
   const DexFile* dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(dex_file_address));
   if (dex_file == NULL) {
     Thread::Current()->ThrowNewExceptionF("Ljava/lang/NullPointerException;", "dex_file == null");
@@ -113,8 +116,12 @@
   return dex_file;
 }
 
-static void DexFile_closeDexFile(JNIEnv*, jclass, jint cookie) {
-  const DexFile* dex_file = toDexFile(cookie);
+static void DexFile_closeDexFile(JNIEnv* env, jclass, jint cookie) {
+  const DexFile* dex_file;
+  {
+    ScopedObjectAccess soa(env);
+    dex_file = toDexFile(cookie);
+  }
   if (dex_file == NULL) {
     return;
   }
@@ -126,7 +133,7 @@
 
 static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader,
                                         jint cookie) {
-  ScopedJniThreadState ts(env);
+  ScopedObjectAccess soa(env);
   const DexFile* dex_file = toDexFile(cookie);
   if (dex_file == NULL) {
     return NULL;
@@ -142,14 +149,18 @@
   }
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   class_linker->RegisterDexFile(*dex_file);
-  Object* class_loader_object = ts.Decode<Object*>(javaLoader);
+  Object* class_loader_object = soa.Decode<Object*>(javaLoader);
   ClassLoader* class_loader = down_cast<ClassLoader*>(class_loader_object);
   Class* result = class_linker->DefineClass(descriptor, class_loader, *dex_file, *dex_class_def);
-  return ts.AddLocalReference<jclass>(result);
+  return soa.AddLocalReference<jclass>(result);
 }
 
 static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jint cookie) {
-  const DexFile* dex_file = toDexFile(cookie);
+  const DexFile* dex_file;
+  {
+    ScopedObjectAccess soa(env);
+    dex_file = toDexFile(cookie);
+  }
   if (dex_file == NULL) {
     return NULL;
   }
@@ -174,6 +185,7 @@
 
   if (!OS::FileExists(filename.c_str())) {
     LOG(ERROR) << "DexFile_isDexOptNeeded file '" << filename.c_str() << "' does not exist";
+    ScopedObjectAccess soa(env);
     Thread::Current()->ThrowNewExceptionF("Ljava/io/FileNotFoundException;", "%s", filename.c_str());
     return JNI_TRUE;
   }
@@ -205,6 +217,7 @@
       }
       return JNI_FALSE;
     }
+    ScopedObjectAccess soa(env);
     if (ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum)) {
       if (debug_logging) {
         LOG(INFO) << "DexFile_isDexOptNeeded precompiled file " << oat_filename
@@ -232,6 +245,7 @@
       // TODO: Ensure this works with multiple image spaces.
       const ImageHeader& image_header = (*cur)->AsImageSpace()->GetImageHeader();
       if (oat_file->GetOatHeader().GetImageFileLocationChecksum() != image_header.GetOatChecksum()) {
+        ScopedObjectAccess soa(env);
         LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
                   << " has out-of-date checksum compared to "
                   << image_header.GetImageRoot(ImageHeader::kOatLocation)->AsString()->ToModifiedUtf8();
@@ -246,9 +260,10 @@
     return JNI_TRUE;
   }
 
+  ScopedObjectAccess soa(env);
   if (!ClassLinker::VerifyOatFileChecksums(oat_file.get(), filename.c_str(), location_checksum)) {
     LOG(INFO) << "DexFile_isDexOptNeeded cache file " << cache_location
-              << " has out-of-date checksum compared to " << filename.c_str();
+        << " has out-of-date checksum compared to " << filename.c_str();
     return JNI_TRUE;
   }
 
diff --git a/src/native/dalvik_system_VMDebug.cc b/src/native/dalvik_system_VMDebug.cc
index 70067fe..3799bbe 100644
--- a/src/native/dalvik_system_VMDebug.cc
+++ b/src/native/dalvik_system_VMDebug.cc
@@ -22,7 +22,7 @@
 #include "hprof/hprof.h"
 #include "jni_internal.h"
 #include "ScopedUtfChars.h"
-#include "scoped_jni_thread_state.h"
+#include "scoped_thread_state_change.h"
 #include "toStringArray.h"
 #include "trace.h"
 
@@ -57,7 +57,8 @@
   Trace::Start("[DDMS]", -1, bufferSize, flags, true);
 }
 
-static void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, jstring javaTraceFilename, jobject javaFd, jint bufferSize, jint flags) {
+static void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, jstring javaTraceFilename,
+                                         jobject javaFd, jint bufferSize, jint flags) {
   int originalFd = jniGetFDFromFileDescriptor(env, javaFd);
   if (originalFd < 0) {
     return;
@@ -65,7 +66,9 @@
 
   int fd = dup(originalFd);
   if (fd < 0) {
-    Thread::Current()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", "dup(%d) failed: %s", originalFd, strerror(errno));
+    ScopedObjectAccess soa(env);
+    Thread::Current()->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
+                                          "dup(%d) failed: %s", originalFd, strerror(errno));
     return;
   }
 
@@ -76,7 +79,8 @@
   Trace::Start(traceFilename.c_str(), fd, bufferSize, flags, false);
 }
 
-static void VMDebug_startMethodTracingFilename(JNIEnv* env, jclass, jstring javaTraceFilename, jint bufferSize, jint flags) {
+static void VMDebug_startMethodTracingFilename(JNIEnv* env, jclass, jstring javaTraceFilename,
+                                               jint bufferSize, jint flags) {
   ScopedUtfChars traceFilename(env, javaTraceFilename);
   if (traceFilename.c_str() == NULL) {
     return;
@@ -114,23 +118,28 @@
   return Dbg::LastDebuggerActivity();
 }
 
-static void VMDebug_startInstructionCounting(JNIEnv*, jclass) {
+static void VMDebug_startInstructionCounting(JNIEnv* env, jclass) {
+  ScopedObjectAccess soa(env);
   Thread::Current()->ThrowNewException("Ljava/lang/UnsupportedOperationException;", "");
 }
 
-static void VMDebug_stopInstructionCounting(JNIEnv*, jclass) {
+static void VMDebug_stopInstructionCounting(JNIEnv* env, jclass) {
+  ScopedObjectAccess soa(env);
   Thread::Current()->ThrowNewException("Ljava/lang/UnsupportedOperationException;", "");
 }
 
-static void VMDebug_getInstructionCount(JNIEnv*, jclass, jintArray /*javaCounts*/) {
+static void VMDebug_getInstructionCount(JNIEnv* env, jclass, jintArray /*javaCounts*/) {
+  ScopedObjectAccess soa(env);
   Thread::Current()->ThrowNewException("Ljava/lang/UnsupportedOperationException;", "");
 }
 
-static void VMDebug_resetInstructionCount(JNIEnv*, jclass) {
+static void VMDebug_resetInstructionCount(JNIEnv* env, jclass) {
+  ScopedObjectAccess soa(env);
   Thread::Current()->ThrowNewException("Ljava/lang/UnsupportedOperationException;", "");
 }
 
-static void VMDebug_printLoadedClasses(JNIEnv*, jclass, jint flags) {
+static void VMDebug_printLoadedClasses(JNIEnv* env, jclass, jint flags) {
+  ScopedObjectAccess soa(env);
   return Runtime::Current()->GetClassLinker()->DumpAllClasses(flags);
 }
 
@@ -155,7 +164,9 @@
 static void VMDebug_dumpHprofData(JNIEnv* env, jclass, jstring javaFilename, jobject javaFd) {
   // Only one of these may be NULL.
   if (javaFilename == NULL && javaFd == NULL) {
-    Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", "fileName == null && fd == null");
+    ScopedObjectAccess soa(env);
+    Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;",
+                                         "fileName == null && fd == null");
     return;
   }
 
@@ -174,7 +185,9 @@
   if (javaFd != NULL) {
     fd = jniGetFDFromFileDescriptor(env, javaFd);
     if (fd < 0) {
-      Thread::Current()->ThrowNewException("Ljava/lang/RuntimeException;", "Invalid file descriptor");
+      ScopedObjectAccess soa(env);
+      Thread::Current()->ThrowNewException("Ljava/lang/RuntimeException;",
+                                           "Invalid file descriptor");
       return;
     }
   }
@@ -187,11 +200,11 @@
 }
 
 static void VMDebug_dumpReferenceTables(JNIEnv* env, jclass) {
+  ScopedObjectAccess soa(env);
   LOG(INFO) << "--- reference table dump ---";
 
-  JNIEnvExt* e = reinterpret_cast<JNIEnvExt*>(env);
-  e->DumpReferenceTables(LOG(INFO));
-  e->vm->DumpReferenceTables(LOG(INFO));
+  soa.Env()->DumpReferenceTables(LOG(INFO));
+  soa.Vm()->DumpReferenceTables(LOG(INFO));
 
   LOG(INFO) << "---";
 }
@@ -204,9 +217,10 @@
   LOG(INFO) << "VMDebug infopoint " << id << " hit";
 }
 
-static jlong VMDebug_countInstancesOfClass(JNIEnv* env, jclass, jclass javaClass, jboolean countAssignable) {
-  ScopedJniThreadState ts(env);
-  Class* c = ts.Decode<Class*>(javaClass);
+static jlong VMDebug_countInstancesOfClass(JNIEnv* env, jclass, jclass javaClass,
+                                           jboolean countAssignable) {
+  ScopedObjectAccess soa(env);
+  Class* c = soa.Decode<Class*>(javaClass);
   if (c == NULL) {
     return 0;
   }
diff --git a/src/native/dalvik_system_VMRuntime.cc b/src/native/dalvik_system_VMRuntime.cc
index 4ec1b92..8dbbc77 100644
--- a/src/native/dalvik_system_VMRuntime.cc
+++ b/src/native/dalvik_system_VMRuntime.cc
@@ -21,9 +21,7 @@
 #include "jni_internal.h"
 #include "object.h"
 #include "object_utils.h"
-#include "scoped_heap_lock.h"
-#include "scoped_jni_thread_state.h"
-#include "scoped_thread_list_lock.h"
+#include "scoped_thread_state_change.h"
 #include "space.h"
 #include "thread.h"
 #include "thread_list.h"
@@ -49,7 +47,7 @@
 }
 
 static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaElementClass, jint length) {
-  ScopedJniThreadState ts(env);
+  ScopedObjectAccess soa(env);
 #ifdef MOVING_GARBAGE_COLLECTOR
   // TODO: right now, we don't have a copying collector, so there's no need
   // to do anything special here, but we ought to pass the non-movability
@@ -57,7 +55,7 @@
   UNIMPLEMENTED(FATAL);
 #endif
 
-  Class* element_class = ts.Decode<Class*>(javaElementClass);
+  Class* element_class = soa.Decode<Class*>(javaElementClass);
   if (element_class == NULL) {
     Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", "element class == null");
     return NULL;
@@ -76,15 +74,15 @@
   if (result == NULL) {
     return NULL;
   }
-  return ts.AddLocalReference<jobject>(result);
+  return soa.AddLocalReference<jobject>(result);
 }
 
 static jlong VMRuntime_addressOf(JNIEnv* env, jobject, jobject javaArray) {
   if (javaArray == NULL) {  // Most likely allocation failed
     return 0;
   }
-  ScopedJniThreadState ts(env);
-  Array* array = ts.Decode<Array*>(javaArray);
+  ScopedObjectAccess soa(env);
+  Array* array = soa.Decode<Array*>(javaArray);
   if (!array->IsArrayInstance()) {
     Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;", "not an array");
     return 0;
@@ -143,7 +141,7 @@
 #if !defined(ART_USE_LLVM_COMPILER)
     if (vm->check_jni) {
       LOG(WARNING) << "Turning off CheckJNI so we can turn on JNI app bug workarounds...";
-      ScopedThreadListLock thread_list_lock;
+      MutexLock mu(*GlobalSynchronization::thread_list_lock_);
       vm->SetCheckJniEnabled(false);
       runtime->GetThreadList()->ForEach(DisableCheckJniCallback, NULL);
     }
@@ -160,8 +158,6 @@
 }
 
 static void VMRuntime_trimHeap(JNIEnv*, jobject) {
-  ScopedHeapLock heap_lock;
-
   // Trim the managed heap.
   Heap* heap = Runtime::Current()->GetHeap();
   const Spaces& spaces = heap->GetSpaces();
diff --git a/src/native/dalvik_system_VMStack.cc b/src/native/dalvik_system_VMStack.cc
index 12fa8db..3284c97 100644
--- a/src/native/dalvik_system_VMStack.cc
+++ b/src/native/dalvik_system_VMStack.cc
@@ -18,19 +18,34 @@
 #include "jni_internal.h"
 #include "nth_caller_visitor.h"
 #include "object.h"
-#include "scoped_heap_lock.h"
-#include "scoped_jni_thread_state.h"
-#include "scoped_thread_list_lock.h"
+#include "scoped_thread_state_change.h"
 #include "thread_list.h"
 
 namespace art {
 
-static jobject GetThreadStack(JNIEnv* env, jobject javaThread) {
-  ScopedJniThreadState ts(env);
-  ScopedHeapLock heap_lock;
-  ScopedThreadListLock thread_list_lock;
-  Thread* thread = Thread::FromManagedThread(ts, javaThread);
-  return (thread != NULL) ? GetThreadStack(ts, thread) : NULL;
+static jobject GetThreadStack(JNIEnv* env, jobject peer) {
+  bool timeout;
+  {
+    ScopedObjectAccess soa(env);
+    Thread* self = Thread::Current();
+    if (soa.Decode<Object*>(peer) == self->GetPeer()) {
+      return self->CreateInternalStackTrace(soa);
+    }
+  }
+  // Suspend thread to build stack trace.
+  Thread* thread = Thread::SuspendForDebugger(peer, true, &timeout);
+  if (thread != NULL) {
+    jobject trace;
+    {
+      ScopedObjectAccess soa(env);
+      trace = thread->CreateInternalStackTrace(soa);
+    }
+    // Restart suspended thread.
+    Runtime::Current()->GetThreadList()->Resume(thread, true);
+    return trace;
+  } else {
+    return NULL;
+  }
 }
 
 static jint VMStack_fillStackTraceElements(JNIEnv* env, jclass, jobject javaThread, jobjectArray javaSteArray) {
@@ -45,10 +60,10 @@
 
 // Returns the defining class loader of the caller's caller.
 static jobject VMStack_getCallingClassLoader(JNIEnv* env, jclass) {
-  ScopedJniThreadState ts(env);
-  NthCallerVisitor visitor(ts.Self()->GetManagedStack(), ts.Self()->GetTraceStack(), 2);
+  ScopedObjectAccess soa(env);
+  NthCallerVisitor visitor(soa.Self()->GetManagedStack(), soa.Self()->GetTraceStack(), 2);
   visitor.WalkStack();
-  return ts.AddLocalReference<jobject>(visitor.caller->GetDeclaringClass()->GetClassLoader());
+  return soa.AddLocalReference<jobject>(visitor.caller->GetDeclaringClass()->GetClassLoader());
 }
 
 static jobject VMStack_getClosestUserClassLoader(JNIEnv* env, jclass, jobject javaBootstrap, jobject javaSystem) {
@@ -74,21 +89,21 @@
     Object* system;
     Object* class_loader;
   };
-  ScopedJniThreadState ts(env);
-  Object* bootstrap = ts.Decode<Object*>(javaBootstrap);
-  Object* system = ts.Decode<Object*>(javaSystem);
-  ClosestUserClassLoaderVisitor visitor(ts.Self()->GetManagedStack(), ts.Self()->GetTraceStack(),
+  ScopedObjectAccess soa(env);
+  Object* bootstrap = soa.Decode<Object*>(javaBootstrap);
+  Object* system = soa.Decode<Object*>(javaSystem);
+  ClosestUserClassLoaderVisitor visitor(soa.Self()->GetManagedStack(), soa.Self()->GetTraceStack(),
                                         bootstrap, system);
   visitor.WalkStack();
-  return ts.AddLocalReference<jobject>(visitor.class_loader);
+  return soa.AddLocalReference<jobject>(visitor.class_loader);
 }
 
 // Returns the class of the caller's caller's caller.
 static jclass VMStack_getStackClass2(JNIEnv* env, jclass) {
-  ScopedJniThreadState ts(env);
-  NthCallerVisitor visitor(ts.Self()->GetManagedStack(), ts.Self()->GetTraceStack(), 3);
+  ScopedObjectAccess soa(env);
+  NthCallerVisitor visitor(soa.Self()->GetManagedStack(), soa.Self()->GetTraceStack(), 3);
   visitor.WalkStack();
-  return ts.AddLocalReference<jclass>(visitor.caller->GetDeclaringClass());
+  return soa.AddLocalReference<jclass>(visitor.caller->GetDeclaringClass());
 }
 
 static jobjectArray VMStack_getThreadStackTrace(JNIEnv* env, jclass, jobject javaThread) {
diff --git a/src/native/java_lang_Class.cc b/src/native/java_lang_Class.cc
index bc1d0de..e63cf1a 100644
--- a/src/native/java_lang_Class.cc
+++ b/src/native/java_lang_Class.cc
@@ -20,15 +20,16 @@
 #include "nth_caller_visitor.h"
 #include "object.h"
 #include "object_utils.h"
-#include "scoped_jni_thread_state.h"
+#include "scoped_thread_state_change.h"
 #include "ScopedLocalRef.h"
 #include "ScopedUtfChars.h"
 #include "well_known_classes.h"
 
 namespace art {
 
-static Class* DecodeClass(const ScopedJniThreadState& ts, jobject java_class) {
-  Class* c = ts.Decode<Class*>(java_class);
+static Class* DecodeClass(const ScopedObjectAccess& soa, jobject java_class)
+    SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
+  Class* c = soa.Decode<Class*>(java_class);
   DCHECK(c != NULL);
   DCHECK(c->IsClass());
   // TODO: we could EnsureInitialized here, rather than on every reflective get/set or invoke .
@@ -39,7 +40,7 @@
 
 // "name" is in "binary name" format, e.g. "dalvik.system.Debug$1".
 static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean initialize, jobject javaLoader) {
-  ScopedJniThreadState ts(env);
+  ScopedObjectAccess soa(env);
   ScopedUtfChars name(env, javaName);
   if (name.c_str() == NULL) {
     return NULL;
@@ -55,7 +56,7 @@
   }
 
   std::string descriptor(DotToDescriptor(name.c_str()));
-  ClassLoader* class_loader = ts.Decode<ClassLoader*>(javaLoader);
+  ClassLoader* class_loader = soa.Decode<ClassLoader*>(javaLoader);
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   Class* c = class_linker->FindClass(descriptor.c_str(), class_loader);
   if (c == NULL) {
@@ -70,12 +71,12 @@
   if (initialize) {
     class_linker->EnsureInitialized(c, true, true);
   }
-  return ts.AddLocalReference<jclass>(c);
+  return soa.AddLocalReference<jclass>(c);
 }
 
 static jint Class_getAnnotationDirectoryOffset(JNIEnv* env, jclass javaClass) {
-  ScopedJniThreadState ts(env);
-  Class* c = DecodeClass(ts, javaClass);
+  ScopedObjectAccess soa(env);
+  Class* c = DecodeClass(soa, javaClass);
   if (c->IsPrimitive() || c->IsArrayClass() || c->IsProxyClass()) {
     return 0;  // primitive, array and proxy classes don't have class definitions
   }
@@ -87,17 +88,22 @@
   }
 }
 
+// TODO: Remove this redundant struct when GCC annotalysis works correctly on top-level functions.
+struct WorkAroundGccAnnotalysisBug {
 template<typename T>
-static jobjectArray ToArray(const ScopedJniThreadState& ts, const char* array_class_name,
-                            const std::vector<T*>& objects) {
-  ScopedLocalRef<jclass> array_class(ts.Env(), ts.Env()->FindClass(array_class_name));
-  jobjectArray result = ts.Env()->NewObjectArray(objects.size(), array_class.get(), NULL);
+static jobjectArray ToArray(const ScopedObjectAccessUnchecked& soa, const char* array_class_name,
+                            const std::vector<T*>& objects)
+    SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
+  ScopedLocalRef<jclass> array_class(soa.Env(), soa.Env()->FindClass(array_class_name));
+  jobjectArray result = soa.Env()->NewObjectArray(objects.size(), array_class.get(), NULL);
   for (size_t i = 0; i < objects.size(); ++i) {
-    ScopedLocalRef<jobject> object(ts.Env(), ts.AddLocalReference<jobject>(objects[i]));
-    ts.Env()->SetObjectArrayElement(result, i, object.get());
+    ScopedLocalRef<jobject> object(soa.Env(), soa.AddLocalReference<jobject>(objects[i]));
+    soa.Env()->SetObjectArrayElement(result, i, object.get());
   }
   return result;
 }
+};
+#define ToArray(a, b, c) WorkAroundGccAnnotalysisBug::ToArray(a, b, c)
 
 static bool IsVisibleConstructor(Method* m, bool public_only) {
   if (public_only && !m->IsPublic()) {
@@ -110,8 +116,8 @@
 }
 
 static jobjectArray Class_getDeclaredConstructors(JNIEnv* env, jclass javaClass, jboolean publicOnly) {
-  ScopedJniThreadState ts(env);
-  Class* c = DecodeClass(ts, javaClass);
+  ScopedObjectAccess soa(env);
+  Class* c = DecodeClass(soa, javaClass);
   std::vector<Method*> constructors;
   for (size_t i = 0; i < c->NumDirectMethods(); ++i) {
     Method* m = c->GetDirectMethod(i);
@@ -120,7 +126,7 @@
     }
   }
 
-  return ToArray(ts, "java/lang/reflect/Constructor", constructors);
+  return ToArray(soa, "java/lang/reflect/Constructor", constructors);
 }
 
 static bool IsVisibleField(Field* f, bool public_only) {
@@ -131,8 +137,8 @@
 }
 
 static jobjectArray Class_getDeclaredFields(JNIEnv* env, jclass javaClass, jboolean publicOnly) {
-  ScopedJniThreadState ts(env);
-  Class* c = DecodeClass(ts, javaClass);
+  ScopedObjectAccess soa(env);
+  Class* c = DecodeClass(soa, javaClass);
   std::vector<Field*> fields;
   FieldHelper fh;
   for (size_t i = 0; i < c->NumInstanceFields(); ++i) {
@@ -164,7 +170,7 @@
     }
   }
 
-  return ToArray(ts, "java/lang/reflect/Field", fields);
+  return ToArray(soa, "java/lang/reflect/Field", fields);
 }
 
 static bool IsVisibleMethod(Method* m, bool public_only) {
@@ -181,8 +187,8 @@
 }
 
 static jobjectArray Class_getDeclaredMethods(JNIEnv* env, jclass javaClass, jboolean publicOnly) {
-  ScopedJniThreadState ts(env);
-  Class* c = DecodeClass(ts, javaClass);
+  ScopedObjectAccess soa(env);
+  Class* c = DecodeClass(soa, javaClass);
   if (c == NULL) {
     return NULL;
   }
@@ -218,12 +224,12 @@
     }
   }
 
-  return ToArray(ts, "java/lang/reflect/Method", methods);
+  return ToArray(soa, "java/lang/reflect/Method", methods);
 }
 
 static jobject Class_getDex(JNIEnv* env, jobject javaClass) {
-  ScopedJniThreadState ts(env);
-  Class* c = DecodeClass(ts, javaClass);
+  ScopedObjectAccess soa(env);
+  Class* c = DecodeClass(soa, javaClass);
 
   DexCache* dex_cache = c->GetDexCache();
   if (dex_cache == NULL) {
@@ -233,7 +239,8 @@
   return Runtime::Current()->GetClassLinker()->FindDexFile(dex_cache).GetDexObject(env);
 }
 
-static bool MethodMatches(MethodHelper* mh, const std::string& name, ObjectArray<Class>* arg_array) {
+static bool MethodMatches(MethodHelper* mh, const std::string& name, ObjectArray<Class>* arg_array)
+    SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
   if (name != mh->GetName()) {
     return false;
   }
@@ -254,7 +261,8 @@
 }
 
 static Method* FindConstructorOrMethodInArray(ObjectArray<Method>* methods, const std::string& name,
-                                              ObjectArray<Class>* arg_array) {
+                                              ObjectArray<Class>* arg_array)
+    SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
   if (methods == NULL) {
     return NULL;
   }
@@ -282,10 +290,10 @@
 
 static jobject Class_getDeclaredConstructorOrMethod(JNIEnv* env, jclass javaClass, jstring javaName,
                                                     jobjectArray javaArgs) {
-  ScopedJniThreadState ts(env);
-  Class* c = DecodeClass(ts, javaClass);
-  std::string name(ts.Decode<String*>(javaName)->ToModifiedUtf8());
-  ObjectArray<Class>* arg_array = ts.Decode<ObjectArray<Class>*>(javaArgs);
+  ScopedObjectAccess soa(env);
+  Class* c = DecodeClass(soa, javaClass);
+  std::string name(soa.Decode<String*>(javaName)->ToModifiedUtf8());
+  ObjectArray<Class>* arg_array = soa.Decode<ObjectArray<Class>*>(javaArgs);
 
   Method* m = FindConstructorOrMethodInArray(c->GetDirectMethods(), name, arg_array);
   if (m == NULL) {
@@ -293,16 +301,16 @@
   }
 
   if (m != NULL) {
-    return ts.AddLocalReference<jobject>(m);
+    return soa.AddLocalReference<jobject>(m);
   } else {
     return NULL;
   }
 }
 
 static jobject Class_getDeclaredFieldNative(JNIEnv* env, jclass java_class, jobject jname) {
-  ScopedJniThreadState ts(env);
-  Class* c = DecodeClass(ts, java_class);
-  String* name = ts.Decode<String*>(jname);
+  ScopedObjectAccess soa(env);
+  Class* c = DecodeClass(soa, java_class);
+  String* name = soa.Decode<String*>(jname);
   DCHECK(name->GetClass()->IsStringClass());
 
   FieldHelper fh;
@@ -314,7 +322,7 @@
         DCHECK(env->ExceptionOccurred());
         return NULL;
       }
-      return ts.AddLocalReference<jclass>(f);
+      return soa.AddLocalReference<jclass>(f);
     }
   }
   for (size_t i = 0; i < c->NumStaticFields(); ++i) {
@@ -325,40 +333,40 @@
         DCHECK(env->ExceptionOccurred());
         return NULL;
       }
-      return ts.AddLocalReference<jclass>(f);
+      return soa.AddLocalReference<jclass>(f);
     }
   }
   return NULL;
 }
 
 static jstring Class_getNameNative(JNIEnv* env, jobject javaThis) {
-  ScopedJniThreadState ts(env);
-  Class* c = DecodeClass(ts, javaThis);
-  return ts.AddLocalReference<jstring>(c->ComputeName());
+  ScopedObjectAccess soa(env);
+  Class* c = DecodeClass(soa, javaThis);
+  return soa.AddLocalReference<jstring>(c->ComputeName());
 }
 
 static jobjectArray Class_getProxyInterfaces(JNIEnv* env, jobject javaThis) {
-  ScopedJniThreadState ts(env);
-  SynthesizedProxyClass* c = down_cast<SynthesizedProxyClass*>(DecodeClass(ts, javaThis));
-  return ts.AddLocalReference<jobjectArray>(c->GetInterfaces()->Clone());
+  ScopedObjectAccess soa(env);
+  SynthesizedProxyClass* c = down_cast<SynthesizedProxyClass*>(DecodeClass(soa, javaThis));
+  return soa.AddLocalReference<jobjectArray>(c->GetInterfaces()->Clone());
 }
 
 static jboolean Class_isAssignableFrom(JNIEnv* env, jobject javaLhs, jclass javaRhs) {
-  ScopedJniThreadState ts(env);
-  Class* lhs = DecodeClass(ts, javaLhs);
-  Class* rhs = ts.Decode<Class*>(javaRhs); // Can be null.
+  ScopedObjectAccess soa(env);
+  Class* lhs = DecodeClass(soa, javaLhs);
+  Class* rhs = soa.Decode<Class*>(javaRhs); // Can be null.
   if (rhs == NULL) {
-    ts.Self()->ThrowNewException("Ljava/lang/NullPointerException;", "class == null");
+    soa.Self()->ThrowNewException("Ljava/lang/NullPointerException;", "class == null");
     return JNI_FALSE;
   }
   return lhs->IsAssignableFrom(rhs) ? JNI_TRUE : JNI_FALSE;
 }
 
 static jobject Class_newInstanceImpl(JNIEnv* env, jobject javaThis) {
-  ScopedJniThreadState ts(env);
-  Class* c = DecodeClass(ts, javaThis);
+  ScopedObjectAccess soa(env);
+  Class* c = DecodeClass(soa, javaThis);
   if (c->IsPrimitive() || c->IsInterface() || c->IsArrayClass() || c->IsAbstract()) {
-    ts.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
+    soa.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
         "Class %s can not be instantiated", PrettyDescriptor(ClassHelper(c).GetDescriptor()).c_str());
     return NULL;
   }
@@ -369,7 +377,7 @@
 
   Method* init = c->FindDeclaredDirectMethod("<init>", "()V");
   if (init == NULL) {
-    ts.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
+    soa.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
         "Class %s has no default <init>()V constructor", PrettyDescriptor(ClassHelper(c).GetDescriptor()).c_str());
     return NULL;
   }
@@ -383,20 +391,20 @@
   // constructor must be public or, if the caller is in the same package,
   // have package scope.
 
-  NthCallerVisitor visitor(ts.Self()->GetManagedStack(), ts.Self()->GetTraceStack(), 2);
+  NthCallerVisitor visitor(soa.Self()->GetManagedStack(), soa.Self()->GetTraceStack(), 2);
   visitor.WalkStack();
   Class* caller_class = visitor.caller->GetDeclaringClass();
 
   ClassHelper caller_ch(caller_class);
   if (!caller_class->CanAccess(c)) {
-    ts.Self()->ThrowNewExceptionF("Ljava/lang/IllegalAccessException;",
+    soa.Self()->ThrowNewExceptionF("Ljava/lang/IllegalAccessException;",
         "Class %s is not accessible from class %s",
         PrettyDescriptor(ClassHelper(c).GetDescriptor()).c_str(),
         PrettyDescriptor(caller_ch.GetDescriptor()).c_str());
     return NULL;
   }
   if (!caller_class->CanAccessMember(init->GetDeclaringClass(), init->GetAccessFlags())) {
-    ts.Self()->ThrowNewExceptionF("Ljava/lang/IllegalAccessException;",
+    soa.Self()->ThrowNewExceptionF("Ljava/lang/IllegalAccessException;",
         "%s is not accessible from class %s",
         PrettyMethod(init).c_str(),
         PrettyDescriptor(caller_ch.GetDescriptor()).c_str());
@@ -405,13 +413,13 @@
 
   Object* new_obj = c->AllocObject();
   if (new_obj == NULL) {
-    DCHECK(ts.Self()->IsExceptionPending());
+    DCHECK(soa.Self()->IsExceptionPending());
     return NULL;
   }
 
   // invoke constructor; unlike reflection calls, we don't wrap exceptions
-  jclass java_class = ts.AddLocalReference<jclass>(c);
-  jmethodID mid = ts.EncodeMethod(init);
+  jclass java_class = soa.AddLocalReference<jclass>(c);
+  jmethodID mid = soa.EncodeMethod(init);
   return env->NewObject(java_class, mid);
 }
 
diff --git a/src/native/java_lang_Object.cc b/src/native/java_lang_Object.cc
index d6b1bd6..89019f7 100644
--- a/src/native/java_lang_Object.cc
+++ b/src/native/java_lang_Object.cc
@@ -16,31 +16,31 @@
 
 #include "jni_internal.h"
 #include "object.h"
-#include "scoped_jni_thread_state.h"
+#include "scoped_thread_state_change.h"
 
 namespace art {
 
 static jobject Object_internalClone(JNIEnv* env, jobject javaThis) {
-  ScopedJniThreadState ts(env);
-  Object* o = ts.Decode<Object*>(javaThis);
-  return ts.AddLocalReference<jobject>(o->Clone());
+  ScopedObjectAccess soa(env);
+  Object* o = soa.Decode<Object*>(javaThis);
+  return soa.AddLocalReference<jobject>(o->Clone());
 }
 
 static void Object_notify(JNIEnv* env, jobject javaThis) {
-  ScopedJniThreadState ts(env);
-  Object* o = ts.Decode<Object*>(javaThis);
+  ScopedObjectAccess soa(env);
+  Object* o = soa.Decode<Object*>(javaThis);
   o->Notify();
 }
 
 static void Object_notifyAll(JNIEnv* env, jobject javaThis) {
-  ScopedJniThreadState ts(env);
-  Object* o = ts.Decode<Object*>(javaThis);
+  ScopedObjectAccess soa(env);
+  Object* o = soa.Decode<Object*>(javaThis);
   o->NotifyAll();
 }
 
 static void Object_wait(JNIEnv* env, jobject javaThis, jlong ms, jint ns) {
-  ScopedJniThreadState ts(env);
-  Object* o = ts.Decode<Object*>(javaThis);
+  ScopedObjectAccess soa(env);
+  Object* o = soa.Decode<Object*>(javaThis);
   o->Wait(ms, ns);
 }
 
diff --git a/src/native/java_lang_Runtime.cc b/src/native/java_lang_Runtime.cc
index 1b657b1..6dc850e 100644
--- a/src/native/java_lang_Runtime.cc
+++ b/src/native/java_lang_Runtime.cc
@@ -22,13 +22,12 @@
 #include "jni_internal.h"
 #include "object.h"
 #include "runtime.h"
-#include "scoped_jni_thread_state.h"
+#include "scoped_thread_state_change.h"
 #include "ScopedUtfChars.h"
 
 namespace art {
 
-static void Runtime_gc(JNIEnv* env, jclass) {
-  ScopedJniThreadState ts(env);
+static void Runtime_gc(JNIEnv*, jclass) {
   Runtime::Current()->GetHeap()->CollectGarbage(false);
 }
 
@@ -45,13 +44,13 @@
  * message on failure.
  */
 static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, jobject javaLoader) {
-  ScopedJniThreadState ts(env);
+  ScopedObjectAccess soa(env);
   ScopedUtfChars filename(env, javaFilename);
   if (filename.c_str() == NULL) {
     return NULL;
   }
 
-  ClassLoader* classLoader = ts.Decode<ClassLoader*>(javaLoader);
+  ClassLoader* classLoader = soa.Decode<ClassLoader*>(javaLoader);
   std::string detail;
   JavaVMExt* vm = Runtime::Current()->GetJavaVM();
   bool success = vm->LoadNativeLibrary(filename.c_str(), classLoader, detail);
diff --git a/src/native/java_lang_String.cc b/src/native/java_lang_String.cc
index 96fcf96..bfdc31a 100644
--- a/src/native/java_lang_String.cc
+++ b/src/native/java_lang_String.cc
@@ -16,7 +16,7 @@
 
 #include "jni_internal.h"
 #include "object.h"
-#include "scoped_jni_thread_state.h"
+#include "scoped_thread_state_change.h"
 
 #ifdef HAVE__MEMCMP16
 // "count" is in 16-bit units.
@@ -36,9 +36,9 @@
 namespace art {
 
 static jint String_compareTo(JNIEnv* env, jobject javaThis, jobject javaRhs) {
-  ScopedJniThreadState ts(env);
-  String* lhs = ts.Decode<String*>(javaThis);
-  String* rhs = ts.Decode<String*>(javaRhs);
+  ScopedObjectAccess soa(env);
+  String* lhs = soa.Decode<String*>(javaThis);
+  String* rhs = soa.Decode<String*>(javaRhs);
 
   if (rhs == NULL) {
     Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", "rhs == null");
@@ -70,11 +70,11 @@
 }
 
 static jint String_fastIndexOf(JNIEnv* env, jobject java_this, jint ch, jint start) {
-  ScopedJniThreadState ts(env);
+  ScopedObjectAccess soa(env);
   // This method does not handle supplementary characters. They're dealt with in managed code.
   DCHECK_LE(ch, 0xffff);
 
-  String* s = ts.Decode<String*>(java_this);
+  String* s = soa.Decode<String*>(java_this);
 
   jint count = s->GetLength();
   if (start < 0) {
@@ -96,10 +96,10 @@
 }
 
 static jstring String_intern(JNIEnv* env, jobject javaThis) {
-  ScopedJniThreadState ts(env);
-  String* s = ts.Decode<String*>(javaThis);
+  ScopedObjectAccess soa(env);
+  String* s = soa.Decode<String*>(javaThis);
   String* result = s->Intern();
-  return ts.AddLocalReference<jstring>(result);
+  return soa.AddLocalReference<jstring>(result);
 }
 
 static JNINativeMethod gMethods[] = {
diff --git a/src/native/java_lang_System.cc b/src/native/java_lang_System.cc
index 76ac670..f4fe6ca 100644
--- a/src/native/java_lang_System.cc
+++ b/src/native/java_lang_System.cc
@@ -16,7 +16,7 @@
 
 #include "jni_internal.h"
 #include "object.h"
-#include "scoped_jni_thread_state.h"
+#include "scoped_thread_state_change.h"
 
 /*
  * We make guarantees about the atomicity of accesses to primitive
@@ -101,28 +101,29 @@
 
 namespace art {
 
-static void ThrowArrayStoreException_NotAnArray(const char* identifier, Object* array) {
+static void ThrowArrayStoreException_NotAnArray(const char* identifier, Object* array)
+    SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
   std::string actualType(PrettyTypeOf(array));
   Thread::Current()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
       "%s of type %s is not an array", identifier, actualType.c_str());
 }
 
 static void System_arraycopy(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, jobject javaDst, jint dstPos, jint length) {
-  ScopedJniThreadState ts(env);
+  ScopedObjectAccess soa(env);
 
   // Null pointer checks.
   if (javaSrc == NULL) {
-    ts.Self()->ThrowNewException("Ljava/lang/NullPointerException;", "src == null");
+    soa.Self()->ThrowNewException("Ljava/lang/NullPointerException;", "src == null");
     return;
   }
   if (javaDst == NULL) {
-    ts.Self()->ThrowNewException("Ljava/lang/NullPointerException;", "dst == null");
+    soa.Self()->ThrowNewException("Ljava/lang/NullPointerException;", "dst == null");
     return;
   }
 
   // Make sure source and destination are both arrays.
-  Object* srcObject = ts.Decode<Object*>(javaSrc);
-  Object* dstObject = ts.Decode<Object*>(javaDst);
+  Object* srcObject = soa.Decode<Object*>(javaSrc);
+  Object* dstObject = soa.Decode<Object*>(javaDst);
   if (!srcObject->IsArrayInstance()) {
     ThrowArrayStoreException_NotAnArray("source", srcObject);
     return;
@@ -138,7 +139,7 @@
 
   // Bounds checking.
   if (srcPos < 0 || dstPos < 0 || length < 0 || srcPos > srcArray->GetLength() - length || dstPos > dstArray->GetLength() - length) {
-    ts.Self()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
+    soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
         "src.length=%d srcPos=%d dst.length=%d dstPos=%d length=%d",
         srcArray->GetLength(), srcPos, dstArray->GetLength(), dstPos, length);
     return;
@@ -150,7 +151,7 @@
     if (srcComponentType->IsPrimitive() != dstComponentType->IsPrimitive() || srcComponentType != dstComponentType) {
       std::string srcType(PrettyTypeOf(srcArray));
       std::string dstType(PrettyTypeOf(dstArray));
-      ts.Self()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
+      soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
           "Incompatible types: src=%s, dst=%s", srcType.c_str(), dstType.c_str());
       return;
     }
@@ -233,7 +234,7 @@
   if (i != length) {
     std::string actualSrcType(PrettyTypeOf(o));
     std::string dstType(PrettyTypeOf(dstArray));
-    ts.Self()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
+    soa.Self()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
         "source[%d] of type %s cannot be stored in destination array of type %s",
         srcPos + i, actualSrcType.c_str(), dstType.c_str());
     return;
@@ -241,9 +242,9 @@
 }
 
 static jint System_identityHashCode(JNIEnv* env, jclass, jobject javaObject) {
-  ScopedJniThreadState ts(env);
-  Object* o = ts.Decode<Object*>(javaObject);
-  return static_cast<jint>(reinterpret_cast<uintptr_t>(o));
+  ScopedObjectAccess soa(env);
+  Object* o = soa.Decode<Object*>(javaObject);
+  return static_cast<jint>(o->IdentityHashCode());
 }
 
 static JNINativeMethod gMethods[] = {
diff --git a/src/native/java_lang_Thread.cc b/src/native/java_lang_Thread.cc
index 626255e..65042e4 100644
--- a/src/native/java_lang_Thread.cc
+++ b/src/native/java_lang_Thread.cc
@@ -17,8 +17,7 @@
 #include "debugger.h"
 #include "jni_internal.h"
 #include "object.h"
-#include "scoped_jni_thread_state.h"
-#include "scoped_thread_list_lock.h"
+#include "scoped_thread_state_change.h"
 #include "ScopedUtfChars.h"
 #include "thread.h"
 #include "thread_list.h"
@@ -26,19 +25,18 @@
 namespace art {
 
 static jobject Thread_currentThread(JNIEnv* env, jclass) {
-  ScopedJniThreadState ts(env);
-  return ts.AddLocalReference<jobject>(ts.Self()->GetPeer());
+  ScopedObjectAccess soa(env);
+  return soa.AddLocalReference<jobject>(soa.Self()->GetPeer());
 }
 
 static jboolean Thread_interrupted(JNIEnv* env, jclass) {
-  ScopedJniThreadState ts(env, kNative);  // Doesn't touch objects, so keep in native state.
-  return ts.Self()->Interrupted();
+  return reinterpret_cast<JNIEnvExt*>(env)->self->Interrupted() ? JNI_TRUE : JNI_FALSE;
 }
 
 static jboolean Thread_isInterrupted(JNIEnv* env, jobject java_thread) {
-  ScopedJniThreadState ts(env);
-  ScopedThreadListLock thread_list_lock;
-  Thread* thread = Thread::FromManagedThread(ts, java_thread);
+  ScopedObjectAccess soa(env);
+  MutexLock mu(*GlobalSynchronization::thread_list_lock_);
+  Thread* thread = Thread::FromManagedThread(soa, java_thread);
   return (thread != NULL) ? thread->IsInterrupted() : JNI_FALSE;
 }
 
@@ -56,53 +54,62 @@
   const jint kJavaTimedWaiting = 4;
   const jint kJavaTerminated = 5;
 
-  ScopedJniThreadState ts(env);
+  ScopedObjectAccess soa(env);
   ThreadState internal_thread_state = (has_been_started ? kTerminated : kStarting);
-  ScopedThreadListLock thread_list_lock;
-  Thread* thread = Thread::FromManagedThread(ts, java_thread);
+  MutexLock mu(*GlobalSynchronization::thread_list_lock_);
+  Thread* thread = Thread::FromManagedThread(soa, java_thread);
   if (thread != NULL) {
+    MutexLock mu(*GlobalSynchronization::thread_suspend_count_lock_);
     internal_thread_state = thread->GetState();
   }
   switch (internal_thread_state) {
-    case kTerminated:   return kJavaTerminated;
-    case kRunnable:     return kJavaRunnable;
-    case kTimedWaiting: return kJavaTimedWaiting;
-    case kBlocked:      return kJavaBlocked;
-    case kWaiting:      return kJavaWaiting;
-    case kStarting:     return kJavaNew;
-    case kNative:       return kJavaRunnable;
-    case kVmWait:       return kJavaWaiting;
-    case kSuspended:    return kJavaRunnable;
+    case kTerminated:                     return kJavaTerminated;
+    case kRunnable:                       return kJavaRunnable;
+    case kTimedWaiting:                   return kJavaTimedWaiting;
+    case kBlocked:                        return kJavaBlocked;
+    case kWaiting:                        return kJavaWaiting;
+    case kStarting:                       return kJavaNew;
+    case kNative:                         return kJavaRunnable;
+    case kWaitingForGcToComplete:         return kJavaWaiting;
+    case kWaitingPerformingGc:            return kJavaWaiting;
+    case kWaitingForDebuggerSend:         return kJavaWaiting;
+    case kWaitingForDebuggerToAttach:     return kJavaWaiting;
+    case kWaitingInMainDebuggerLoop:      return kJavaWaiting;
+    case kWaitingForDebuggerSuspension:   return kJavaWaiting;
+    case kWaitingForJniOnLoad:            return kJavaWaiting;
+    case kWaitingForSignalCatcherOutput:  return kJavaWaiting;
+    case kWaitingInMainSignalCatcherLoop: return kJavaWaiting;
+    case kSuspended:                      return kJavaRunnable;
     // Don't add a 'default' here so the compiler can spot incompatible enum changes.
   }
   return -1; // Unreachable.
 }
 
 static jboolean Thread_nativeHoldsLock(JNIEnv* env, jobject java_thread, jobject java_object) {
-  ScopedJniThreadState ts(env);
-  Object* object = ts.Decode<Object*>(java_object);
+  ScopedObjectAccess soa(env);
+  Object* object = soa.Decode<Object*>(java_object);
   if (object == NULL) {
     Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", "object == null");
     return JNI_FALSE;
   }
-  ScopedThreadListLock thread_list_lock;
-  Thread* thread = Thread::FromManagedThread(ts, java_thread);
+  MutexLock mu(*GlobalSynchronization::thread_list_lock_);
+  Thread* thread = Thread::FromManagedThread(soa, java_thread);
   return thread->HoldsLock(object);
 }
 
 static void Thread_nativeInterrupt(JNIEnv* env, jobject java_thread) {
-  ScopedJniThreadState ts(env);
-  ScopedThreadListLock thread_list_lock;
-  Thread* thread = Thread::FromManagedThread(ts, java_thread);
+  ScopedObjectAccess soa(env);
+  MutexLock mu(*GlobalSynchronization::thread_list_lock_);
+  Thread* thread = Thread::FromManagedThread(soa, java_thread);
   if (thread != NULL) {
     thread->Interrupt();
   }
 }
 
 static void Thread_nativeSetName(JNIEnv* env, jobject java_thread, jstring java_name) {
-  ScopedJniThreadState ts(env);
-  ScopedThreadListLock thread_list_lock;
-  Thread* thread = Thread::FromManagedThread(ts, java_thread);
+  ScopedObjectAccess soa(env);
+  MutexLock mu(*GlobalSynchronization::thread_list_lock_);
+  Thread* thread = Thread::FromManagedThread(soa, java_thread);
   if (thread == NULL) {
     return;
   }
@@ -119,9 +126,9 @@
  * threads at Thread.NORM_PRIORITY (5).
  */
 static void Thread_nativeSetPriority(JNIEnv* env, jobject java_thread, jint new_priority) {
-  ScopedJniThreadState ts(env);
-  ScopedThreadListLock thread_list_lock;
-  Thread* thread = Thread::FromManagedThread(ts, java_thread);
+  ScopedObjectAccess soa(env);
+  MutexLock mu(*GlobalSynchronization::thread_list_lock_);
+  Thread* thread = Thread::FromManagedThread(soa, java_thread);
   if (thread != NULL) {
     thread->SetNativePriority(new_priority);
   }
diff --git a/src/native/java_lang_Throwable.cc b/src/native/java_lang_Throwable.cc
index 1c59a34..332a130 100644
--- a/src/native/java_lang_Throwable.cc
+++ b/src/native/java_lang_Throwable.cc
@@ -15,14 +15,14 @@
  */
 
 #include "jni_internal.h"
-#include "scoped_jni_thread_state.h"
+#include "scoped_thread_state_change.h"
 #include "thread.h"
 
 namespace art {
 
 static jobject Throwable_nativeFillInStackTrace(JNIEnv* env, jclass) {
-  ScopedJniThreadState ts(env);
-  return ts.Self()->CreateInternalStackTrace(ts);
+  ScopedObjectAccess soa(env);
+  return soa.Self()->CreateInternalStackTrace(soa);
 }
 
 static jobjectArray Throwable_nativeGetStackTrace(JNIEnv* env, jclass, jobject javaStackState) {
diff --git a/src/native/java_lang_VMClassLoader.cc b/src/native/java_lang_VMClassLoader.cc
index 0689f74..4b5c31c 100644
--- a/src/native/java_lang_VMClassLoader.cc
+++ b/src/native/java_lang_VMClassLoader.cc
@@ -17,15 +17,15 @@
 #include "class_linker.h"
 #include "class_loader.h"
 #include "jni_internal.h"
-#include "scoped_jni_thread_state.h"
+#include "scoped_thread_state_change.h"
 #include "ScopedUtfChars.h"
 #include "zip_archive.h"
 
 namespace art {
 
 static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoader, jstring javaName) {
-  ScopedJniThreadState ts(env);
-  ClassLoader* loader = ts.Decode<ClassLoader*>(javaLoader);
+  ScopedObjectAccess soa(env);
+  ClassLoader* loader = soa.Decode<ClassLoader*>(javaLoader);
   ScopedUtfChars name(env, javaName);
   if (name.c_str() == NULL) {
     return NULL;
@@ -34,7 +34,7 @@
   std::string descriptor(DotToDescriptor(name.c_str()));
   Class* c = Runtime::Current()->GetClassLinker()->LookupClass(descriptor.c_str(), loader);
   if (c != NULL && c->IsResolved()) {
-    return ts.AddLocalReference<jclass>(c);
+    return soa.AddLocalReference<jclass>(c);
   } else {
     // Class wasn't resolved so it may be erroneous or not yet ready, force the caller to go into
     // the regular loadClass code.
diff --git a/src/native/java_lang_reflect_Array.cc b/src/native/java_lang_reflect_Array.cc
index 729312e..fa59750 100644
--- a/src/native/java_lang_reflect_Array.cc
+++ b/src/native/java_lang_reflect_Array.cc
@@ -18,13 +18,14 @@
 #include "jni_internal.h"
 #include "object.h"
 #include "object_utils.h"
-#include "scoped_jni_thread_state.h"
+#include "scoped_thread_state_change.h"
 
 namespace art {
 
 // Recursively create an array with multiple dimensions.  Elements may be
 // Objects or primitive types.
-static Array* CreateMultiArray(Class* array_class, int current_dimension, IntArray* dimensions) {
+static Array* CreateMultiArray(Class* array_class, int current_dimension, IntArray* dimensions)
+    SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
   int32_t array_length = dimensions->Get(current_dimension++);
   SirtRef<Array> new_array(Array::Alloc(array_class, array_length));
   if (new_array.get() == NULL) {
@@ -69,12 +70,12 @@
 // subtract pieces off.  Besides, we want to start with the outermost
 // piece and work our way in.
 static jobject Array_createMultiArray(JNIEnv* env, jclass, jclass javaElementClass, jobject javaDimArray) {
-  ScopedJniThreadState ts(env);
+  ScopedObjectAccess soa(env);
   DCHECK(javaElementClass != NULL);
-  Class* element_class = ts.Decode<Class*>(javaElementClass);
+  Class* element_class = soa.Decode<Class*>(javaElementClass);
   DCHECK(element_class->IsClass());
   DCHECK(javaDimArray != NULL);
-  Object* dimensions_obj = ts.Decode<Object*>(javaDimArray);
+  Object* dimensions_obj = soa.Decode<Object*>(javaDimArray);
   DCHECK(dimensions_obj->IsArrayInstance());
   DCHECK_STREQ(ClassHelper(dimensions_obj->GetClass()).GetDescriptor(), "[I");
   IntArray* dimensions_array = down_cast<IntArray*>(dimensions_obj);
@@ -90,7 +91,7 @@
   for (int i = 0; i < num_dimensions; i++) {
     int dimension = dimensions_array->Get(i);
     if (dimension < 0) {
-      ts.Self()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;",
+      soa.Self()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;",
           "Dimension %d: %d", i, dimension);
       return NULL;
     }
@@ -113,15 +114,15 @@
     CHECK(Thread::Current()->IsExceptionPending());
     return NULL;
   }
-  return ts.AddLocalReference<jobject>(new_array);
+  return soa.AddLocalReference<jobject>(new_array);
 }
 
 static jobject Array_createObjectArray(JNIEnv* env, jclass, jclass javaElementClass, jint length) {
-  ScopedJniThreadState ts(env);
+  ScopedObjectAccess soa(env);
   DCHECK(javaElementClass != NULL);
-  Class* element_class = ts.Decode<Class*>(javaElementClass);
+  Class* element_class = soa.Decode<Class*>(javaElementClass);
   if (length < 0) {
-    ts.Self()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", length);
+    soa.Self()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", length);
     return NULL;
   }
   std::string descriptor;
@@ -131,16 +132,16 @@
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   Class* array_class = class_linker->FindClass(descriptor.c_str(), element_class->GetClassLoader());
   if (array_class == NULL) {
-    CHECK(ts.Self()->IsExceptionPending());
+    CHECK(soa.Self()->IsExceptionPending());
     return NULL;
   }
   DCHECK(array_class->IsArrayClass());
   Array* new_array = Array::Alloc(array_class, length);
   if (new_array == NULL) {
-    CHECK(ts.Self()->IsExceptionPending());
+    CHECK(soa.Self()->IsExceptionPending());
     return NULL;
   }
-  return ts.AddLocalReference<jobject>(new_array);
+  return soa.AddLocalReference<jobject>(new_array);
 }
 
 static JNINativeMethod gMethods[] = {
diff --git a/src/native/java_lang_reflect_Constructor.cc b/src/native/java_lang_reflect_Constructor.cc
index 564d6db..a6bd450 100644
--- a/src/native/java_lang_reflect_Constructor.cc
+++ b/src/native/java_lang_reflect_Constructor.cc
@@ -19,7 +19,7 @@
 #include "object.h"
 #include "object_utils.h"
 #include "reflection.h"
-#include "scoped_jni_thread_state.h"
+#include "scoped_thread_state_change.h"
 
 namespace art {
 
@@ -31,17 +31,17 @@
  * with an interface, array, or primitive class.
  */
 static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs) {
-  ScopedJniThreadState ts(env);
-  Method* m = ts.Decode<Object*>(javaMethod)->AsMethod();
+  ScopedObjectAccess soa(env);
+  Method* m = soa.Decode<Object*>(javaMethod)->AsMethod();
   Class* c = m->GetDeclaringClass();
   if (c->IsAbstract()) {
-    ts.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
+    soa.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
         "Can't instantiate abstract class %s", PrettyDescriptor(c).c_str());
     return NULL;
   }
 
   if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) {
-    DCHECK(ts.Self()->IsExceptionPending());
+    DCHECK(soa.Self()->IsExceptionPending());
     return NULL;
   }
 
@@ -50,8 +50,8 @@
     return NULL;
   }
 
-  jobject javaReceiver = ts.AddLocalReference<jobject>(receiver);
-  InvokeMethod(ts, javaMethod, javaReceiver, javaArgs);
+  jobject javaReceiver = soa.AddLocalReference<jobject>(receiver);
+  InvokeMethod(soa, javaMethod, javaReceiver, javaArgs);
 
   // Constructors are ()V methods, so we shouldn't touch the result of InvokeMethod.
   return javaReceiver;
diff --git a/src/native/java_lang_reflect_Field.cc b/src/native/java_lang_reflect_Field.cc
index b2ede63..e764b25 100644
--- a/src/native/java_lang_reflect_Field.cc
+++ b/src/native/java_lang_reflect_Field.cc
@@ -19,14 +19,16 @@
 #include "object.h"
 #include "object_utils.h"
 #include "reflection.h"
-#include "scoped_jni_thread_state.h"
+#include "scoped_thread_state_change.h"
 
 namespace art {
 
-static bool GetFieldValue(const ScopedJniThreadState& ts, Object* o, Field* f, JValue& value,
-                          bool allow_references) {
+static bool GetFieldValue(const ScopedObjectAccess& soa, Object* o, Field* f,
+                          JValue& value, bool allow_references)
+    SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
   DCHECK_EQ(value.GetJ(), 0LL);
-  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(f->GetDeclaringClass(), true, true)) {
+  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(f->GetDeclaringClass(),
+                                                               true, true)) {
     return false;
   }
   switch (FieldHelper(f).GetTypeAsPrimitiveType()) {
@@ -65,18 +67,20 @@
     // Never okay.
     break;
   }
-  ts.Self()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
+  soa.Self()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
       "Not a primitive field: %s", PrettyField(f).c_str());
   return false;
 }
 
-static bool CheckReceiver(const ScopedJniThreadState& ts, jobject javaObj, Field* f, Object*& o) {
+static bool CheckReceiver(const ScopedObjectAccess& soa, jobject javaObj, Field* f,
+                          Object*& o)
+    SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
   if (f->IsStatic()) {
     o = NULL;
     return true;
   }
 
-  o = ts.Decode<Object*>(javaObj);
+  o = soa.Decode<Object*>(javaObj);
   Class* declaringClass = f->GetDeclaringClass();
   if (!VerifyObjectInClass(o, declaringClass)) {
     return false;
@@ -85,34 +89,34 @@
 }
 
 static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj) {
-  ScopedJniThreadState ts(env);
-  Field* f = ts.DecodeField(env->FromReflectedField(javaField));
+  ScopedObjectAccess soa(env);
+  Field* f = soa.DecodeField(env->FromReflectedField(javaField));
   Object* o = NULL;
-  if (!CheckReceiver(ts, javaObj, f, o)) {
+  if (!CheckReceiver(soa, javaObj, f, o)) {
     return NULL;
   }
 
   // Get the field's value, boxing if necessary.
   JValue value;
-  if (!GetFieldValue(ts, o, f, value, true)) {
+  if (!GetFieldValue(soa, o, f, value, true)) {
     return NULL;
   }
   BoxPrimitive(FieldHelper(f).GetTypeAsPrimitiveType(), value);
 
-  return ts.AddLocalReference<jobject>(value.GetL());
+  return soa.AddLocalReference<jobject>(value.GetL());
 }
 
 static JValue GetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, char dst_descriptor) {
-  ScopedJniThreadState ts(env);
-  Field* f = ts.DecodeField(env->FromReflectedField(javaField));
+  ScopedObjectAccess soa(env);
+  Field* f = soa.DecodeField(env->FromReflectedField(javaField));
   Object* o = NULL;
-  if (!CheckReceiver(ts, javaObj, f, o)) {
+  if (!CheckReceiver(soa, javaObj, f, o)) {
     return JValue();
   }
 
   // Read the value.
   JValue field_value;
-  if (!GetFieldValue(ts, o, f, field_value, false)) {
+  if (!GetFieldValue(soa, o, f, field_value, false)) {
     return JValue();
   }
 
@@ -158,8 +162,10 @@
   return GetPrimitiveField(env, javaField, javaObj, 'S').GetS();
 }
 
-static void SetFieldValue(Object* o, Field* f, const JValue& new_value, bool allow_references) {
-  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(f->GetDeclaringClass(), true, true)) {
+static void SetFieldValue(Object* o, Field* f, const JValue& new_value, bool allow_references)
+    SHARED_LOCKS_REQUIRED(GlobalSynchronization::mutator_lock_) {
+  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(f->GetDeclaringClass(),
+                                                               true, true)) {
     return;
   }
   switch (FieldHelper(f).GetTypeAsPrimitiveType()) {
@@ -208,11 +214,11 @@
 }
 
 static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject javaValue) {
-  ScopedJniThreadState ts(env);
-  Field* f = ts.DecodeField(env->FromReflectedField(javaField));
+  ScopedObjectAccess soa(env);
+  Field* f = soa.DecodeField(env->FromReflectedField(javaField));
 
   // Unbox the value, if necessary.
-  Object* boxed_value = ts.Decode<Object*>(javaValue);
+  Object* boxed_value = soa.Decode<Object*>(javaValue);
   JValue unboxed_value;
   if (!UnboxPrimitiveForField(boxed_value, FieldHelper(f).GetType(), unboxed_value, f)) {
     return;
@@ -220,7 +226,7 @@
 
   // Check that the receiver is non-null and an instance of the field's declaring class.
   Object* o = NULL;
-  if (!CheckReceiver(ts, javaObj, f, o)) {
+  if (!CheckReceiver(soa, javaObj, f, o)) {
     return;
   }
 
@@ -229,15 +235,15 @@
 
 static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, char src_descriptor,
                               const JValue& new_value) {
-  ScopedJniThreadState ts(env);
-  Field* f = ts.DecodeField(env->FromReflectedField(javaField));
+  ScopedObjectAccess soa(env);
+  Field* f = soa.DecodeField(env->FromReflectedField(javaField));
   Object* o = NULL;
-  if (!CheckReceiver(ts, javaObj, f, o)) {
+  if (!CheckReceiver(soa, javaObj, f, o)) {
     return;
   }
   FieldHelper fh(f);
   if (!fh.IsPrimitiveType()) {
-    ts.Self()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
+    soa.Self()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
         "Not a primitive field: %s", PrettyField(f).c_str());
     return;
   }
diff --git a/src/native/java_lang_reflect_Method.cc b/src/native/java_lang_reflect_Method.cc
index 2695822..2a6ee50 100644
--- a/src/native/java_lang_reflect_Method.cc
+++ b/src/native/java_lang_reflect_Method.cc
@@ -19,18 +19,18 @@
 #include "object.h"
 #include "object_utils.h"
 #include "reflection.h"
-#include "scoped_jni_thread_state.h"
+#include "scoped_thread_state_change.h"
 
 namespace art {
 
 static jobject Method_invoke(JNIEnv* env, jobject javaMethod, jobject javaReceiver, jobject javaArgs) {
-  ScopedJniThreadState ts(env);
-  return InvokeMethod(ts, javaMethod, javaReceiver, javaArgs);
+  ScopedObjectAccess soa(env);
+  return InvokeMethod(soa, javaMethod, javaReceiver, javaArgs);
 }
 
 static jobject Method_getExceptionTypesNative(JNIEnv* env, jobject javaMethod) {
-  ScopedJniThreadState ts(env);
-  Method* proxy_method = ts.Decode<Object*>(javaMethod)->AsMethod();
+  ScopedObjectAccess soa(env);
+  Method* proxy_method = soa.Decode<Object*>(javaMethod)->AsMethod();
   CHECK(proxy_method->GetDeclaringClass()->IsProxyClass());
   SynthesizedProxyClass* proxy_class =
       down_cast<SynthesizedProxyClass*>(proxy_method->GetDeclaringClass());
@@ -44,13 +44,13 @@
   }
   CHECK_NE(throws_index, -1);
   ObjectArray<Class>* declared_exceptions = proxy_class->GetThrows()->Get(throws_index);
-  return ts.AddLocalReference<jobject>(declared_exceptions->Clone());
+  return soa.AddLocalReference<jobject>(declared_exceptions->Clone());
 }
 
 static jobject Method_findOverriddenMethodNative(JNIEnv* env, jobject javaMethod) {
-  ScopedJniThreadState ts(env);
-  Method* method = ts.Decode<Object*>(javaMethod)->AsMethod();
-  return ts.AddLocalReference<jobject>(method->FindOverriddenMethod());
+  ScopedObjectAccess soa(env);
+  Method* method = soa.Decode<Object*>(javaMethod)->AsMethod();
+  return soa.AddLocalReference<jobject>(method->FindOverriddenMethod());
 }
 
 static JNINativeMethod gMethods[] = {
diff --git a/src/native/java_lang_reflect_Proxy.cc b/src/native/java_lang_reflect_Proxy.cc
index a1337a6..81e3f16 100644
--- a/src/native/java_lang_reflect_Proxy.cc
+++ b/src/native/java_lang_reflect_Proxy.cc
@@ -18,20 +18,20 @@
 #include "class_loader.h"
 #include "jni_internal.h"
 #include "object.h"
-#include "scoped_jni_thread_state.h"
+#include "scoped_thread_state_change.h"
 
 namespace art {
 
 static jclass Proxy_generateProxy(JNIEnv* env, jclass, jstring javaName, jobjectArray javaInterfaces, jobject javaLoader, jobjectArray javaMethods, jobjectArray javaThrows) {
-  ScopedJniThreadState ts(env);
-  String* name = ts.Decode<String*>(javaName);
-  ObjectArray<Class>* interfaces = ts.Decode<ObjectArray<Class>*>(javaInterfaces);
-  ClassLoader* loader = ts.Decode<ClassLoader*>(javaLoader);
-  ObjectArray<Method>* methods = ts.Decode<ObjectArray<Method>*>(javaMethods);
-  ObjectArray<ObjectArray<Class> >* throws = ts.Decode<ObjectArray<ObjectArray<Class> >*>(javaThrows);
+  ScopedObjectAccess soa(env);
+  String* name = soa.Decode<String*>(javaName);
+  ObjectArray<Class>* interfaces = soa.Decode<ObjectArray<Class>*>(javaInterfaces);
+  ClassLoader* loader = soa.Decode<ClassLoader*>(javaLoader);
+  ObjectArray<Method>* methods = soa.Decode<ObjectArray<Method>*>(javaMethods);
+  ObjectArray<ObjectArray<Class> >* throws = soa.Decode<ObjectArray<ObjectArray<Class> >*>(javaThrows);
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   Class* result = class_linker->CreateProxyClass(name, interfaces, loader, methods, throws);
-  return ts.AddLocalReference<jclass>(result);
+  return soa.AddLocalReference<jclass>(result);
 }
 
 static JNINativeMethod gMethods[] = {
diff --git a/src/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc b/src/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
index e3c31b0..fe95746 100644
--- a/src/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
+++ b/src/native/org_apache_harmony_dalvik_ddmc_DdmServer.cc
@@ -16,12 +16,14 @@
 
 #include "debugger.h"
 #include "logging.h"
+#include "scoped_thread_state_change.h"
 #include "ScopedPrimitiveArray.h"
 
 namespace art {
 
 static void DdmServer_nativeSendChunk(JNIEnv* env, jclass, jint type,
                                       jbyteArray javaData, jint offset, jint length) {
+  ScopedObjectAccess soa(env);
   ScopedByteArrayRO data(env, javaData);
   DCHECK_LE(offset + length, static_cast<int32_t>(data.size()));
   Dbg::DdmSendChunk(type, length, reinterpret_cast<const uint8_t*>(&data[offset]));
diff --git a/src/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/src/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
index 87d2b22..b14d6ff 100644
--- a/src/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
+++ b/src/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
@@ -17,9 +17,9 @@
 #include "debugger.h"
 #include "jni_internal.h"
 #include "logging.h"
-#include "scoped_heap_lock.h"
-#include "scoped_jni_thread_state.h"
-#include "scoped_thread_list_lock.h"
+#include "../mutex.h"  // Avoid pulling in icu4c's mutex.h
+#include "scoped_thread_state_change.h"
+#include "ScopedLocalRef.h"
 #include "ScopedPrimitiveArray.h"
 #include "stack.h"
 #include "thread_list.h"
@@ -30,7 +30,8 @@
   Dbg::SetAllocTrackingEnabled(enable);
 }
 
-static jbyteArray DdmVmInternal_getRecentAllocations(JNIEnv*, jclass) {
+static jbyteArray DdmVmInternal_getRecentAllocations(JNIEnv* env, jclass) {
+  ScopedObjectAccess soa(env);
   return Dbg::GetRecentAllocations();
 }
 
@@ -38,7 +39,7 @@
   return Dbg::IsAllocTrackingEnabled();
 }
 
-static Thread* FindThreadByThinLockId(uint32_t thin_lock_id) {
+static jobject FindThreadByThinLockId(JNIEnv* env, uint32_t thin_lock_id) {
   struct ThreadFinder {
     explicit ThreadFinder(uint32_t thin_lock_id) : thin_lock_id(thin_lock_id), thread(NULL) {
     }
@@ -54,8 +55,16 @@
     Thread* thread;
   };
   ThreadFinder finder(thin_lock_id);
-  Runtime::Current()->GetThreadList()->ForEach(ThreadFinder::Callback, &finder);
-  return finder.thread;
+  {
+    MutexLock mu(*GlobalSynchronization::thread_list_lock_);
+    Runtime::Current()->GetThreadList()->ForEach(ThreadFinder::Callback, &finder);
+  }
+  if (finder.thread != NULL) {
+    ScopedObjectAccess soa(env);
+    return soa.AddLocalReference<jobject>(finder.thread->GetPeer());
+  } else {
+    return NULL;
+  }
 }
 
 /*
@@ -63,15 +72,26 @@
  * NULL on failure, e.g. if the threadId couldn't be found.
  */
 static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint thin_lock_id) {
-  ScopedHeapLock heap_lock;
-  ScopedThreadListLock thread_list_lock;
-  Thread* thread = FindThreadByThinLockId(static_cast<uint32_t>(thin_lock_id));
-  if (thread == NULL) {
+  ScopedLocalRef<jobject> peer(env,
+                               FindThreadByThinLockId(env, static_cast<uint32_t>(thin_lock_id)));
+  if (peer.get() == NULL) {
     return NULL;
   }
-  ScopedJniThreadState ts(env);
-  jobject stack = GetThreadStack(ts, thread);
-  return (stack != NULL) ? Thread::InternalStackTraceToStackTraceElementArray(env, stack) : NULL;
+  bool timeout;
+  // Suspend thread to build stack trace.
+  Thread* thread = Thread::SuspendForDebugger(peer.get(), true, &timeout);
+  if (thread != NULL) {
+    jobject trace;
+    {
+      ScopedObjectAccess soa(env);
+      trace = thread->CreateInternalStackTrace(soa);
+    }
+    // Restart suspended thread.
+    Runtime::Current()->GetThreadList()->Resume(thread, true);
+    return Thread::InternalStackTraceToStackTraceElementArray(env, trace);
+  } else {
+    return NULL;
+  }
 }
 
 static void ThreadCountCallback(Thread*, void* context) {
@@ -109,7 +129,10 @@
 
   std::vector<uint8_t>& bytes = *reinterpret_cast<std::vector<uint8_t>*>(context);
   JDWP::Append4BE(bytes, t->GetThinLockId());
-  JDWP::Append1BE(bytes, t->GetState());
+  {
+    MutexLock mu(*GlobalSynchronization::thread_suspend_count_lock_);
+    JDWP::Append1BE(bytes, t->GetState());
+  }
   JDWP::Append4BE(bytes, t->GetTid());
   JDWP::Append4BE(bytes, utime);
   JDWP::Append4BE(bytes, stime);
@@ -119,7 +142,7 @@
 static jbyteArray DdmVmInternal_getThreadStats(JNIEnv* env, jclass) {
   std::vector<uint8_t> bytes;
   {
-    ScopedThreadListLock thread_list_lock;
+    MutexLock mu(*GlobalSynchronization::thread_list_lock_);
     ThreadList* thread_list = Runtime::Current()->GetThreadList();
 
     uint16_t thread_count = 0;
@@ -139,7 +162,8 @@
   return result;
 }
 
-static jint DdmVmInternal_heapInfoNotify(JNIEnv*, jclass, jint when) {
+static jint DdmVmInternal_heapInfoNotify(JNIEnv* env, jclass, jint when) {
+  ScopedObjectAccess soa(env);
   return Dbg::DdmHandleHpifChunk(static_cast<Dbg::HpifWhen>(when));
 }
 
diff --git a/src/native/sun_misc_Unsafe.cc b/src/native/sun_misc_Unsafe.cc
index dfddd86..282731d 100644
--- a/src/native/sun_misc_Unsafe.cc
+++ b/src/native/sun_misc_Unsafe.cc
@@ -16,34 +16,34 @@
 
 #include "jni_internal.h"
 #include "object.h"
-#include "scoped_jni_thread_state.h"
+#include "scoped_thread_state_change.h"
 
 namespace art {
 
 static jlong Unsafe_objectFieldOffset0(JNIEnv* env, jclass, jobject javaField) {
   // TODO: move to Java code
   jfieldID fid = env->FromReflectedField(javaField);
-  ScopedJniThreadState ts(env);
-  Field* field = ts.DecodeField(fid);
+  ScopedObjectAccess soa(env);
+  Field* field = soa.DecodeField(fid);
   return field->GetOffset().Int32Value();
 }
 
 static jint Unsafe_arrayBaseOffset0(JNIEnv* env, jclass, jclass javaArrayClass) {
   // TODO: move to Java code
-  ScopedJniThreadState ts(env);
-  Class* array_class = ts.Decode<Class*>(javaArrayClass);
+  ScopedObjectAccess soa(env);
+  Class* array_class = soa.Decode<Class*>(javaArrayClass);
   return Array::DataOffset(array_class->GetComponentSize()).Int32Value();
 }
 
 static jint Unsafe_arrayIndexScale0(JNIEnv* env, jclass, jclass javaClass) {
-  ScopedJniThreadState ts(env);
-  Class* c = ts.Decode<Class*>(javaClass);
+  ScopedObjectAccess soa(env);
+  Class* c = soa.Decode<Class*>(javaClass);
   return c->GetComponentSize();
 }
 
 static jboolean Unsafe_compareAndSwapInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint expectedValue, jint newValue) {
-  ScopedJniThreadState ts(env);
-  Object* obj = ts.Decode<Object*>(javaObj);
+  ScopedObjectAccess soa(env);
+  Object* obj = soa.Decode<Object*>(javaObj);
   byte* raw_addr = reinterpret_cast<byte*>(obj) + offset;
   volatile int32_t* address = reinterpret_cast<volatile int32_t*>(raw_addr);
   // Note: android_atomic_release_cas() returns 0 on success, not failure.
@@ -52,8 +52,8 @@
 }
 
 static jboolean Unsafe_compareAndSwapLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong expectedValue, jlong newValue) {
-  ScopedJniThreadState ts(env);
-  Object* obj = ts.Decode<Object*>(javaObj);
+  ScopedObjectAccess soa(env);
+  Object* obj = soa.Decode<Object*>(javaObj);
   byte* raw_addr = reinterpret_cast<byte*>(obj) + offset;
   volatile int64_t* address = reinterpret_cast<volatile int64_t*>(raw_addr);
   // Note: android_atomic_cmpxchg() returns 0 on success, not failure.
@@ -62,10 +62,10 @@
 }
 
 static jboolean Unsafe_compareAndSwapObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaExpectedValue, jobject javaNewValue) {
-  ScopedJniThreadState ts(env);
-  Object* obj = ts.Decode<Object*>(javaObj);
-  Object* expectedValue = ts.Decode<Object*>(javaExpectedValue);
-  Object* newValue = ts.Decode<Object*>(javaNewValue);
+  ScopedObjectAccess soa(env);
+  Object* obj = soa.Decode<Object*>(javaObj);
+  Object* expectedValue = soa.Decode<Object*>(javaExpectedValue);
+  Object* newValue = soa.Decode<Object*>(javaNewValue);
   byte* raw_addr = reinterpret_cast<byte*>(obj) + offset;
   int32_t* address = reinterpret_cast<int32_t*>(raw_addr);
   // Note: android_atomic_cmpxchg() returns 0 on success, not failure.
@@ -78,105 +78,105 @@
 }
 
 static jint Unsafe_getInt(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-  ScopedJniThreadState ts(env);
-  Object* obj = ts.Decode<Object*>(javaObj);
+  ScopedObjectAccess soa(env);
+  Object* obj = soa.Decode<Object*>(javaObj);
   return obj->GetField32(MemberOffset(offset), false);
 }
 
 static jint Unsafe_getIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-  ScopedJniThreadState ts(env);
-  Object* obj = ts.Decode<Object*>(javaObj);
+  ScopedObjectAccess soa(env);
+  Object* obj = soa.Decode<Object*>(javaObj);
   byte* raw_addr = reinterpret_cast<byte*>(obj) + offset;
   volatile int32_t* address = reinterpret_cast<volatile int32_t*>(raw_addr);
   return android_atomic_acquire_load(address);
 }
 
 static void Unsafe_putInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) {
-  ScopedJniThreadState ts(env);
-  Object* obj = ts.Decode<Object*>(javaObj);
+  ScopedObjectAccess soa(env);
+  Object* obj = soa.Decode<Object*>(javaObj);
   obj->SetField32(MemberOffset(offset), newValue, false);
 }
 
 static void Unsafe_putIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) {
-  ScopedJniThreadState ts(env);
-  Object* obj = ts.Decode<Object*>(javaObj);
+  ScopedObjectAccess soa(env);
+  Object* obj = soa.Decode<Object*>(javaObj);
   byte* raw_addr = reinterpret_cast<byte*>(obj) + offset;
   volatile int32_t* address = reinterpret_cast<volatile int32_t*>(raw_addr);
   android_atomic_release_store(newValue, address);
 }
 
 static void Unsafe_putOrderedInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) {
-  ScopedJniThreadState ts(env);
-  Object* obj = ts.Decode<Object*>(javaObj);
+  ScopedObjectAccess soa(env);
+  Object* obj = soa.Decode<Object*>(javaObj);
   ANDROID_MEMBAR_STORE();
   obj->SetField32(MemberOffset(offset), newValue, false);
 }
 
 static jlong Unsafe_getLong(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-  ScopedJniThreadState ts(env);
-  Object* obj = ts.Decode<Object*>(javaObj);
+  ScopedObjectAccess soa(env);
+  Object* obj = soa.Decode<Object*>(javaObj);
   byte* raw_addr = reinterpret_cast<byte*>(obj) + offset;
   int64_t* address = reinterpret_cast<int64_t*>(raw_addr);
   return *address;
 }
 
 static jlong Unsafe_getLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-  ScopedJniThreadState ts(env);
-  Object* obj = ts.Decode<Object*>(javaObj);
+  ScopedObjectAccess soa(env);
+  Object* obj = soa.Decode<Object*>(javaObj);
   return obj->GetField64(MemberOffset(offset), true);
 }
 
 static void Unsafe_putLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) {
-  ScopedJniThreadState ts(env);
-  Object* obj = ts.Decode<Object*>(javaObj);
+  ScopedObjectAccess soa(env);
+  Object* obj = soa.Decode<Object*>(javaObj);
   obj->SetField64(MemberOffset(offset), newValue, false);
 }
 
 static void Unsafe_putLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) {
-  ScopedJniThreadState ts(env);
-  Object* obj = ts.Decode<Object*>(javaObj);
+  ScopedObjectAccess soa(env);
+  Object* obj = soa.Decode<Object*>(javaObj);
   obj->SetField64(MemberOffset(offset), newValue, true);
 }
 
 static void Unsafe_putOrderedLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) {
-  ScopedJniThreadState ts(env);
-  Object* obj = ts.Decode<Object*>(javaObj);
+  ScopedObjectAccess soa(env);
+  Object* obj = soa.Decode<Object*>(javaObj);
   ANDROID_MEMBAR_STORE();
   obj->SetField64(MemberOffset(offset), newValue, false);
 }
 
 static jobject Unsafe_getObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-  ScopedJniThreadState ts(env);
-  Object* obj = ts.Decode<Object*>(javaObj);
+  ScopedObjectAccess soa(env);
+  Object* obj = soa.Decode<Object*>(javaObj);
   Object* value = obj->GetFieldObject<Object*>(MemberOffset(offset), true);
-  return ts.AddLocalReference<jobject>(value);
+  return soa.AddLocalReference<jobject>(value);
 }
 
 static jobject Unsafe_getObject(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-  ScopedJniThreadState ts(env);
-  Object* obj = ts.Decode<Object*>(javaObj);
+  ScopedObjectAccess soa(env);
+  Object* obj = soa.Decode<Object*>(javaObj);
   Object* value = obj->GetFieldObject<Object*>(MemberOffset(offset), false);
-  return ts.AddLocalReference<jobject>(value);
+  return soa.AddLocalReference<jobject>(value);
 }
 
 static void Unsafe_putObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) {
-  ScopedJniThreadState ts(env);
-  Object* obj = ts.Decode<Object*>(javaObj);
-  Object* newValue = ts.Decode<Object*>(javaNewValue);
+  ScopedObjectAccess soa(env);
+  Object* obj = soa.Decode<Object*>(javaObj);
+  Object* newValue = soa.Decode<Object*>(javaNewValue);
   obj->SetFieldObject(MemberOffset(offset), newValue, false);
 }
 
 static void Unsafe_putObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) {
-  ScopedJniThreadState ts(env);
-  Object* obj = ts.Decode<Object*>(javaObj);
-  Object* newValue = ts.Decode<Object*>(javaNewValue);
+  ScopedObjectAccess soa(env);
+  Object* obj = soa.Decode<Object*>(javaObj);
+  Object* newValue = soa.Decode<Object*>(javaNewValue);
   obj->SetFieldObject(MemberOffset(offset), newValue, true);
 }
 
 static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) {
-  ScopedJniThreadState ts(env);
-  Object* obj = ts.Decode<Object*>(javaObj);
-  Object* newValue = ts.Decode<Object*>(javaNewValue);
+  ScopedObjectAccess soa(env);
+  Object* obj = soa.Decode<Object*>(javaObj);
+  Object* newValue = soa.Decode<Object*>(javaNewValue);
   ANDROID_MEMBAR_STORE();
   obj->SetFieldObject(MemberOffset(offset), newValue, false);
 }