Snap for 4739962 from 8917309cc1096d898d2c90847b8e969891a8ddf0 to pi-release

Change-Id: I1fdc3c856b70d3f02b6dd119c1ca8648a5123da9
diff --git a/compiler/Android.bp b/compiler/Android.bp
index e42261c..32e42bc 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -218,7 +218,10 @@
 
 art_cc_library {
     name: "libart-compiler",
-    defaults: ["libart-compiler-defaults"],
+    defaults: [
+        "libart-compiler-defaults",
+        "dex2oat-pgo-defaults",
+    ],
     codegen: {
         arm: {
             // VIXL assembly support for ARM targets.
@@ -252,11 +255,6 @@
         "libdexfile",
     ],
 
-    pgo: {
-        instrumentation: true,
-        profile_file: "art/dex2oat.profdata",
-        benchmarks: ["dex2oat"],
-    },
     target: {
         android: {
             lto: {
diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp
index b158231..b793406 100644
--- a/dex2oat/Android.bp
+++ b/dex2oat/Android.bp
@@ -112,10 +112,51 @@
     ],
 }
 
+cc_defaults {
+    name: "dex2oat-pgo-defaults",
+    pgo: {
+        instrumentation: true,
+        benchmarks: ["dex2oat"],
+    },
+    target: {
+        android_arm64: {
+            pgo: {
+                profile_file: "art/dex2oat_arm_arm64.profdata",
+            },
+        },
+        android_arm: {
+            pgo: {
+                profile_file: "art/dex2oat_arm_arm64.profdata",
+            },
+        },
+        android_x86_64: {
+            pgo: {
+                profile_file: "art/dex2oat_x86_x86_64.profdata",
+            },
+        },
+        android_x86: {
+            pgo: {
+                profile_file: "art/dex2oat_x86_x86_64.profdata",
+            },
+        },
+        android_mips64: {
+            pgo: {
+                profile_file: "art/dex2oat_mips_mips64.profdata",
+            },
+        },
+        android_mips: {
+            pgo: {
+                profile_file: "art/dex2oat_mips_mips64.profdata",
+            },
+        },
+    },
+}
+
 art_cc_binary {
     name: "dex2oat",
     defaults: [
         "dex2oat-defaults",
+        "dex2oat-pgo-defaults",
     ],
     shared_libs: [
         "libart-compiler",
@@ -131,9 +172,7 @@
     ],
 
     pgo: {
-        instrumentation: true,
-        profile_file: "art/dex2oat.profdata",
-        benchmarks: ["dex2oat"],
+        // Additional cflags just for dex2oat during PGO instrumentation
         cflags: [
             // Ignore frame-size increase resulting from instrumentation.
             "-Wno-frame-larger-than=",
diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp
index facda11..33ba58f 100644
--- a/dexlayout/Android.bp
+++ b/dexlayout/Android.bp
@@ -34,17 +34,15 @@
 
 art_cc_library {
     name: "libart-dexlayout",
-    defaults: ["libart-dexlayout-defaults"],
+    defaults: [
+        "libart-dexlayout-defaults",
+        "dex2oat-pgo-defaults",
+    ],
     shared_libs: [
         "libart",
         "libdexfile",
     ],
 
-    pgo: {
-        instrumentation: true,
-        profile_file: "art/dex2oat.profdata",
-        benchmarks: ["dex2oat"],
-    },
     target: {
         android: {
             lto: {
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 900ce0e..b1ededf 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -273,6 +273,43 @@
   };
 };
 
+// Check whether the current thread is attached. This is usually required
+// to be the first check, as ScopedCheck needs a ScopedObjectAccess for
+// checking heap values (and that will fail with unattached threads).
+bool CheckAttachedThread(const char* function_name) {
+  Thread* self = Thread::Current();
+  if (UNLIKELY(self == nullptr)) {
+    // Need to attach this thread for a proper abort to work. We prefer this
+    // to get reasonable stacks and environment, rather than relying on
+    // tombstoned.
+    JNIEnv* env;
+    Runtime::Current()->GetJavaVM()->AttachCurrentThread(&env, /* thread_args */ nullptr);
+
+    std::string tmp = android::base::StringPrintf(
+        "a thread (tid %" PRId64 " is making JNI calls without being attached",
+        static_cast<int64_t>(GetTid()));
+    Runtime::Current()->GetJavaVM()->JniAbort(function_name, tmp.c_str());
+
+    CHECK_NE(Runtime::Current()->GetJavaVM()->DetachCurrentThread(), JNI_ERR);
+    return false;
+  }
+  return true;
+}
+
+// Macro helpers for the above.
+#define CHECK_ATTACHED_THREAD(function_name, fail_val)  \
+  do {                                                  \
+    if (!CheckAttachedThread((function_name))) {        \
+      return fail_val;                                  \
+    }                                                   \
+  } while (false)
+#define CHECK_ATTACHED_THREAD_VOID(function_name)       \
+  do {                                                  \
+    if (!CheckAttachedThread((function_name))) {        \
+      return;                                           \
+    }                                                   \
+  } while (false)
+
 class ScopedCheck {
  public:
   ScopedCheck(uint16_t flags, const char* functionName, bool has_method = true)
@@ -1254,10 +1291,7 @@
 
   bool CheckThread(JNIEnv* env) REQUIRES_SHARED(Locks::mutator_lock_) {
     Thread* self = Thread::Current();
-    if (self == nullptr) {
-      AbortF("a thread (tid %d) is making JNI calls without being attached", GetTid());
-      return false;
-    }
+    CHECK(self != nullptr);
 
     // Get the current thread's JNIEnv by going through our TLS pointer.
     JNIEnvExt* threadEnv = self->GetJniEnv();
@@ -1707,6 +1741,7 @@
 class CheckJNI {
  public:
   static jint GetVersion(JNIEnv* env) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[1] = {{.E = env }};
@@ -1721,6 +1756,7 @@
   }
 
   static jint GetJavaVM(JNIEnv *env, JavaVM **vm) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[2] = {{.E = env }, {.p = vm}};
@@ -1735,6 +1771,7 @@
   }
 
   static jint RegisterNatives(JNIEnv* env, jclass c, const JNINativeMethod* methods, jint nMethods) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[4] = {{.E = env }, {.c = c}, {.p = methods}, {.I = nMethods}};
@@ -1749,6 +1786,7 @@
   }
 
   static jint UnregisterNatives(JNIEnv* env, jclass c) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[2] = {{.E = env }, {.c = c}};
@@ -1763,6 +1801,7 @@
   }
 
   static jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, JNIInvalidRefType);
     // Note: we use "EL" here but "Ep" has been used in the past on the basis that we'd like to
     // know the object is invalid. The spec says that passing invalid objects or even ones that
     // are deleted isn't supported.
@@ -1781,6 +1820,7 @@
 
   static jclass DefineClass(JNIEnv* env, const char* name, jobject loader, const jbyte* buf,
                             jsize bufLen) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[5] = {{.E = env}, {.u = name}, {.L = loader}, {.p = buf}, {.z = bufLen}};
@@ -1795,6 +1835,7 @@
   }
 
   static jclass FindClass(JNIEnv* env, const char* name) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.u = name}};
@@ -1809,6 +1850,7 @@
   }
 
   static jclass GetSuperclass(JNIEnv* env, jclass c) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.c = c}};
