Move JniConstants to points of use

Removes the JniConstants from the libnativehelper API.

Most of the constants are moved into libcore.

Light tidying up in touched files.

Bug: 119840313
Test: atest libnativehelper/test
Test: atest CtsLibcoreTestCases
Test: art/test.py --host -g -j16
Test: device boots

Change-Id: I4c0daab450d2efd7e6bd7705e27dbb6309fcb92d
diff --git a/JniConstants.cpp b/JniConstants.cpp
index 7343678..3a0de9d 100644
--- a/JniConstants.cpp
+++ b/JniConstants.cpp
@@ -15,131 +15,168 @@
  */
 
 #define LOG_TAG "JniConstants"
-
 #include "ALog-priv.h"
-#include "JNIHelp-priv.h"
-#include <nativehelper/JniConstants.h>
-#include <nativehelper/JniConstants-priv.h>
-#include <nativehelper/ScopedLocalRef.h>
 
-#include <stdlib.h>
+#include "JniConstants.h"
 
 #include <atomic>
 #include <mutex>
+#include <string>
 
-static std::atomic<bool> g_constants_initialized(false);
-static std::mutex g_constants_mutex;
+#include "nativehelper/ScopedLocalRef.h"
 
-jclass JniConstants::booleanClass;
-jclass JniConstants::byteArrayClass;
-jclass JniConstants::calendarClass;
-jclass JniConstants::charsetICUClass;
-jclass JniConstants::doubleClass;
-jclass JniConstants::errnoExceptionClass;
-jclass JniConstants::fileDescriptorClass;
-jclass JniConstants::gaiExceptionClass;
-jclass JniConstants::inet6AddressClass;
-jclass JniConstants::inet6AddressHolderClass;
-jclass JniConstants::inetAddressClass;
-jclass JniConstants::inetAddressHolderClass;
-jclass JniConstants::inetSocketAddressClass;
-jclass JniConstants::inetSocketAddressHolderClass;
-jclass JniConstants::integerClass;
-jclass JniConstants::localeDataClass;
-jclass JniConstants::longClass;
-jclass JniConstants::netlinkSocketAddressClass;
-jclass JniConstants::packetSocketAddressClass;
-jclass JniConstants::patternSyntaxExceptionClass;
-jclass JniConstants::referenceClass;
-jclass JniConstants::socketTaggerClass;
-jclass JniConstants::stringClass;
-jclass JniConstants::structAddrinfoClass;
-jclass JniConstants::structFlockClass;
-jclass JniConstants::structGroupReqClass;
-jclass JniConstants::structIfaddrs;
-jclass JniConstants::structLingerClass;
-jclass JniConstants::structPasswdClass;
-jclass JniConstants::structPollfdClass;
-jclass JniConstants::structStatClass;
-jclass JniConstants::structStatVfsClass;
-jclass JniConstants::structTimevalClass;
-jclass JniConstants::structTimespecClass;
-jclass JniConstants::structUcredClass;
-jclass JniConstants::structUtsnameClass;
-jclass JniConstants::unixSocketAddressClass;
-jclass JniConstants::zipEntryClass;
+namespace {
 
-static jclass findClass(JNIEnv* env, const char* name) {
-    ScopedLocalRef<jclass> localClass(env, env->FindClass(name));
-    jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get()));
-    if (result == NULL) {
-        ALOGE("failed to find class '%s'", name);
-        abort();
-    }
+// Mutex protecting the initialization of cached class references.
+std::mutex g_class_refs_mutex;
+
+// Atomic boolean flag for double locked checking that class references are
+// initialized before use.
+std::atomic<bool> g_class_refs_initialized(false);
+
+// Cached global references to class instances.
+//
+// These are GC heap references that are initialized under the protection of
+// |g_class_refs_mutex| as they should only be initialized once to avoid losing a
+// global reference. Initialization happens lazily when an accessor tries to
+// retrieve one of these classes.
+
+jclass g_file_descriptor_class = nullptr;  // java.io.FileDescriptor
+jclass g_reference_class = nullptr;        // java.lang.ref.Reference
+jclass g_string_class = nullptr;           // java.lang.String
+
+// Cached field and method ids.
+//
+// These are non-GC heap values. They are initialized lazily and racily. We
+// avoid holding a mutex here because the JNI API supports concurrent calls to
+// Get{Field,Method}ID and also because finding an id may recursively call into
+// Get{Field,Method}ID.
+//
+// The recursion issue occurs here for the fields in the FileDescriptor class
+// since retrieving a field id requires the class to be initialized. Class
+// initialization leads to the initialization of static fields. The
+// FileDescriptor class has static fields that are FileDescriptor instances. The
+// initialization of these static FileDescriptor fields follows a convoluted
+// path that that leads to a call to jniGetFDFromFileDescriptor() which then
+// needs to call GetFieldID() which is in the call stack. If thread-safety were
+// desirable here, a recursive mutex would be required.
+//
+// These field and method ids have default values of nullptr. They are reset
+// back to nullptr in JniConstants::Uninitialize(), along with the class
+// references, when a new runtime instance is created via JNI_CreateJavaVM(). The
+// reset happens before the new runtime instance is returned to the caller and
+// under the protection of the |g_class_refs_mutex|.
+
+jfieldID g_file_descriptor_descriptor_field = nullptr;  // java.io.FileDescriptor.descriptor
+jfieldID g_file_descriptor_owner_id_field = nullptr;    // java.io.FileDescriptor.ownerId
+jmethodID g_file_descriptor_init_method = nullptr;      // void java.io.FileDescriptor.<init>()
+jmethodID g_reference_get_method = nullptr;             // Object java.lang.ref.Reference.get()
+
+jclass FindClass(JNIEnv* env, const char* name) {
+    ScopedLocalRef<jclass> klass(env, env->FindClass(name));
+    ALOG_ALWAYS_FATAL_IF(klass.get() == nullptr, "failed to find class '%s'", name);
+    return reinterpret_cast<jclass>(env->NewGlobalRef(klass.get()));
+}
+
+jfieldID FindField(JNIEnv* env, jclass klass, const char* name, const char* desc) {
+    jfieldID result = env->GetFieldID(klass, name, desc);
+    ALOG_ALWAYS_FATAL_IF(result == nullptr, "failed to find field '%s:%s'", name, desc);
     return result;
 }
 
