Project import
diff --git a/libnativehelper/ALog-priv.h b/libnativehelper/ALog-priv.h
new file mode 100644
index 0000000..7b3ed97
--- /dev/null
+++ b/libnativehelper/ALog-priv.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef NATIVEHELPER_ALOGPRIV_H_
+#define NATIVEHELPER_ALOGPRIV_H_
+
+#include <android/log.h>
+
+#ifndef LOG_NDEBUG
+#ifdef NDEBUG
+#define LOG_NDEBUG 1
+#else
+#define LOG_NDEBUG 0
+#endif
+#endif
+
+
+/*
+ * Basic log message macros intended to emulate the behavior of log/log.h
+ * in system core.  This should be dependent only on ndk exposed logging
+ * functionality.
+ */
+
+#ifndef ALOG
+#define ALOG(priority, tag, fmt...) \
+    __android_log_print(ANDROID_##priority, tag, fmt)
+#endif
+
+#ifndef ALOGV
+#if LOG_NDEBUG
+#define ALOGV(...)   ((void)0)
+#else
+#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
+#endif
+#endif
+
+#ifndef ALOGD
+#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGI
+#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGW
+#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
+#endif
+
+#ifndef ALOGE
+#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
+#endif
+
+#endif
diff --git a/libnativehelper/Android.bp b/libnativehelper/Android.bp
new file mode 100644
index 0000000..59ddbdd
--- /dev/null
+++ b/libnativehelper/Android.bp
@@ -0,0 +1,83 @@
+// Copyright (C) 2009 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library {
+    name: "libnativehelper",
+    host_supported: true,
+    srcs: [
+        "JNIHelp.cpp",
+        "JniConstants.cpp",
+        "toStringArray.cpp",
+        "JniInvocation.cpp",
+    ],
+
+    target: {
+        android: {
+            srcs: ["AsynchronousCloseMonitor.cpp"],
+            shared_libs: ["libdl"],
+        },
+        linux: {
+            srcs: ["AsynchronousCloseMonitor.cpp"],
+            host_ldlibs: ["-ldl"],
+        },
+   },
+
+    shared_libs: [
+        "liblog",
+    ],
+    clang: true,
+    cflags: [
+        "-Werror",
+        "-fvisibility=protected",
+    ],
+    export_include_dirs: ["include"],
+}
+
+//
+// NDK-only build for the target (device), using libc++.
+// - Relies only on NDK exposed functionality.
+// - This doesn't include JniInvocation.
+//
+
+cc_library_shared {
+    name: "libnativehelper_compat_libc++",
+    clang: true,
+    local_include_dirs: ["include/nativehelper"],
+    export_include_dirs: ["include/nativehelper"],
+    cflags: ["-Werror"],
+    srcs: [
+        "JNIHelp.cpp",
+        "JniConstants.cpp",
+        "toStringArray.cpp",
+    ],
+    shared_libs: [
+        "liblog",
+        "libdl",
+    ],
+    sdk_version: "19",
+    stl: "c++_static",
+}
+
+ndk_headers {
+    name: "ndk_jni.h",
+    from: "include/nativehelper",
+    to: "",
+    srcs: ["include/nativehelper/jni.h"],
+}
+
+//
+// Tests.
+//
+
+subdirs = ["tests"]
diff --git a/libnativehelper/AsynchronousCloseMonitor.cpp b/libnativehelper/AsynchronousCloseMonitor.cpp
new file mode 100644
index 0000000..1815580
--- /dev/null
+++ b/libnativehelper/AsynchronousCloseMonitor.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AsynchronousCloseMonitor"
+
+#include "AsynchronousCloseMonitor.h"
+#include "cutils/log.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+
+#include <mutex>
+
+/**
+ * We use an intrusive doubly-linked list to keep track of blocked threads.
+ * This gives us O(1) insertion and removal, and means we don't need to do any allocation.
+ * (The objects themselves are stack-allocated.)
+ * Waking potentially-blocked threads when a file descriptor is closed is O(n) in the total number
+ * of blocked threads (not the number of threads actually blocked on the file descriptor in
+ * question). For now at least, this seems like a good compromise for Android.
+ */
+static std::mutex blockedThreadListMutex;
+static AsynchronousCloseMonitor* blockedThreadList = NULL;
+
+/**
+ * The specific signal chosen here is arbitrary, but bionic needs to know so that SIGRTMIN
+ * starts at a higher value.
+ */
+static const int BLOCKED_THREAD_SIGNAL = __SIGRTMIN + 2;
+
+static void blockedThreadSignalHandler(int /*signal*/) {
+    // Do nothing. We only sent this signal for its side-effect of interrupting syscalls.
+}
+
+void AsynchronousCloseMonitor::init() {
+    // Ensure that the signal we send interrupts system calls but doesn't kill threads.
+    // Using sigaction(2) lets us ensure that the SA_RESTART flag is not set.
+    // (The whole reason we're sending this signal is to unblock system calls!)
+    struct sigaction sa;
+    memset(&sa, 0, sizeof(sa));
+    sa.sa_handler = blockedThreadSignalHandler;
+    sa.sa_flags = 0;
+    int rc = sigaction(BLOCKED_THREAD_SIGNAL, &sa, NULL);
+    if (rc == -1) {
+        ALOGE("setting blocked thread signal handler failed: %s", strerror(errno));
+    }
+}
+
+void AsynchronousCloseMonitor::signalBlockedThreads(int fd) {
+    std::lock_guard<std::mutex> lock(blockedThreadListMutex);
+    for (AsynchronousCloseMonitor* it = blockedThreadList; it != NULL; it = it->mNext) {
+        if (it->mFd == fd) {
+            it->mSignaled = true;
+            pthread_kill(it->mThread, BLOCKED_THREAD_SIGNAL);
+            // Keep going, because there may be more than one thread...
+        }
+    }
+}
+
+bool AsynchronousCloseMonitor::wasSignaled() const {
+    return mSignaled;
+}
+
+AsynchronousCloseMonitor::AsynchronousCloseMonitor(int fd) {
+    std::lock_guard<std::mutex> lock(blockedThreadListMutex);
+    // Who are we, and what are we waiting for?
+    mThread = pthread_self();
+    mFd = fd;
+    mSignaled = false;
+    // Insert ourselves at the head of the intrusive doubly-linked list...
+    mPrev = NULL;
+    mNext = blockedThreadList;
+    if (mNext != NULL) {
+        mNext->mPrev = this;
+    }
+    blockedThreadList = this;
+}
+
+AsynchronousCloseMonitor::~AsynchronousCloseMonitor() {
+    std::lock_guard<std::mutex> lock(blockedThreadListMutex);
+    // Unlink ourselves from the intrusive doubly-linked list...
+    if (mNext != NULL) {
+        mNext->mPrev = mPrev;
+    }
+    if (mPrev == NULL) {
+        blockedThreadList = mNext;
+    } else {
+        mPrev->mNext = mNext;
+    }
+}
diff --git a/libnativehelper/JNIHelp.cpp b/libnativehelper/JNIHelp.cpp
new file mode 100644
index 0000000..2ab6ed5
--- /dev/null
+++ b/libnativehelper/JNIHelp.cpp
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#if defined(__ANDROID__)
+/* libnativehelper is built by NDK 19 in one variant, which doesn't yet have the GNU strerror_r. */
+#undef _GNU_SOURCE
+/* ...but this code uses asprintf, which is a BSD/GNU extension. */
+#define _BSD_SOURCE
+#endif
+
+#define LOG_TAG "JNIHelp"
+
+#include "JniConstants.h"
+#include "JNIHelp.h"
+#include "ALog-priv.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <string>
+
+/**
+ * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
+ */
+template<typename T>
+class scoped_local_ref {
+public:
+    explicit scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
+    : mEnv(env), mLocalRef(localRef)
+    {
+    }
+
+    ~scoped_local_ref() {
+        reset();
+    }
+
+    void reset(T localRef = NULL) {
+        if (mLocalRef != NULL) {
+            (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef);
+            mLocalRef = localRef;
+        }
+    }
+
+    T get() const {
+        return mLocalRef;
+    }
+
+private:
+    C_JNIEnv* const mEnv;
+    T mLocalRef;
+
+    DISALLOW_COPY_AND_ASSIGN(scoped_local_ref);
+};
+
+static jclass findClass(C_JNIEnv* env, const char* className) {
+    JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
+    return (*env)->FindClass(e, className);
+}
+
+extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
+    const JNINativeMethod* gMethods, int numMethods)
+{
+    JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
+
+    ALOGV("Registering %s's %d native methods...", className, numMethods);
+
+    scoped_local_ref<jclass> c(env, findClass(env, className));
+    if (c.get() == NULL) {
+        char* tmp;
+        const char* msg;
+        if (asprintf(&tmp,
+                     "Native registration unable to find class '%s'; aborting...",
+                     className) == -1) {
+            // Allocation failed, print default warning.
+            msg = "Native registration unable to find class; aborting...";
+        } else {
+            msg = tmp;
+        }
+        e->FatalError(msg);
+    }
+
+    if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
+        char* tmp;
+        const char* msg;
+        if (asprintf(&tmp, "RegisterNatives failed for '%s'; aborting...", className) == -1) {
+            // Allocation failed, print default warning.
+            msg = "RegisterNatives failed; aborting...";
+        } else {
+            msg = tmp;
+        }
+        e->FatalError(msg);
+    }
+
+    return 0;
+}
+
+/*
+ * Returns a human-readable summary of an exception object.  The buffer will
+ * be populated with the "binary" class name and, if present, the
+ * exception message.
+ */
+static bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) {
+    JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
+
+    /* get the name of the exception's class */
+    scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
+    scoped_local_ref<jclass> classClass(env,
+            (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
+    jmethodID classGetNameMethod =
+            (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
+    scoped_local_ref<jstring> classNameStr(env,
+            (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
+    if (classNameStr.get() == NULL) {
+        (*env)->ExceptionClear(e);
+        result = "<error getting class name>";
+        return false;
+    }
+    const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
+    if (classNameChars == NULL) {
+        (*env)->ExceptionClear(e);
+        result = "<error getting class name UTF-8>";
+        return false;
+    }
+    result += classNameChars;
+    (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
+
+    /* if the exception has a detail message, get that */
+    jmethodID getMessage =
+            (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
+    scoped_local_ref<jstring> messageStr(env,
+            (jstring) (*env)->CallObjectMethod(e, exception, getMessage));
+    if (messageStr.get() == NULL) {
+        return true;
+    }
+
+    result += ": ";
+
+    const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
+    if (messageChars != NULL) {
+        result += messageChars;
+        (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
+    } else {
+        result += "<error getting message>";
+        (*env)->ExceptionClear(e); // clear OOM
+    }
+
+    return true;
+}
+
+/*
+ * Returns an exception (with stack trace) as a string.
+ */
+static bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) {
+    JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
+
+    scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
+    if (stringWriterClass.get() == NULL) {
+        return false;
+    }
+
+    jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V");
+    jmethodID stringWriterToStringMethod =
+            (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;");
+
+    scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter"));
+    if (printWriterClass.get() == NULL) {
+        return false;
+    }
+
+    jmethodID printWriterCtor =
+            (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V");
+
+    scoped_local_ref<jobject> stringWriter(env,
+            (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor));
+    if (stringWriter.get() == NULL) {
+        return false;
+    }
+
+    scoped_local_ref<jobject> printWriter(env,
+            (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get()));
+    if (printWriter.get() == NULL) {
+        return false;
+    }
+
+    scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
+    jmethodID printStackTraceMethod =
+            (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V");
+    (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter.get());
+
+    if ((*env)->ExceptionCheck(e)) {
+        return false;
+    }
+
+    scoped_local_ref<jstring> messageStr(env,
+            (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
+    if (messageStr.get() == NULL) {
+        return false;
+    }
+
+    const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
+    if (utfChars == NULL) {
+        return false;
+    }
+
+    result = utfChars;
+
+    (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
+    return true;
+}
+
+extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
+    JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
+
+    if ((*env)->ExceptionCheck(e)) {
+        /* TODO: consider creating the new exception with this as "cause" */
+        scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
+        (*env)->ExceptionClear(e);
+
+        if (exception.get() != NULL) {
+            std::string text;
+            getExceptionSummary(env, exception.get(), text);
+            ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className);
+        }
+    }
+
+    scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
+    if (exceptionClass.get() == NULL) {
+        ALOGE("Unable to find exception class %s", className);
+        /* ClassNotFoundException now pending */
+        return -1;
+    }
+
+    if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
+        ALOGE("Failed throwing '%s' '%s'", className, msg);
+        /* an exception, most likely OOM, will now be pending */
+        return -1;
+    }
+
+    return 0;
+}
+
+int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
+    char msgBuf[512];
+    vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
+    return jniThrowException(env, className, msgBuf);
+}
+
+int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
+    return jniThrowException(env, "java/lang/NullPointerException", msg);
+}
+
+int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
+    return jniThrowException(env, "java/lang/RuntimeException", msg);
+}
+
+int jniThrowIOException(C_JNIEnv* env, int errnum) {
+    char buffer[80];
+    const char* message = jniStrError(errnum, buffer, sizeof(buffer));
+    return jniThrowException(env, "java/io/IOException", message);
+}
+
+static std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) {
+    JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
+
+    scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e));
+    if (exception == NULL) {
+        exception = currentException.get();
+        if (exception == NULL) {
+          return "<no pending exception>";
+        }
+    }
+
+    if (currentException.get() != NULL) {
+        (*env)->ExceptionClear(e);
+    }
+
+    std::string trace;
+    if (!getStackTrace(env, exception, trace)) {
+        (*env)->ExceptionClear(e);
+        getExceptionSummary(env, exception, trace);
+    }
+
+    if (currentException.get() != NULL) {
+        (*env)->Throw(e, currentException.get()); // rethrow
+    }
+
+    return trace;
+}
+
+void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
+    std::string trace(jniGetStackTrace(env, exception));
+    __android_log_write(priority, tag, trace.c_str());
+}
+
+const char* jniStrError(int errnum, char* buf, size_t buflen) {
+#if __GLIBC__
+    // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
+    // char *strerror_r(int errnum, char *buf, size_t n);
+    return strerror_r(errnum, buf, buflen);
+#else
+    int rc = strerror_r(errnum, buf, buflen);
+    if (rc != 0) {
+        // (POSIX only guarantees a value other than 0. The safest
+        // way to implement this function is to use C++ and overload on the
+        // type of strerror_r to accurately distinguish GNU from POSIX.)
+        snprintf(buf, buflen, "errno %d", errnum);
+    }
+    return buf;
+#endif
+}
+
+jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
+    JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
+    JniConstants::init(e);
+    static jmethodID ctor = e->GetMethodID(JniConstants::fileDescriptorClass, "<init>", "()V");
+    jobject fileDescriptor = (*env)->NewObject(e, JniConstants::fileDescriptorClass, ctor);
+    // NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java
+    // caller if the alloc fails, so we just return NULL when that happens.
+    if (fileDescriptor != NULL)  {
+        jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
+    }
+    return fileDescriptor;
+}
+
+int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
+    JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
+    JniConstants::init(e);
+    static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
+    if (fileDescriptor != NULL) {
+        return (*env)->GetIntField(e, fileDescriptor, fid);
+    } else {
+        return -1;
+    }
+}
+
+void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
+    JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
+    JniConstants::init(e);
+    static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
+    (*env)->SetIntField(e, fileDescriptor, fid, value);
+}
+
+jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
+    JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
+    JniConstants::init(e);
+    static jmethodID get = e->GetMethodID(JniConstants::referenceClass, "get", "()Ljava/lang/Object;");
+    return (*env)->CallObjectMethod(e, ref, get);
+}
+
diff --git a/libnativehelper/JniConstants.cpp b/libnativehelper/JniConstants.cpp
new file mode 100644
index 0000000..cfea131
--- /dev/null
+++ b/libnativehelper/JniConstants.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "JniConstants"
+
+#include "ALog-priv.h"
+#include "JniConstants.h"
+#include "ScopedLocalRef.h"
+
+#include <stdlib.h>
+
+#include <atomic>
+#include <mutex>
+
+static std::atomic<bool> g_constants_initialized(false);
+static std::mutex g_constants_mutex;
+
+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::mutableIntClass;
+jclass JniConstants::mutableLongClass;
+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::structGroupSourceReqClass;
+jclass JniConstants::structIfaddrs;
+jclass JniConstants::structLingerClass;
+jclass JniConstants::structPasswdClass;
+jclass JniConstants::structPollfdClass;
+jclass JniConstants::structStatClass;
+jclass JniConstants::structStatVfsClass;
+jclass JniConstants::structTimevalClass;
+jclass JniConstants::structUcredClass;
+jclass JniConstants::structUtsnameClass;
+jclass JniConstants::unixSocketAddressClass;
+jclass JniConstants::zipEntryClass;
+
+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();
+    }
+    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");
+    mutableIntClass = findClass(env, "android/util/MutableInt");
+    mutableLongClass = findClass(env, "android/util/MutableLong");
+    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");
+    structGroupSourceReqClass = findClass(env, "android/system/StructGroupSourceReq");
+    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");
+    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;
+}
diff --git a/libnativehelper/JniInvocation.cpp b/libnativehelper/JniInvocation.cpp
new file mode 100644
index 0000000..b869914
--- /dev/null
+++ b/libnativehelper/JniInvocation.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "JniInvocation.h"
+
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <cstddef>
+
+#define LOG_TAG "JniInvocation"
+#include "cutils/log.h"
+
+#ifdef __ANDROID__
+#include <sys/system_properties.h>
+#endif
+
+JniInvocation* JniInvocation::jni_invocation_ = NULL;
+
+JniInvocation::JniInvocation() :
+    handle_(NULL),
+    JNI_GetDefaultJavaVMInitArgs_(NULL),
+    JNI_CreateJavaVM_(NULL),
+    JNI_GetCreatedJavaVMs_(NULL) {
+
+  LOG_ALWAYS_FATAL_IF(jni_invocation_ != NULL, "JniInvocation instance already initialized");
+  jni_invocation_ = this;
+}
+
+JniInvocation::~JniInvocation() {
+  jni_invocation_ = NULL;
+  if (handle_ != NULL) {
+    dlclose(handle_);
+  }
+}
+
+#ifdef __ANDROID__
+static const char* kLibrarySystemProperty = "persist.sys.dalvik.vm.lib.2";
+static const char* kDebuggableSystemProperty = "ro.debuggable";
+#endif
+static const char* kLibraryFallback = "libart.so";
+
+template<typename T> void UNUSED(const T&) {}
+
+const char* JniInvocation::GetLibrary(const char* library, char* buffer) {
+#ifdef __ANDROID__
+  const char* default_library;
+
+  char debuggable[PROP_VALUE_MAX];
+  __system_property_get(kDebuggableSystemProperty, debuggable);
+
+  if (strcmp(debuggable, "1") != 0) {
+    // Not a debuggable build.
+    // Do not allow arbitrary library. Ignore the library parameter. This
+    // will also ignore the default library, but initialize to fallback
+    // for cleanliness.
+    library = kLibraryFallback;
+    default_library = kLibraryFallback;
+  } else {
+    // Debuggable build.
+    // Accept the library parameter. For the case it is NULL, load the default
+    // library from the system property.
+    if (buffer != NULL) {
+      if (__system_property_get(kLibrarySystemProperty, buffer) > 0) {
+        default_library = buffer;
+      } else {
+        default_library = kLibraryFallback;
+      }
+    } else {
+      // No buffer given, just use default fallback.
+      default_library = kLibraryFallback;
+    }
+  }
+#else
+  UNUSED(buffer);
+  const char* default_library = kLibraryFallback;
+#endif
+  if (library == NULL) {
+    library = default_library;
+  }
+
+  return library;
+}
+
+bool JniInvocation::Init(const char* library) {
+#ifdef __ANDROID__
+  char buffer[PROP_VALUE_MAX];
+#else
+  char* buffer = NULL;
+#endif
+  library = GetLibrary(library, buffer);
+  // Load with RTLD_NODELETE in order to ensure that libart.so is not unmapped when it is closed.
+  // This is due to the fact that it is possible that some threads might have yet to finish
+  // exiting even after JNI_DeleteJavaVM returns, which can lead to segfaults if the library is
+  // unloaded.
+  const int kDlopenFlags = RTLD_NOW | RTLD_NODELETE;
+  handle_ = dlopen(library, kDlopenFlags);
+  if (handle_ == NULL) {
+    if (strcmp(library, kLibraryFallback) == 0) {
+      // Nothing else to try.
+      ALOGE("Failed to dlopen %s: %s", library, dlerror());
+      return false;
+    }
+    // Note that this is enough to get something like the zygote
+    // running, we can't property_set here to fix this for the future
+    // because we are root and not the system user. See
+    // RuntimeInit.commonInit for where we fix up the property to
+    // avoid future fallbacks. http://b/11463182
+    ALOGW("Falling back from %s to %s after dlopen error: %s",
+          library, kLibraryFallback, dlerror());
+    library = kLibraryFallback;
+    handle_ = dlopen(library, kDlopenFlags);
+    if (handle_ == NULL) {
+      ALOGE("Failed to dlopen %s: %s", library, dlerror());
+      return false;
+    }
+  }
+  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_),
+                  "JNI_GetDefaultJavaVMInitArgs")) {
+    return false;
+  }
+  if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_),
+                  "JNI_CreateJavaVM")) {
+    return false;
+  }
+  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_),
+                  "JNI_GetCreatedJavaVMs")) {
+    return false;
+  }
+  return true;
+}
+
+jint JniInvocation::JNI_GetDefaultJavaVMInitArgs(void* vmargs) {
+  return JNI_GetDefaultJavaVMInitArgs_(vmargs);
+}
+
+jint JniInvocation::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
+  return JNI_CreateJavaVM_(p_vm, p_env, vm_args);
+}
+
+jint JniInvocation::JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) {
+  return JNI_GetCreatedJavaVMs_(vms, size, vm_count);
+}
+
+bool JniInvocation::FindSymbol(void** pointer, const char* symbol) {
+  *pointer = dlsym(handle_, symbol);
+  if (*pointer == NULL) {
+    ALOGE("Failed to find symbol %s: %s\n", symbol, dlerror());
+    dlclose(handle_);
+    handle_ = NULL;
+    return false;
+  }
+  return true;
+}
+
+JniInvocation& JniInvocation::GetJniInvocation() {
+  LOG_ALWAYS_FATAL_IF(jni_invocation_ == NULL,
+                      "Failed to create JniInvocation instance before using JNI invocation API");
+  return *jni_invocation_;
+}
+
+extern "C" jint JNI_GetDefaultJavaVMInitArgs(void* vm_args) {
+  return JniInvocation::GetJniInvocation().JNI_GetDefaultJavaVMInitArgs(vm_args);
+}
+
+extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
+  return JniInvocation::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args);
+}
+
+extern "C" jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count) {
+  return JniInvocation::GetJniInvocation().JNI_GetCreatedJavaVMs(vms, size, vm_count);
+}
diff --git a/libnativehelper/MODULE_LICENSE_APACHE2 b/libnativehelper/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libnativehelper/MODULE_LICENSE_APACHE2
diff --git a/libnativehelper/NOTICE b/libnativehelper/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libnativehelper/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/libnativehelper/README b/libnativehelper/README
new file mode 100644
index 0000000..5a5f5d4
--- /dev/null
+++ b/libnativehelper/README
@@ -0,0 +1,11 @@
+Support functions for Android's class libraries
+
+
+These are VM-agnostic native functions that implement methods for system
+class libraries.  All code here:
+
+ - MUST not be associated with an android.* class (that code lives in
+   frameworks/base/).
+ - SHOULD be written in C rather than C++ where possible.
+
+Some helper functions are defined in include/nativehelper/JNIHelp.h.
diff --git a/libnativehelper/include/nativehelper/AsynchronousCloseMonitor.h b/libnativehelper/include/nativehelper/AsynchronousCloseMonitor.h
new file mode 100644
index 0000000..2afc35c
--- /dev/null
+++ b/libnativehelper/include/nativehelper/AsynchronousCloseMonitor.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ASYNCHRONOUS_CLOSE_MONITOR_H_included
+#define ASYNCHRONOUS_CLOSE_MONITOR_H_included
+
+#include <pthread.h>
+
+/**
+ * AsynchronousCloseMonitor helps implement Java's asynchronous close semantics.
+ *
+ * AsynchronousCloseMonitor::init must be called before anything else.
+ *
+ * Every blocking I/O operation must be surrounded by an AsynchronousCloseMonitor
+ * instance. For example:
+ *
+ *   {
+ *     AsynchronousCloseMonitor monitor(fd);
+ *     byteCount = ::read(fd, buf, sizeof(buf));
+ *   }
+ *
+ * To interrupt all threads currently blocked on file descriptor 'fd', call signalBlockedThreads:
+ *
+ *   AsynchronousCloseMonitor::signalBlockedThreads(fd);
+ *
+ * To test to see if the interruption was due to the signalBlockedThreads call:
+ *
+ *   monitor.wasSignaled();
+ */
+class AsynchronousCloseMonitor {
+public:
+    explicit AsynchronousCloseMonitor(int fd);
+    ~AsynchronousCloseMonitor();
+    bool wasSignaled() const;
+
+    static void init();
+
+    static void signalBlockedThreads(int fd);
+
+private:
+    AsynchronousCloseMonitor* mPrev;
+    AsynchronousCloseMonitor* mNext;
+    pthread_t mThread;
+    int mFd;
+    bool mSignaled;
+
+    // Disallow copy and assignment.
+    AsynchronousCloseMonitor(const AsynchronousCloseMonitor&);
+    void operator=(const AsynchronousCloseMonitor&);
+};
+
+#endif  // ASYNCHRONOUS_CLOSE_MONITOR_H_included
diff --git a/libnativehelper/include/nativehelper/JNIHelp.h b/libnativehelper/include/nativehelper/JNIHelp.h
new file mode 100644
index 0000000..bf01986
--- /dev/null
+++ b/libnativehelper/include/nativehelper/JNIHelp.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * JNI helper functions.
+ *
+ * This file may be included by C or C++ code, which is trouble because jni.h
+ * uses different typedefs for JNIEnv in each language.
+ *
+ * TODO: remove C support.
+ */
+#ifndef NATIVEHELPER_JNIHELP_H_
+#define NATIVEHELPER_JNIHELP_H_
+
+#include "jni.h"
+#include <errno.h>
+#include <unistd.h>
+
+#ifndef NELEM
+# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Register one or more native methods with a particular class.
+ * "className" looks like "java/lang/String". Aborts on failure.
+ * TODO: fix all callers and change the return type to void.
+ */
+int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods);
+
+/*
+ * Throw an exception with the specified class and an optional message.
+ *
+ * The "className" argument will be passed directly to FindClass, which
+ * takes strings with slashes (e.g. "java/lang/Object").
+ *
+ * If an exception is currently pending, we log a warning message and
+ * clear it.
+ *
+ * Returns 0 on success, nonzero if something failed (e.g. the exception
+ * class couldn't be found, so *an* exception will still be pending).
+ *
+ * Currently aborts the VM if it can't throw the exception.
+ */
+int jniThrowException(C_JNIEnv* env, const char* className, const char* msg);
+
+/*
+ * Throw a java.lang.NullPointerException, with an optional message.
+ */
+int jniThrowNullPointerException(C_JNIEnv* env, const char* msg);
+
+/*
+ * Throw a java.lang.RuntimeException, with an optional message.
+ */
+int jniThrowRuntimeException(C_JNIEnv* env, const char* msg);
+
+/*
+ * Throw a java.io.IOException, generating the message from errno.
+ */
+int jniThrowIOException(C_JNIEnv* env, int errnum);
+
+/*
+ * Return a pointer to a locale-dependent error string explaining errno
+ * value 'errnum'. The returned pointer may or may not be equal to 'buf'.
+ * This function is thread-safe (unlike strerror) and portable (unlike
+ * strerror_r).
+ */
+const char* jniStrError(int errnum, char* buf, size_t buflen);
+
+/*
+ * Returns a new java.io.FileDescriptor for the given int fd.
+ */
+jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd);
+
+/*
+ * Returns the int fd from a java.io.FileDescriptor.
+ */
+int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor);
+
+/*
+ * Sets the int fd in a java.io.FileDescriptor.
+ */
+void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value);
+
+/*
+ * Returns the reference from a java.lang.ref.Reference.
+ */
+jobject jniGetReferent(C_JNIEnv* env, jobject ref);
+
+/*
+ * Log a message and an exception.
+ * If exception is NULL, logs the current exception in the JNI environment.
+ */
+void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/*
+ * For C++ code, we provide inlines that map to the C functions.  g++ always
+ * inlines these, even on non-optimized builds.
+ */
+#if defined(__cplusplus)
+inline int jniRegisterNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods) {
+    return jniRegisterNativeMethods(&env->functions, className, gMethods, numMethods);
+}
+
+inline int jniThrowException(JNIEnv* env, const char* className, const char* msg) {
+    return jniThrowException(&env->functions, className, msg);
+}
+
+extern "C" int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args);
+
+/*
+ * Equivalent to jniThrowException but with a printf-like format string and
+ * variable-length argument list. This is only available in C++.
+ */
+inline int jniThrowExceptionFmt(JNIEnv* env, const char* className, const char* fmt, ...) {
+    va_list args;
+    va_start(args, fmt);
+    return jniThrowExceptionFmt(&env->functions, className, fmt, args);
+    va_end(args);
+}
+
+inline int jniThrowNullPointerException(JNIEnv* env, const char* msg) {
+    return jniThrowNullPointerException(&env->functions, msg);
+}
+
+inline int jniThrowRuntimeException(JNIEnv* env, const char* msg) {
+    return jniThrowRuntimeException(&env->functions, msg);
+}
+
+inline int jniThrowIOException(JNIEnv* env, int errnum) {
+    return jniThrowIOException(&env->functions, errnum);
+}
+
+inline jobject jniCreateFileDescriptor(JNIEnv* env, int fd) {
+    return jniCreateFileDescriptor(&env->functions, fd);
+}
+
+inline int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) {
+    return jniGetFDFromFileDescriptor(&env->functions, fileDescriptor);
+}
+
+inline void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) {
+    jniSetFileDescriptorOfFD(&env->functions, fileDescriptor, value);
+}
+
+inline jobject jniGetReferent(JNIEnv* env, jobject ref) {
+    return jniGetReferent(&env->functions, ref);
+}
+
+inline void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable exception = NULL) {
+    jniLogException(&env->functions, priority, tag, exception);
+}
+
+#if !defined(DISALLOW_COPY_AND_ASSIGN)
+// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private:
+// declarations in a class.
+#if __cplusplus >= 201103L
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+  TypeName(const TypeName&) = delete;  \
+  void operator=(const TypeName&) = delete
+#else
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+  TypeName(const TypeName&);  \
+  void operator=(const TypeName&)
+#endif  // __has_feature(cxx_deleted_functions)
+#endif  // !defined(DISALLOW_COPY_AND_ASSIGN)
+
+#endif
+
+/*
+ * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
+ * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
+ * not already defined, then define it here.
+ */
+#ifndef TEMP_FAILURE_RETRY
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) ({         \
+    typeof (exp) _rc;                      \
+    do {                                   \
+        _rc = (exp);                       \
+    } while (_rc == -1 && errno == EINTR); \
+    _rc; })
+#endif
+
+#endif  /* NATIVEHELPER_JNIHELP_H_ */
diff --git a/libnativehelper/include/nativehelper/JniConstants.h b/libnativehelper/include/nativehelper/JniConstants.h
new file mode 100644
index 0000000..a4ce4fc
--- /dev/null
+++ b/libnativehelper/include/nativehelper/JniConstants.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef JNI_CONSTANTS_H_included
+#define JNI_CONSTANTS_H_included
+
+#include "JNIHelp.h"
+
+/**
+ * A cache to avoid calling FindClass at runtime.
+ *
+ * Class lookup is relatively expensive, so we do these lookups at startup. This means that code
+ * that never uses, say, java.util.zip.Deflater still has to pay for the lookup, but it means that
+ * on device the cost is paid during boot and amortized. A central cache also removes the temptation
+ * to dynamically call FindClass rather than add a small cache to each file that needs one. Another
+ * cost is that each class cached here requires a global reference, though in practice we save
+ * enough by not having a global reference for each file that uses a class such as java.lang.String
+ * which is used in several files.
+ *
+ * FindClass is still called in a couple of situations: when throwing exceptions, and in some of
+ * the serialization code. The former is clearly not a performance case, and we're currently
+ * assuming that neither is the latter.
+ *
+ * TODO: similar arguments hold for field and method IDs; we should cache them centrally too.
+ */
+struct JniConstants {
+    static void init(JNIEnv* env);
+
+    static jclass booleanClass;
+    static jclass byteArrayClass;
+    static jclass calendarClass;
+    static jclass charsetICUClass;
+    static jclass doubleClass;
+    static jclass errnoExceptionClass;
+    static jclass fileDescriptorClass;
+    static jclass gaiExceptionClass;
+    static jclass inet6AddressClass;
+    static jclass inet6AddressHolderClass;
+    static jclass inetAddressClass;
+    static jclass inetAddressHolderClass;
+    static jclass inetSocketAddressClass;
+    static jclass inetSocketAddressHolderClass;
+    static jclass integerClass;
+    static jclass localeDataClass;
+    static jclass longClass;
+    static jclass mutableIntClass;
+    static jclass mutableLongClass;
+    static jclass netlinkSocketAddressClass;
+    static jclass packetSocketAddressClass;
+    static jclass patternSyntaxExceptionClass;
+    static jclass referenceClass;
+    static jclass socketTaggerClass;
+    static jclass stringClass;
+    static jclass structAddrinfoClass;
+    static jclass structFlockClass;
+    static jclass structGroupReqClass;
+    static jclass structGroupSourceReqClass;
+    static jclass structIfaddrs;
+    static jclass structLingerClass;
+    static jclass structPasswdClass;
+    static jclass structPollfdClass;
+    static jclass structStatClass;
+    static jclass structStatVfsClass;
+    static jclass structTimevalClass;
+    static jclass structUcredClass;
+    static jclass structUtsnameClass;
+    static jclass unixSocketAddressClass;
+    static jclass zipEntryClass;
+};
+
+#define NATIVE_METHOD(className, functionName, signature) \
+    { #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName) }
+
+#endif  // JNI_CONSTANTS_H_included
diff --git a/libnativehelper/include/nativehelper/JniInvocation.h b/libnativehelper/include/nativehelper/JniInvocation.h
new file mode 100644
index 0000000..fc2ed0a
--- /dev/null
+++ b/libnativehelper/include/nativehelper/JniInvocation.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef JNI_INVOCATION_H_included
+#define JNI_INVOCATION_H_included
+
+#include <jni.h>
+
+// JniInvocation adds a layer of indirection for applications using
+// the JNI invocation API to allow the JNI implementation to be
+// selected dynamically. Apps can specify a specific implementation to
+// be used by calling InitJniInvocation. If this is not done, the
+// library will chosen based on the value of Android system property
+// persist.sys.dalvik.vm.lib on the device, and otherwise fall back to
+// a hard-coded default implementation.
+class JniInvocation {
+ public:
+  JniInvocation();
+
+  ~JniInvocation();
+
+  // Initialize JNI invocation API. library should specifiy a valid
+  // shared library for opening via dlopen providing a JNI invocation
+  // implementation, or null to allow defaulting via
+  // persist.sys.dalvik.vm.lib.
+  bool Init(const char* library);
+
+  // Exposes which library is actually loaded from the given name. The
+  // buffer of size PROPERTY_VALUE_MAX will be used to load the system
+  // property for the default library, if necessary. If no buffer is
+  // provided, the fallback value will be used.
+  static const char* GetLibrary(const char* library, char* buffer);
+
+ private:
+
+  bool FindSymbol(void** pointer, const char* symbol);
+
+  static JniInvocation& GetJniInvocation();
+
+  jint JNI_GetDefaultJavaVMInitArgs(void* vmargs);
+  jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args);
+  jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count);
+
+  static JniInvocation* jni_invocation_;
+
+  void* handle_;
+  jint (*JNI_GetDefaultJavaVMInitArgs_)(void*);
+  jint (*JNI_CreateJavaVM_)(JavaVM**, JNIEnv**, void*);
+  jint (*JNI_GetCreatedJavaVMs_)(JavaVM**, jsize, jsize*);
+
+  friend jint JNI_GetDefaultJavaVMInitArgs(void* vm_args);
+  friend jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args);
+  friend jint JNI_GetCreatedJavaVMs(JavaVM** vms, jsize size, jsize* vm_count);
+};
+
+#endif  // JNI_INVOCATION_H_included
diff --git a/libnativehelper/include/nativehelper/ScopedBytes.h b/libnativehelper/include/nativehelper/ScopedBytes.h
new file mode 100644
index 0000000..fec46e8
--- /dev/null
+++ b/libnativehelper/include/nativehelper/ScopedBytes.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SCOPED_BYTES_H_included
+#define SCOPED_BYTES_H_included
+
+#include "JNIHelp.h"
+
+/**
+ * ScopedBytesRO and ScopedBytesRW attempt to paper over the differences between byte[]s and
+ * ByteBuffers. This in turn helps paper over the differences between non-direct ByteBuffers backed
+ * by byte[]s, direct ByteBuffers backed by bytes[]s, and direct ByteBuffers not backed by byte[]s.
+ * (On Android, this last group only contains MappedByteBuffers.)
+ */
+template<bool readOnly>
+class ScopedBytes {
+public:
+    ScopedBytes(JNIEnv* env, jobject object)
+    : mEnv(env), mObject(object), mByteArray(NULL), mPtr(NULL)
+    {
+        if (mObject == NULL) {
+            jniThrowNullPointerException(mEnv, NULL);
+        } else if (mEnv->IsInstanceOf(mObject, JniConstants::byteArrayClass)) {
+            mByteArray = reinterpret_cast<jbyteArray>(mObject);
+            mPtr = mEnv->GetByteArrayElements(mByteArray, NULL);
+        } else {
+            mPtr = reinterpret_cast<jbyte*>(mEnv->GetDirectBufferAddress(mObject));
+        }
+    }
+
+    ~ScopedBytes() {
+        if (mByteArray != NULL) {
+            mEnv->ReleaseByteArrayElements(mByteArray, mPtr, readOnly ? JNI_ABORT : 0);
+        }
+    }
+
+private:
+    JNIEnv* const mEnv;
+    const jobject mObject;
+    jbyteArray mByteArray;
+
+protected:
+    jbyte* mPtr;
+
+private:
+    DISALLOW_COPY_AND_ASSIGN(ScopedBytes);
+};
+
+class ScopedBytesRO : public ScopedBytes<true> {
+public:
+    ScopedBytesRO(JNIEnv* env, jobject object) : ScopedBytes<true>(env, object) {}
+    const jbyte* get() const {
+        return mPtr;
+    }
+};
+
+class ScopedBytesRW : public ScopedBytes<false> {
+public:
+    ScopedBytesRW(JNIEnv* env, jobject object) : ScopedBytes<false>(env, object) {}
+    jbyte* get() {
+        return mPtr;
+    }
+};
+
+#endif  // SCOPED_BYTES_H_included
diff --git a/libnativehelper/include/nativehelper/ScopedLocalFrame.h b/libnativehelper/include/nativehelper/ScopedLocalFrame.h
new file mode 100644
index 0000000..a74611f
--- /dev/null
+++ b/libnativehelper/include/nativehelper/ScopedLocalFrame.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SCOPED_LOCAL_FRAME_H_included
+#define SCOPED_LOCAL_FRAME_H_included
+
+#include "JNIHelp.h"
+
+class ScopedLocalFrame {
+public:
+    explicit ScopedLocalFrame(JNIEnv* env) : mEnv(env) {
+        mEnv->PushLocalFrame(128);
+    }
+
+    ~ScopedLocalFrame() {
+        mEnv->PopLocalFrame(NULL);
+    }
+
+private:
+    JNIEnv* const mEnv;
+
+    DISALLOW_COPY_AND_ASSIGN(ScopedLocalFrame);
+};
+
+#endif  // SCOPED_LOCAL_FRAME_H_included
diff --git a/libnativehelper/include/nativehelper/ScopedLocalRef.h b/libnativehelper/include/nativehelper/ScopedLocalRef.h
new file mode 100644
index 0000000..6e30eb3
--- /dev/null
+++ b/libnativehelper/include/nativehelper/ScopedLocalRef.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SCOPED_LOCAL_REF_H_included
+#define SCOPED_LOCAL_REF_H_included
+
+#include "jni.h"
+
+#include <stddef.h>
+#include "JNIHelp.h"  // for DISALLOW_COPY_AND_ASSIGN.
+
+// A smart pointer that deletes a JNI local reference when it goes out of scope.
+template<typename T>
+class ScopedLocalRef {
+public:
+    ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) {
+    }
+
+    ~ScopedLocalRef() {
+        reset();
+    }
+
+    void reset(T ptr = NULL) {
+        if (ptr != mLocalRef) {
+            if (mLocalRef != NULL) {
+                mEnv->DeleteLocalRef(mLocalRef);
+            }
+            mLocalRef = ptr;
+        }
+    }
+
+    T release() __attribute__((warn_unused_result)) {
+        T localRef = mLocalRef;
+        mLocalRef = NULL;
+        return localRef;
+    }
+
+    T get() const {
+        return mLocalRef;
+    }
+
+private:
+    JNIEnv* const mEnv;
+    T mLocalRef;
+
+    DISALLOW_COPY_AND_ASSIGN(ScopedLocalRef);
+};
+
+#endif  // SCOPED_LOCAL_REF_H_included
diff --git a/libnativehelper/include/nativehelper/ScopedPrimitiveArray.h b/libnativehelper/include/nativehelper/ScopedPrimitiveArray.h
new file mode 100644
index 0000000..46f1786
--- /dev/null
+++ b/libnativehelper/include/nativehelper/ScopedPrimitiveArray.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SCOPED_PRIMITIVE_ARRAY_H_included
+#define SCOPED_PRIMITIVE_ARRAY_H_included
+
+#include "JNIHelp.h"
+
+#ifdef POINTER_TYPE
+#error POINTER_TYPE is defined.
+#else
+#define POINTER_TYPE(T) T*  /* NOLINT */
+#endif
+
+#ifdef REFERENCE_TYPE
+#error REFERENCE_TYPE is defined.
+#else
+#define REFERENCE_TYPE(T) T&  /* NOLINT */
+#endif
+
+// ScopedBooleanArrayRO, ScopedByteArrayRO, ScopedCharArrayRO, ScopedDoubleArrayRO,
+// ScopedFloatArrayRO, ScopedIntArrayRO, ScopedLongArrayRO, and ScopedShortArrayRO provide
+// convenient read-only access to Java arrays from JNI code. This is cheaper than read-write
+// access and should be used by default.
+#define INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(PRIMITIVE_TYPE, NAME) \
+    class Scoped ## NAME ## ArrayRO { \
+    public: \
+        explicit Scoped ## NAME ## ArrayRO(JNIEnv* env) \
+        : mEnv(env), mJavaArray(NULL), mRawArray(NULL), mSize(0) {} \
+        Scoped ## NAME ## ArrayRO(JNIEnv* env, PRIMITIVE_TYPE ## Array javaArray) \
+        : mEnv(env) { \
+            if (javaArray == NULL) { \
+                mJavaArray = NULL; \
+                mSize = 0; \
+                mRawArray = NULL; \
+                jniThrowNullPointerException(mEnv, NULL); \
+            } else { \
+                reset(javaArray); \
+            } \
+        } \
+        ~Scoped ## NAME ## ArrayRO() { \
+            if (mRawArray != NULL && mRawArray != mBuffer) { \
+                mEnv->Release ## NAME ## ArrayElements(mJavaArray, mRawArray, JNI_ABORT); \
+            } \
+        } \
+        void reset(PRIMITIVE_TYPE ## Array javaArray) { \
+            mJavaArray = javaArray; \
+            mSize = mEnv->GetArrayLength(mJavaArray); \
+            if (mSize <= buffer_size) { \
+                mEnv->Get ## NAME ## ArrayRegion(mJavaArray, 0, mSize, mBuffer); \
+                mRawArray = mBuffer; \
+            } else { \
+                mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, NULL); \
+            } \
+        } \
+        const PRIMITIVE_TYPE* get() const { return mRawArray; } \
+        PRIMITIVE_TYPE ## Array getJavaArray() const { return mJavaArray; } \
+        const PRIMITIVE_TYPE& operator[](size_t n) const { return mRawArray[n]; } \
+        size_t size() const { return mSize; } \
+    private: \
+        static const jsize buffer_size = 1024; \
+        JNIEnv* const mEnv; \
+        PRIMITIVE_TYPE ## Array mJavaArray; \
+        POINTER_TYPE(PRIMITIVE_TYPE) mRawArray; \
+        jsize mSize; \
+        PRIMITIVE_TYPE mBuffer[buffer_size]; \
+        DISALLOW_COPY_AND_ASSIGN(Scoped ## NAME ## ArrayRO); \
+    }
+
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jboolean, Boolean);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jbyte, Byte);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jchar, Char);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jdouble, Double);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jfloat, Float);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jint, Int);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jlong, Long);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jshort, Short);
+
+#undef INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO
+
+// ScopedBooleanArrayRW, ScopedByteArrayRW, ScopedCharArrayRW, ScopedDoubleArrayRW,
+// ScopedFloatArrayRW, ScopedIntArrayRW, ScopedLongArrayRW, and ScopedShortArrayRW provide
+// convenient read-write access to Java arrays from JNI code. These are more expensive,
+// since they entail a copy back onto the Java heap, and should only be used when necessary.
+#define INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(PRIMITIVE_TYPE, NAME) \
+    class Scoped ## NAME ## ArrayRW { \
+    public: \
+        explicit Scoped ## NAME ## ArrayRW(JNIEnv* env) \
+        : mEnv(env), mJavaArray(NULL), mRawArray(NULL) {} \
+        Scoped ## NAME ## ArrayRW(JNIEnv* env, PRIMITIVE_TYPE ## Array javaArray) \
+        : mEnv(env), mJavaArray(javaArray), mRawArray(NULL) { \
+            if (mJavaArray == NULL) { \
+                jniThrowNullPointerException(mEnv, NULL); \
+            } else { \
+                mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, NULL); \
+            } \
+        } \
+        ~Scoped ## NAME ## ArrayRW() { \
+            if (mRawArray) { \
+                mEnv->Release ## NAME ## ArrayElements(mJavaArray, mRawArray, 0); \
+            } \
+        } \
+        void reset(PRIMITIVE_TYPE ## Array javaArray) { \
+            mJavaArray = javaArray; \
+            mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, NULL); \
+        } \
+        const PRIMITIVE_TYPE* get() const { return mRawArray; } \
+        PRIMITIVE_TYPE ## Array getJavaArray() const { return mJavaArray; } \
+        const PRIMITIVE_TYPE& operator[](size_t n) const { return mRawArray[n]; } \
+        POINTER_TYPE(PRIMITIVE_TYPE) get() { return mRawArray; }  \
+        REFERENCE_TYPE(PRIMITIVE_TYPE) operator[](size_t n) { return mRawArray[n]; } \
+        size_t size() const { return mEnv->GetArrayLength(mJavaArray); } \
+    private: \
+        JNIEnv* const mEnv; \
+        PRIMITIVE_TYPE ## Array mJavaArray; \
+        POINTER_TYPE(PRIMITIVE_TYPE) mRawArray; \
+        DISALLOW_COPY_AND_ASSIGN(Scoped ## NAME ## ArrayRW); \
+    }
+
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jboolean, Boolean);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jbyte, Byte);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jchar, Char);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jdouble, Double);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jfloat, Float);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jint, Int);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jlong, Long);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jshort, Short);
+
+#undef INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW
+#undef POINTER_TYPE
+#undef REFERENCE_TYPE
+
+#endif  // SCOPED_PRIMITIVE_ARRAY_H_included
diff --git a/libnativehelper/include/nativehelper/ScopedStringChars.h b/libnativehelper/include/nativehelper/ScopedStringChars.h
new file mode 100644
index 0000000..688291d
--- /dev/null
+++ b/libnativehelper/include/nativehelper/ScopedStringChars.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SCOPED_STRING_CHARS_H_included
+#define SCOPED_STRING_CHARS_H_included
+
+#include "JNIHelp.h"
+
+// A smart pointer that provides access to a jchar* given a JNI jstring.
+// Unlike GetStringChars, we throw NullPointerException rather than abort if
+// passed a null jstring, and get will return NULL.
+// This makes the correct idiom very simple:
+//
+//   ScopedStringChars name(env, java_name);
+//   if (name.get() == NULL) {
+//     return NULL;
+//   }
+class ScopedStringChars {
+ public:
+  ScopedStringChars(JNIEnv* env, jstring s) : env_(env), string_(s), size_(0) {
+    if (s == NULL) {
+      chars_ = NULL;
+      jniThrowNullPointerException(env, NULL);
+    } else {
+      chars_ = env->GetStringChars(string_, NULL);
+      if (chars_ != NULL) {
+        size_ = env->GetStringLength(string_);
+      }
+    }
+  }
+
+  ~ScopedStringChars() {
+    if (chars_ != NULL) {
+      env_->ReleaseStringChars(string_, chars_);
+    }
+  }
+
+  const jchar* get() const {
+    return chars_;
+  }
+
+  size_t size() const {
+    return size_;
+  }
+
+  const jchar& operator[](size_t n) const {
+    return chars_[n];
+  }
+
+ private:
+  JNIEnv* const env_;
+  const jstring string_;
+  const jchar* chars_;
+  size_t size_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedStringChars);
+};
+
+#endif  // SCOPED_STRING_CHARS_H_included
diff --git a/libnativehelper/include/nativehelper/ScopedUtfChars.h b/libnativehelper/include/nativehelper/ScopedUtfChars.h
new file mode 100644
index 0000000..9cfa9a0
--- /dev/null
+++ b/libnativehelper/include/nativehelper/ScopedUtfChars.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SCOPED_UTF_CHARS_H_included
+#define SCOPED_UTF_CHARS_H_included
+
+#include "JNIHelp.h"
+#include <string.h>
+
+// A smart pointer that provides read-only access to a Java string's UTF chars.
+// Unlike GetStringUTFChars, we throw NullPointerException rather than abort if
+// passed a null jstring, and c_str will return NULL.
+// This makes the correct idiom very simple:
+//
+//   ScopedUtfChars name(env, java_name);
+//   if (name.c_str() == NULL) {
+//     return NULL;
+//   }
+class ScopedUtfChars {
+ public:
+  ScopedUtfChars(JNIEnv* env, jstring s) : env_(env), string_(s) {
+    if (s == NULL) {
+      utf_chars_ = NULL;
+      jniThrowNullPointerException(env, NULL);
+    } else {
+      utf_chars_ = env->GetStringUTFChars(s, NULL);
+    }
+  }
+
+  ~ScopedUtfChars() {
+    if (utf_chars_) {
+      env_->ReleaseStringUTFChars(string_, utf_chars_);
+    }
+  }
+
+  const char* c_str() const {
+    return utf_chars_;
+  }
+
+  size_t size() const {
+    return strlen(utf_chars_);
+  }
+
+  const char& operator[](size_t n) const {
+    return utf_chars_[n];
+  }
+
+ private:
+  JNIEnv* const env_;
+  const jstring string_;
+  const char* utf_chars_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedUtfChars);
+};
+
+#endif  // SCOPED_UTF_CHARS_H_included
diff --git a/libnativehelper/include/nativehelper/UniquePtr.h b/libnativehelper/include/nativehelper/UniquePtr.h
new file mode 100644
index 0000000..ac88be1
--- /dev/null
+++ b/libnativehelper/include/nativehelper/UniquePtr.h
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef UNIQUE_PTR_H_included
+#define UNIQUE_PTR_H_included
+
+#include <cstdlib> // For NULL.
+#include "JNIHelp.h"  // For DISALLOW_COPY_AND_ASSIGN.
+
+// Default deleter for pointer types.
+template <typename T>
+struct DefaultDelete {
+    enum { type_must_be_complete = sizeof(T) };
+    DefaultDelete() {}
+    void operator()(T* p) const {
+        delete p;
+    }
+};
+
+// Default deleter for array types.
+template <typename T>
+struct DefaultDelete<T[]> {
+    enum { type_must_be_complete = sizeof(T) };
+    void operator()(T* p) const {
+        delete[] p;
+    }
+};
+
+// A smart pointer that deletes the given pointer on destruction.
+// Equivalent to C++0x's std::unique_ptr (a combination of boost::scoped_ptr
+// and boost::scoped_array).
+// Named to be in keeping with Android style but also to avoid
+// collision with any other implementation, until we can switch over
+// to unique_ptr.
+// Use thus:
+//   UniquePtr<C> c(new C);
+template <typename T, typename D = DefaultDelete<T> >
+class UniquePtr {
+public:
+    // Construct a new UniquePtr, taking ownership of the given raw pointer.
+    explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) {
+    }
+
+    ~UniquePtr() {
+        reset();
+    }
+
+    // Accessors.
+    T& operator*() const { return *mPtr; }
+    T* operator->() const { return mPtr; }
+    T* get() const { return mPtr; }
+
+    // Returns the raw pointer and hands over ownership to the caller.
+    // The pointer will not be deleted by UniquePtr.
+    T* release() __attribute__((warn_unused_result)) {
+        T* result = mPtr;
+        mPtr = NULL;
+        return result;
+    }
+
+    // Takes ownership of the given raw pointer.
+    // If this smart pointer previously owned a different raw pointer, that
+    // raw pointer will be freed.
+    void reset(T* ptr = NULL) {
+        if (ptr != mPtr) {
+            D()(mPtr);
+            mPtr = ptr;
+        }
+    }
+
+private:
+    // The raw pointer.
+    T* mPtr;
+
+    // Comparing unique pointers is probably a mistake, since they're unique.
+    template <typename T2> bool operator==(const UniquePtr<T2>& p) const;
+    template <typename T2> bool operator!=(const UniquePtr<T2>& p) const;
+
+    DISALLOW_COPY_AND_ASSIGN(UniquePtr);
+};
+
+// Partial specialization for array types. Like std::unique_ptr, this removes
+// operator* and operator-> but adds operator[].
+template <typename T, typename D>
+class UniquePtr<T[], D> {
+public:
+    explicit UniquePtr(T* ptr = NULL) : mPtr(ptr) {
+    }
+
+    ~UniquePtr() {
+        reset();
+    }
+
+    T& operator[](size_t i) const {
+        return mPtr[i];
+    }
+    T* get() const { return mPtr; }
+
+    T* release() __attribute__((warn_unused_result)) {
+        T* result = mPtr;
+        mPtr = NULL;
+        return result;
+    }
+
+    void reset(T* ptr = NULL) {
+        if (ptr != mPtr) {
+            D()(mPtr);
+            mPtr = ptr;
+        }
+    }
+
+private:
+    T* mPtr;
+
+    DISALLOW_COPY_AND_ASSIGN(UniquePtr);
+};
+
+#if UNIQUE_PTR_TESTS
+
+// Run these tests with:
+// g++ -g -DUNIQUE_PTR_TESTS -x c++ UniquePtr.h && ./a.out
+
+#include <stdio.h>
+
+static void assert(bool b) {
+    if (!b) {
+        fprintf(stderr, "FAIL\n");
+        abort();
+    }
+    fprintf(stderr, "OK\n");
+}
+static int cCount = 0;
+struct C {
+    C() { ++cCount; }
+    ~C() { --cCount; }
+};
+static bool freed = false;
+struct Freer {
+    void operator()(int* p) {
+        assert(*p == 123);
+        free(p);
+        freed = true;
+    }
+};
+
+int main(int argc, char* argv[]) {
+    //
+    // UniquePtr<T> tests...
+    //
+
+    // Can we free a single object?
+    {
+        UniquePtr<C> c(new C);
+        assert(cCount == 1);
+    }
+    assert(cCount == 0);
+    // Does release work?
+    C* rawC;
+    {
+        UniquePtr<C> c(new C);
+        assert(cCount == 1);
+        rawC = c.release();
+    }
+    assert(cCount == 1);
+    delete rawC;
+    // Does reset work?
+    {
+        UniquePtr<C> c(new C);
+        assert(cCount == 1);
+        c.reset(new C);
+        assert(cCount == 1);
+    }
+    assert(cCount == 0);
+
+    //
+    // UniquePtr<T[]> tests...
+    //
+
+    // Can we free an array?
+    {
+        UniquePtr<C[]> cs(new C[4]);
+        assert(cCount == 4);
+    }
+    assert(cCount == 0);
+    // Does release work?
+    {
+        UniquePtr<C[]> c(new C[4]);
+        assert(cCount == 4);
+        rawC = c.release();
+    }
+    assert(cCount == 4);
+    delete[] rawC;
+    // Does reset work?
+    {
+        UniquePtr<C[]> c(new C[4]);
+        assert(cCount == 4);
+        c.reset(new C[2]);
+        assert(cCount == 2);
+    }
+    assert(cCount == 0);
+
+    //
+    // Custom deleter tests...
+    //
+    assert(!freed);
+    {
+        UniquePtr<int, Freer> i(reinterpret_cast<int*>(malloc(sizeof(int))));
+        *i = 123;
+    }
+    assert(freed);
+    return 0;
+}
+#endif
+
+#endif  // UNIQUE_PTR_H_included
diff --git a/libnativehelper/include/nativehelper/jni.h b/libnativehelper/include/nativehelper/jni.h
new file mode 100644
index 0000000..1c2fb0c
--- /dev/null
+++ b/libnativehelper/include/nativehelper/jni.h
@@ -0,0 +1,1141 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * JNI specification, as defined by Sun:
+ * http://java.sun.com/javase/6/docs/technotes/guides/jni/spec/jniTOC.html
+ *
+ * Everything here is expected to be VM-neutral.
+ */
+
+#ifndef JNI_H_
+#define JNI_H_
+
+#include <stdarg.h>
+#include <stdint.h>
+
+/* Primitive types that match up with Java equivalents. */
+typedef uint8_t  jboolean; /* unsigned 8 bits */
+typedef int8_t   jbyte;    /* signed 8 bits */
+typedef uint16_t jchar;    /* unsigned 16 bits */
+typedef int16_t  jshort;   /* signed 16 bits */
+typedef int32_t  jint;     /* signed 32 bits */
+typedef int64_t  jlong;    /* signed 64 bits */
+typedef float    jfloat;   /* 32-bit IEEE 754 */
+typedef double   jdouble;  /* 64-bit IEEE 754 */
+
+/* "cardinal indices and sizes" */
+typedef jint     jsize;
+
+#ifdef __cplusplus
+/*
+ * Reference types, in C++
+ */
+class _jobject {};
+class _jclass : public _jobject {};
+class _jstring : public _jobject {};
+class _jarray : public _jobject {};
+class _jobjectArray : public _jarray {};
+class _jbooleanArray : public _jarray {};
+class _jbyteArray : public _jarray {};
+class _jcharArray : public _jarray {};
+class _jshortArray : public _jarray {};
+class _jintArray : public _jarray {};
+class _jlongArray : public _jarray {};
+class _jfloatArray : public _jarray {};
+class _jdoubleArray : public _jarray {};
+class _jthrowable : public _jobject {};
+
+typedef _jobject*       jobject;
+typedef _jclass*        jclass;
+typedef _jstring*       jstring;
+typedef _jarray*        jarray;
+typedef _jobjectArray*  jobjectArray;
+typedef _jbooleanArray* jbooleanArray;
+typedef _jbyteArray*    jbyteArray;
+typedef _jcharArray*    jcharArray;
+typedef _jshortArray*   jshortArray;
+typedef _jintArray*     jintArray;
+typedef _jlongArray*    jlongArray;
+typedef _jfloatArray*   jfloatArray;
+typedef _jdoubleArray*  jdoubleArray;
+typedef _jthrowable*    jthrowable;
+typedef _jobject*       jweak;
+
+
+#else /* not __cplusplus */
+
+/*
+ * Reference types, in C.
+ */
+typedef void*           jobject;
+typedef jobject         jclass;
+typedef jobject         jstring;
+typedef jobject         jarray;
+typedef jarray          jobjectArray;
+typedef jarray          jbooleanArray;
+typedef jarray          jbyteArray;
+typedef jarray          jcharArray;
+typedef jarray          jshortArray;
+typedef jarray          jintArray;
+typedef jarray          jlongArray;
+typedef jarray          jfloatArray;
+typedef jarray          jdoubleArray;
+typedef jobject         jthrowable;
+typedef jobject         jweak;
+
+#endif /* not __cplusplus */
+
+struct _jfieldID;                       /* opaque structure */
+typedef struct _jfieldID* jfieldID;     /* field IDs */
+
+struct _jmethodID;                      /* opaque structure */
+typedef struct _jmethodID* jmethodID;   /* method IDs */
+
+struct JNIInvokeInterface;
+
+typedef union jvalue {
+    jboolean    z;
+    jbyte       b;
+    jchar       c;
+    jshort      s;
+    jint        i;
+    jlong       j;
+    jfloat      f;
+    jdouble     d;
+    jobject     l;
+} jvalue;
+
+typedef enum jobjectRefType {
+    JNIInvalidRefType = 0,
+    JNILocalRefType = 1,
+    JNIGlobalRefType = 2,
+    JNIWeakGlobalRefType = 3
+} jobjectRefType;
+
+typedef struct {
+    const char* name;
+    const char* signature;
+    void*       fnPtr;
+} JNINativeMethod;
+
+struct _JNIEnv;
+struct _JavaVM;
+typedef const struct JNINativeInterface* C_JNIEnv;
+
+#if defined(__cplusplus)
+typedef _JNIEnv JNIEnv;
+typedef _JavaVM JavaVM;
+#else
+typedef const struct JNINativeInterface* JNIEnv;
+typedef const struct JNIInvokeInterface* JavaVM;
+#endif
+
+/*
+ * Table of interface function pointers.
+ */
+struct JNINativeInterface {
+    void*       reserved0;
+    void*       reserved1;
+    void*       reserved2;
+    void*       reserved3;
+
+    jint        (*GetVersion)(JNIEnv *);
+
+    jclass      (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*,
+                        jsize);
+    jclass      (*FindClass)(JNIEnv*, const char*);
+
+    jmethodID   (*FromReflectedMethod)(JNIEnv*, jobject);
+    jfieldID    (*FromReflectedField)(JNIEnv*, jobject);
+    /* spec doesn't show jboolean parameter */
+    jobject     (*ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean);
+
+    jclass      (*GetSuperclass)(JNIEnv*, jclass);
+    jboolean    (*IsAssignableFrom)(JNIEnv*, jclass, jclass);
+
+    /* spec doesn't show jboolean parameter */
+    jobject     (*ToReflectedField)(JNIEnv*, jclass, jfieldID, jboolean);
+
+    jint        (*Throw)(JNIEnv*, jthrowable);
+    jint        (*ThrowNew)(JNIEnv *, jclass, const char *);
+    jthrowable  (*ExceptionOccurred)(JNIEnv*);
+    void        (*ExceptionDescribe)(JNIEnv*);
+    void        (*ExceptionClear)(JNIEnv*);
+    void        (*FatalError)(JNIEnv*, const char*);
+
+    jint        (*PushLocalFrame)(JNIEnv*, jint);
+    jobject     (*PopLocalFrame)(JNIEnv*, jobject);
+
+    jobject     (*NewGlobalRef)(JNIEnv*, jobject);
+    void        (*DeleteGlobalRef)(JNIEnv*, jobject);
+    void        (*DeleteLocalRef)(JNIEnv*, jobject);
+    jboolean    (*IsSameObject)(JNIEnv*, jobject, jobject);
+
+    jobject     (*NewLocalRef)(JNIEnv*, jobject);
+    jint        (*EnsureLocalCapacity)(JNIEnv*, jint);
+
+    jobject     (*AllocObject)(JNIEnv*, jclass);
+    jobject     (*NewObject)(JNIEnv*, jclass, jmethodID, ...);
+    jobject     (*NewObjectV)(JNIEnv*, jclass, jmethodID, va_list);
+    jobject     (*NewObjectA)(JNIEnv*, jclass, jmethodID, jvalue*);
+
+    jclass      (*GetObjectClass)(JNIEnv*, jobject);
+    jboolean    (*IsInstanceOf)(JNIEnv*, jobject, jclass);
+    jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
+
+    jobject     (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
+    jobject     (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    jobject     (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+    jboolean    (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...);
+    jboolean    (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    jboolean    (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+    jbyte       (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...);
+    jbyte       (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    jbyte       (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+    jchar       (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...);
+    jchar       (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    jchar       (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+    jshort      (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...);
+    jshort      (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    jshort      (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+    jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
+    jint        (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    jint        (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+    jlong       (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...);
+    jlong       (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    jlong       (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+    jfloat      (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...);
+    jfloat      (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    jfloat      (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+    jdouble     (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...);
+    jdouble     (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    jdouble     (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+    void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
+    void        (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list);
+    void        (*CallVoidMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
+
+    jobject     (*CallNonvirtualObjectMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    jobject     (*CallNonvirtualObjectMethodV)(JNIEnv*, jobject, jclass,
+                        jmethodID, va_list);
+    jobject     (*CallNonvirtualObjectMethodA)(JNIEnv*, jobject, jclass,
+                        jmethodID, jvalue*);
+    jboolean    (*CallNonvirtualBooleanMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    jboolean    (*CallNonvirtualBooleanMethodV)(JNIEnv*, jobject, jclass,
+                         jmethodID, va_list);
+    jboolean    (*CallNonvirtualBooleanMethodA)(JNIEnv*, jobject, jclass,
+                         jmethodID, jvalue*);
+    jbyte       (*CallNonvirtualByteMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    jbyte       (*CallNonvirtualByteMethodV)(JNIEnv*, jobject, jclass,
+                        jmethodID, va_list);
+    jbyte       (*CallNonvirtualByteMethodA)(JNIEnv*, jobject, jclass,
+                        jmethodID, jvalue*);
+    jchar       (*CallNonvirtualCharMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    jchar       (*CallNonvirtualCharMethodV)(JNIEnv*, jobject, jclass,
+                        jmethodID, va_list);
+    jchar       (*CallNonvirtualCharMethodA)(JNIEnv*, jobject, jclass,
+                        jmethodID, jvalue*);
+    jshort      (*CallNonvirtualShortMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    jshort      (*CallNonvirtualShortMethodV)(JNIEnv*, jobject, jclass,
+                        jmethodID, va_list);
+    jshort      (*CallNonvirtualShortMethodA)(JNIEnv*, jobject, jclass,
+                        jmethodID, jvalue*);
+    jint        (*CallNonvirtualIntMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    jint        (*CallNonvirtualIntMethodV)(JNIEnv*, jobject, jclass,
+                        jmethodID, va_list);
+    jint        (*CallNonvirtualIntMethodA)(JNIEnv*, jobject, jclass,
+                        jmethodID, jvalue*);
+    jlong       (*CallNonvirtualLongMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    jlong       (*CallNonvirtualLongMethodV)(JNIEnv*, jobject, jclass,
+                        jmethodID, va_list);
+    jlong       (*CallNonvirtualLongMethodA)(JNIEnv*, jobject, jclass,
+                        jmethodID, jvalue*);
+    jfloat      (*CallNonvirtualFloatMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    jfloat      (*CallNonvirtualFloatMethodV)(JNIEnv*, jobject, jclass,
+                        jmethodID, va_list);
+    jfloat      (*CallNonvirtualFloatMethodA)(JNIEnv*, jobject, jclass,
+                        jmethodID, jvalue*);
+    jdouble     (*CallNonvirtualDoubleMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    jdouble     (*CallNonvirtualDoubleMethodV)(JNIEnv*, jobject, jclass,
+                        jmethodID, va_list);
+    jdouble     (*CallNonvirtualDoubleMethodA)(JNIEnv*, jobject, jclass,
+                        jmethodID, jvalue*);
+    void        (*CallNonvirtualVoidMethod)(JNIEnv*, jobject, jclass,
+                        jmethodID, ...);
+    void        (*CallNonvirtualVoidMethodV)(JNIEnv*, jobject, jclass,
+                        jmethodID, va_list);
+    void        (*CallNonvirtualVoidMethodA)(JNIEnv*, jobject, jclass,
+                        jmethodID, jvalue*);
+
+    jfieldID    (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);
+
+    jobject     (*GetObjectField)(JNIEnv*, jobject, jfieldID);
+    jboolean    (*GetBooleanField)(JNIEnv*, jobject, jfieldID);
+    jbyte       (*GetByteField)(JNIEnv*, jobject, jfieldID);
+    jchar       (*GetCharField)(JNIEnv*, jobject, jfieldID);
+    jshort      (*GetShortField)(JNIEnv*, jobject, jfieldID);
+    jint        (*GetIntField)(JNIEnv*, jobject, jfieldID);
+    jlong       (*GetLongField)(JNIEnv*, jobject, jfieldID);
+    jfloat      (*GetFloatField)(JNIEnv*, jobject, jfieldID);
+    jdouble     (*GetDoubleField)(JNIEnv*, jobject, jfieldID);
+
+    void        (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject);
+    void        (*SetBooleanField)(JNIEnv*, jobject, jfieldID, jboolean);
+    void        (*SetByteField)(JNIEnv*, jobject, jfieldID, jbyte);
+    void        (*SetCharField)(JNIEnv*, jobject, jfieldID, jchar);
+    void        (*SetShortField)(JNIEnv*, jobject, jfieldID, jshort);
+    void        (*SetIntField)(JNIEnv*, jobject, jfieldID, jint);
+    void        (*SetLongField)(JNIEnv*, jobject, jfieldID, jlong);
+    void        (*SetFloatField)(JNIEnv*, jobject, jfieldID, jfloat);
+    void        (*SetDoubleField)(JNIEnv*, jobject, jfieldID, jdouble);
+
+    jmethodID   (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
+
+    jobject     (*CallStaticObjectMethod)(JNIEnv*, jclass, jmethodID, ...);
+    jobject     (*CallStaticObjectMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+    jobject     (*CallStaticObjectMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+    jboolean    (*CallStaticBooleanMethod)(JNIEnv*, jclass, jmethodID, ...);
+    jboolean    (*CallStaticBooleanMethodV)(JNIEnv*, jclass, jmethodID,
+                        va_list);
+    jboolean    (*CallStaticBooleanMethodA)(JNIEnv*, jclass, jmethodID,
+                        jvalue*);
+    jbyte       (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...);
+    jbyte       (*CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+    jbyte       (*CallStaticByteMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+    jchar       (*CallStaticCharMethod)(JNIEnv*, jclass, jmethodID, ...);
+    jchar       (*CallStaticCharMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+    jchar       (*CallStaticCharMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+    jshort      (*CallStaticShortMethod)(JNIEnv*, jclass, jmethodID, ...);
+    jshort      (*CallStaticShortMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+    jshort      (*CallStaticShortMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+    jint        (*CallStaticIntMethod)(JNIEnv*, jclass, jmethodID, ...);
+    jint        (*CallStaticIntMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+    jint        (*CallStaticIntMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+    jlong       (*CallStaticLongMethod)(JNIEnv*, jclass, jmethodID, ...);
+    jlong       (*CallStaticLongMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+    jlong       (*CallStaticLongMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+    jfloat      (*CallStaticFloatMethod)(JNIEnv*, jclass, jmethodID, ...);
+    jfloat      (*CallStaticFloatMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+    jfloat      (*CallStaticFloatMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+    jdouble     (*CallStaticDoubleMethod)(JNIEnv*, jclass, jmethodID, ...);
+    jdouble     (*CallStaticDoubleMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+    jdouble     (*CallStaticDoubleMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+    void        (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
+    void        (*CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list);
+    void        (*CallStaticVoidMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
+
+    jfieldID    (*GetStaticFieldID)(JNIEnv*, jclass, const char*,
+                        const char*);
+
+    jobject     (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID);
+    jboolean    (*GetStaticBooleanField)(JNIEnv*, jclass, jfieldID);
+    jbyte       (*GetStaticByteField)(JNIEnv*, jclass, jfieldID);
+    jchar       (*GetStaticCharField)(JNIEnv*, jclass, jfieldID);
+    jshort      (*GetStaticShortField)(JNIEnv*, jclass, jfieldID);
+    jint        (*GetStaticIntField)(JNIEnv*, jclass, jfieldID);
+    jlong       (*GetStaticLongField)(JNIEnv*, jclass, jfieldID);
+    jfloat      (*GetStaticFloatField)(JNIEnv*, jclass, jfieldID);
+    jdouble     (*GetStaticDoubleField)(JNIEnv*, jclass, jfieldID);
+
+    void        (*SetStaticObjectField)(JNIEnv*, jclass, jfieldID, jobject);
+    void        (*SetStaticBooleanField)(JNIEnv*, jclass, jfieldID, jboolean);
+    void        (*SetStaticByteField)(JNIEnv*, jclass, jfieldID, jbyte);
+    void        (*SetStaticCharField)(JNIEnv*, jclass, jfieldID, jchar);
+    void        (*SetStaticShortField)(JNIEnv*, jclass, jfieldID, jshort);
+    void        (*SetStaticIntField)(JNIEnv*, jclass, jfieldID, jint);
+    void        (*SetStaticLongField)(JNIEnv*, jclass, jfieldID, jlong);
+    void        (*SetStaticFloatField)(JNIEnv*, jclass, jfieldID, jfloat);
+    void        (*SetStaticDoubleField)(JNIEnv*, jclass, jfieldID, jdouble);
+
+    jstring     (*NewString)(JNIEnv*, const jchar*, jsize);
+    jsize       (*GetStringLength)(JNIEnv*, jstring);
+    const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*);
+    void        (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*);
+    jstring     (*NewStringUTF)(JNIEnv*, const char*);
+    jsize       (*GetStringUTFLength)(JNIEnv*, jstring);
+    /* JNI spec says this returns const jbyte*, but that's inconsistent */
+    const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
+    void        (*ReleaseStringUTFChars)(JNIEnv*, jstring, const char*);
+    jsize       (*GetArrayLength)(JNIEnv*, jarray);
+    jobjectArray (*NewObjectArray)(JNIEnv*, jsize, jclass, jobject);
+    jobject     (*GetObjectArrayElement)(JNIEnv*, jobjectArray, jsize);
+    void        (*SetObjectArrayElement)(JNIEnv*, jobjectArray, jsize, jobject);
+
+    jbooleanArray (*NewBooleanArray)(JNIEnv*, jsize);
+    jbyteArray    (*NewByteArray)(JNIEnv*, jsize);
+    jcharArray    (*NewCharArray)(JNIEnv*, jsize);
+    jshortArray   (*NewShortArray)(JNIEnv*, jsize);
+    jintArray     (*NewIntArray)(JNIEnv*, jsize);
+    jlongArray    (*NewLongArray)(JNIEnv*, jsize);
+    jfloatArray   (*NewFloatArray)(JNIEnv*, jsize);
+    jdoubleArray  (*NewDoubleArray)(JNIEnv*, jsize);
+
+    jboolean*   (*GetBooleanArrayElements)(JNIEnv*, jbooleanArray, jboolean*);
+    jbyte*      (*GetByteArrayElements)(JNIEnv*, jbyteArray, jboolean*);
+    jchar*      (*GetCharArrayElements)(JNIEnv*, jcharArray, jboolean*);
+    jshort*     (*GetShortArrayElements)(JNIEnv*, jshortArray, jboolean*);
+    jint*       (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
+    jlong*      (*GetLongArrayElements)(JNIEnv*, jlongArray, jboolean*);
+    jfloat*     (*GetFloatArrayElements)(JNIEnv*, jfloatArray, jboolean*);
+    jdouble*    (*GetDoubleArrayElements)(JNIEnv*, jdoubleArray, jboolean*);
+
+    void        (*ReleaseBooleanArrayElements)(JNIEnv*, jbooleanArray,
+                        jboolean*, jint);
+    void        (*ReleaseByteArrayElements)(JNIEnv*, jbyteArray,
+                        jbyte*, jint);
+    void        (*ReleaseCharArrayElements)(JNIEnv*, jcharArray,
+                        jchar*, jint);
+    void        (*ReleaseShortArrayElements)(JNIEnv*, jshortArray,
+                        jshort*, jint);
+    void        (*ReleaseIntArrayElements)(JNIEnv*, jintArray,
+                        jint*, jint);
+    void        (*ReleaseLongArrayElements)(JNIEnv*, jlongArray,
+                        jlong*, jint);
+    void        (*ReleaseFloatArrayElements)(JNIEnv*, jfloatArray,
+                        jfloat*, jint);
+    void        (*ReleaseDoubleArrayElements)(JNIEnv*, jdoubleArray,
+                        jdouble*, jint);
+
+    void        (*GetBooleanArrayRegion)(JNIEnv*, jbooleanArray,
+                        jsize, jsize, jboolean*);
+    void        (*GetByteArrayRegion)(JNIEnv*, jbyteArray,
+                        jsize, jsize, jbyte*);
+    void        (*GetCharArrayRegion)(JNIEnv*, jcharArray,
+                        jsize, jsize, jchar*);
+    void        (*GetShortArrayRegion)(JNIEnv*, jshortArray,
+                        jsize, jsize, jshort*);
+    void        (*GetIntArrayRegion)(JNIEnv*, jintArray,
+                        jsize, jsize, jint*);
+    void        (*GetLongArrayRegion)(JNIEnv*, jlongArray,
+                        jsize, jsize, jlong*);
+    void        (*GetFloatArrayRegion)(JNIEnv*, jfloatArray,
+                        jsize, jsize, jfloat*);
+    void        (*GetDoubleArrayRegion)(JNIEnv*, jdoubleArray,
+                        jsize, jsize, jdouble*);
+
+    /* spec shows these without const; some jni.h do, some don't */
+    void        (*SetBooleanArrayRegion)(JNIEnv*, jbooleanArray,
+                        jsize, jsize, const jboolean*);
+    void        (*SetByteArrayRegion)(JNIEnv*, jbyteArray,
+                        jsize, jsize, const jbyte*);
+    void        (*SetCharArrayRegion)(JNIEnv*, jcharArray,
+                        jsize, jsize, const jchar*);
+    void        (*SetShortArrayRegion)(JNIEnv*, jshortArray,
+                        jsize, jsize, const jshort*);
+    void        (*SetIntArrayRegion)(JNIEnv*, jintArray,
+                        jsize, jsize, const jint*);
+    void        (*SetLongArrayRegion)(JNIEnv*, jlongArray,
+                        jsize, jsize, const jlong*);
+    void        (*SetFloatArrayRegion)(JNIEnv*, jfloatArray,
+                        jsize, jsize, const jfloat*);
+    void        (*SetDoubleArrayRegion)(JNIEnv*, jdoubleArray,
+                        jsize, jsize, const jdouble*);
+
+    jint        (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*,
+                        jint);
+    jint        (*UnregisterNatives)(JNIEnv*, jclass);
+    jint        (*MonitorEnter)(JNIEnv*, jobject);
+    jint        (*MonitorExit)(JNIEnv*, jobject);
+    jint        (*GetJavaVM)(JNIEnv*, JavaVM**);
+
+    void        (*GetStringRegion)(JNIEnv*, jstring, jsize, jsize, jchar*);
+    void        (*GetStringUTFRegion)(JNIEnv*, jstring, jsize, jsize, char*);
+
+    void*       (*GetPrimitiveArrayCritical)(JNIEnv*, jarray, jboolean*);
+    void        (*ReleasePrimitiveArrayCritical)(JNIEnv*, jarray, void*, jint);
+
+    const jchar* (*GetStringCritical)(JNIEnv*, jstring, jboolean*);
+    void        (*ReleaseStringCritical)(JNIEnv*, jstring, const jchar*);
+
+    jweak       (*NewWeakGlobalRef)(JNIEnv*, jobject);
+    void        (*DeleteWeakGlobalRef)(JNIEnv*, jweak);
+
+    jboolean    (*ExceptionCheck)(JNIEnv*);
+
+    jobject     (*NewDirectByteBuffer)(JNIEnv*, void*, jlong);
+    void*       (*GetDirectBufferAddress)(JNIEnv*, jobject);
+    jlong       (*GetDirectBufferCapacity)(JNIEnv*, jobject);
+
+    /* added in JNI 1.6 */
+    jobjectRefType (*GetObjectRefType)(JNIEnv*, jobject);
+};
+
+/*
+ * C++ object wrapper.
+ *
+ * This is usually overlaid on a C struct whose first element is a
+ * JNINativeInterface*.  We rely somewhat on compiler behavior.
+ */
+struct _JNIEnv {
+    /* do not rename this; it does not seem to be entirely opaque */
+    const struct JNINativeInterface* functions;
+
+#if defined(__cplusplus)
+
+    jint GetVersion()
+    { return functions->GetVersion(this); }
+
+    jclass DefineClass(const char *name, jobject loader, const jbyte* buf,
+        jsize bufLen)
+    { return functions->DefineClass(this, name, loader, buf, bufLen); }
+
+    jclass FindClass(const char* name)
+    { return functions->FindClass(this, name); }
+
+    jmethodID FromReflectedMethod(jobject method)
+    { return functions->FromReflectedMethod(this, method); }
+
+    jfieldID FromReflectedField(jobject field)
+    { return functions->FromReflectedField(this, field); }
+
+    jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic)
+    { return functions->ToReflectedMethod(this, cls, methodID, isStatic); }
+
+    jclass GetSuperclass(jclass clazz)
+    { return functions->GetSuperclass(this, clazz); }
+
+    jboolean IsAssignableFrom(jclass clazz1, jclass clazz2)
+    { return functions->IsAssignableFrom(this, clazz1, clazz2); }
+
+    jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic)
+    { return functions->ToReflectedField(this, cls, fieldID, isStatic); }
+
+    jint Throw(jthrowable obj)
+    { return functions->Throw(this, obj); }
+
+    jint ThrowNew(jclass clazz, const char* message)
+    { return functions->ThrowNew(this, clazz, message); }
+
+    jthrowable ExceptionOccurred()
+    { return functions->ExceptionOccurred(this); }
+
+    void ExceptionDescribe()
+    { functions->ExceptionDescribe(this); }
+
+    void ExceptionClear()
+    { functions->ExceptionClear(this); }
+
+    void FatalError(const char* msg)
+    { functions->FatalError(this, msg); }
+
+    jint PushLocalFrame(jint capacity)
+    { return functions->PushLocalFrame(this, capacity); }
+
+    jobject PopLocalFrame(jobject result)
+    { return functions->PopLocalFrame(this, result); }
+
+    jobject NewGlobalRef(jobject obj)
+    { return functions->NewGlobalRef(this, obj); }
+
+    void DeleteGlobalRef(jobject globalRef)
+    { functions->DeleteGlobalRef(this, globalRef); }
+
+    void DeleteLocalRef(jobject localRef)
+    { functions->DeleteLocalRef(this, localRef); }
+
+    jboolean IsSameObject(jobject ref1, jobject ref2)
+    { return functions->IsSameObject(this, ref1, ref2); }
+
+    jobject NewLocalRef(jobject ref)
+    { return functions->NewLocalRef(this, ref); }
+
+    jint EnsureLocalCapacity(jint capacity)
+    { return functions->EnsureLocalCapacity(this, capacity); }
+
+    jobject AllocObject(jclass clazz)
+    { return functions->AllocObject(this, clazz); }
+
+    jobject NewObject(jclass clazz, jmethodID methodID, ...)
+    {
+        va_list args;
+        va_start(args, methodID);
+        jobject result = functions->NewObjectV(this, clazz, methodID, args);
+        va_end(args);
+        return result;
+    }
+
+    jobject NewObjectV(jclass clazz, jmethodID methodID, va_list args)
+    { return functions->NewObjectV(this, clazz, methodID, args); }
+
+    jobject NewObjectA(jclass clazz, jmethodID methodID, jvalue* args)
+    { return functions->NewObjectA(this, clazz, methodID, args); }
+
+    jclass GetObjectClass(jobject obj)
+    { return functions->GetObjectClass(this, obj); }
+
+    jboolean IsInstanceOf(jobject obj, jclass clazz)
+    { return functions->IsInstanceOf(this, obj, clazz); }
+
+    jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
+    { return functions->GetMethodID(this, clazz, name, sig); }
+
+#define CALL_TYPE_METHOD(_jtype, _jname)                                    \
+    _jtype Call##_jname##Method(jobject obj, jmethodID methodID, ...)       \
+    {                                                                       \
+        _jtype result;                                                      \
+        va_list args;                                                       \
+        va_start(args, methodID);                                           \
+        result = functions->Call##_jname##MethodV(this, obj, methodID,      \
+                    args);                                                  \
+        va_end(args);                                                       \
+        return result;                                                      \
+    }
+#define CALL_TYPE_METHODV(_jtype, _jname)                                   \
+    _jtype Call##_jname##MethodV(jobject obj, jmethodID methodID,           \
+        va_list args)                                                       \
+    { return functions->Call##_jname##MethodV(this, obj, methodID, args); }
+#define CALL_TYPE_METHODA(_jtype, _jname)                                   \
+    _jtype Call##_jname##MethodA(jobject obj, jmethodID methodID,           \
+        jvalue* args)                                                       \
+    { return functions->Call##_jname##MethodA(this, obj, methodID, args); }
+
+#define CALL_TYPE(_jtype, _jname)                                           \
+    CALL_TYPE_METHOD(_jtype, _jname)                                        \
+    CALL_TYPE_METHODV(_jtype, _jname)                                       \
+    CALL_TYPE_METHODA(_jtype, _jname)
+
+    CALL_TYPE(jobject, Object)
+    CALL_TYPE(jboolean, Boolean)
+    CALL_TYPE(jbyte, Byte)
+    CALL_TYPE(jchar, Char)
+    CALL_TYPE(jshort, Short)
+    CALL_TYPE(jint, Int)
+    CALL_TYPE(jlong, Long)
+    CALL_TYPE(jfloat, Float)
+    CALL_TYPE(jdouble, Double)
+
+    void CallVoidMethod(jobject obj, jmethodID methodID, ...)
+    {
+        va_list args;
+        va_start(args, methodID);
+        functions->CallVoidMethodV(this, obj, methodID, args);
+        va_end(args);
+    }
+    void CallVoidMethodV(jobject obj, jmethodID methodID, va_list args)
+    { functions->CallVoidMethodV(this, obj, methodID, args); }
+    void CallVoidMethodA(jobject obj, jmethodID methodID, jvalue* args)
+    { functions->CallVoidMethodA(this, obj, methodID, args); }
+
+#define CALL_NONVIRT_TYPE_METHOD(_jtype, _jname)                            \
+    _jtype CallNonvirtual##_jname##Method(jobject obj, jclass clazz,        \
+        jmethodID methodID, ...)                                            \
+    {                                                                       \
+        _jtype result;                                                      \
+        va_list args;                                                       \
+        va_start(args, methodID);                                           \
+        result = functions->CallNonvirtual##_jname##MethodV(this, obj,      \
+                    clazz, methodID, args);                                 \
+        va_end(args);                                                       \
+        return result;                                                      \
+    }
+#define CALL_NONVIRT_TYPE_METHODV(_jtype, _jname)                           \
+    _jtype CallNonvirtual##_jname##MethodV(jobject obj, jclass clazz,       \
+        jmethodID methodID, va_list args)                                   \
+    { return functions->CallNonvirtual##_jname##MethodV(this, obj, clazz,   \
+        methodID, args); }
+#define CALL_NONVIRT_TYPE_METHODA(_jtype, _jname)                           \
+    _jtype CallNonvirtual##_jname##MethodA(jobject obj, jclass clazz,       \
+        jmethodID methodID, jvalue* args)                                   \
+    { return functions->CallNonvirtual##_jname##MethodA(this, obj, clazz,   \
+        methodID, args); }
+
+#define CALL_NONVIRT_TYPE(_jtype, _jname)                                   \
+    CALL_NONVIRT_TYPE_METHOD(_jtype, _jname)                                \
+    CALL_NONVIRT_TYPE_METHODV(_jtype, _jname)                               \
+    CALL_NONVIRT_TYPE_METHODA(_jtype, _jname)
+
+    CALL_NONVIRT_TYPE(jobject, Object)
+    CALL_NONVIRT_TYPE(jboolean, Boolean)
+    CALL_NONVIRT_TYPE(jbyte, Byte)
+    CALL_NONVIRT_TYPE(jchar, Char)
+    CALL_NONVIRT_TYPE(jshort, Short)
+    CALL_NONVIRT_TYPE(jint, Int)
+    CALL_NONVIRT_TYPE(jlong, Long)
+    CALL_NONVIRT_TYPE(jfloat, Float)
+    CALL_NONVIRT_TYPE(jdouble, Double)
+
+    void CallNonvirtualVoidMethod(jobject obj, jclass clazz,
+        jmethodID methodID, ...)
+    {
+        va_list args;
+        va_start(args, methodID);
+        functions->CallNonvirtualVoidMethodV(this, obj, clazz, methodID, args);
+        va_end(args);
+    }
+    void CallNonvirtualVoidMethodV(jobject obj, jclass clazz,
+        jmethodID methodID, va_list args)
+    { functions->CallNonvirtualVoidMethodV(this, obj, clazz, methodID, args); }
+    void CallNonvirtualVoidMethodA(jobject obj, jclass clazz,
+        jmethodID methodID, jvalue* args)
+    { functions->CallNonvirtualVoidMethodA(this, obj, clazz, methodID, args); }
+
+    jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
+    { return functions->GetFieldID(this, clazz, name, sig); }
+
+    jobject GetObjectField(jobject obj, jfieldID fieldID)
+    { return functions->GetObjectField(this, obj, fieldID); }
+    jboolean GetBooleanField(jobject obj, jfieldID fieldID)
+    { return functions->GetBooleanField(this, obj, fieldID); }
+    jbyte GetByteField(jobject obj, jfieldID fieldID)
+    { return functions->GetByteField(this, obj, fieldID); }
+    jchar GetCharField(jobject obj, jfieldID fieldID)
+    { return functions->GetCharField(this, obj, fieldID); }
+    jshort GetShortField(jobject obj, jfieldID fieldID)
+    { return functions->GetShortField(this, obj, fieldID); }
+    jint GetIntField(jobject obj, jfieldID fieldID)
+    { return functions->GetIntField(this, obj, fieldID); }
+    jlong GetLongField(jobject obj, jfieldID fieldID)
+    { return functions->GetLongField(this, obj, fieldID); }
+    jfloat GetFloatField(jobject obj, jfieldID fieldID)
+    { return functions->GetFloatField(this, obj, fieldID); }
+    jdouble GetDoubleField(jobject obj, jfieldID fieldID)
+    { return functions->GetDoubleField(this, obj, fieldID); }
+
+    void SetObjectField(jobject obj, jfieldID fieldID, jobject value)
+    { functions->SetObjectField(this, obj, fieldID, value); }
+    void SetBooleanField(jobject obj, jfieldID fieldID, jboolean value)
+    { functions->SetBooleanField(this, obj, fieldID, value); }
+    void SetByteField(jobject obj, jfieldID fieldID, jbyte value)
+    { functions->SetByteField(this, obj, fieldID, value); }
+    void SetCharField(jobject obj, jfieldID fieldID, jchar value)
+    { functions->SetCharField(this, obj, fieldID, value); }
+    void SetShortField(jobject obj, jfieldID fieldID, jshort value)
+    { functions->SetShortField(this, obj, fieldID, value); }
+    void SetIntField(jobject obj, jfieldID fieldID, jint value)
+    { functions->SetIntField(this, obj, fieldID, value); }
+    void SetLongField(jobject obj, jfieldID fieldID, jlong value)
+    { functions->SetLongField(this, obj, fieldID, value); }
+    void SetFloatField(jobject obj, jfieldID fieldID, jfloat value)
+    { functions->SetFloatField(this, obj, fieldID, value); }
+    void SetDoubleField(jobject obj, jfieldID fieldID, jdouble value)
+    { functions->SetDoubleField(this, obj, fieldID, value); }
+
+    jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)
+    { return functions->GetStaticMethodID(this, clazz, name, sig); }
+
+#define CALL_STATIC_TYPE_METHOD(_jtype, _jname)                             \
+    _jtype CallStatic##_jname##Method(jclass clazz, jmethodID methodID,     \
+        ...)                                                                \
+    {                                                                       \
+        _jtype result;                                                      \
+        va_list args;                                                       \
+        va_start(args, methodID);                                           \
+        result = functions->CallStatic##_jname##MethodV(this, clazz,        \
+                    methodID, args);                                        \
+        va_end(args);                                                       \
+        return result;                                                      \
+    }
+#define CALL_STATIC_TYPE_METHODV(_jtype, _jname)                            \
+    _jtype CallStatic##_jname##MethodV(jclass clazz, jmethodID methodID,    \
+        va_list args)                                                       \
+    { return functions->CallStatic##_jname##MethodV(this, clazz, methodID,  \
+        args); }
+#define CALL_STATIC_TYPE_METHODA(_jtype, _jname)                            \
+    _jtype CallStatic##_jname##MethodA(jclass clazz, jmethodID methodID,    \
+        jvalue* args)                                                       \
+    { return functions->CallStatic##_jname##MethodA(this, clazz, methodID,  \
+        args); }
+
+#define CALL_STATIC_TYPE(_jtype, _jname)                                    \
+    CALL_STATIC_TYPE_METHOD(_jtype, _jname)                                 \
+    CALL_STATIC_TYPE_METHODV(_jtype, _jname)                                \
+    CALL_STATIC_TYPE_METHODA(_jtype, _jname)
+
+    CALL_STATIC_TYPE(jobject, Object)
+    CALL_STATIC_TYPE(jboolean, Boolean)
+    CALL_STATIC_TYPE(jbyte, Byte)
+    CALL_STATIC_TYPE(jchar, Char)
+    CALL_STATIC_TYPE(jshort, Short)
+    CALL_STATIC_TYPE(jint, Int)
+    CALL_STATIC_TYPE(jlong, Long)
+    CALL_STATIC_TYPE(jfloat, Float)
+    CALL_STATIC_TYPE(jdouble, Double)
+
+    void CallStaticVoidMethod(jclass clazz, jmethodID methodID, ...)
+    {
+        va_list args;
+        va_start(args, methodID);
+        functions->CallStaticVoidMethodV(this, clazz, methodID, args);
+        va_end(args);
+    }
+    void CallStaticVoidMethodV(jclass clazz, jmethodID methodID, va_list args)
+    { functions->CallStaticVoidMethodV(this, clazz, methodID, args); }
+    void CallStaticVoidMethodA(jclass clazz, jmethodID methodID, jvalue* args)
+    { functions->CallStaticVoidMethodA(this, clazz, methodID, args); }
+
+    jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
+    { return functions->GetStaticFieldID(this, clazz, name, sig); }
+
+    jobject GetStaticObjectField(jclass clazz, jfieldID fieldID)
+    { return functions->GetStaticObjectField(this, clazz, fieldID); }
+    jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID)
+    { return functions->GetStaticBooleanField(this, clazz, fieldID); }
+    jbyte GetStaticByteField(jclass clazz, jfieldID fieldID)
+    { return functions->GetStaticByteField(this, clazz, fieldID); }
+    jchar GetStaticCharField(jclass clazz, jfieldID fieldID)
+    { return functions->GetStaticCharField(this, clazz, fieldID); }
+    jshort GetStaticShortField(jclass clazz, jfieldID fieldID)
+    { return functions->GetStaticShortField(this, clazz, fieldID); }
+    jint GetStaticIntField(jclass clazz, jfieldID fieldID)
+    { return functions->GetStaticIntField(this, clazz, fieldID); }
+    jlong GetStaticLongField(jclass clazz, jfieldID fieldID)
+    { return functions->GetStaticLongField(this, clazz, fieldID); }
+    jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID)
+    { return functions->GetStaticFloatField(this, clazz, fieldID); }
+    jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID)
+    { return functions->GetStaticDoubleField(this, clazz, fieldID); }
+
+    void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value)
+    { functions->SetStaticObjectField(this, clazz, fieldID, value); }
+    void SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value)
+    { functions->SetStaticBooleanField(this, clazz, fieldID, value); }
+    void SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte value)
+    { functions->SetStaticByteField(this, clazz, fieldID, value); }
+    void SetStaticCharField(jclass clazz, jfieldID fieldID, jchar value)
+    { functions->SetStaticCharField(this, clazz, fieldID, value); }
+    void SetStaticShortField(jclass clazz, jfieldID fieldID, jshort value)
+    { functions->SetStaticShortField(this, clazz, fieldID, value); }
+    void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value)
+    { functions->SetStaticIntField(this, clazz, fieldID, value); }
+    void SetStaticLongField(jclass clazz, jfieldID fieldID, jlong value)
+    { functions->SetStaticLongField(this, clazz, fieldID, value); }
+    void SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat value)
+    { functions->SetStaticFloatField(this, clazz, fieldID, value); }
+    void SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble value)
+    { functions->SetStaticDoubleField(this, clazz, fieldID, value); }
+
+    jstring NewString(const jchar* unicodeChars, jsize len)
+    { return functions->NewString(this, unicodeChars, len); }
+
+    jsize GetStringLength(jstring string)
+    { return functions->GetStringLength(this, string); }
+
+    const jchar* GetStringChars(jstring string, jboolean* isCopy)
+    { return functions->GetStringChars(this, string, isCopy); }
+
+    void ReleaseStringChars(jstring string, const jchar* chars)
+    { functions->ReleaseStringChars(this, string, chars); }
+
+    jstring NewStringUTF(const char* bytes)
+    { return functions->NewStringUTF(this, bytes); }
+
+    jsize GetStringUTFLength(jstring string)
+    { return functions->GetStringUTFLength(this, string); }
+
+    const char* GetStringUTFChars(jstring string, jboolean* isCopy)
+    { return functions->GetStringUTFChars(this, string, isCopy); }
+
+    void ReleaseStringUTFChars(jstring string, const char* utf)
+    { functions->ReleaseStringUTFChars(this, string, utf); }
+
+    jsize GetArrayLength(jarray array)
+    { return functions->GetArrayLength(this, array); }
+
+    jobjectArray NewObjectArray(jsize length, jclass elementClass,
+        jobject initialElement)
+    { return functions->NewObjectArray(this, length, elementClass,
+        initialElement); }
+
+    jobject GetObjectArrayElement(jobjectArray array, jsize index)
+    { return functions->GetObjectArrayElement(this, array, index); }
+
+    void SetObjectArrayElement(jobjectArray array, jsize index, jobject value)
+    { functions->SetObjectArrayElement(this, array, index, value); }
+
+    jbooleanArray NewBooleanArray(jsize length)
+    { return functions->NewBooleanArray(this, length); }
+    jbyteArray NewByteArray(jsize length)
+    { return functions->NewByteArray(this, length); }
+    jcharArray NewCharArray(jsize length)
+    { return functions->NewCharArray(this, length); }
+    jshortArray NewShortArray(jsize length)
+    { return functions->NewShortArray(this, length); }
+    jintArray NewIntArray(jsize length)
+    { return functions->NewIntArray(this, length); }
+    jlongArray NewLongArray(jsize length)
+    { return functions->NewLongArray(this, length); }
+    jfloatArray NewFloatArray(jsize length)
+    { return functions->NewFloatArray(this, length); }
+    jdoubleArray NewDoubleArray(jsize length)
+    { return functions->NewDoubleArray(this, length); }
+
+    jboolean* GetBooleanArrayElements(jbooleanArray array, jboolean* isCopy)
+    { return functions->GetBooleanArrayElements(this, array, isCopy); }
+    jbyte* GetByteArrayElements(jbyteArray array, jboolean* isCopy)
+    { return functions->GetByteArrayElements(this, array, isCopy); }
+    jchar* GetCharArrayElements(jcharArray array, jboolean* isCopy)
+    { return functions->GetCharArrayElements(this, array, isCopy); }
+    jshort* GetShortArrayElements(jshortArray array, jboolean* isCopy)
+    { return functions->GetShortArrayElements(this, array, isCopy); }
+    jint* GetIntArrayElements(jintArray array, jboolean* isCopy)
+    { return functions->GetIntArrayElements(this, array, isCopy); }
+    jlong* GetLongArrayElements(jlongArray array, jboolean* isCopy)
+    { return functions->GetLongArrayElements(this, array, isCopy); }
+    jfloat* GetFloatArrayElements(jfloatArray array, jboolean* isCopy)
+    { return functions->GetFloatArrayElements(this, array, isCopy); }
+    jdouble* GetDoubleArrayElements(jdoubleArray array, jboolean* isCopy)
+    { return functions->GetDoubleArrayElements(this, array, isCopy); }
+
+    void ReleaseBooleanArrayElements(jbooleanArray array, jboolean* elems,
+        jint mode)
+    { functions->ReleaseBooleanArrayElements(this, array, elems, mode); }
+    void ReleaseByteArrayElements(jbyteArray array, jbyte* elems,
+        jint mode)
+    { functions->ReleaseByteArrayElements(this, array, elems, mode); }
+    void ReleaseCharArrayElements(jcharArray array, jchar* elems,
+        jint mode)
+    { functions->ReleaseCharArrayElements(this, array, elems, mode); }
+    void ReleaseShortArrayElements(jshortArray array, jshort* elems,
+        jint mode)
+    { functions->ReleaseShortArrayElements(this, array, elems, mode); }
+    void ReleaseIntArrayElements(jintArray array, jint* elems,
+        jint mode)
+    { functions->ReleaseIntArrayElements(this, array, elems, mode); }
+    void ReleaseLongArrayElements(jlongArray array, jlong* elems,
+        jint mode)
+    { functions->ReleaseLongArrayElements(this, array, elems, mode); }
+    void ReleaseFloatArrayElements(jfloatArray array, jfloat* elems,
+        jint mode)
+    { functions->ReleaseFloatArrayElements(this, array, elems, mode); }
+    void ReleaseDoubleArrayElements(jdoubleArray array, jdouble* elems,
+        jint mode)
+    { functions->ReleaseDoubleArrayElements(this, array, elems, mode); }
+
+    void GetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len,
+        jboolean* buf)
+    { functions->GetBooleanArrayRegion(this, array, start, len, buf); }
+    void GetByteArrayRegion(jbyteArray array, jsize start, jsize len,
+        jbyte* buf)
+    { functions->GetByteArrayRegion(this, array, start, len, buf); }
+    void GetCharArrayRegion(jcharArray array, jsize start, jsize len,
+        jchar* buf)
+    { functions->GetCharArrayRegion(this, array, start, len, buf); }
+    void GetShortArrayRegion(jshortArray array, jsize start, jsize len,
+        jshort* buf)
+    { functions->GetShortArrayRegion(this, array, start, len, buf); }
+    void GetIntArrayRegion(jintArray array, jsize start, jsize len,
+        jint* buf)
+    { functions->GetIntArrayRegion(this, array, start, len, buf); }
+    void GetLongArrayRegion(jlongArray array, jsize start, jsize len,
+        jlong* buf)
+    { functions->GetLongArrayRegion(this, array, start, len, buf); }
+    void GetFloatArrayRegion(jfloatArray array, jsize start, jsize len,
+        jfloat* buf)
+    { functions->GetFloatArrayRegion(this, array, start, len, buf); }
+    void GetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len,
+        jdouble* buf)
+    { functions->GetDoubleArrayRegion(this, array, start, len, buf); }
+
+    void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len,
+        const jboolean* buf)
+    { functions->SetBooleanArrayRegion(this, array, start, len, buf); }
+    void SetByteArrayRegion(jbyteArray array, jsize start, jsize len,
+        const jbyte* buf)
+    { functions->SetByteArrayRegion(this, array, start, len, buf); }
+    void SetCharArrayRegion(jcharArray array, jsize start, jsize len,
+        const jchar* buf)
+    { functions->SetCharArrayRegion(this, array, start, len, buf); }
+    void SetShortArrayRegion(jshortArray array, jsize start, jsize len,
+        const jshort* buf)
+    { functions->SetShortArrayRegion(this, array, start, len, buf); }
+    void SetIntArrayRegion(jintArray array, jsize start, jsize len,
+        const jint* buf)
+    { functions->SetIntArrayRegion(this, array, start, len, buf); }
+    void SetLongArrayRegion(jlongArray array, jsize start, jsize len,
+        const jlong* buf)
+    { functions->SetLongArrayRegion(this, array, start, len, buf); }
+    void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len,
+        const jfloat* buf)
+    { functions->SetFloatArrayRegion(this, array, start, len, buf); }
+    void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len,
+        const jdouble* buf)
+    { functions->SetDoubleArrayRegion(this, array, start, len, buf); }
+
+    jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,
+        jint nMethods)
+    { return functions->RegisterNatives(this, clazz, methods, nMethods); }
+
+    jint UnregisterNatives(jclass clazz)
+    { return functions->UnregisterNatives(this, clazz); }
+
+    jint MonitorEnter(jobject obj)
+    { return functions->MonitorEnter(this, obj); }
+
+    jint MonitorExit(jobject obj)
+    { return functions->MonitorExit(this, obj); }
+
+    jint GetJavaVM(JavaVM** vm)
+    { return functions->GetJavaVM(this, vm); }
+
+    void GetStringRegion(jstring str, jsize start, jsize len, jchar* buf)
+    { functions->GetStringRegion(this, str, start, len, buf); }
+
+    void GetStringUTFRegion(jstring str, jsize start, jsize len, char* buf)
+    { return functions->GetStringUTFRegion(this, str, start, len, buf); }
+
+    void* GetPrimitiveArrayCritical(jarray array, jboolean* isCopy)
+    { return functions->GetPrimitiveArrayCritical(this, array, isCopy); }
+
+    void ReleasePrimitiveArrayCritical(jarray array, void* carray, jint mode)
+    { functions->ReleasePrimitiveArrayCritical(this, array, carray, mode); }
+
+    const jchar* GetStringCritical(jstring string, jboolean* isCopy)
+    { return functions->GetStringCritical(this, string, isCopy); }
+
+    void ReleaseStringCritical(jstring string, const jchar* carray)
+    { functions->ReleaseStringCritical(this, string, carray); }
+
+    jweak NewWeakGlobalRef(jobject obj)
+    { return functions->NewWeakGlobalRef(this, obj); }
+
+    void DeleteWeakGlobalRef(jweak obj)
+    { functions->DeleteWeakGlobalRef(this, obj); }
+
+    jboolean ExceptionCheck()
+    { return functions->ExceptionCheck(this); }
+
+    jobject NewDirectByteBuffer(void* address, jlong capacity)
+    { return functions->NewDirectByteBuffer(this, address, capacity); }
+
+    void* GetDirectBufferAddress(jobject buf)
+    { return functions->GetDirectBufferAddress(this, buf); }
+
+    jlong GetDirectBufferCapacity(jobject buf)
+    { return functions->GetDirectBufferCapacity(this, buf); }
+
+    /* added in JNI 1.6 */
+    jobjectRefType GetObjectRefType(jobject obj)
+    { return functions->GetObjectRefType(this, obj); }
+#endif /*__cplusplus*/
+};
+
+
+/*
+ * JNI invocation interface.
+ */
+struct JNIInvokeInterface {
+    void*       reserved0;
+    void*       reserved1;
+    void*       reserved2;
+
+    jint        (*DestroyJavaVM)(JavaVM*);
+    jint        (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*);
+    jint        (*DetachCurrentThread)(JavaVM*);
+    jint        (*GetEnv)(JavaVM*, void**, jint);
+    jint        (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*);
+};
+
+/*
+ * C++ version.
+ */
+struct _JavaVM {
+    const struct JNIInvokeInterface* functions;
+
+#if defined(__cplusplus)
+    jint DestroyJavaVM()
+    { return functions->DestroyJavaVM(this); }
+    jint AttachCurrentThread(JNIEnv** p_env, void* thr_args)
+    { return functions->AttachCurrentThread(this, p_env, thr_args); }
+    jint DetachCurrentThread()
+    { return functions->DetachCurrentThread(this); }
+    jint GetEnv(void** env, jint version)
+    { return functions->GetEnv(this, env, version); }
+    jint AttachCurrentThreadAsDaemon(JNIEnv** p_env, void* thr_args)
+    { return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); }
+#endif /*__cplusplus*/
+};
+
+struct JavaVMAttachArgs {
+    jint        version;    /* must be >= JNI_VERSION_1_2 */
+    const char* name;       /* NULL or name of thread as modified UTF-8 str */
+    jobject     group;      /* global ref of a ThreadGroup object, or NULL */
+};
+typedef struct JavaVMAttachArgs JavaVMAttachArgs;
+
+/*
+ * JNI 1.2+ initialization.  (As of 1.6, the pre-1.2 structures are no
+ * longer supported.)
+ */
+typedef struct JavaVMOption {
+    const char* optionString;
+    void*       extraInfo;
+} JavaVMOption;
+
+typedef struct JavaVMInitArgs {
+    jint        version;    /* use JNI_VERSION_1_2 or later */
+
+    jint        nOptions;
+    JavaVMOption* options;
+    jboolean    ignoreUnrecognized;
+} JavaVMInitArgs;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * VM initialization functions.
+ *
+ * Note these are the only symbols exported for JNI by the VM.
+ */
+jint JNI_GetDefaultJavaVMInitArgs(void*);
+jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
+jint JNI_GetCreatedJavaVMs(JavaVM**, jsize, jsize*);
+
+#define JNIIMPORT
+#define JNIEXPORT  __attribute__ ((visibility ("default")))
+#define JNICALL
+
+/*
+ * Prototypes for functions exported by loadable shared libs.  These are
+ * called by JNI, not provided by JNI.
+ */
+JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);
+JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+/*
+ * Manifest constants.
+ */
+#define JNI_FALSE   0
+#define JNI_TRUE    1
+
+#define JNI_VERSION_1_1 0x00010001
+#define JNI_VERSION_1_2 0x00010002
+#define JNI_VERSION_1_4 0x00010004
+#define JNI_VERSION_1_6 0x00010006
+
+#define JNI_OK          (0)         /* no error */
+#define JNI_ERR         (-1)        /* generic error */
+#define JNI_EDETACHED   (-2)        /* thread detached from the VM */
+#define JNI_EVERSION    (-3)        /* JNI version error */
+
+#define JNI_COMMIT      1           /* copy content, do not free buffer */
+#define JNI_ABORT       2           /* free buffer w/o copying back */
+
+#endif  /* JNI_H_ */
diff --git a/libnativehelper/include/nativehelper/toStringArray.h b/libnativehelper/include/nativehelper/toStringArray.h
new file mode 100644
index 0000000..cb46024
--- /dev/null
+++ b/libnativehelper/include/nativehelper/toStringArray.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TO_STRING_ARRAY_H_included
+#define TO_STRING_ARRAY_H_included
+
+#include "jni.h"
+#include "ScopedLocalRef.h"
+
+#include <string>
+#include <vector>
+
+jobjectArray newStringArray(JNIEnv* env, size_t count);
+
+template <typename Counter, typename Getter>
+jobjectArray toStringArray(JNIEnv* env, Counter* counter, Getter* getter) {
+    size_t count = (*counter)();
+    jobjectArray result = newStringArray(env, count);
+    if (result == NULL) {
+        return NULL;
+    }
+    for (size_t i = 0; i < count; ++i) {
+        ScopedLocalRef<jstring> s(env, env->NewStringUTF((*getter)(i)));
+        if (env->ExceptionCheck()) {
+            return NULL;
+        }
+        env->SetObjectArrayElement(result, i, s.get());
+        if (env->ExceptionCheck()) {
+            return NULL;
+        }
+    }
+    return result;
+}
+
+struct VectorCounter {
+    const std::vector<std::string>& strings;
+    explicit VectorCounter(const std::vector<std::string>& strings) : strings(strings) {}
+    size_t operator()() {
+        return strings.size();
+    }
+};
+struct VectorGetter {
+    const std::vector<std::string>& strings;
+    explicit VectorGetter(const std::vector<std::string>& strings) : strings(strings) {}
+    const char* operator()(size_t i) {
+        return strings[i].c_str();
+    }
+};
+
+inline jobjectArray toStringArray(JNIEnv* env, const std::vector<std::string>& strings) {
+    VectorCounter counter(strings);
+    VectorGetter getter(strings);
+    return toStringArray<VectorCounter, VectorGetter>(env, &counter, &getter);
+}
+
+JNIEXPORT jobjectArray toStringArray(JNIEnv* env, const char* const* strings);
+
+#endif  // TO_STRING_ARRAY_H_included
diff --git a/libnativehelper/tests/Android.bp b/libnativehelper/tests/Android.bp
new file mode 100644
index 0000000..b4d928b
--- /dev/null
+++ b/libnativehelper/tests/Android.bp
@@ -0,0 +1,9 @@
+// Build the unit tests.
+
+cc_test {
+    name: "JniInvocation_test",
+    host_supported: true,
+    clang: true,
+    srcs: ["JniInvocation_test.cpp"],
+    shared_libs: ["libnativehelper"],
+}
diff --git a/libnativehelper/tests/JniInvocation_test.cpp b/libnativehelper/tests/JniInvocation_test.cpp
new file mode 100644
index 0000000..186b74e
--- /dev/null
+++ b/libnativehelper/tests/JniInvocation_test.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "NativeBridge_test"
+
+#include <JniInvocation.h>
+#include <gtest/gtest.h>
+
+
+#include "string.h"
+
+#if defined(__ANDROID__) && defined(__BIONIC__)
+#define HAVE_TEST_STUFF 1
+#else
+#undef HAVE_TEST_STUFF
+#endif
+
+#ifdef HAVE_TEST_STUFF
+
+// PROPERTY_VALUE_MAX.
+#include "cutils/properties.h"
+
+// Ability to have fake local system properties.
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+
+extern void *__system_property_area__;
+
+struct LocalPropertyTestState {
+    LocalPropertyTestState() : valid(false) {
+        const char* ANDROID_DATA = getenv("ANDROID_DATA");
+        char dir_template[PATH_MAX];
+        snprintf(dir_template, sizeof(dir_template), "%s/local/tmp/prop-XXXXXX", ANDROID_DATA);
+        char* dirname = mkdtemp(dir_template);
+        if (!dirname) {
+            fprintf(stderr, "making temp file for test state failed (is %s writable?): %s",
+                    dir_template, strerror(errno));
+            return;
+        }
+
+        old_pa = __system_property_area__;
+        __system_property_area__ = NULL;
+
+        pa_dirname = dirname;
+        pa_filename = pa_dirname + "/__properties__";
+
+        __system_property_set_filename(pa_filename.c_str());
+        __system_property_area_init();
+        valid = true;
+    }
+
+    ~LocalPropertyTestState() {
+        if (!valid) {
+            return;
+        }
+
+        __system_property_area__ = old_pa;
+
+        __system_property_set_filename(PROP_FILENAME);
+        unlink(pa_filename.c_str());
+        rmdir(pa_dirname.c_str());
+    }
+public:
+    bool valid;
+private:
+    std::string pa_dirname;
+    std::string pa_filename;
+    void *old_pa;
+};
+#endif
+
+namespace android {
+
+class JNIInvocationTest : public testing::Test {
+};
+
+#ifdef HAVE_TEST_STUFF
+static const char* kDebuggableSystemProperty = "ro.debuggable";
+static const char* kIsDebuggableValue = "1";
+static const char* kIsNotDebuggableValue = "0";
+
+static const char* kLibrarySystemProperty = "persist.sys.dalvik.vm.lib.2";
+static const char* kTestNonNull = "libartd.so";
+static const char* kTestNonNull2 = "libartd2.so";
+static const char* kExpected = "libart.so";
+#endif
+
+TEST_F(JNIInvocationTest, Debuggable) {
+#ifdef HAVE_TEST_STUFF
+    LocalPropertyTestState pa;
+    ASSERT_TRUE(pa.valid);
+    ASSERT_EQ(0, __system_property_add(kDebuggableSystemProperty, 13, kIsDebuggableValue, 1));
+    ASSERT_EQ(0, __system_property_add(kLibrarySystemProperty, 27, kTestNonNull2, 11));
+
+    char buffer[PROPERTY_VALUE_MAX];
+    const char* result = JniInvocation::GetLibrary(NULL, buffer);
+    EXPECT_FALSE(result == NULL);
+    if (result != NULL) {
+        EXPECT_TRUE(strcmp(result, kTestNonNull2) == 0);
+        EXPECT_FALSE(strcmp(result, kExpected) == 0);
+    }
+
+    result = JniInvocation::GetLibrary(kTestNonNull, buffer);
+    EXPECT_FALSE(result == NULL);
+    if (result != NULL) {
+        EXPECT_TRUE(strcmp(result, kTestNonNull) == 0);
+        EXPECT_FALSE(strcmp(result, kTestNonNull2) == 0);
+    }
+#else
+    GTEST_LOG_(WARNING) << "Host testing unsupported. Please run target tests.";
+#endif
+}
+
+TEST_F(JNIInvocationTest, NonDebuggable) {
+#ifdef HAVE_TEST_STUFF
+    LocalPropertyTestState pa;
+    ASSERT_TRUE(pa.valid);
+    ASSERT_EQ(0, __system_property_add(kDebuggableSystemProperty, 13, kIsNotDebuggableValue, 1));
+
+    char buffer[PROPERTY_VALUE_MAX];
+    const char* result = JniInvocation::GetLibrary(NULL, buffer);
+    EXPECT_FALSE(result == NULL);
+    if (result != NULL) {
+        EXPECT_TRUE(strcmp(result, kExpected) == 0);
+        EXPECT_FALSE(strcmp(result, kTestNonNull) == 0);
+        EXPECT_FALSE(strcmp(result, kTestNonNull2) == 0);
+    }
+
+    result = JniInvocation::GetLibrary(kTestNonNull, buffer);
+    EXPECT_FALSE(result == NULL);
+    if (result != NULL) {
+        EXPECT_TRUE(strcmp(result, kExpected) == 0);
+        EXPECT_FALSE(strcmp(result, kTestNonNull) == 0);
+    }
+#else
+    GTEST_LOG_(WARNING) << "Host testing unsupported. Please run target tests.";
+#endif
+}
+
+}  // namespace android
diff --git a/libnativehelper/toStringArray.cpp b/libnativehelper/toStringArray.cpp
new file mode 100644
index 0000000..f2b0add
--- /dev/null
+++ b/libnativehelper/toStringArray.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "JniConstants.h"
+#include "toStringArray.h"
+
+jobjectArray newStringArray(JNIEnv* env, size_t count) {
+    return env->NewObjectArray(count, JniConstants::stringClass, NULL);
+}
+
+struct ArrayCounter {
+    const char* const* strings;
+    explicit ArrayCounter(const char* const* strings) : strings(strings) {}
+    size_t operator()() {
+        size_t count = 0;
+        while (strings[count] != NULL) {
+            ++count;
+        }
+        return count;
+    }
+};
+
+struct ArrayGetter {
+    const char* const* strings;
+    explicit ArrayGetter(const char* const* strings) : strings(strings) {}
+    const char* operator()(size_t i) {
+        return strings[i];
+    }
+};
+
+jobjectArray toStringArray(JNIEnv* env, const char* const* strings) {
+    ArrayCounter counter(strings);
+    ArrayGetter getter(strings);
+    return toStringArray(env, &counter, &getter);
+}