@@ -1823,6 +1865,7 @@
   }
 
   static jboolean IsAssignableFrom(JNIEnv* env, jclass c1, jclass c2) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_FALSE);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[3] = {{.E = env}, {.c = c1}, {.c = c2}};
@@ -1837,6 +1880,7 @@
   }
 
   static jmethodID FromReflectedMethod(JNIEnv* env, jobject method) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.L = method}};
@@ -1851,6 +1895,7 @@
   }
 
   static jfieldID FromReflectedField(JNIEnv* env, jobject field) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.L = field}};
@@ -1865,6 +1910,7 @@
   }
 
   static jobject ToReflectedMethod(JNIEnv* env, jclass cls, jmethodID mid, jboolean isStatic) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[4] = {{.E = env}, {.c = cls}, {.m = mid}, {.I = isStatic}};
@@ -1880,6 +1926,7 @@
   }
 
   static jobject ToReflectedField(JNIEnv* env, jclass cls, jfieldID fid, jboolean isStatic) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[4] = {{.E = env}, {.c = cls}, {.f = fid}, {.I = isStatic}};
@@ -1895,6 +1942,7 @@
   }
 
   static jint Throw(JNIEnv* env, jthrowable obj) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.t = obj}};
@@ -1909,6 +1957,7 @@
   }
 
   static jint ThrowNew(JNIEnv* env, jclass c, const char* message) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_NullableUtf, __FUNCTION__);
     JniValueType args[3] = {{.E = env}, {.c = c}, {.u = message}};
