Don't allocate arbitrary-length buffers on the stack.

A new LocalArray C++ class lets us specify a "reasonable" amount of stack to
use, but transparently fall back to using the heap if we need more space.

The three places I've chosen to use LocalArray in this patch are fairly
random; all they have in common is that they're the places where we call
GetStringUTFRegion. There are more places LocalArray will be useful: the
java.io.File JNI in particular.

Bug: 2257819
diff --git a/NativeCode.mk b/NativeCode.mk
index 0dfad3b..57c4903 100644
--- a/NativeCode.mk
+++ b/NativeCode.mk
@@ -74,7 +74,7 @@
 
 # Extract out the allowed LOCAL_* variables. Note: $(sort) also
 # removes duplicates.
-core_c_includes := $(sort $(LOCAL_C_INCLUDES) $(JNI_H_INCLUDE))
+core_c_includes := $(sort dalvik/libcore/include $(LOCAL_C_INCLUDES) $(JNI_H_INCLUDE))
 core_shared_libraries := $(sort $(LOCAL_SHARED_LIBRARIES))
 core_static_libraries := $(sort $(LOCAL_STATIC_LIBRARIES))
 
@@ -118,4 +118,4 @@
 
     # TODO: Figure out cacerts.bks for the host.
 
-endif
\ No newline at end of file
+endif
diff --git a/include/LocalArray.h b/include/LocalArray.h
new file mode 100644
index 0000000..85b5a49
--- /dev/null
+++ b/include/LocalArray.h
@@ -0,0 +1,55 @@
+#ifndef LOCAL_ARRAY_H_included
+#define LOCAL_ARRAY_H_included
+
+#include <cstddef>
+#include <new>
+
+/**
+ * A fixed-size array with a size hint. That number of bytes will be allocated
+ * on the stack, and used if possible, but if more bytes are requested at
+ * construction time, a buffer will be allocated on the heap (and deallocated
+ * by the destructor).
+ *
+ * The API is intended to be a compatible subset of C++0x's std::array.
+ */
+template <size_t STACK_BYTE_COUNT>
+class LocalArray {
+public:
+    /**
+     * Allocates a new fixed-size array of the given size. If this size is
+     * less than or equal to the template parameter STACK_BYTE_COUNT, an
+     * internal on-stack buffer will be used. Otherwise a heap buffer will
+     * be allocated.
+     */
+    LocalArray(size_t desiredByteCount) : mSize(desiredByteCount) {
+        if (desiredByteCount > STACK_BYTE_COUNT) {
+            mPtr = new char[mSize];
+        } else {
+            mPtr = &mOnStackBuffer[0];
+        }
+    }
+
+    /**
+     * Frees the heap-allocated buffer, if there was one.
+     */
+    ~LocalArray() {
+        if (mPtr != &mOnStackBuffer[0]) {
+            delete[] mPtr;
+        }
+    }
+
+    // Capacity.
+    size_t size() { return mSize; }
+    bool empty() { return mSize == 0; }
+
+    // Element access.
+    char& operator[](size_t n) { return mPtr[n]; }
+    const char& operator[](size_t n) const { return mPtr[n]; }
+
+private:
+    char mOnStackBuffer[STACK_BYTE_COUNT];
+    char* mPtr;
+    size_t mSize;
+};
+
+#endif // LOCAL_ARRAY_H_included
diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
index 3f58736..b4b6d92 100644
--- a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
+++ b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
@@ -25,6 +25,7 @@
 
 #include "AndroidSystemNatives.h"
 #include "JNIHelp.h"
+#include "LocalArray.h"
 #include "jni.h"
 
 #include <arpa/inet.h>
@@ -485,13 +486,10 @@
         return NULL;
     }
 
-    // We need to reserve two extra bytes for the possible '[' and ']'.
-    char ipString[INET6_ADDRSTRLEN + 2];
+    // Convert the String to UTF bytes.
     size_t byteCount = env->GetStringUTFLength(javaString);
