Add support for accessing native bidi implementation via jni.

Include a simple test to verify that the bidi code works.
diff --git a/core/java/android/text/AndroidBidi.java b/core/java/android/text/AndroidBidi.java
new file mode 100644
index 0000000..e4f934e
--- /dev/null
+++ b/core/java/android/text/AndroidBidi.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package android.text;
+
+/**
+ * Access the ICU bidi implementation.
+ * @hide
+ */
+/* package */ class AndroidBidi {
+
+    public static int bidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo) {
+        if (chs == null || chInfo == null) {
+            throw new NullPointerException();
+        }
+
+        if (n < 0 || chs.length < n || chInfo.length < n) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        switch(dir) {
+            case Layout.DIR_REQUEST_LTR: dir = 0; break;
+            case Layout.DIR_REQUEST_RTL: dir = 1; break;
+            case Layout.DIR_REQUEST_DEFAULT_LTR: dir = -2; break;
+            case Layout.DIR_REQUEST_DEFAULT_RTL: dir = -1; break;
+            default: dir = 0; break;
+        }
+
+        int result = runBidi(dir, chs, chInfo, n, haveInfo);
+        result = (result & 0x1) == 0 ? Layout.DIR_LEFT_TO_RIGHT : Layout.DIR_RIGHT_TO_LEFT;
+        return result;
+    }
+
+    private native static int runBidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo);
+}
\ No newline at end of file
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index e8749ed..85d1a6f 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -45,6 +45,7 @@
 	android_view_Surface.cpp \
 	android_view_ViewRoot.cpp \
 	android_text_AndroidCharacter.cpp \
+	android_text_AndroidBidi.cpp \
 	android_text_KeyCharacterMap.cpp \
 	android_os_Debug.cpp \
 	android_os_FileUtils.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 7c8df03..8586aca 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -142,6 +142,7 @@
 extern int register_android_net_wifi_WifiManager(JNIEnv* env);
 extern int register_android_security_Md5MessageDigest(JNIEnv *env);
 extern int register_android_text_AndroidCharacter(JNIEnv *env);
+extern int register_android_text_AndroidBidi(JNIEnv *env);
 extern int register_android_text_KeyCharacterMap(JNIEnv *env);
 extern int register_android_opengl_classes(JNIEnv *env);
 extern int register_android_bluetooth_HeadsetBase(JNIEnv* env);
@@ -1184,6 +1185,7 @@
     REG_JNI(register_android_emoji_EmojiFactory),
     REG_JNI(register_android_security_Md5MessageDigest),
     REG_JNI(register_android_text_AndroidCharacter),
+    REG_JNI(register_android_text_AndroidBidi),
     REG_JNI(register_android_text_KeyCharacterMap),
     REG_JNI(register_android_os_Process),
     REG_JNI(register_android_os_Binder),
diff --git a/core/jni/android_text_AndroidBidi.cpp b/core/jni/android_text_AndroidBidi.cpp
new file mode 100644
index 0000000..7696bb3
--- /dev/null
+++ b/core/jni/android_text_AndroidBidi.cpp
@@ -0,0 +1,81 @@
+/* //device/libs/android_runtime/android_text_AndroidBidi.cpp
+**
+** Copyright 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 "AndroidUnicode"
+
+#include <jni.h>
+#include <android_runtime/AndroidRuntime.h>
+#include "utils/misc.h"
+#include "utils/Log.h"
+#include "unicode/ubidi.h"
+
+namespace android {
+    
+static void jniThrowException(JNIEnv* env, const char* exc, const char* msg = NULL)
+{
+    jclass excClazz = env->FindClass(exc);
+    LOG_ASSERT(excClazz, "Unable to find class %s", exc);
+
+    env->ThrowNew(excClazz, msg);
+}
+
+static jint runBidi(JNIEnv* env, jobject obj, jint dir, jcharArray chsArray, 
+                    jbyteArray infoArray, int n, jboolean haveInfo)
+{
+    // Parameters are checked on java side
+    // Failures from GetXXXArrayElements indicate a serious out-of-memory condition
+    // that we don't bother to report, we're probably dead anyway.
+    jint result = 0;
+    jchar* chs = env->GetCharArrayElements(chsArray, NULL);
+    if (chs != NULL) {
+        jbyte* info = env->GetByteArrayElements(infoArray, NULL);
+        if (info != NULL) {
+            UErrorCode status = U_ZERO_ERROR;
+            UBiDi* bidi = ubidi_openSized(n, 0, &status);
+            ubidi_setPara(bidi, chs, n, dir, NULL, &status);
+            if (U_SUCCESS(status)) {
+                for (int i = 0; i < n; ++i) {
+                  info[i] = ubidi_getLevelAt(bidi, i);
+                }
+                result = ubidi_getParaLevel(bidi);
+            } else {
+                jniThrowException(env, "java/lang/RuntimeException", NULL);
+            }
+            ubidi_close(bidi);
+
+            env->ReleaseByteArrayElements(infoArray, info, 0);
+        }
+        env->ReleaseCharArrayElements(chsArray, chs, JNI_ABORT);
+    }
+    return result;
+}
+
+static JNINativeMethod gMethods[] = {
+        { "runBidi", "(I[C[BIZ)I",
+        (void*) runBidi }
+};
+
+int register_android_text_AndroidBidi(JNIEnv* env)
+{
+    jclass clazz = env->FindClass("android/text/AndroidBidi");
+    LOG_ASSERT(clazz, "Cannot find android/text/AndroidBidi");
+    
+    return AndroidRuntime::registerNativeMethods(env, "android/text/AndroidBidi",
+            gMethods, NELEM(gMethods));
+}
+
+}
diff --git a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
index ccd0dae..da6036a 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutBidiTest.java
@@ -88,7 +88,38 @@
         int resultDir = StaticLayout.bidi(dir, chs, chInfo, n, false);
         
         {
-            StringBuilder sb = new StringBuilder("xdirs:");
+            StringBuilder sb = new StringBuilder("info:");
+            for (int i = 0; i < n; ++i) {
+                sb.append(" ").append(String.valueOf(chInfo[i]));
+            }
+            Log.i("BIDI", sb.toString());
+        }
+        
+        char[] resultLevelChars = new char[n];
+        for (int i = 0; i < n; ++i) {
+            resultLevelChars[i] = (char)('0' + chInfo[i]);
+        }
+        String resultLevels = new String(resultLevelChars);
+        assertEquals("direction", expectedDir, resultDir);
+        assertEquals("levels", expectedLevels, resultLevels);
+    }
+    
+    @SmallTest
+    public void testNativeBidi() {
+        // native bidi returns levels, not simply directions
+        expectNativeBidi(REQ_DL,  ALEF + BET + GIMEL + " abc", "1111222", R);
+    }
+    
+    private void expectNativeBidi(int dir, String text, 
+            String expectedLevels, int expectedDir) {
+        char[] chs = text.toCharArray();
+        int n = chs.length;
+        byte[] chInfo = new byte[n];
+        
+        int resultDir = AndroidBidi.bidi(dir, chs, chInfo, n, false);
+        
+        {
+            StringBuilder sb = new StringBuilder("info:");
             for (int i = 0; i < n; ++i) {
                 sb.append(" ").append(String.valueOf(chInfo[i]));
             }