@@ -1923,6 +1972,7 @@
   }
 
   static jthrowable ExceptionOccurred(JNIEnv* env) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
     JniValueType args[1] = {{.E = env}};
@@ -1937,6 +1987,7 @@
   }
 
   static void ExceptionDescribe(JNIEnv* env) {
+    CHECK_ATTACHED_THREAD_VOID(__FUNCTION__);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
     JniValueType args[1] = {{.E = env}};
@@ -1949,6 +2000,7 @@
   }
 
   static void ExceptionClear(JNIEnv* env) {
+    CHECK_ATTACHED_THREAD_VOID(__FUNCTION__);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
     JniValueType args[1] = {{.E = env}};
@@ -1961,6 +2013,7 @@
   }
 
   static jboolean ExceptionCheck(JNIEnv* env) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_FALSE);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_CritOkay | kFlag_ExcepOkay, __FUNCTION__);
     JniValueType args[1] = {{.E = env}};
@@ -1975,6 +2028,7 @@
   }
 
   static void FatalError(JNIEnv* env, const char* msg) {
+    CHECK_ATTACHED_THREAD_VOID(__FUNCTION__);
     // The JNI specification doesn't say it's okay to call FatalError with a pending exception,
     // but you're about to abort anyway, and it's quite likely that you have a pending exception,
     // and it's not unimaginable that you don't know that you do. So we allow it.
@@ -1991,6 +2045,7 @@
   }
 
   static jint PushLocalFrame(JNIEnv* env, jint capacity) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.I = capacity}};
@@ -2005,6 +2060,7 @@
   }
 
   static jobject PopLocalFrame(JNIEnv* env, jobject res) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.L = res}};
@@ -2042,6 +2098,7 @@
   }
 
   static jint EnsureLocalCapacity(JNIEnv *env, jint capacity) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.I = capacity}};
@@ -2056,6 +2113,7 @@
   }
 
   static jboolean IsSameObject(JNIEnv* env, jobject ref1, jobject ref2) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_FALSE);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[3] = {{.E = env}, {.L = ref1}, {.L = ref2}};
@@ -2070,6 +2128,7 @@
   }
 
   static jobject AllocObject(JNIEnv* env, jclass c) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.c = c}};
@@ -2084,6 +2143,7 @@
   }
 
   static jobject NewObjectV(JNIEnv* env, jclass c, jmethodID mid, va_list vargs) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     VarArgs rest(mid, vargs);
@@ -2100,6 +2160,7 @@
   }
 
   static jobject NewObject(JNIEnv* env, jclass c, jmethodID mid, ...) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     va_list args;
     va_start(args, mid);
     jobject result = NewObjectV(env, c, mid, args);
@@ -2108,6 +2169,7 @@
   }
 
   static jobject NewObjectA(JNIEnv* env, jclass c, jmethodID mid, jvalue* vargs) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     VarArgs rest(mid, vargs);
@@ -2124,6 +2186,7 @@
   }
 
   static jclass GetObjectClass(JNIEnv* env, jobject obj) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.L = obj}};
@@ -2138,6 +2201,7 @@
   }
 
   static jboolean IsInstanceOf(JNIEnv* env, jobject obj, jclass c) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_FALSE);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[3] = {{.E = env}, {.L = obj}, {.c = c}};
