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