Merge changes I34de4baf,Ie2b4593d,I4b84a9e1,I664b55d3,I60381a25
* changes:
Manually merge JniConstants changes from 81fd7cc0.
Add Calendar, Object, Object[] to well-known list
Add InputStream and OutputStream to the well-known classes.
Fix build. JniConstants is in libnativehelper, not libcore, in dalvik-dev.
Moving libnativehelper additions from libcore
diff --git a/Android.mk b/Android.mk
index a113eb3..11ba0a3 100644
--- a/Android.mk
+++ b/Android.mk
@@ -16,7 +16,9 @@
LOCAL_PATH := $(call my-dir)
local_src_files := \
- JNIHelp.cpp
+ JNIHelp.cpp \
+ JniConstants.cpp \
+ toStringArray.cpp
#
@@ -29,7 +31,7 @@
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := libnativehelper
-LOCAL_C_INCLUDES := external/stlport/stlport bionic/ bionic/libstdc++/include
+LOCAL_C_INCLUDES := external/stlport/stlport bionic/ bionic/libstdc++/include libcore/include
LOCAL_SHARED_LIBRARIES += libstlport
include $(BUILD_SHARED_LIBRARY)
@@ -43,5 +45,6 @@
LOCAL_MODULE := libnativehelper
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(local_src_files)
+LOCAL_C_INCLUDES := libcore/include
LOCAL_SHARED_LIBRARIES := liblog
include $(BUILD_HOST_SHARED_LIBRARY)
diff --git a/JNIHelp.cpp b/JNIHelp.cpp
index 4430c56..b5fba99 100644
--- a/JNIHelp.cpp
+++ b/JNIHelp.cpp
@@ -16,6 +16,9 @@
#define LOG_TAG "JNIHelp"
+#define LIBCORE_CPP_JNI_HELPERS
+
+#include "JniConstants.h"
#include "JNIHelp.h"
#include "cutils/log.h"
@@ -23,6 +26,8 @@
#include <string.h>
#include <assert.h>
+#include <string>
+
/**
* Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
*/
@@ -68,18 +73,18 @@
{
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
- ALOGV("Registering %s natives", className);
+ ALOGV("Registering %s's %d native methods...", className, numMethods);
scoped_local_ref<jclass> c(env, findClass(env, className));
if (c.get() == NULL) {
char* msg;
- asprintf(&msg, "Native registration unable to find class '%s', aborting", className);
+ asprintf(&msg, "Native registration unable to find class '%s', aborting...", className);
e->FatalError(msg);
}
if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
char* msg;
- asprintf(&msg, "RegisterNatives failed for '%s', aborting", className);
+ asprintf(&msg, "RegisterNatives failed for '%s', aborting...", className);
e->FatalError(msg);
}
@@ -91,7 +96,7 @@
* be populated with the "binary" class name and, if present, the
* exception message.
*/
-static char* getExceptionSummary0(C_JNIEnv* env, jthrowable exception) {
+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 */
@@ -103,14 +108,18 @@
scoped_local_ref<jstring> classNameStr(env,
(jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
if (classNameStr.get() == NULL) {
- return NULL;
+ (*env)->ExceptionClear(e);
+ result = "<error getting class name>";
+ return false;
}
-
- /* get printable string */
const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
if (classNameChars == NULL) {
- return 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 =
@@ -118,42 +127,32 @@
scoped_local_ref<jstring> messageStr(env,
(jstring) (*env)->CallObjectMethod(e, exception, getMessage));
if (messageStr.get() == NULL) {
- return strdup(classNameChars);
+ return true;
}
- char* result = NULL;
+ result += ": ";
+
const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
if (messageChars != NULL) {
- asprintf(&result, "%s: %s", classNameChars, messageChars);
+ result += messageChars;
(*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
} else {
+ result += "<error getting message>";
(*env)->ExceptionClear(e); // clear OOM
- asprintf(&result, "%s: <error getting message>", classNameChars);
}
- (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
- return result;
-}
-
-static char* getExceptionSummary(C_JNIEnv* env, jthrowable exception) {
- JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
- char* result = getExceptionSummary0(env, exception);
- if (result == NULL) {
- (*env)->ExceptionClear(e);
- result = strdup("<error getting class name>");
- }
- return result;
+ return true;
}
/*
* Returns an exception (with stack trace) as a string.
*/
-static char* getStackTrace(C_JNIEnv* env, jthrowable exception) {
+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 NULL;
+ return false;
}
jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V");
@@ -162,7 +161,7 @@
scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter"));
if (printWriterClass.get() == NULL) {
- return NULL;
+ return false;
}
jmethodID printWriterCtor =
@@ -171,13 +170,13 @@
scoped_local_ref<jobject> stringWriter(env,
(*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor));
if (stringWriter.get() == NULL) {
- return NULL;
+ return false;
}
jobject printWriter =
(*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get());
if (printWriter == NULL) {
- return NULL;
+ return false;
}
scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
@@ -186,23 +185,24 @@
(*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter);
if ((*env)->ExceptionCheck(e)) {
- return NULL;
+ return false;
}
scoped_local_ref<jstring> messageStr(env,
(jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
if (messageStr.get() == NULL) {
- return NULL;
+ return false;
}
const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
if (utfChars == NULL) {
- return NULL;
+ return false;
}
- char* result = strdup(utfChars);
+ result = utfChars;
+
(*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
- return result;
+ return true;
}
extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
@@ -214,9 +214,9 @@
(*env)->ExceptionClear(e);
if (exception.get() != NULL) {
- char* text = getExceptionSummary(env, exception.get());
- ALOGW("Discarding pending exception (%s) to throw %s", text, className);
- free(text);
+ std::string text;
+ getExceptionSummary(env, exception.get(), text);
+ ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className);
}
}
@@ -257,13 +257,18 @@
}
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());
+}
+
+extern "C" 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;
+ return "<no pending exception>";
}
}
@@ -271,18 +276,17 @@
(*env)->ExceptionClear(e);
}
- char* buffer = getStackTrace(env, exception);
- if (buffer == NULL) {
+ std::string trace;
+ if (!getStackTrace(env, exception, trace)) {
(*env)->ExceptionClear(e);
- buffer = getExceptionSummary(env, exception);
+ getExceptionSummary(env, exception, trace);
}
- __android_log_write(priority, tag, buffer);
- free(buffer);
-
if (currentException.get() != NULL) {
(*env)->Throw(e, currentException.get()); // rethrow
}
+
+ return trace;
}
const char* jniStrError(int errnum, char* buf, size_t buflen) {
@@ -306,56 +310,24 @@
}
}
-static struct CachedFields {
- jclass fileDescriptorClass;
- jmethodID fileDescriptorCtor;
- jfieldID descriptorField;
-} gCachedFields;
-
-jint JNI_OnLoad(JavaVM* vm, void*) {
- JNIEnv* env;
- if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
- ALOGE("JavaVM::GetEnv() failed");
- abort();
- }
-
- gCachedFields.fileDescriptorClass =
- reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass("java/io/FileDescriptor")));
- if (gCachedFields.fileDescriptorClass == NULL) {
- abort();
- }
-
- gCachedFields.fileDescriptorCtor =
- env->GetMethodID(gCachedFields.fileDescriptorClass, "<init>", "()V");
- if (gCachedFields.fileDescriptorCtor == NULL) {
- abort();
- }
-
- gCachedFields.descriptorField =
- env->GetFieldID(gCachedFields.fileDescriptorClass, "descriptor", "I");
- if (gCachedFields.descriptorField == NULL) {
- abort();
- }
-
- return JNI_VERSION_1_6;
-}
-
jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
- jobject fileDescriptor = (*env)->NewObject(e,
- gCachedFields.fileDescriptorClass, gCachedFields.fileDescriptorCtor);
+ static jmethodID ctor = e->GetMethodID(JniConstants::fileDescriptorClass, "<init>", "()V");
+ jobject fileDescriptor = (*env)->NewObject(e, JniConstants::fileDescriptorClass, ctor);
jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
return fileDescriptor;
}
int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
- return (*env)->GetIntField(e, fileDescriptor, gCachedFields.descriptorField);
+ static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
+ return (*env)->GetIntField(e, fileDescriptor, fid);
}
void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
- (*env)->SetIntField(e, fileDescriptor, gCachedFields.descriptorField, value);
+ static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
+ (*env)->SetIntField(e, fileDescriptor, fid, value);
}
/*
diff --git a/JniConstants.cpp b/JniConstants.cpp
new file mode 100644
index 0000000..6c3b9aa
--- /dev/null
+++ b/JniConstants.cpp
@@ -0,0 +1,134 @@
+/*
+ * 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.
+ */
+
+#include "JniConstants.h"
+#include "ScopedLocalRef.h"
+
+#include <stdlib.h>
+
+jclass JniConstants::bidiRunClass;
+jclass JniConstants::bigDecimalClass;
+jclass JniConstants::booleanClass;
+jclass JniConstants::byteArrayClass;
+jclass JniConstants::byteClass;
+jclass JniConstants::calendarClass;
+jclass JniConstants::characterClass;
+jclass JniConstants::charsetICUClass;
+jclass JniConstants::constructorClass;
+jclass JniConstants::deflaterClass;
+jclass JniConstants::doubleClass;
+jclass JniConstants::errnoExceptionClass;
+jclass JniConstants::fieldClass;
+jclass JniConstants::fieldPositionIteratorClass;
+jclass JniConstants::fileDescriptorClass;
+jclass JniConstants::floatClass;
+jclass JniConstants::gaiExceptionClass;
+jclass JniConstants::inet6AddressClass;
+jclass JniConstants::inetAddressClass;
+jclass JniConstants::inetSocketAddressClass;
+jclass JniConstants::inetUnixAddressClass;
+jclass JniConstants::inflaterClass;
+jclass JniConstants::inputStreamClass;
+jclass JniConstants::integerClass;
+jclass JniConstants::localeDataClass;
+jclass JniConstants::longClass;
+jclass JniConstants::methodClass;
+jclass JniConstants::mutableIntClass;
+jclass JniConstants::mutableLongClass;
+jclass JniConstants::objectClass;
+jclass JniConstants::objectArrayClass;
+jclass JniConstants::outputStreamClass;
+jclass JniConstants::parsePositionClass;
+jclass JniConstants::patternSyntaxExceptionClass;
+jclass JniConstants::realToStringClass;
+jclass JniConstants::shortClass;
+jclass JniConstants::socketClass;
+jclass JniConstants::socketImplClass;
+jclass JniConstants::stringClass;
+jclass JniConstants::structAddrinfoClass;
+jclass JniConstants::structFlockClass;
+jclass JniConstants::structGroupReqClass;
+jclass JniConstants::structLingerClass;
+jclass JniConstants::structPasswdClass;
+jclass JniConstants::structPollfdClass;
+jclass JniConstants::structStatClass;
+jclass JniConstants::structStatFsClass;
+jclass JniConstants::structTimevalClass;
+jclass JniConstants::structUcredClass;
+jclass JniConstants::structUtsnameClass;
+
+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) {
+ bidiRunClass = findClass(env, "java/text/Bidi$Run");
+ bigDecimalClass = findClass(env, "java/math/BigDecimal");
+ booleanClass = findClass(env, "java/lang/Boolean");
+ byteClass = findClass(env, "java/lang/Byte");
+ byteArrayClass = findClass(env, "[B");
+ calendarClass = findClass(env, "java/util/Calendar");
+ characterClass = findClass(env, "java/lang/Character");
+ charsetICUClass = findClass(env, "java/nio/charset/CharsetICU");
+ constructorClass = findClass(env, "java/lang/reflect/Constructor");
+ floatClass = findClass(env, "java/lang/Float");
+ deflaterClass = findClass(env, "java/util/zip/Deflater");
+ doubleClass = findClass(env, "java/lang/Double");
+ errnoExceptionClass = findClass(env, "libcore/io/ErrnoException");
+ fieldClass = findClass(env, "java/lang/reflect/Field");
+ fieldPositionIteratorClass = findClass(env, "libcore/icu/NativeDecimalFormat$FieldPositionIterator");
+ fileDescriptorClass = findClass(env, "java/io/FileDescriptor");
+ gaiExceptionClass = findClass(env, "libcore/io/GaiException");
+ inet6AddressClass = findClass(env, "java/net/Inet6Address");
+ inetAddressClass = findClass(env, "java/net/InetAddress");
+ inetSocketAddressClass = findClass(env, "java/net/InetSocketAddress");
+ inetUnixAddressClass = findClass(env, "java/net/InetUnixAddress");
+ inflaterClass = findClass(env, "java/util/zip/Inflater");
+ inputStreamClass = findClass(env, "java/io/InputStream");
+ integerClass = findClass(env, "java/lang/Integer");
+ localeDataClass = findClass(env, "libcore/icu/LocaleData");
+ longClass = findClass(env, "java/lang/Long");
+ methodClass = findClass(env, "java/lang/reflect/Method");
+ mutableIntClass = findClass(env, "libcore/util/MutableInt");
+ mutableLongClass = findClass(env, "libcore/util/MutableLong");
+ objectClass = findClass(env, "java/lang/Object");
+ objectArrayClass = findClass(env, "[Ljava/lang/Object;");
+ outputStreamClass = findClass(env, "java/io/OutputStream");
+ parsePositionClass = findClass(env, "java/text/ParsePosition");
+ patternSyntaxExceptionClass = findClass(env, "java/util/regex/PatternSyntaxException");
+ realToStringClass = findClass(env, "java/lang/RealToString");
+ shortClass = findClass(env, "java/lang/Short");
+ socketClass = findClass(env, "java/net/Socket");
+ socketImplClass = findClass(env, "java/net/SocketImpl");
+ stringClass = findClass(env, "java/lang/String");
+ structAddrinfoClass = findClass(env, "libcore/io/StructAddrinfo");
+ structFlockClass = findClass(env, "libcore/io/StructFlock");
+ structGroupReqClass = findClass(env, "libcore/io/StructGroupReq");
+ structLingerClass = findClass(env, "libcore/io/StructLinger");
+ structPasswdClass = findClass(env, "libcore/io/StructPasswd");
+ structPollfdClass = findClass(env, "libcore/io/StructPollfd");
+ structStatClass = findClass(env, "libcore/io/StructStat");
+ structStatFsClass = findClass(env, "libcore/io/StructStatFs");
+ structTimevalClass = findClass(env, "libcore/io/StructTimeval");
+ structUcredClass = findClass(env, "libcore/io/StructUcred");
+ structUtsnameClass = findClass(env, "libcore/io/StructUtsname");
+}
diff --git a/include/nativehelper/JNIHelp.h b/include/nativehelper/JNIHelp.h
index 446a710..35cdf2e 100644
--- a/include/nativehelper/JNIHelp.h
+++ b/include/nativehelper/JNIHelp.h
@@ -19,6 +19,8 @@
*
* 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_
@@ -31,6 +33,12 @@
# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
#endif
+// TODO: the build system doesn't ensure the standard C++ library header files are on the include
+// path when compiling C++, and this file is included all over the place.
+#ifdef LIBCORE_CPP_JNI_HELPERS
+#include <string>
+#endif // LIBCORE_CPP_JNI_HELPERS
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -160,6 +168,17 @@
inline void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable exception = NULL) {
jniLogException(&env->functions, priority, tag, exception);
}
+
+#ifdef LIBCORE_CPP_JNI_HELPERS
+
+extern "C" std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception);
+
+inline std::string jniGetStackTrace(JNIEnv* env, jthrowable exception = NULL) {
+ return jniGetStackTrace(&env->functions, exception);
+}
+
+#endif // LIBCORE_CPP_JNI_HELPERS
+
#endif
/* Logging macros.
diff --git a/include/nativehelper/JniConstants.h b/include/nativehelper/JniConstants.h
new file mode 100644
index 0000000..f11ec9c
--- /dev/null
+++ b/include/nativehelper/JniConstants.h
@@ -0,0 +1,98 @@
+/*
+ * 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 (2.5us on passion-eng at the time of writing), so we do
+ * all such lookups eagerly 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 a device the cost
+ * is definitely 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 bidiRunClass;
+ static jclass bigDecimalClass;
+ static jclass booleanClass;
+ static jclass byteArrayClass;
+ static jclass byteClass;
+ static jclass calendarClass;
+ static jclass characterClass;
+ static jclass charsetICUClass;
+ static jclass constructorClass;
+ static jclass deflaterClass;
+ static jclass doubleClass;
+ static jclass errnoExceptionClass;
+ static jclass fieldClass;
+ static jclass fieldPositionIteratorClass;
+ static jclass fileDescriptorClass;
+ static jclass floatClass;
+ static jclass gaiExceptionClass;
+ static jclass inet6AddressClass;
+ static jclass inetAddressClass;
+ static jclass inetSocketAddressClass;
+ static jclass inetUnixAddressClass;
+ static jclass inflaterClass;
+ static jclass inputStreamClass;
+ static jclass integerClass;
+ static jclass localeDataClass;
+ static jclass longClass;
+ static jclass methodClass;
+ static jclass mutableIntClass;
+ static jclass mutableLongClass;
+ static jclass objectClass;
+ static jclass objectArrayClass;
+ static jclass outputStreamClass;
+ static jclass parsePositionClass;
+ static jclass patternSyntaxExceptionClass;
+ static jclass realToStringClass;
+ static jclass shortClass;
+ static jclass socketClass;
+ static jclass socketImplClass;
+ static jclass stringClass;
+ static jclass structAddrinfoClass;
+ static jclass structFlockClass;
+ static jclass structGroupReqClass;
+ static jclass structLingerClass;
+ static jclass structPasswdClass;
+ static jclass structPollfdClass;
+ static jclass structStatClass;
+ static jclass structStatFsClass;
+ static jclass structTimevalClass;
+ static jclass structUcredClass;
+ static jclass structUtsnameClass;
+};
+
+#define NATIVE_METHOD(className, functionName, signature) \
+ { #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName) }
+
+#endif // JNI_CONSTANTS_H_included
diff --git a/include/nativehelper/toStringArray.h b/include/nativehelper/toStringArray.h
new file mode 100644
index 0000000..edd5f26
--- /dev/null
+++ b/include/nativehelper/toStringArray.h
@@ -0,0 +1,73 @@
+/*
+ * 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;
+}
+
+template <typename Counter, typename Getter>
+jobjectArray toStringArray16(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) {
+ int32_t charCount;
+ const jchar* chars = (*getter)(&charCount);
+ ScopedLocalRef<jstring> s(env, env->NewString(chars, charCount));
+ if (env->ExceptionCheck()) {
+ return NULL;
+ }
+ env->SetObjectArrayElement(result, i, s.get());
+ if (env->ExceptionCheck()) {
+ return NULL;
+ }
+ }
+ return result;
+}
+
+JNIEXPORT jobjectArray toStringArray(JNIEnv* env, const std::vector<std::string>& strings);
+JNIEXPORT jobjectArray toStringArray(JNIEnv* env, const char* const* strings);
+
+#endif // TO_STRING_ARRAY_H_included
diff --git a/toStringArray.cpp b/toStringArray.cpp
new file mode 100644
index 0000000..414d43c
--- /dev/null
+++ b/toStringArray.cpp
@@ -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.
+ */
+
+#include "JniConstants.h"
+#include "toStringArray.h"
+
+#include <string>
+#include <vector>
+
+jobjectArray newStringArray(JNIEnv* env, size_t count) {
+ return env->NewObjectArray(count, JniConstants::stringClass, NULL);
+}
+
+struct VectorCounter {
+ const std::vector<std::string>& strings;
+ VectorCounter(const std::vector<std::string>& strings) : strings(strings) {}
+ size_t operator()() {
+ return strings.size();
+ }
+};
+struct VectorGetter {
+ const std::vector<std::string>& strings;
+ VectorGetter(const std::vector<std::string>& strings) : strings(strings) {}
+ const char* operator()(size_t i) {
+ return strings[i].c_str();
+ }
+};
+
+jobjectArray toStringArray(JNIEnv* env, const std::vector<std::string>& strings) {
+ VectorCounter counter(strings);
+ VectorGetter getter(strings);
+ return toStringArray<VectorCounter, VectorGetter>(env, &counter, &getter);
+}
+
+struct ArrayCounter {
+ const char* const* strings;
+ 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;
+ 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);
+}