@@ -2313,6 +2377,7 @@
 #undef CALL
 
   static jstring NewString(JNIEnv* env, const jchar* unicode_chars, jsize len) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[3] = {{.E = env}, {.p = unicode_chars}, {.z = len}};
@@ -2327,6 +2392,7 @@
   }
 
   static jstring NewStringUTF(JNIEnv* env, const char* chars) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_NullableUtf, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.u = chars}};
@@ -2342,6 +2408,7 @@
   }
 
   static jsize GetStringLength(JNIEnv* env, jstring string) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.s = string}};
@@ -2356,6 +2423,7 @@
   }
 
   static jsize GetStringUTFLength(JNIEnv* env, jstring string) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.s = string}};
@@ -2397,6 +2465,7 @@
   }
 
   static void GetStringRegion(JNIEnv* env, jstring string, jsize start, jsize len, jchar* buf) {
+    CHECK_ATTACHED_THREAD_VOID(__FUNCTION__);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
     JniValueType args[5] = {{.E = env}, {.s = string}, {.z = start}, {.z = len}, {.p = buf}};
@@ -2411,6 +2480,7 @@
   }
 
   static void GetStringUTFRegion(JNIEnv* env, jstring string, jsize start, jsize len, char* buf) {
+    CHECK_ATTACHED_THREAD_VOID(__FUNCTION__);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
     JniValueType args[5] = {{.E = env}, {.s = string}, {.z = start}, {.z = len}, {.p = buf}};
@@ -2425,6 +2495,7 @@
   }
 
   static jsize GetArrayLength(JNIEnv* env, jarray array) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_CritOkay, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.a = array}};
@@ -2440,6 +2511,7 @@
 
   static jobjectArray NewObjectArray(JNIEnv* env, jsize length, jclass element_class,
                                      jobject initial_element) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[4] =
@@ -2456,6 +2528,7 @@
   }
 
   static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[3] = {{.E = env}, {.a = array}, {.z = index}};
@@ -2470,6 +2543,7 @@
   }
 
   static void SetObjectArrayElement(JNIEnv* env, jobjectArray array, jsize index, jobject value) {
+    CHECK_ATTACHED_THREAD_VOID(__FUNCTION__);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[4] = {{.E = env}, {.a = array}, {.z = index}, {.L = value}};
@@ -2556,6 +2630,7 @@
 #undef PRIMITIVE_ARRAY_FUNCTIONS
 
   static jint MonitorEnter(JNIEnv* env, jobject obj) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.L = obj}};
@@ -2573,6 +2648,7 @@
   }
 
   static jint MonitorExit(JNIEnv* env, jobject obj) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_ExcepOkay, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.L = obj}};
@@ -2590,6 +2666,7 @@
   }
 
   static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray array, jboolean* is_copy) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_CritGet, __FUNCTION__);
     JniValueType args[3] = {{.E = env}, {.a = array}, {.p = is_copy}};
@@ -2608,6 +2685,7 @@
   }
 
   static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array, void* carray, jint mode) {
+    CHECK_ATTACHED_THREAD_VOID(__FUNCTION__);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_CritRelease | kFlag_ExcepOkay, __FUNCTION__);
     sc.CheckNonNull(carray);
@@ -2624,6 +2702,7 @@
   }
 
   static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[3] = {{.E = env}, {.p = address}, {.J = capacity}};
@@ -2639,6 +2718,7 @@
   }
 
   static void* GetDirectBufferAddress(JNIEnv* env, jobject buf) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.L = buf}};
@@ -2655,6 +2735,7 @@
   }
 
   static jlong GetDirectBufferCapacity(JNIEnv* env, jobject buf) {
+    CHECK_ATTACHED_THREAD(__FUNCTION__, JNI_ERR);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, __FUNCTION__);
     JniValueType args[2] = {{.E = env}, {.L = buf}};
