Increase use of ScopedJniThreadState.

Move the routines for changing Object* to jobject and vice-versa
(AddLocalReference and Decode) to ScopedJniThreadState to enforce use of
Object*s in the Runnable thread state. In the Runnable thread state
suspension is necessary before GC can take place.

Reduce use of const ClassLoader* as the code bottoms out in FindClass
and with a field assignment where the const is cast away (ie if we're
not going to enforce the const-ness we shouldn't pretend it is).

Refactor the Thread::Attach API so that we're not handling raw Objects on
unattached threads.

Remove some unreachable code.

Change-Id: I0fa969f49ee6a8f10752af74a6b0e04d46b4cd97
diff --git a/src/native/dalvik_system_DexFile.cc b/src/native/dalvik_system_DexFile.cc
index 89d7130..3bf0ea5 100644
--- a/src/native/dalvik_system_DexFile.cc
+++ b/src/native/dalvik_system_DexFile.cc
@@ -126,7 +126,7 @@
 
 static jclass DexFile_defineClassNative(JNIEnv* env, jclass, jstring javaName, jobject javaLoader,
                                         jint cookie) {
-  ScopedJniThreadState tsc(env);
+  ScopedJniThreadState ts(env);
   const DexFile* dex_file = toDexFile(cookie);
   if (dex_file == NULL) {
     return NULL;
@@ -142,10 +142,10 @@
   }
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   class_linker->RegisterDexFile(*dex_file);
-  Object* class_loader_object = Decode<Object*>(env, javaLoader);
+  Object* class_loader_object = ts.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 AddLocalReference<jclass>(env, result);
+  return ts.AddLocalReference<jclass>(result);
 }
 
 static jobjectArray DexFile_getClassNameList(JNIEnv* env, jclass, jint cookie) {
diff --git a/src/native/dalvik_system_VMDebug.cc b/src/native/dalvik_system_VMDebug.cc
index 9b10cda..70067fe 100644
--- a/src/native/dalvik_system_VMDebug.cc
+++ b/src/native/dalvik_system_VMDebug.cc
@@ -22,6 +22,7 @@
 #include "hprof/hprof.h"
 #include "jni_internal.h"
 #include "ScopedUtfChars.h"
+#include "scoped_jni_thread_state.h"
 #include "toStringArray.h"
 #include "trace.h"
 
@@ -204,7 +205,8 @@
 }
 
 static jlong VMDebug_countInstancesOfClass(JNIEnv* env, jclass, jclass javaClass, jboolean countAssignable) {
-  Class* c = Decode<Class*>(env, javaClass);
+  ScopedJniThreadState ts(env);
+  Class* c = ts.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 09ca251..417ae5b 100644
--- a/src/native/dalvik_system_VMRuntime.cc
+++ b/src/native/dalvik_system_VMRuntime.cc
@@ -22,6 +22,7 @@
 #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 "space.h"
 #include "thread.h"
@@ -48,7 +49,7 @@
 }
 
 static jobject VMRuntime_newNonMovableArray(JNIEnv* env, jobject, jclass javaElementClass, jint length) {
-  ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
+  ScopedJniThreadState ts(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
@@ -56,7 +57,7 @@
   UNIMPLEMENTED(FATAL);
 #endif
 
-  Class* element_class = Decode<Class*>(env, javaElementClass);
+  Class* element_class = ts.Decode<Class*>(javaElementClass);
   if (element_class == NULL) {
     Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", "element class == null");
     return NULL;
@@ -75,15 +76,15 @@
   if (result == NULL) {
     return NULL;
   }
-  return AddLocalReference<jobject>(env, result);
+  return ts.AddLocalReference<jobject>(result);
 }
 
 static jlong VMRuntime_addressOf(JNIEnv* env, jobject, jobject javaArray) {
   if (javaArray == NULL) {  // Most likely allocation failed
     return 0;
   }
-  ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
-  Array* array = Decode<Array*>(env, javaArray);
+  ScopedJniThreadState ts(env);
+  Array* array = ts.Decode<Array*>(javaArray);
   if (!array->IsArrayInstance()) {
     Thread::Current()->ThrowNewException("Ljava/lang/IllegalArgumentException;", "not an array");
     return 0;
diff --git a/src/native/dalvik_system_VMStack.cc b/src/native/dalvik_system_VMStack.cc
index e3ecbd9..933a5d5 100644
--- a/src/native/dalvik_system_VMStack.cc
+++ b/src/native/dalvik_system_VMStack.cc
@@ -26,10 +26,11 @@
 namespace art {
 
 static jobject GetThreadStack(JNIEnv* env, jobject javaThread) {
+  ScopedJniThreadState ts(env);
   ScopedHeapLock heap_lock;
   ScopedThreadListLock thread_list_lock;
-  Thread* thread = Thread::FromManagedThread(env, javaThread);
-  return (thread != NULL) ? GetThreadStack(env, thread) : NULL;
+  Thread* thread = Thread::FromManagedThread(ts, javaThread);
+  return (thread != NULL) ? GetThreadStack(ts, thread) : NULL;
 }
 
 static jint VMStack_fillStackTraceElements(JNIEnv* env, jclass, jobject javaThread, jobjectArray javaSteArray) {
@@ -44,10 +45,10 @@
 
 // Returns the defining class loader of the caller's caller.
 static jobject VMStack_getCallingClassLoader(JNIEnv* env, jclass) {
-  ScopedJniThreadState ts(env, kNative);  // Not a state change out of native.
+  ScopedJniThreadState ts(env);
   NthCallerVisitor visitor(ts.Self()->GetManagedStack(), ts.Self()->GetTraceStack(), 2);
   visitor.WalkStack();
-  return AddLocalReference<jobject>(env, visitor.caller->GetDeclaringClass()->GetClassLoader());
+  return ts.AddLocalReference<jobject>(visitor.caller->GetDeclaringClass()->GetClassLoader());
 }
 
 static jobject VMStack_getClosestUserClassLoader(JNIEnv* env, jclass, jobject javaBootstrap, jobject javaSystem) {
@@ -72,20 +73,20 @@
     Object* class_loader;
   };
   ScopedJniThreadState ts(env);
-  Object* bootstrap = Decode<Object*>(env, javaBootstrap);
-  Object* system = Decode<Object*>(env, javaSystem);
+  Object* bootstrap = ts.Decode<Object*>(javaBootstrap);
+  Object* system = ts.Decode<Object*>(javaSystem);
   ClosestUserClassLoaderVisitor visitor(ts.Self()->GetManagedStack(), ts.Self()->GetTraceStack(),
                                         bootstrap, system);
   visitor.WalkStack();
-  return AddLocalReference<jobject>(env, visitor.class_loader);
+  return ts.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, kNative);  // Not a state change out of native.
+  ScopedJniThreadState ts(env);
   NthCallerVisitor visitor(ts.Self()->GetManagedStack(), ts.Self()->GetTraceStack(), 3);
   visitor.WalkStack();
-  return AddLocalReference<jclass>(env, visitor.caller->GetDeclaringClass());
+  return ts.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 99e3a26..ecab777 100644
--- a/src/native/java_lang_Class.cc
+++ b/src/native/java_lang_Class.cc
@@ -27,8 +27,8 @@
 
 namespace art {
 
-static Class* DecodeClass(JNIEnv* env, jobject java_class) {
-  Class* c = Decode<Class*>(env, java_class);
+static Class* DecodeClass(const ScopedJniThreadState& ts, jobject java_class) {
+  Class* c = ts.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 +39,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) {
-  ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
+  ScopedJniThreadState ts(env);
   ScopedUtfChars name(env, javaName);
   if (name.c_str() == NULL) {
     return NULL;
@@ -55,8 +55,7 @@
   }
 
   std::string descriptor(DotToDescriptor(name.c_str()));
-  Object* loader = Decode<Object*>(env, javaLoader);
-  ClassLoader* class_loader = down_cast<ClassLoader*>(loader);
+  ClassLoader* class_loader = ts.Decode<ClassLoader*>(javaLoader);
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   Class* c = class_linker->FindClass(descriptor.c_str(), class_loader);
   if (c == NULL) {
@@ -71,11 +70,12 @@
   if (initialize) {
     class_linker->EnsureInitialized(c, true, true);
   }
-  return AddLocalReference<jclass>(env, c);
+  return ts.AddLocalReference<jclass>(c);
 }
 
 static jint Class_getAnnotationDirectoryOffset(JNIEnv* env, jclass javaClass) {
-  Class* c = DecodeClass(env, javaClass);
+  ScopedJniThreadState ts(env);
+  Class* c = DecodeClass(ts, javaClass);
   if (c->IsPrimitive() || c->IsArrayClass() || c->IsProxyClass()) {
     return 0;  // primitive, array and proxy classes don't have class definitions
   }
@@ -88,12 +88,13 @@
 }
 
 template<typename T>
-static jobjectArray ToArray(JNIEnv* env, const char* array_class_name, const std::vector<T*>& objects) {
-  ScopedLocalRef<jclass> array_class(env, env->FindClass(array_class_name));
-  jobjectArray result = env->NewObjectArray(objects.size(), array_class.get(), NULL);
+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);
   for (size_t i = 0; i < objects.size(); ++i) {
-    ScopedLocalRef<jobject> object(env, AddLocalReference<jobject>(env, objects[i]));
-    env->SetObjectArrayElement(result, i, object.get());
+    ScopedLocalRef<jobject> object(ts.Env(), ts.AddLocalReference<jobject>(objects[i]));
+    ts.Env()->SetObjectArrayElement(result, i, object.get());
   }
   return result;
 }
@@ -109,11 +110,8 @@
 }
 
 static jobjectArray Class_getDeclaredConstructors(JNIEnv* env, jclass javaClass, jboolean publicOnly) {
-  Class* c = DecodeClass(env, javaClass);
-  if (c == NULL) {
-    return NULL;
-  }
-
+  ScopedJniThreadState ts(env);
+  Class* c = DecodeClass(ts, javaClass);
   std::vector<Method*> constructors;
   for (size_t i = 0; i < c->NumDirectMethods(); ++i) {
     Method* m = c->GetDirectMethod(i);
@@ -122,7 +120,7 @@
     }
   }
 
-  return ToArray(env, "java/lang/reflect/Constructor", constructors);
+  return ToArray(ts, "java/lang/reflect/Constructor", constructors);
 }
 
 static bool IsVisibleField(Field* f, bool public_only) {
@@ -133,12 +131,8 @@
 }
 
 static jobjectArray Class_getDeclaredFields(JNIEnv* env, jclass javaClass, jboolean publicOnly) {
-  ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
-  Class* c = DecodeClass(env, javaClass);
-  if (c == NULL) {
-    return NULL;
-  }
-
+  ScopedJniThreadState ts(env);
+  Class* c = DecodeClass(ts, javaClass);
   std::vector<Field*> fields;
   FieldHelper fh;
   for (size_t i = 0; i < c->NumInstanceFields(); ++i) {
@@ -170,7 +164,7 @@
     }
   }
 
-  return ToArray(env, "java/lang/reflect/Field", fields);
+  return ToArray(ts, "java/lang/reflect/Field", fields);
 }
 
 static bool IsVisibleMethod(Method* m, bool public_only) {
@@ -187,8 +181,8 @@
 }
 
 static jobjectArray Class_getDeclaredMethods(JNIEnv* env, jclass javaClass, jboolean publicOnly) {
-  ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
-  Class* c = DecodeClass(env, javaClass);
+  ScopedJniThreadState ts(env);
+  Class* c = DecodeClass(ts, javaClass);
   if (c == NULL) {
     return NULL;
   }
@@ -224,11 +218,12 @@
     }
   }
 
-  return ToArray(env, "java/lang/reflect/Method", methods);
+  return ToArray(ts, "java/lang/reflect/Method", methods);
 }
 
 static jobject Class_getDex(JNIEnv* env, jobject javaClass) {
-  Class* c = DecodeClass(env, javaClass);
+  ScopedJniThreadState ts(env);
+  Class* c = DecodeClass(ts, javaClass);
 
   DexCache* dex_cache = c->GetDexCache();
   if (dex_cache == NULL) {
@@ -287,13 +282,10 @@
 
 static jobject Class_getDeclaredConstructorOrMethod(JNIEnv* env, jclass javaClass, jstring javaName,
                                                     jobjectArray javaArgs) {
-  Class* c = DecodeClass(env, javaClass);
-  if (c == NULL) {
-    return NULL;
-  }
-
-  std::string name(Decode<String*>(env, javaName)->ToModifiedUtf8());
-  ObjectArray<Class>* arg_array = Decode<ObjectArray<Class>*>(env, 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);
 
   Method* m = FindConstructorOrMethodInArray(c->GetDirectMethods(), name, arg_array);
   if (m == NULL) {
@@ -301,7 +293,7 @@
   }
 
   if (m != NULL) {
-    return AddLocalReference<jobject>(env, m);
+    return ts.AddLocalReference<jobject>(m);
   } else {
     return NULL;
   }
@@ -309,12 +301,8 @@
 
 static jobject Class_getDeclaredFieldNative(JNIEnv* env, jclass java_class, jobject jname) {
   ScopedJniThreadState ts(env);
-  Class* c = DecodeClass(env, java_class);
-  if (c == NULL) {
-    return NULL;
-  }
-
-  String* name = Decode<String*>(env, jname);
+  Class* c = DecodeClass(ts, java_class);
+  String* name = ts.Decode<String*>(jname);
   DCHECK(name->GetClass()->IsStringClass());
 
   FieldHelper fh;
@@ -326,7 +314,7 @@
         DCHECK(env->ExceptionOccurred());
         return NULL;
       }
-      return AddLocalReference<jclass>(env, f);
+      return ts.AddLocalReference<jclass>(f);
     }
   }
   for (size_t i = 0; i < c->NumStaticFields(); ++i) {
@@ -337,7 +325,7 @@
         DCHECK(env->ExceptionOccurred());
         return NULL;
       }
-      return AddLocalReference<jclass>(env, f);
+      return ts.AddLocalReference<jclass>(f);
     }
   }
   return NULL;
@@ -345,20 +333,20 @@
 
 static jstring Class_getNameNative(JNIEnv* env, jobject javaThis) {
   ScopedJniThreadState ts(env);
-  Class* c = DecodeClass(env, javaThis);
-  return AddLocalReference<jstring>(env, c->ComputeName());
+  Class* c = DecodeClass(ts, javaThis);
+  return ts.AddLocalReference<jstring>(c->ComputeName());
 }
 
 static jobjectArray Class_getProxyInterfaces(JNIEnv* env, jobject javaThis) {
   ScopedJniThreadState ts(env);
-  SynthesizedProxyClass* c = down_cast<SynthesizedProxyClass*>(DecodeClass(env, javaThis));
-  return AddLocalReference<jobjectArray>(env, c->GetInterfaces()->Clone());
+  SynthesizedProxyClass* c = down_cast<SynthesizedProxyClass*>(DecodeClass(ts, javaThis));
+  return ts.AddLocalReference<jobjectArray>(c->GetInterfaces()->Clone());
 }
 
 static jboolean Class_isAssignableFrom(JNIEnv* env, jobject javaLhs, jclass javaRhs) {
   ScopedJniThreadState ts(env);
-  Class* lhs = DecodeClass(env, javaLhs);
-  Class* rhs = Decode<Class*>(env, javaRhs); // Can be null.
+  Class* lhs = DecodeClass(ts, javaLhs);
+  Class* rhs = ts.Decode<Class*>(javaRhs); // Can be null.
   if (rhs == NULL) {
     ts.Self()->ThrowNewException("Ljava/lang/NullPointerException;", "class == null");
     return JNI_FALSE;
@@ -397,7 +385,7 @@
 
 static jobject Class_newInstanceImpl(JNIEnv* env, jobject javaThis) {
   ScopedJniThreadState ts(env);
-  Class* c = DecodeClass(env, javaThis);
+  Class* c = DecodeClass(ts, javaThis);
   if (c->IsPrimitive() || c->IsInterface() || c->IsArrayClass() || c->IsAbstract()) {
     ts.Self()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
         "Class %s can not be instantiated", PrettyDescriptor(ClassHelper(c).GetDescriptor()).c_str());
@@ -451,8 +439,8 @@
   }
 
   // invoke constructor; unlike reflection calls, we don't wrap exceptions
-  jclass java_class = AddLocalReference<jclass>(env, c);
-  jmethodID mid = EncodeMethod(init);
+  jclass java_class = ts.AddLocalReference<jclass>(c);
+  jmethodID mid = ts.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 51e4581..d6b1bd6 100644
--- a/src/native/java_lang_Object.cc
+++ b/src/native/java_lang_Object.cc
@@ -16,27 +16,31 @@
 
 #include "jni_internal.h"
 #include "object.h"
+#include "scoped_jni_thread_state.h"
 
 namespace art {
 
 static jobject Object_internalClone(JNIEnv* env, jobject javaThis) {
-  ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
-  Object* o = Decode<Object*>(env, javaThis);
-  return AddLocalReference<jobject>(env, o->Clone());
+  ScopedJniThreadState ts(env);
+  Object* o = ts.Decode<Object*>(javaThis);
+  return ts.AddLocalReference<jobject>(o->Clone());
 }
 
 static void Object_notify(JNIEnv* env, jobject javaThis) {
-  Object* o = Decode<Object*>(env, javaThis);
+  ScopedJniThreadState ts(env);
+  Object* o = ts.Decode<Object*>(javaThis);
   o->Notify();
 }
 
 static void Object_notifyAll(JNIEnv* env, jobject javaThis) {
-  Object* o = Decode<Object*>(env, javaThis);
+  ScopedJniThreadState ts(env);
+  Object* o = ts.Decode<Object*>(javaThis);
   o->NotifyAll();
 }
 
 static void Object_wait(JNIEnv* env, jobject javaThis, jlong ms, jint ns) {
-  Object* o = Decode<Object*>(env, javaThis);
+  ScopedJniThreadState ts(env);
+  Object* o = ts.Decode<Object*>(javaThis);
   o->Wait(ms, ns);
 }
 
diff --git a/src/native/java_lang_Runtime.cc b/src/native/java_lang_Runtime.cc
index 3019e95..1b657b1 100644
--- a/src/native/java_lang_Runtime.cc
+++ b/src/native/java_lang_Runtime.cc
@@ -17,16 +17,18 @@
 #include <limits.h>
 #include <unistd.h>
 
+#include "class_loader.h"
 #include "heap.h"
 #include "jni_internal.h"
 #include "object.h"
 #include "runtime.h"
+#include "scoped_jni_thread_state.h"
 #include "ScopedUtfChars.h"
 
 namespace art {
 
-static void Runtime_gc(JNIEnv*, jclass) {
-  ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
+static void Runtime_gc(JNIEnv* env, jclass) {
+  ScopedJniThreadState ts(env);
   Runtime::Current()->GetHeap()->CollectGarbage(false);
 }
 
@@ -43,12 +45,13 @@
  * message on failure.
  */
 static jstring Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, jobject javaLoader) {
+  ScopedJniThreadState ts(env);
   ScopedUtfChars filename(env, javaFilename);
   if (filename.c_str() == NULL) {
     return NULL;
   }
 
-  ClassLoader* classLoader = Decode<ClassLoader*>(env, javaLoader);
+  ClassLoader* classLoader = ts.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 f8fb4a7..96fcf96 100644
--- a/src/native/java_lang_String.cc
+++ b/src/native/java_lang_String.cc
@@ -16,6 +16,7 @@
 
 #include "jni_internal.h"
 #include "object.h"
+#include "scoped_jni_thread_state.h"
 
 #ifdef HAVE__MEMCMP16
 // "count" is in 16-bit units.
@@ -35,9 +36,9 @@
 namespace art {
 
 static jint String_compareTo(JNIEnv* env, jobject javaThis, jobject javaRhs) {
-  ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
-  String* lhs = Decode<String*>(env, javaThis);
-  String* rhs = Decode<String*>(env, javaRhs);
+  ScopedJniThreadState ts(env);
+  String* lhs = ts.Decode<String*>(javaThis);
+  String* rhs = ts.Decode<String*>(javaRhs);
 
   if (rhs == NULL) {
     Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", "rhs == null");
@@ -69,10 +70,11 @@
 }
 
 static jint String_fastIndexOf(JNIEnv* env, jobject java_this, jint ch, jint start) {
+  ScopedJniThreadState ts(env);
   // This method does not handle supplementary characters. They're dealt with in managed code.
   DCHECK_LE(ch, 0xffff);
 
-  String* s = Decode<String*>(env, java_this);
+  String* s = ts.Decode<String*>(java_this);
 
   jint count = s->GetLength();
   if (start < 0) {
@@ -94,9 +96,10 @@
 }
 
 static jstring String_intern(JNIEnv* env, jobject javaThis) {
-  String* s = Decode<String*>(env, javaThis);
+  ScopedJniThreadState ts(env);
+  String* s = ts.Decode<String*>(javaThis);
   String* result = s->Intern();
-  return AddLocalReference<jstring>(env, result);
+  return ts.AddLocalReference<jstring>(result);
 }
 
 static JNINativeMethod gMethods[] = {
diff --git a/src/native/java_lang_System.cc b/src/native/java_lang_System.cc
index b0d1eec..76ac670 100644
--- a/src/native/java_lang_System.cc
+++ b/src/native/java_lang_System.cc
@@ -16,6 +16,7 @@
 
 #include "jni_internal.h"
 #include "object.h"
+#include "scoped_jni_thread_state.h"
 
 /*
  * We make guarantees about the atomicity of accesses to primitive
@@ -107,22 +108,21 @@
 }
 
 static void System_arraycopy(JNIEnv* env, jclass, jobject javaSrc, jint srcPos, jobject javaDst, jint dstPos, jint length) {
-  ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
-  Thread* self = Thread::Current();
+  ScopedJniThreadState ts(env);
 
   // Null pointer checks.
   if (javaSrc == NULL) {
-    self->ThrowNewException("Ljava/lang/NullPointerException;", "src == null");
+    ts.Self()->ThrowNewException("Ljava/lang/NullPointerException;", "src == null");
     return;
   }
   if (javaDst == NULL) {
-    self->ThrowNewException("Ljava/lang/NullPointerException;", "dst == null");
+    ts.Self()->ThrowNewException("Ljava/lang/NullPointerException;", "dst == null");
     return;
   }
 
   // Make sure source and destination are both arrays.
-  Object* srcObject = Decode<Object*>(env, javaSrc);
-  Object* dstObject = Decode<Object*>(env, javaDst);
+  Object* srcObject = ts.Decode<Object*>(javaSrc);
+  Object* dstObject = ts.Decode<Object*>(javaDst);
   if (!srcObject->IsArrayInstance()) {
     ThrowArrayStoreException_NotAnArray("source", srcObject);
     return;
@@ -138,7 +138,7 @@
 
   // Bounds checking.
   if (srcPos < 0 || dstPos < 0 || length < 0 || srcPos > srcArray->GetLength() - length || dstPos > dstArray->GetLength() - length) {
-    self->ThrowNewExceptionF("Ljava/lang/ArrayIndexOutOfBoundsException;",
+    ts.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 +150,7 @@
     if (srcComponentType->IsPrimitive() != dstComponentType->IsPrimitive() || srcComponentType != dstComponentType) {
       std::string srcType(PrettyTypeOf(srcArray));
       std::string dstType(PrettyTypeOf(dstArray));
-      self->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
+      ts.Self()->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
           "Incompatible types: src=%s, dst=%s", srcType.c_str(), dstType.c_str());
       return;
     }
@@ -233,7 +233,7 @@
   if (i != length) {
     std::string actualSrcType(PrettyTypeOf(o));
     std::string dstType(PrettyTypeOf(dstArray));
-    self->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
+    ts.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,7 +241,8 @@
 }
 
 static jint System_identityHashCode(JNIEnv* env, jclass, jobject javaObject) {
-  Object* o = Decode<Object*>(env, javaObject);
+  ScopedJniThreadState ts(env);
+  Object* o = ts.Decode<Object*>(javaObject);
   return static_cast<jint>(reinterpret_cast<uintptr_t>(o));
 }
 
diff --git a/src/native/java_lang_Thread.cc b/src/native/java_lang_Thread.cc
index ed95a6c..86b3a20 100644
--- a/src/native/java_lang_Thread.cc
+++ b/src/native/java_lang_Thread.cc
@@ -17,6 +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 "ScopedUtfChars.h"
 #include "thread.h"
@@ -25,22 +26,24 @@
 namespace art {
 
 static jobject Thread_currentThread(JNIEnv* env, jclass) {
-  return AddLocalReference<jobject>(env, Thread::Current()->GetPeer());
+  ScopedJniThreadState ts(env);
+  return ts.AddLocalReference<jobject>(ts.Self()->GetPeer());
 }
 
-static jboolean Thread_interrupted(JNIEnv*, jclass) {
-  return Thread::Current()->Interrupted();
+static jboolean Thread_interrupted(JNIEnv* env, jclass) {
+  ScopedJniThreadState ts(env, kNative);  // Doesn't touch objects, so keep in native state.
+  return ts.Self()->Interrupted();
 }
 
 static jboolean Thread_isInterrupted(JNIEnv* env, jobject java_thread) {
+  ScopedJniThreadState ts(env);
   ScopedThreadListLock thread_list_lock;
-  Thread* thread = Thread::FromManagedThread(env, java_thread);
+  Thread* thread = Thread::FromManagedThread(ts, java_thread);
   return (thread != NULL) ? thread->IsInterrupted() : JNI_FALSE;
 }
 
 static void Thread_nativeCreate(JNIEnv* env, jclass, jobject java_thread, jlong stack_size) {
-  Object* managedThread = Decode<Object*>(env, java_thread);
-  Thread::CreateNativeThread(managedThread, stack_size);
+  Thread::CreateNativeThread(env, java_thread, stack_size);
 }
 
 static jint Thread_nativeGetStatus(JNIEnv* env, jobject java_thread, jboolean has_been_started) {
@@ -52,9 +55,10 @@
   const jint kJavaTimedWaiting = 4;
   const jint kJavaTerminated = 5;
 
+  ScopedJniThreadState ts(env);
   ThreadState internal_thread_state = (has_been_started ? kTerminated : kStarting);
   ScopedThreadListLock thread_list_lock;
-  Thread* thread = Thread::FromManagedThread(env, java_thread);
+  Thread* thread = Thread::FromManagedThread(ts, java_thread);
   if (thread != NULL) {
     internal_thread_state = thread->GetState();
   }
@@ -74,28 +78,30 @@
 }
 
 static jboolean Thread_nativeHoldsLock(JNIEnv* env, jobject java_thread, jobject java_object) {
-  Object* object = Decode<Object*>(env, java_object);
+  ScopedJniThreadState ts(env);
+  Object* object = ts.Decode<Object*>(java_object);
   if (object == NULL) {
-    ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
     Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", "object == null");
     return JNI_FALSE;
   }
   ScopedThreadListLock thread_list_lock;
-  Thread* thread = Thread::FromManagedThread(env, java_thread);
+  Thread* thread = Thread::FromManagedThread(ts, 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(env, java_thread);
+  Thread* thread = Thread::FromManagedThread(ts, 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(env, java_thread);
+  Thread* thread = Thread::FromManagedThread(ts, java_thread);
   if (thread == NULL) {
     return;
   }
@@ -112,8 +118,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(env, java_thread);
+  Thread* thread = Thread::FromManagedThread(ts, 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 625a34b..1c59a34 100644
--- a/src/native/java_lang_Throwable.cc
+++ b/src/native/java_lang_Throwable.cc
@@ -15,13 +15,14 @@
  */
 
 #include "jni_internal.h"
+#include "scoped_jni_thread_state.h"
 #include "thread.h"
 
 namespace art {
 
 static jobject Throwable_nativeFillInStackTrace(JNIEnv* env, jclass) {
-  JNIEnvExt* env_ext = reinterpret_cast<JNIEnvExt*>(env);
-  return env_ext->self->CreateInternalStackTrace(env);
+  ScopedJniThreadState ts(env);
+  return ts.Self()->CreateInternalStackTrace(ts);
 }
 
 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 a976933..0689f74 100644
--- a/src/native/java_lang_VMClassLoader.cc
+++ b/src/native/java_lang_VMClassLoader.cc
@@ -15,14 +15,17 @@
  */
 
 #include "class_linker.h"
+#include "class_loader.h"
 #include "jni_internal.h"
+#include "scoped_jni_thread_state.h"
 #include "ScopedUtfChars.h"
 #include "zip_archive.h"
 
 namespace art {
 
 static jclass VMClassLoader_findLoadedClass(JNIEnv* env, jclass, jobject javaLoader, jstring javaName) {
-  ClassLoader* loader = Decode<ClassLoader*>(env, javaLoader);
+  ScopedJniThreadState ts(env);
+  ClassLoader* loader = ts.Decode<ClassLoader*>(javaLoader);
   ScopedUtfChars name(env, javaName);
   if (name.c_str() == NULL) {
     return NULL;
@@ -31,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 AddLocalReference<jclass>(env, c);
+    return ts.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 ea635d3..729312e 100644
--- a/src/native/java_lang_reflect_Array.cc
+++ b/src/native/java_lang_reflect_Array.cc
@@ -18,6 +18,7 @@
 #include "jni_internal.h"
 #include "object.h"
 #include "object_utils.h"
+#include "scoped_jni_thread_state.h"
 
 namespace art {
 
@@ -68,12 +69,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) {
-  ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
+  ScopedJniThreadState ts(env);
   DCHECK(javaElementClass != NULL);
-  Class* element_class = Decode<Class*>(env, javaElementClass);
+  Class* element_class = ts.Decode<Class*>(javaElementClass);
   DCHECK(element_class->IsClass());
   DCHECK(javaDimArray != NULL);
-  Object* dimensions_obj = Decode<Object*>(env, javaDimArray);
+  Object* dimensions_obj = ts.Decode<Object*>(javaDimArray);
   DCHECK(dimensions_obj->IsArrayInstance());
   DCHECK_STREQ(ClassHelper(dimensions_obj->GetClass()).GetDescriptor(), "[I");
   IntArray* dimensions_array = down_cast<IntArray*>(dimensions_obj);
@@ -89,7 +90,7 @@
   for (int i = 0; i < num_dimensions; i++) {
     int dimension = dimensions_array->Get(i);
     if (dimension < 0) {
-      Thread::Current()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;",
+      ts.Self()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;",
           "Dimension %d: %d", i, dimension);
       return NULL;
     }
@@ -112,15 +113,15 @@
     CHECK(Thread::Current()->IsExceptionPending());
     return NULL;
   }
-  return AddLocalReference<jobject>(env, new_array);
+  return ts.AddLocalReference<jobject>(new_array);
 }
 
 static jobject Array_createObjectArray(JNIEnv* env, jclass, jclass javaElementClass, jint length) {
-  ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
+  ScopedJniThreadState ts(env);
   DCHECK(javaElementClass != NULL);
-  Class* element_class = Decode<Class*>(env, javaElementClass);
+  Class* element_class = ts.Decode<Class*>(javaElementClass);
   if (length < 0) {
-    Thread::Current()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", length);
+    ts.Self()->ThrowNewExceptionF("Ljava/lang/NegativeArraySizeException;", "%d", length);
     return NULL;
   }
   std::string descriptor;
@@ -130,16 +131,16 @@
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   Class* array_class = class_linker->FindClass(descriptor.c_str(), element_class->GetClassLoader());
   if (array_class == NULL) {
-    CHECK(Thread::Current()->IsExceptionPending());
+    CHECK(ts.Self()->IsExceptionPending());
     return NULL;
   }
   DCHECK(array_class->IsArrayClass());
   Array* new_array = Array::Alloc(array_class, length);
   if (new_array == NULL) {
-    CHECK(Thread::Current()->IsExceptionPending());
+    CHECK(ts.Self()->IsExceptionPending());
     return NULL;
   }
-  return AddLocalReference<jobject>(env, new_array);
+  return ts.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 1094d06..564d6db 100644
--- a/src/native/java_lang_reflect_Constructor.cc
+++ b/src/native/java_lang_reflect_Constructor.cc
@@ -19,6 +19,7 @@
 #include "object.h"
 #include "object_utils.h"
 #include "reflection.h"
+#include "scoped_jni_thread_state.h"
 
 namespace art {
 
@@ -30,17 +31,17 @@
  * with an interface, array, or primitive class.
  */
 static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs) {
-  ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
-  Method* m = Decode<Object*>(env, javaMethod)->AsMethod();
+  ScopedJniThreadState ts(env);
+  Method* m = ts.Decode<Object*>(javaMethod)->AsMethod();
   Class* c = m->GetDeclaringClass();
   if (c->IsAbstract()) {
-    Thread::Current()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
+    ts.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(Thread::Current()->IsExceptionPending());
+    DCHECK(ts.Self()->IsExceptionPending());
     return NULL;
   }
 
@@ -49,8 +50,8 @@
     return NULL;
   }
 
-  jobject javaReceiver = AddLocalReference<jobject>(env, receiver);
-  InvokeMethod(env, javaMethod, javaReceiver, javaArgs);
+  jobject javaReceiver = ts.AddLocalReference<jobject>(receiver);
+  InvokeMethod(ts, 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 bd33c0e..b2ede63 100644
--- a/src/native/java_lang_reflect_Field.cc
+++ b/src/native/java_lang_reflect_Field.cc
@@ -19,12 +19,13 @@
 #include "object.h"
 #include "object_utils.h"
 #include "reflection.h"
+#include "scoped_jni_thread_state.h"
 
 namespace art {
 
-static bool GetFieldValue(Object* o, Field* f, JValue& value, bool allow_references) {
+static bool GetFieldValue(const ScopedJniThreadState& ts, Object* o, Field* f, JValue& value,
+                          bool allow_references) {
   DCHECK_EQ(value.GetJ(), 0LL);
-  ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
   if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(f->GetDeclaringClass(), true, true)) {
     return false;
   }
@@ -64,18 +65,18 @@
     // Never okay.
     break;
   }
-  Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
+  ts.Self()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
       "Not a primitive field: %s", PrettyField(f).c_str());
   return false;
 }
 
-static bool CheckReceiver(JNIEnv* env, jobject javaObj, Field* f, Object*& o) {
+static bool CheckReceiver(const ScopedJniThreadState& ts, jobject javaObj, Field* f, Object*& o) {
   if (f->IsStatic()) {
     o = NULL;
     return true;
   }
 
-  o = Decode<Object*>(env, javaObj);
+  o = ts.Decode<Object*>(javaObj);
   Class* declaringClass = f->GetDeclaringClass();
   if (!VerifyObjectInClass(o, declaringClass)) {
     return false;
@@ -84,32 +85,34 @@
 }
 
 static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj) {
-  Field* f = DecodeField(env->FromReflectedField(javaField));
+  ScopedJniThreadState ts(env);
+  Field* f = ts.DecodeField(env->FromReflectedField(javaField));
   Object* o = NULL;
-  if (!CheckReceiver(env, javaObj, f, o)) {
+  if (!CheckReceiver(ts, javaObj, f, o)) {
     return NULL;
   }
 
   // Get the field's value, boxing if necessary.
   JValue value;
-  if (!GetFieldValue(o, f, value, true)) {
+  if (!GetFieldValue(ts, o, f, value, true)) {
     return NULL;
   }
   BoxPrimitive(FieldHelper(f).GetTypeAsPrimitiveType(), value);
 
-  return AddLocalReference<jobject>(env, value.GetL());
+  return ts.AddLocalReference<jobject>(value.GetL());
 }
 
 static JValue GetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, char dst_descriptor) {
-  Field* f = DecodeField(env->FromReflectedField(javaField));
+  ScopedJniThreadState ts(env);
+  Field* f = ts.DecodeField(env->FromReflectedField(javaField));
   Object* o = NULL;
-  if (!CheckReceiver(env, javaObj, f, o)) {
+  if (!CheckReceiver(ts, javaObj, f, o)) {
     return JValue();
   }
 
   // Read the value.
   JValue field_value;
-  if (!GetFieldValue(o, f, field_value, false)) {
+  if (!GetFieldValue(ts, o, f, field_value, false)) {
     return JValue();
   }
 
@@ -205,11 +208,11 @@
 }
 
 static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject javaValue) {
-  ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
-  Field* f = DecodeField(env->FromReflectedField(javaField));
+  ScopedJniThreadState ts(env);
+  Field* f = ts.DecodeField(env->FromReflectedField(javaField));
 
   // Unbox the value, if necessary.
-  Object* boxed_value = Decode<Object*>(env, javaValue);
+  Object* boxed_value = ts.Decode<Object*>(javaValue);
   JValue unboxed_value;
   if (!UnboxPrimitiveForField(boxed_value, FieldHelper(f).GetType(), unboxed_value, f)) {
     return;
@@ -217,7 +220,7 @@
 
   // Check that the receiver is non-null and an instance of the field's declaring class.
   Object* o = NULL;
-  if (!CheckReceiver(env, javaObj, f, o)) {
+  if (!CheckReceiver(ts, javaObj, f, o)) {
     return;
   }
 
@@ -226,15 +229,15 @@
 
 static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, char src_descriptor,
                               const JValue& new_value) {
-  ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
-  Field* f = DecodeField(env->FromReflectedField(javaField));
+  ScopedJniThreadState ts(env);
+  Field* f = ts.DecodeField(env->FromReflectedField(javaField));
   Object* o = NULL;
-  if (!CheckReceiver(env, javaObj, f, o)) {
+  if (!CheckReceiver(ts, javaObj, f, o)) {
     return;
   }
   FieldHelper fh(f);
   if (!fh.IsPrimitiveType()) {
-    Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;",
+    ts.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 bf5c850..2695822 100644
--- a/src/native/java_lang_reflect_Method.cc
+++ b/src/native/java_lang_reflect_Method.cc
@@ -19,15 +19,18 @@
 #include "object.h"
 #include "object_utils.h"
 #include "reflection.h"
+#include "scoped_jni_thread_state.h"
 
 namespace art {
 
 static jobject Method_invoke(JNIEnv* env, jobject javaMethod, jobject javaReceiver, jobject javaArgs) {
-  return InvokeMethod(env, javaMethod, javaReceiver, javaArgs);
+  ScopedJniThreadState ts(env);
+  return InvokeMethod(ts, javaMethod, javaReceiver, javaArgs);
 }
 
 static jobject Method_getExceptionTypesNative(JNIEnv* env, jobject javaMethod) {
-  Method* proxy_method = Decode<Object*>(env, javaMethod)->AsMethod();
+  ScopedJniThreadState ts(env);
+  Method* proxy_method = ts.Decode<Object*>(javaMethod)->AsMethod();
   CHECK(proxy_method->GetDeclaringClass()->IsProxyClass());
   SynthesizedProxyClass* proxy_class =
       down_cast<SynthesizedProxyClass*>(proxy_method->GetDeclaringClass());
@@ -41,14 +44,13 @@
   }
   CHECK_NE(throws_index, -1);
   ObjectArray<Class>* declared_exceptions = proxy_class->GetThrows()->Get(throws_index);
-  // Change thread state for allocation
-  ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
-  return AddLocalReference<jobject>(env, declared_exceptions->Clone());
+  return ts.AddLocalReference<jobject>(declared_exceptions->Clone());
 }
 
 static jobject Method_findOverriddenMethodNative(JNIEnv* env, jobject javaMethod) {
-  Method* method = Decode<Object*>(env, javaMethod)->AsMethod();
-  return AddLocalReference<jobject>(env, method->FindOverriddenMethod());
+  ScopedJniThreadState ts(env);
+  Method* method = ts.Decode<Object*>(javaMethod)->AsMethod();
+  return ts.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 eca6c32..a1337a6 100644
--- a/src/native/java_lang_reflect_Proxy.cc
+++ b/src/native/java_lang_reflect_Proxy.cc
@@ -15,22 +15,23 @@
  */
 
 #include "class_linker.h"
+#include "class_loader.h"
 #include "jni_internal.h"
 #include "object.h"
+#include "scoped_jni_thread_state.h"
 
 namespace art {
 
 static jclass Proxy_generateProxy(JNIEnv* env, jclass, jstring javaName, jobjectArray javaInterfaces, jobject javaLoader, jobjectArray javaMethods, jobjectArray javaThrows) {
-  // Allocates Class so transition thread state to runnable
-  ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
-  String* name = Decode<String*>(env, javaName);
-  ObjectArray<Class>* interfaces = Decode<ObjectArray<Class>*>(env, javaInterfaces);
-  ClassLoader* loader = Decode<ClassLoader*>(env, javaLoader);
-  ObjectArray<Method>* methods = Decode<ObjectArray<Method>*>(env, javaMethods);
-  ObjectArray<ObjectArray<Class> >* throws = Decode<ObjectArray<ObjectArray<Class> >*>(env, 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);
   ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
   Class* result = class_linker->CreateProxyClass(name, interfaces, loader, methods, throws);
-  return AddLocalReference<jclass>(env, result);
+  return ts.AddLocalReference<jclass>(result);
 }
 
 static JNINativeMethod gMethods[] = {
diff --git a/src/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/src/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
index 3766546..87d2b22 100644
--- a/src/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
+++ b/src/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc
@@ -18,6 +18,7 @@
 #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 "ScopedPrimitiveArray.h"
 #include "stack.h"
@@ -68,7 +69,8 @@
   if (thread == NULL) {
     return NULL;
   }
-  jobject stack = GetThreadStack(env, thread);
+  ScopedJniThreadState ts(env);
+  jobject stack = GetThreadStack(ts, thread);
   return (stack != NULL) ? Thread::InternalStackTraceToStackTraceElementArray(env, stack) : NULL;
 }
 
diff --git a/src/native/sun_misc_Unsafe.cc b/src/native/sun_misc_Unsafe.cc
index 360f241..dfddd86 100644
--- a/src/native/sun_misc_Unsafe.cc
+++ b/src/native/sun_misc_Unsafe.cc
@@ -16,30 +16,34 @@
 
 #include "jni_internal.h"
 #include "object.h"
+#include "scoped_jni_thread_state.h"
 
 namespace art {
 
 static jlong Unsafe_objectFieldOffset0(JNIEnv* env, jclass, jobject javaField) {
   // TODO: move to Java code
   jfieldID fid = env->FromReflectedField(javaField);
-  Field* field = DecodeField(fid);
+  ScopedJniThreadState ts(env);
+  Field* field = ts.DecodeField(fid);
   return field->GetOffset().Int32Value();
 }
 
 static jint Unsafe_arrayBaseOffset0(JNIEnv* env, jclass, jclass javaArrayClass) {
   // TODO: move to Java code
-  ScopedThreadStateChange tsc(Thread::Current(), kRunnable);
-  Class* array_class = Decode<Class*>(env, javaArrayClass);
+  ScopedJniThreadState ts(env);
+  Class* array_class = ts.Decode<Class*>(javaArrayClass);
   return Array::DataOffset(array_class->GetComponentSize()).Int32Value();
 }
 
 static jint Unsafe_arrayIndexScale0(JNIEnv* env, jclass, jclass javaClass) {
-  Class* c = Decode<Class*>(env, javaClass);
+  ScopedJniThreadState ts(env);
+  Class* c = ts.Decode<Class*>(javaClass);
   return c->GetComponentSize();
 }
 
 static jboolean Unsafe_compareAndSwapInt(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint expectedValue, jint newValue) {
-  Object* obj = Decode<Object*>(env, javaObj);
+  ScopedJniThreadState ts(env);
+  Object* obj = ts.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.
@@ -48,7 +52,8 @@
 }
 
 static jboolean Unsafe_compareAndSwapLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong expectedValue, jlong newValue) {
-  Object* obj = Decode<Object*>(env, javaObj);
+  ScopedJniThreadState ts(env);
+  Object* obj = ts.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.
@@ -57,9 +62,10 @@
 }
 
 static jboolean Unsafe_compareAndSwapObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaExpectedValue, jobject javaNewValue) {
-  Object* obj = Decode<Object*>(env, javaObj);
-  Object* expectedValue = Decode<Object*>(env, javaExpectedValue);
-  Object* newValue = Decode<Object*>(env, javaNewValue);
+  ScopedJniThreadState ts(env);
+  Object* obj = ts.Decode<Object*>(javaObj);
+  Object* expectedValue = ts.Decode<Object*>(javaExpectedValue);
+  Object* newValue = ts.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.
@@ -72,90 +78,105 @@
 }
 
 static jint Unsafe_getInt(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-  Object* obj = Decode<Object*>(env, javaObj);
+  ScopedJniThreadState ts(env);
+  Object* obj = ts.Decode<Object*>(javaObj);
   return obj->GetField32(MemberOffset(offset), false);
 }
 
 static jint Unsafe_getIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-  Object* obj = Decode<Object*>(env, javaObj);
+  ScopedJniThreadState ts(env);
+  Object* obj = ts.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) {
-  Object* obj = Decode<Object*>(env, javaObj);
+  ScopedJniThreadState ts(env);
+  Object* obj = ts.Decode<Object*>(javaObj);
   obj->SetField32(MemberOffset(offset), newValue, false);
 }
 
 static void Unsafe_putIntVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jint newValue) {
-  Object* obj = Decode<Object*>(env, javaObj);
+  ScopedJniThreadState ts(env);
+  Object* obj = ts.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) {
-  Object* obj = Decode<Object*>(env, javaObj);
+  ScopedJniThreadState ts(env);
+  Object* obj = ts.Decode<Object*>(javaObj);
   ANDROID_MEMBAR_STORE();
   obj->SetField32(MemberOffset(offset), newValue, false);
 }
 
 static jlong Unsafe_getLong(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-  Object* obj = Decode<Object*>(env, javaObj);
+  ScopedJniThreadState ts(env);
+  Object* obj = ts.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) {
-  Object* obj = Decode<Object*>(env, javaObj);
+  ScopedJniThreadState ts(env);
+  Object* obj = ts.Decode<Object*>(javaObj);
   return obj->GetField64(MemberOffset(offset), true);
 }
 
 static void Unsafe_putLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) {
-  Object* obj = Decode<Object*>(env, javaObj);
+  ScopedJniThreadState ts(env);
+  Object* obj = ts.Decode<Object*>(javaObj);
   obj->SetField64(MemberOffset(offset), newValue, false);
 }
 
 static void Unsafe_putLongVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) {
-  Object* obj = Decode<Object*>(env, javaObj);
+  ScopedJniThreadState ts(env);
+  Object* obj = ts.Decode<Object*>(javaObj);
   obj->SetField64(MemberOffset(offset), newValue, true);
 }
 
 static void Unsafe_putOrderedLong(JNIEnv* env, jobject, jobject javaObj, jlong offset, jlong newValue) {
-  Object* obj = Decode<Object*>(env, javaObj);
+  ScopedJniThreadState ts(env);
+  Object* obj = ts.Decode<Object*>(javaObj);
   ANDROID_MEMBAR_STORE();
   obj->SetField64(MemberOffset(offset), newValue, false);
 }
 
 static jobject Unsafe_getObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-  Object* obj = Decode<Object*>(env, javaObj);
+  ScopedJniThreadState ts(env);
+  Object* obj = ts.Decode<Object*>(javaObj);
   Object* value = obj->GetFieldObject<Object*>(MemberOffset(offset), true);
-  return AddLocalReference<jobject>(env, value);
+  return ts.AddLocalReference<jobject>(value);
 }
 
 static jobject Unsafe_getObject(JNIEnv* env, jobject, jobject javaObj, jlong offset) {
-  Object* obj = Decode<Object*>(env, javaObj);
+  ScopedJniThreadState ts(env);
+  Object* obj = ts.Decode<Object*>(javaObj);
   Object* value = obj->GetFieldObject<Object*>(MemberOffset(offset), false);
-  return AddLocalReference<jobject>(env, value);
+  return ts.AddLocalReference<jobject>(value);
 }
 
 static void Unsafe_putObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) {
-  Object* obj = Decode<Object*>(env, javaObj);
-  Object* newValue = Decode<Object*>(env, javaNewValue);
+  ScopedJniThreadState ts(env);
+  Object* obj = ts.Decode<Object*>(javaObj);
+  Object* newValue = ts.Decode<Object*>(javaNewValue);
   obj->SetFieldObject(MemberOffset(offset), newValue, false);
 }
 
 static void Unsafe_putObjectVolatile(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) {
-  Object* obj = Decode<Object*>(env, javaObj);
-  Object* newValue = Decode<Object*>(env, javaNewValue);
+  ScopedJniThreadState ts(env);
+  Object* obj = ts.Decode<Object*>(javaObj);
+  Object* newValue = ts.Decode<Object*>(javaNewValue);
   obj->SetFieldObject(MemberOffset(offset), newValue, true);
 }
 
 static void Unsafe_putOrderedObject(JNIEnv* env, jobject, jobject javaObj, jlong offset, jobject javaNewValue) {
-  Object* obj = Decode<Object*>(env, javaObj);
-  Object* newValue = Decode<Object*>(env, javaNewValue);
+  ScopedJniThreadState ts(env);
+  Object* obj = ts.Decode<Object*>(javaObj);
+  Object* newValue = ts.Decode<Object*>(javaNewValue);
   ANDROID_MEMBAR_STORE();
   obj->SetFieldObject(MemberOffset(offset), newValue, false);
 }