-    if (byteCount + 1 > sizeof(ipString)) {
-        jniThrowException(env, "java/lang/IllegalArgumentException", "string too long");
-        return NULL;
-    }
+    LocalArray<INET6_ADDRSTRLEN> bytes(byteCount + 1);
+    char* ipString = &bytes[0];
     env->GetStringUTFRegion(javaString, 0, env->GetStringLength(javaString), ipString);
 
     // Accept IPv6 addresses (only) in square brackets for compatibility.
diff --git a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp
index 538b7f3..1a6c3ae 100644
--- a/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp
+++ b/x-net/src/main/native/org_apache_harmony_xnet_provider_jsse_OpenSSLSocketImpl.cpp
@@ -16,9 +16,10 @@
 
 #define LOG_TAG "OpenSSLSocketImpl"
 
-#include <cutils/log.h>
-#include <jni.h>
-#include <JNIHelp.h>
+#include "JNIHelp.h"
+#include "LocalArray.h"
+#include "cutils/log.h"
+#include "jni.h"
 
 #include <stdio.h>
 #include <string.h>
@@ -53,14 +54,12 @@
  * stored in a freshly-allocated BIO memory buffer.
  */
 static BIO *stringToMemBuf(JNIEnv* env, jstring string) {
-    BIO *result = BIO_new(BIO_s_mem());
-    jsize length = env->GetStringUTFLength(string);
-    char buf[length + 1];
-    
-    env->GetStringUTFRegion(string, 0, env->GetStringLength(string), buf);
-    buf[length] = '\0';
+    jsize byteCount = env->GetStringUTFLength(string);
+    LocalArray<1024> buf(byteCount + 1);
+    env->GetStringUTFRegion(string, 0, env->GetStringLength(string), &buf[0]);
 
-    BIO_puts(result, buf);
+    BIO* result = BIO_new(BIO_s_mem());
+    BIO_puts(result, &buf[0]);
     return result;
 }
 
diff --git a/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp b/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
index 9192b1a..701dbd9 100644
--- a/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
+++ b/xml/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
@@ -16,9 +16,10 @@
 
 #define LOG_TAG "ExpatParser"
 
+#include "JNIHelp.h"
+#include "LocalArray.h"
 #include "jni.h"
 #include "utils/Log.h"
-#include "JNIHelp.h"
 
 #include <string.h>
 #include <utils/misc.h>
@@ -1242,30 +1243,24 @@
 static jint getAttributeIndex(JNIEnv* env, jobject clazz,
         jint attributePointer, jstring uri, jstring localName) {
     const char** attributes = (const char**) attributePointer;
-    int uriLength = env->GetStringUTFLength(uri);
-
-    if (uriLength == 0) {
+    int uriByteCount = env->GetStringUTFLength(uri);
+    if (uriByteCount == 0) {
         // If there's no URI, then a local name works just like a qName.
         return getAttributeIndexForQName(
                 env, clazz, attributePointer, localName);
     }
-    int localNameLength = env->GetStringUTFLength(localName);
 
-    // Create string in the same format used by Expat: "uri|localName"
-    // TODO: do we have a guarantee that uriLength and localNameLength are small?
-    char concatenated[uriLength + localNameLength + 2];
+    // Create string in the same format used by Expat: "uri|localName\0".
+    // Note that we need byte counts to size the array but Unicode char counts
+    // for GetStringUTFRegion indexes and counts.
+    int localNameByteCount = env->GetStringUTFLength(localName);
+    LocalArray<1024> concatenated(uriByteCount + 1 + localNameByteCount + 1);
+    env->GetStringUTFRegion(uri, 0, env->GetStringLength(uri), &concatenated[0]);
+    concatenated[uriByteCount] = '|';
+    env->GetStringUTFRegion(localName, 0, env->GetStringLength(localName),
+            &concatenated[uriByteCount + 1]);
 
-    // Append uri.
-    env->GetStringUTFRegion(uri, 0, uriLength, concatenated);
-
-    // Separator.
-    concatenated[uriLength] = '|';
-
-    // Append local name.
-    env->GetStringUTFRegion(localName, 0, localNameLength,
-                            concatenated + uriLength + 1);
-
-    return findAttributeByName(attributes, concatenated);
+    return findAttributeByName(attributes, &concatenated[0]);
 }
 
 /**