@@ -2680,6 +2761,7 @@
   }
 
   static jobject NewRef(const char* function_name, JNIEnv* env, jobject obj, IndirectRefKind kind) {
+    CHECK_ATTACHED_THREAD(function_name, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, function_name);
     JniValueType args[2] = {{.E = env}, {.L = obj}};
@@ -2708,6 +2790,7 @@
   }
 
   static void DeleteRef(const char* function_name, JNIEnv* env, jobject obj, IndirectRefKind kind) {
+    CHECK_ATTACHED_THREAD_VOID(function_name);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_ExcepOkay, function_name);
     JniValueType args[2] = {{.E = env}, {.L = obj}};
@@ -2734,6 +2817,7 @@
 
   static jmethodID GetMethodIDInternal(const char* function_name, JNIEnv* env, jclass c,
                                        const char* name, const char* sig, bool is_static) {
+    CHECK_ATTACHED_THREAD(function_name, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, function_name);
     JniValueType args[4] = {{.E = env}, {.c = c}, {.u = name}, {.u = sig}};
@@ -2753,6 +2837,7 @@
 
   static jfieldID GetFieldIDInternal(const char* function_name, JNIEnv* env, jclass c,
                                      const char* name, const char* sig, bool is_static) {
+    CHECK_ATTACHED_THREAD(function_name, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, function_name);
     JniValueType args[4] = {{.E = env}, {.c = c}, {.u = name}, {.u = sig}};
@@ -2772,6 +2857,7 @@
 
   static JniValueType GetField(const char* function_name, JNIEnv* env, jobject obj, jfieldID fid,
                                bool is_static, Primitive::Type type) {
+    CHECK_ATTACHED_THREAD(function_name, JniValueType());
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, function_name);
     JniValueType args[3] = {{.E = env}, {.L = obj}, {.f = fid}};
@@ -2866,6 +2952,7 @@
 
   static void SetField(const char* function_name, JNIEnv* env, jobject obj, jfieldID fid,
                        bool is_static, Primitive::Type type, JniValueType value) {
+    CHECK_ATTACHED_THREAD_VOID(function_name);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, function_name);
     JniValueType args[4] = {{.E = env}, {.L = obj}, {.f = fid}, value};
@@ -2980,6 +3067,7 @@
   static JniValueType CallMethodA(const char* function_name, JNIEnv* env, jobject obj, jclass c,
                                   jmethodID mid, jvalue* vargs, Primitive::Type type,
                                   InvokeType invoke) {
+    CHECK_ATTACHED_THREAD(function_name, JniValueType());
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, function_name);
     JniValueType result;
@@ -3164,6 +3252,7 @@
   static JniValueType CallMethodV(const char* function_name, JNIEnv* env, jobject obj, jclass c,
                                   jmethodID mid, va_list vargs, Primitive::Type type,
                                   InvokeType invoke) {
+    CHECK_ATTACHED_THREAD(function_name, JniValueType());
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, function_name);
     JniValueType result;
@@ -3347,6 +3436,7 @@
 
   static const void* GetStringCharsInternal(const char* function_name, JNIEnv* env, jstring string,
                                             jboolean* is_copy, bool utf, bool critical) {
+    CHECK_ATTACHED_THREAD(function_name, nullptr);
     ScopedObjectAccess soa(env);
     int flags = critical ? kFlag_CritGet : kFlag_CritOkay;
     ScopedCheck sc(flags, function_name);
@@ -3387,6 +3477,7 @@
 
   static void ReleaseStringCharsInternal(const char* function_name, JNIEnv* env, jstring string,
                                          const void* chars, bool utf, bool critical) {
+    CHECK_ATTACHED_THREAD_VOID(function_name);
     ScopedObjectAccess soa(env);
     int flags = kFlag_ExcepOkay | kFlag_Release;
     if (critical) {
@@ -3419,6 +3510,7 @@
 
   static jarray NewPrimitiveArray(const char* function_name, JNIEnv* env, jsize length,
                                   Primitive::Type type) {
+    CHECK_ATTACHED_THREAD(function_name, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, function_name);
     JniValueType args[2] = {{.E = env}, {.z = length}};
@@ -3461,6 +3553,7 @@
 
   static void* GetPrimitiveArrayElements(const char* function_name, Primitive::Type type,
                                          JNIEnv* env, jarray array, jboolean* is_copy) {
+    CHECK_ATTACHED_THREAD(function_name, nullptr);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, function_name);
     JniValueType args[3] = {{.E = env}, {.a = array}, {.p = is_copy}};
@@ -3512,6 +3605,7 @@
 
   static void ReleasePrimitiveArrayElements(const char* function_name, Primitive::Type type,
                                             JNIEnv* env, jarray array, void* elems, jint mode) {
+    CHECK_ATTACHED_THREAD_VOID(function_name);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_ExcepOkay, function_name);
     if (sc.CheckNonNull(elems) && sc.CheckPrimitiveArrayType(soa, array, type)) {
@@ -3567,6 +3661,7 @@
 
   static void GetPrimitiveArrayRegion(const char* function_name, Primitive::Type type, JNIEnv* env,
                                       jarray array, jsize start, jsize len, void* buf) {
+    CHECK_ATTACHED_THREAD_VOID(function_name);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, function_name);
     JniValueType args[5] = {{.E = env}, {.a = array}, {.z = start}, {.z = len}, {.p = buf}};
@@ -3617,6 +3712,7 @@
 
   static void SetPrimitiveArrayRegion(const char* function_name, Primitive::Type type, JNIEnv* env,
                                       jarray array, jsize start, jsize len, const void* buf) {
+    CHECK_ATTACHED_THREAD_VOID(function_name);
     ScopedObjectAccess soa(env);
     ScopedCheck sc(kFlag_Default, function_name);
     JniValueType args[5] = {{.E = env}, {.a = array}, {.z = start}, {.z = len}, {.p = buf}};
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index 293e18a..5d74181 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -2517,4 +2517,32 @@
   env_->DeleteGlobalRef(global2);
 }
 
+TEST_F(JniInternalTest, NonAttachedThread) {
+  // This tests leads to warnings and errors in the log.
+  ScopedLogSeverity sls(LogSeverity::FATAL);
+  CheckJniAbortCatcher check_jni_abort_catcher;
+
+  auto callee = [](void* env_ptr) -> void* {
+    JNIEnv* env = reinterpret_cast<JNIEnv*>(env_ptr);
+    env->NewStringUTF("test");
+    return nullptr;
+  };
+
+  bool old_check_jni = vm_->SetCheckJniEnabled(false);
+  vm_->SetCheckJniEnabled(true);
+  {
+    pthread_t pthread;
+    int pthread_create_result = pthread_create(&pthread,
+                                               /* pthread_attr */ nullptr,
+                                               callee,
+                                               reinterpret_cast<void*>(env_));
+    CHECK_EQ(pthread_create_result, 0);
+    int pthread_join_result = pthread_join(pthread, /* thread_return */ nullptr);
+    CHECK_EQ(pthread_join_result, 0);
+  }
+  vm_->SetCheckJniEnabled(old_check_jni);
+
+  check_jni_abort_catcher.Check("is making JNI calls without being attached");
+}
+
 }  // namespace art
diff --git a/tools/veridex/flow_analysis.cc b/tools/veridex/flow_analysis.cc
index a4553f9..736abb7 100644
--- a/tools/veridex/flow_analysis.cc
+++ b/tools/veridex/flow_analysis.cc
@@ -262,8 +262,17 @@
         last_result_ = GetReturnType(instruction.VRegB_35c());
       } else if (method == VeriClass::getClass_) {
         RegisterValue obj = GetRegister(args[0]);
-        last_result_ = RegisterValue(
-            obj.GetSource(), obj.GetDexFileReference(), VeriClass::class_);
+        const VeriClass* cls = obj.GetType();
+        if (cls != nullptr && cls->GetClassDef() != nullptr) {
+          const DexFile::ClassDef* def = cls->GetClassDef();
+          last_result_ = RegisterValue(
+              RegisterSource::kClass,
+              DexFileReference(&resolver_->GetDexFileOf(*cls), def->class_idx_.index_),
+              VeriClass::class_);
+        } else {
+          last_result_ = RegisterValue(
+              obj.GetSource(), obj.GetDexFileReference(), VeriClass::class_);
+        }
       } else if (method == VeriClass::loadClass_) {
         RegisterValue value = GetRegister(args[1]);
         last_result_ = RegisterValue(
diff --git a/tools/veridex/hidden_api_finder.cc b/tools/veridex/hidden_api_finder.cc
index b1ae7dd..8c6139f 100644
--- a/tools/veridex/hidden_api_finder.cc
+++ b/tools/veridex/hidden_api_finder.cc
@@ -31,11 +31,8 @@
 void HiddenApiFinder::CheckMethod(uint32_t method_id,
                                   VeridexResolver* resolver,
                                   MethodReference ref) {
-  // Cheap check that the method is resolved. If it is, we know it's not in
-  // a restricted list.
-  if (resolver->GetMethod(method_id) != nullptr) {
-    return;
-  }
+  // Note: we always query whether a method is in a list, as the app
+  // might define blacklisted APIs (which won't be used at runtime).
   std::string name = HiddenApi::GetApiMethodName(resolver->GetDexFile(), method_id);
   if (hidden_api_.IsInRestrictionList(name)) {
     method_locations_[name].push_back(ref);
@@ -45,11 +42,8 @@
 void HiddenApiFinder::CheckField(uint32_t field_id,
                                  VeridexResolver* resolver,
                                  MethodReference ref) {
-  // Cheap check that the field is resolved. If it is, we know it's not in
-  // a restricted list.
-  if (resolver->GetField(field_id) != nullptr) {
-    return;
-  }
+  // Note: we always query whether a field is in a list, as the app
+  // might define blacklisted APIs (which won't be used at runtime).
   std::string name = HiddenApi::GetApiFieldName(resolver->GetDexFile(), field_id);
   if (hidden_api_.IsInRestrictionList(name)) {
     field_locations_[name].push_back(ref);
diff --git a/tools/veridex/precise_hidden_api_finder.cc b/tools/veridex/precise_hidden_api_finder.cc
index 2092af3..4ae5769 100644
--- a/tools/veridex/precise_hidden_api_finder.cc
+++ b/tools/veridex/precise_hidden_api_finder.cc
@@ -63,6 +63,7 @@
 
 void PreciseHiddenApiFinder::Dump(std::ostream& os, HiddenApiStats* stats) {
   static const char* kPrefix = "       ";
+  std::map<std::string, std::vector<MethodReference>> uses;
   for (auto kinds : { field_uses_, method_uses_ }) {
     for (auto it : kinds) {
       MethodReference ref = it.first;
@@ -74,19 +75,26 @@
           std::string name(info.second.ToString());
           std::string full_name = cls + "->" + name;
           HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name);
-          stats->api_counts[api_list]++;
           if (api_list != HiddenApiAccessFlags::kWhitelist) {
-            ++stats->reflection_count;
-            os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name
-               << " use:";
-            os << std::endl;
-            os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl;
-            os << std::endl;
+            uses[full_name].push_back(ref);
           }
         }
       }
     }
   }
+
+  for (auto it : uses) {
+    ++stats->reflection_count;
+    const std::string& full_name = it.first;
+    HiddenApiAccessFlags::ApiList api_list = hidden_api_.GetApiList(full_name);
+    stats->api_counts[api_list]++;
+    os << "#" << ++stats->count << ": Reflection " << api_list << " " << full_name << " use(s):";
+    os << std::endl;
+    for (const MethodReference& ref : it.second) {
+      os << kPrefix << HiddenApi::GetApiMethodName(ref) << std::endl;
+    }
+    os << std::endl;
+  }
 }
 
 }  // namespace art
diff --git a/tools/veridex/resolver.h b/tools/veridex/resolver.h
index 52b15fe..65b2961 100644
--- a/tools/veridex/resolver.h
+++ b/tools/veridex/resolver.h
@@ -79,6 +79,10 @@
     return dex_file_;
   }
 
+  const DexFile& GetDexFileOf(const VeriClass& kls) {
+    return GetResolverOf(kls)->dex_file_;
+  }
+
  private:
   // Return the resolver where `kls` is from.
   VeridexResolver* GetResolverOf(const VeriClass& kls) const;