-void JniConstants::init(JNIEnv* env) {
-    // Fast check
-    if (g_constants_initialized) {
-      // already initialized
-      return;
-    }
-
-    // Slightly slower check
-    std::lock_guard<std::mutex> guard(g_constants_mutex);
-    if (g_constants_initialized) {
-      // already initialized
-      return;
-    }
-
-    booleanClass = findClass(env, "java/lang/Boolean");
-    byteArrayClass = findClass(env, "[B");
-    calendarClass = findClass(env, "java/util/Calendar");
-    charsetICUClass = findClass(env, "java/nio/charset/CharsetICU");
-    doubleClass = findClass(env, "java/lang/Double");
-    errnoExceptionClass = findClass(env, "android/system/ErrnoException");
-    fileDescriptorClass = findClass(env, "java/io/FileDescriptor");
-    gaiExceptionClass = findClass(env, "android/system/GaiException");
-    inet6AddressClass = findClass(env, "java/net/Inet6Address");
-    inet6AddressHolderClass = findClass(env, "java/net/Inet6Address$Inet6AddressHolder");
-    inetAddressClass = findClass(env, "java/net/InetAddress");
-    inetAddressHolderClass = findClass(env, "java/net/InetAddress$InetAddressHolder");
-    inetSocketAddressClass = findClass(env, "java/net/InetSocketAddress");
-    inetSocketAddressHolderClass = findClass(env, "java/net/InetSocketAddress$InetSocketAddressHolder");
-    integerClass = findClass(env, "java/lang/Integer");
-    localeDataClass = findClass(env, "libcore/icu/LocaleData");
-    longClass = findClass(env, "java/lang/Long");
-    netlinkSocketAddressClass = findClass(env, "android/system/NetlinkSocketAddress");
-    packetSocketAddressClass = findClass(env, "android/system/PacketSocketAddress");
-    patternSyntaxExceptionClass = findClass(env, "java/util/regex/PatternSyntaxException");
-    referenceClass = findClass(env, "java/lang/ref/Reference");
-    socketTaggerClass = findClass(env, "dalvik/system/SocketTagger");
-    stringClass = findClass(env, "java/lang/String");
-    structAddrinfoClass = findClass(env, "android/system/StructAddrinfo");
-    structFlockClass = findClass(env, "android/system/StructFlock");
-    structGroupReqClass = findClass(env, "android/system/StructGroupReq");
-    structIfaddrs = findClass(env, "android/system/StructIfaddrs");
-    structLingerClass = findClass(env, "android/system/StructLinger");
-    structPasswdClass = findClass(env, "android/system/StructPasswd");
-    structPollfdClass = findClass(env, "android/system/StructPollfd");
-    structStatClass = findClass(env, "android/system/StructStat");
-    structStatVfsClass = findClass(env, "android/system/StructStatVfs");
-    structTimevalClass = findClass(env, "android/system/StructTimeval");
-    structTimespecClass = findClass(env, "android/system/StructTimespec");
-    structUcredClass = findClass(env, "android/system/StructUcred");
-    structUtsnameClass = findClass(env, "android/system/StructUtsname");
-    unixSocketAddressClass = findClass(env, "android/system/UnixSocketAddress");
-    zipEntryClass = findClass(env, "java/util/zip/ZipEntry");
-
-    g_constants_initialized = true;
+jmethodID FindMethod(JNIEnv* env, jclass klass, const char* name, const char* signature) {
+    jmethodID result = env->GetMethodID(klass, name, signature);
+    ALOG_ALWAYS_FATAL_IF(result == nullptr, "failed to find method '%s%s'", name, signature);
+    return result;
 }
 
-namespace android {
+}  // namespace
 
-void ClearJniConstantsCache() {
-    g_constants_initialized = false;
-    ClearJNIHelpLocalCache();
+jclass JniConstants::GetReferenceClass(JNIEnv* env) {
+    EnsureClassReferencesInitialized(env);
+    return g_reference_class;
 }
 
+jclass JniConstants::GetFileDescriptorClass(JNIEnv* env) {
+    EnsureClassReferencesInitialized(env);
+    return g_file_descriptor_class;
+}
+
+jclass JniConstants::GetStringClass(JNIEnv* env) {
+    EnsureClassReferencesInitialized(env);
+    return g_string_class;
+}
+
+jfieldID JniConstants::GetFileDescriptorDescriptorField(JNIEnv* env) {
+    if (g_file_descriptor_descriptor_field == nullptr) {
+        jclass klass = GetFileDescriptorClass(env);
+        g_file_descriptor_descriptor_field = FindField(env, klass, "descriptor", "I");
+    }
+    return g_file_descriptor_descriptor_field;
+}
+
+jfieldID JniConstants::GetFileDescriptorOwnerIdField(JNIEnv* env) {
+    if (g_file_descriptor_owner_id_field == nullptr) {
+        jclass klass = GetFileDescriptorClass(env);
+        g_file_descriptor_owner_id_field = FindField(env, klass, "ownerId", "J");
+    }
+    return g_file_descriptor_owner_id_field;
+}
+
+jmethodID JniConstants::GetFileDescriptorInitMethod(JNIEnv* env) {
+    if (g_file_descriptor_init_method == nullptr) {
+        jclass klass = GetFileDescriptorClass(env);
+        g_file_descriptor_init_method = FindMethod(env, klass, "<init>", "()V");
+    }
+    return g_file_descriptor_init_method;
+}
+
+jmethodID JniConstants::GetReferenceGetMethod(JNIEnv* env) {
+    if (g_reference_get_method == nullptr) {
+        jclass klass = GetReferenceClass(env);
+        g_reference_get_method = FindMethod(env, klass, "get", "()Ljava/lang/Object;");
+    }
+    return g_reference_get_method;
+}
+
+void JniConstants::EnsureClassReferencesInitialized(JNIEnv* env) {
+    // Fast check if class references are initialized.
+    if (g_class_refs_initialized.load(std::memory_order_acquire)) {
+        return;
+    }
+
+    // Slower check with initialization if necessary.
+    std::lock_guard<std::mutex> guard(g_class_refs_mutex);
+    if (g_class_refs_initialized.load(std::memory_order_relaxed)) {
+        return;
+    }
+
+    // Class constants should be initialized only once because they global
+    // references. Field ids and Method ids can be initialized later since they
+    // are not references and races only have trivial performance
+    // consequences.
+    g_file_descriptor_class = FindClass(env, "java/io/FileDescriptor");
+    g_reference_class = FindClass(env, "java/lang/ref/Reference");
+    g_string_class = FindClass(env, "java/lang/String");
+    g_class_refs_initialized.store(true, std::memory_order_release);
+}
+
+void JniConstants::Uninitialize() {
+    // This method is called when a new runtime instance is created. There is no
+    // notification of a runtime instance being destroyed in the JNI interface
+    // so we piggyback on creation. Since only one runtime is supported at a
+    // time, we know the constants are invalid when JNI_CreateJavaVM() is
+    // called.
+    //
+    // Clean shutdown would require calling DeleteGlobalRef() for each of the
+    // class references.
+    std::lock_guard<std::mutex> guard(g_class_refs_mutex);
+    g_file_descriptor_class = nullptr;
+    g_file_descriptor_descriptor_field = nullptr;
+    g_file_descriptor_owner_id_field = nullptr;
+    g_file_descriptor_init_method = nullptr;
+    g_reference_class = nullptr;
+    g_reference_get_method = nullptr;
+    g_string_class = nullptr;
+    g_class_refs_initialized.store(false, std::memory_order_release);
 }