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); +}