Change the Bidi class over to ICU4J.

The Harmony BidiTest passes before and after this change.

This change requires a corresponding change in libnativehelper
to remove the reference to java_text_bidi.cpp.

After this change (ICU4J):

                   benchmark    us linear runtime
    _complicatedOverrideBidi 18.64 =========================
    _createBidiFromCharArray 14.28 ===================
         _createBidiFromIter 11.54 ===============
       _createBidiFromString 11.08 ===============
                 _hebrewBidi 21.92 ==============================
            _reorderVisually  2.41 ===
               _requiresBidi  1.37 =
    vm: app_process
    trial: 0

Before this change (ICU4C):
                   benchmark    us linear runtime
    _complicatedOverrideBidi 66.94 ======================
    _createBidiFromCharArray 44.25 ==============
         _createBidiFromIter 21.76 =======
       _createBidiFromString 20.23 ======
                 _hebrewBidi 89.72 ==============================
            _reorderVisually  7.10 ==
               _requiresBidi 29.02 =========
    vm: app_process
    trial: 0

Change-Id: I733ac3f9983b8285883dee70958c5cf8054d334d
diff --git a/benchmarks/src/benchmarks/regression/BidiBenchmark.java b/benchmarks/src/benchmarks/regression/BidiBenchmark.java
new file mode 100644
index 0000000..bb5087e
--- /dev/null
+++ b/benchmarks/src/benchmarks/regression/BidiBenchmark.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2015 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 benchmarks.regression;
+
+import com.google.caliper.SimpleBenchmark;
+
+import java.math.BigDecimal;
+import java.text.AttributedCharacterIterator;
+import java.text.Bidi;
+import java.text.DecimalFormat;
+
+public class BidiBenchmark extends SimpleBenchmark {
+
+    private static final AttributedCharacterIterator charIter =
+            DecimalFormat.getInstance().formatToCharacterIterator(new BigDecimal(Math.PI));
+
+    public void time_createBidiFromIter(int reps) {
+        for (int i = 0; i < reps; i++) {
+            Bidi bidi = new Bidi(charIter);
+        }
+    }
+
+    public void time_createBidiFromCharArray(int reps) {
+        for (int i = 0; i < reps; i++) {
+            Bidi bd = new Bidi(new char[]{'s', 's', 's'}, 0, new byte[]{(byte) 1,
+                    (byte) 2, (byte) 3}, 0, 3, Bidi.DIRECTION_RIGHT_TO_LEFT);
+        }
+    }
+
+    public void time_createBidiFromString(int reps) {
+        for (int i = 0; i < reps; i++) {
+            Bidi bidi = new Bidi("Hello", Bidi.DIRECTION_LEFT_TO_RIGHT);
+        }
+    }
+
+    public void time_reorderVisually(int reps) {
+        for (int i = 0; i < reps; i++) {
+            Bidi.reorderVisually(new byte[]{2, 1, 3, 0, 4}, 0,
+                    new String[]{"H", "e", "l", "l", "o"}, 0, 5);
+        }
+    }
+
+    public void time_hebrewBidi(int reps) {
+        for (int i = 0; i < reps; i++) {
+            Bidi bd = new Bidi(new char[]{'\u05D0', '\u05D0', '\u05D0'}, 0,
+                    new byte[]{(byte) -1, (byte) -2, (byte) -3}, 0, 3,
+                    Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT);
+            bd = new Bidi(new char[]{'\u05D0', '\u05D0', '\u05D0'}, 0,
+                    new byte[]{(byte) -1, (byte) -2, (byte) -3}, 0, 3,
+                    Bidi.DIRECTION_LEFT_TO_RIGHT);
+        }
+    }
+
+    public void time_complicatedOverrideBidi(int reps) {
+        for (int i = 0; i < reps; i++) {
+            Bidi bd = new Bidi("a\u05D0a\"a\u05D0\"\u05D0a".toCharArray(), 0,
+                    new byte[]{0, 0, 0, -3, -3, 2, 2, 0, 3}, 0, 9,
+                    Bidi.DIRECTION_RIGHT_TO_LEFT);
+        }
+    }
+
+    public void time_requiresBidi(int reps) {
+        for (int i = 0; i < reps; i++) {
+            Bidi.requiresBidi("\u05D0".toCharArray(), 1, 1);  // false.
+            Bidi.requiresBidi("\u05D0".toCharArray(), 0, 1);  // true.
+        }
+    }
+
+}
diff --git a/luni/src/main/java/java/text/Bidi.java b/luni/src/main/java/java/text/Bidi.java
index d73ea4a..65861a7 100644
--- a/luni/src/main/java/java/text/Bidi.java
+++ b/luni/src/main/java/java/text/Bidi.java
@@ -17,11 +17,6 @@
 
 package java.text;
 
-import java.awt.font.NumericShaper;
-import java.awt.font.TextAttribute;
-import java.util.ArrayList;
-import java.util.Arrays;
-
 /**
  * Implements the <a href="http://unicode.org/reports/tr9/">Unicode Bidirectional Algorithm</a>.
  *
@@ -58,33 +53,28 @@
      */
     public static final int DIRECTION_RIGHT_TO_LEFT = 1;
 
-    /**
-     * TODO: if we care about performance, we might just want to use an int[] instead of a Run[].
-     */
-    static class Run {
-        private final int start;
-        private final int limit;
-        private final int level;
-
-        public Run(int start, int limit, int level) {
-            this.start = start;
-            this.limit = limit;
-            this.level = level;
-        }
-
-        public int getLevel() {
-            return level;
-        }
-
-        public int getLimit() {
-            return limit;
-        }
-
-        public int getStart() {
-            return start;
+    private static int translateConstToIcu(int javaInt) {
+        switch (javaInt) {
+            case DIRECTION_DEFAULT_LEFT_TO_RIGHT:
+                return com.ibm.icu.text.Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
+            case DIRECTION_DEFAULT_RIGHT_TO_LEFT:
+                return com.ibm.icu.text.Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT;
+            case DIRECTION_LEFT_TO_RIGHT:
+                return com.ibm.icu.text.Bidi.DIRECTION_LEFT_TO_RIGHT;
+            case DIRECTION_RIGHT_TO_LEFT:
+                return com.ibm.icu.text.Bidi.DIRECTION_RIGHT_TO_LEFT;
+            // If the parameter was unrecognized use LEFT_TO_RIGHT.
+            default:
+                return com.ibm.icu.text.Bidi.DIRECTION_LEFT_TO_RIGHT;
         }
     }
 
+    private boolean isUnidirectional() {
+        return icuBidi.getRunCount() == 0;
+    }
+
+    private final com.ibm.icu.text.Bidi icuBidi;
+
     /**
      * Creates a {@code Bidi} object from the {@code
      * AttributedCharacterIterator} of a paragraph text. The RUN_DIRECTION
@@ -114,65 +104,7 @@
             throw new IllegalArgumentException("paragraph is null");
         }
 
-        int begin = paragraph.getBeginIndex();
-        int end = paragraph.getEndIndex();
-        int length = end - begin;
-        char[] text = new char[length + 1]; // One more char for AttributedCharacterIterator.DONE
-
-        if (length != 0) {
-            text[0] = paragraph.first();
-        } else {
-            paragraph.first();
-        }
-
-        // First check the RUN_DIRECTION attribute.
-        int flags = DIRECTION_DEFAULT_LEFT_TO_RIGHT;
-        Object direction = paragraph.getAttribute(TextAttribute.RUN_DIRECTION);
-        if (direction != null && direction instanceof Boolean) {
-            if (direction.equals(TextAttribute.RUN_DIRECTION_LTR)) {
-                flags = DIRECTION_LEFT_TO_RIGHT;
-            } else {
-                flags = DIRECTION_RIGHT_TO_LEFT;
-            }
-        }
-
-        // Retrieve the text and gather BIDI_EMBEDDINGS
-        byte[] embeddings = null;
-        for (int textLimit = 1, i = 1; i < length; textLimit = paragraph
-                .getRunLimit(TextAttribute.BIDI_EMBEDDING)
-                - begin + 1) {
-            Object embedding = paragraph.getAttribute(TextAttribute.BIDI_EMBEDDING);
-            if (embedding != null && embedding instanceof Integer) {
-                int embLevel = ((Integer) embedding).intValue();
-
-                if (embeddings == null) {
-                    embeddings = new byte[length];
-                }
-
-                for (; i < textLimit; i++) {
-                    text[i] = paragraph.next();
-                    embeddings[i - 1] = (byte) embLevel;
-                }
-            } else {
-                for (; i < textLimit; i++) {
-                    text[i] = paragraph.next();
-                }
-            }
-        }
-
-        // Apply NumericShaper to the text
-        Object numericShaper = paragraph.getAttribute(TextAttribute.NUMERIC_SHAPING);
-        if (numericShaper != null && numericShaper instanceof NumericShaper) {
-            ((NumericShaper) numericShaper).shape(text, 0, length);
-        }
-
-        long bidi = 0;
-        try {
-            bidi = createUBiDi(text, 0, embeddings, 0, length, flags);
-            readBidiInfo(bidi);
-        } finally {
-            ubidi_close(bidi);
-        }
+        this.icuBidi = new com.ibm.icu.text.Bidi(paragraph);
     }
 
     /**
@@ -214,13 +146,11 @@
         if (text == null || text.length - textStart < paragraphLength) {
             throw new IllegalArgumentException();
         }
-
         if (embeddings != null) {
             if (embeddings.length - embStart < paragraphLength) {
                 throw new IllegalArgumentException();
             }
         }
-
         if (textStart < 0) {
             throw new IllegalArgumentException("Negative textStart value " + textStart);
         }
@@ -231,13 +161,9 @@
             throw new IllegalArgumentException("Negative paragraph length " + paragraphLength);
         }
 
-        long bidi = 0;
-        try {
-            bidi = createUBiDi(text, textStart, embeddings, embStart, paragraphLength, flags);
-            readBidiInfo(bidi);
-        } finally {
-            ubidi_close(bidi);
-        }
+        this.icuBidi = new com.ibm.icu.text.Bidi(text, textStart, embeddings, embStart,
+                paragraphLength, translateConstToIcu(flags));
+
     }
 
     /**
@@ -261,109 +187,18 @@
                 (paragraph == null ? 0 : paragraph.length()), flags);
     }
 
-    // create the native UBiDi struct, need to be closed with ubidi_close().
-    private static long createUBiDi(char[] text, int textStart,
-            byte[] embeddings, int embStart, int paragraphLength, int flags) {
-        char[] realText = null;
 
-        byte[] realEmbeddings = null;
-
-        if (text == null || text.length - textStart < paragraphLength) {
-            throw new IllegalArgumentException();
-        }
-        realText = new char[paragraphLength];
-        System.arraycopy(text, textStart, realText, 0, paragraphLength);
-
-        if (embeddings != null) {
-            if (embeddings.length - embStart < paragraphLength) {
-                throw new IllegalArgumentException();
-            }
-            if (paragraphLength > 0) {
-                Bidi temp = new Bidi(text, textStart, null, 0, paragraphLength, flags);
-                realEmbeddings = new byte[paragraphLength];
-                System.arraycopy(temp.offsetLevel, 0, realEmbeddings, 0, paragraphLength);
-                for (int i = 0; i < paragraphLength; i++) {
-                    byte e = embeddings[i];
-                    if (e < 0) {
-                        realEmbeddings[i] = (byte) (UBIDI_LEVEL_OVERRIDE - e);
-                    } else if (e > 0) {
-                        realEmbeddings[i] = e;
-                    } else {
-                        realEmbeddings[i] |= (byte) UBIDI_LEVEL_OVERRIDE;
-                    }
-                }
-            }
-        }
-
-        if (flags > 1 || flags < -2) {
-            flags = 0;
-        }
-
-        long bidi = 0;
-        boolean needsDeletion = true;
-        try {
-            bidi = ubidi_open();
-            ubidi_setPara(bidi, realText, paragraphLength, flags, realEmbeddings);
-            needsDeletion = false;
-        } finally {
-            if (needsDeletion) {
-                ubidi_close(bidi);
-            }
-        }
-        return bidi;
+    private Bidi(com.ibm.icu.text.Bidi icuBidi) {
+        this.icuBidi = icuBidi;
     }
 
-    /* private constructor used by createLineBidi() */
-    private Bidi(long pBidi) {
-        readBidiInfo(pBidi);
-    }
-
-    // read info from the native UBiDi struct
-    private void readBidiInfo(long pBidi) {
-        length = ubidi_getLength(pBidi);
-
-        offsetLevel = (length == 0) ? null : ubidi_getLevels(pBidi);
-
-        baseLevel = ubidi_getParaLevel(pBidi);
-
-        int runCount = ubidi_countRuns(pBidi);
-        if (runCount == 0) {
-            unidirectional = true;
-            runs = null;
-        } else if (runCount < 0) {
-            runs = null;
-        } else {
-            runs = ubidi_getRuns(pBidi);
-
-            // Simplified case for one run which has the base level
-            if (runCount == 1 && runs[0].getLevel() == baseLevel) {
-                unidirectional = true;
-                runs = null;
-            }
-        }
-
-        direction = ubidi_getDirection(pBidi);
-    }
-
-    private int baseLevel;
-
-    private int length;
-
-    private byte[] offsetLevel;
-
-    private Run[] runs;
-
-    private int direction;
-
-    private boolean unidirectional;
-
     /**
      * Returns whether the base level is from left to right.
      *
      * @return true if the base level is from left to right.
      */
     public boolean baseIsLeftToRight() {
-        return baseLevel % 2 == 0 ? true : false;
+        return icuBidi.baseIsLeftToRight();
     }
 
     /**
@@ -382,55 +217,36 @@
      *             than the length of this object's paragraph text.
      */
     public Bidi createLineBidi(int lineStart, int lineLimit) {
-        if (lineStart < 0 || lineLimit < 0 || lineLimit > length || lineStart > lineLimit) {
+        if (lineStart < 0 || lineLimit < 0 || lineLimit > getLength() || lineStart > lineLimit) {
             throw new IllegalArgumentException("Invalid ranges (start=" + lineStart + ", " +
-                    "limit=" + lineLimit + ", length=" + length + ")");
+                    "limit=" + lineLimit + ", length=" + getLength() + ")");
         }
 
-        char[] text = new char[this.length];
-        Arrays.fill(text, 'a');
-        byte[] embeddings = new byte[this.length];
-        for (int i = 0; i < embeddings.length; i++) {
-            embeddings[i] = (byte) -this.offsetLevel[i];
+        // In the special case where the start and end positions are the same, we return a new bidi
+        // instance which is empty. Note that the default constructor for an empty ICU4J bidi
+        // instance is not the same as passing in empty values. This way allows one to call
+        // .getLength() for example and return a correct value instead of an IllegalStateException
+        // being thrown, which happens in the case of using the empty constructor.
+        if (lineStart == lineLimit) {
+            return new Bidi(new com.ibm.icu.text.Bidi(new char[] {}, 0, new byte[] {}, 0, 0,
+                    translateConstToIcu(DIRECTION_LEFT_TO_RIGHT)));
         }
 
-        int dir = this.baseIsLeftToRight()
-                ? Bidi.DIRECTION_LEFT_TO_RIGHT
-                : Bidi.DIRECTION_RIGHT_TO_LEFT;
-        long parent = 0;
-        try {
-            parent = createUBiDi(text, 0, embeddings, 0, this.length, dir);
-            if (lineStart == lineLimit) {
-                return createEmptyLineBidi(parent);
-            }
-            return new Bidi(ubidi_setLine(parent, lineStart, lineLimit));
-        } finally {
-            ubidi_close(parent);
-        }
-    }
-
-    private Bidi createEmptyLineBidi(long parent) {
-        // ICU4C doesn't allow this case, but the RI does.
-        Bidi result = new Bidi(parent);
-        result.length = 0;
-        result.offsetLevel = null;
-        result.runs = null;
-        result.unidirectional = true;
-        return result;
+        return new Bidi(icuBidi.createLineBidi(lineStart, lineLimit));
     }
 
     /**
      * Returns the base level.
      */
     public int getBaseLevel() {
-        return baseLevel;
+        return icuBidi.getBaseLevel();
     }
 
     /**
      * Returns the length of the text.
      */
     public int getLength() {
-        return length;
+        return icuBidi.getLength();
     }
 
     /**
@@ -438,9 +254,9 @@
      */
     public int getLevelAt(int offset) {
         try {
-            return offsetLevel[offset] & ~UBIDI_LEVEL_OVERRIDE;
-        } catch (RuntimeException e) {
-            return baseLevel;
+            return icuBidi.getLevelAt(offset);
+        } catch (IllegalArgumentException e) {
+            return getBaseLevel();
         }
     }
 
@@ -448,28 +264,43 @@
      * Returns the number of runs in the text, at least 1.
      */
     public int getRunCount() {
-        return unidirectional ? 1 : runs.length;
+        return isUnidirectional() ? 1 : icuBidi.getRunCount();
     }
 
     /**
      * Returns the level of the given run.
      */
     public int getRunLevel(int run) {
-        return unidirectional ? baseLevel : runs[run].getLevel();
+        // Paper over a the ICU4J behaviour of strictly enforcing run must be strictly less than
+        // the number of runs. Done to maintain compatibility with previous C implementation.
+        if (run == getRunCount()) {
+            return getBaseLevel();
+        }
+        return isUnidirectional() ? icuBidi.getBaseLevel() : icuBidi.getRunLevel(run);
     }
 
     /**
      * Returns the limit offset of the given run.
      */
     public int getRunLimit(int run) {
-        return unidirectional ? length : runs[run].getLimit();
+        // Paper over a the ICU4J behaviour of strictly enforcing run must be strictly less than
+        // the number of runs. Done to maintain compatibility with previous C implementation.
+        if (run == getRunCount()) {
+            return getBaseLevel();
+        }
+        return isUnidirectional() ? icuBidi.getLength() : icuBidi.getRunLimit(run);
     }
 
     /**
      * Returns the start offset of the given run.
      */
     public int getRunStart(int run) {
-        return unidirectional ? 0 : runs[run].getStart();
+        // Paper over a the ICU4J behaviour of strictly enforcing run must be strictly less than
+        // the number of runs. Done to maintain compatibility with previous C implementation.
+        if (run == getRunCount()) {
+            return getBaseLevel();
+        }
+        return isUnidirectional() ? 0 : icuBidi.getRunStart(run);
     }
 
     /**
@@ -477,14 +308,14 @@
      * direction and the text direction is from left to right.
      */
     public boolean isLeftToRight() {
-        return direction == UBiDiDirection_UBIDI_LTR;
+        return icuBidi.isLeftToRight();
     }
 
     /**
      * Returns true if the text direction is mixed.
      */
     public boolean isMixed() {
-        return direction == UBiDiDirection_UBIDI_MIXED;
+        return icuBidi.isMixed();
     }
 
     /**
@@ -492,7 +323,7 @@
      * direction and the text direction is from right to left.
      */
     public boolean isRightToLeft() {
-        return direction == UBiDiDirection_UBIDI_RTL;
+        return icuBidi.isRightToLeft();
     }
 
     /**
@@ -519,6 +350,7 @@
      */
     public static void reorderVisually(byte[] levels, int levelStart,
             Object[] objects, int objectStart, int count) {
+
         if (count < 0 || levelStart < 0 || objectStart < 0
                 || count > levels.length - levelStart
                 || count > objects.length - objectStart) {
@@ -527,17 +359,7 @@
                     ", objectStart=" + objectStart + ", count=" + count + ")");
         }
 
-        byte[] realLevels = new byte[count];
-        System.arraycopy(levels, levelStart, realLevels, 0, count);
-
-        int[] indices = ubidi_reorderVisual(realLevels, count);
-
-        ArrayList<Object> result = new ArrayList<Object>(count);
-        for (int i = 0; i < count; i++) {
-            result.add(objects[objectStart + indices[i]]);
-        }
-
-        System.arraycopy(result.toArray(), 0, objects, objectStart, count);
+        com.ibm.icu.text.Bidi.reorderVisually(levels, levelStart, objects, objectStart, count);
     }
 
     /**
@@ -562,33 +384,13 @@
             throw new IllegalArgumentException();
         }
 
-        Bidi bidi = new Bidi(text, start, null, 0, limit - start, 0);
-        return !bidi.isLeftToRight();
+        return com.ibm.icu.text.Bidi.requiresBidi(text, start, limit);
     }
 
     @Override
     public String toString() {
         return getClass().getName()
-                + "[direction: " + direction + " baseLevel: " + baseLevel
-                + " length: " + length + " runs: " + Arrays.toString(runs) + "]";
+                + "[direction: " + icuBidi.getDirection() + " baseLevel: " + icuBidi.getBaseLevel()
+                + " length: " + icuBidi.getLength() + " runs: " + icuBidi.getRunCount() + "]";
     }
-
-    // ICU4C constants.
-    private static final int UBIDI_LEVEL_OVERRIDE = 0x80;
-    private static final int UBiDiDirection_UBIDI_LTR = 0;
-    private static final int UBiDiDirection_UBIDI_RTL = 1;
-    private static final int UBiDiDirection_UBIDI_MIXED = 2;
-
-    // ICU4C functions.
-    private static native long ubidi_open();
-    private static native void ubidi_close(long pBiDi);
-    private static native void ubidi_setPara(long pBiDi, char[] text, int length, int paraLevel, byte[] embeddingLevels);
-    private static native long ubidi_setLine(final long pParaBiDi, int start, int limit);
-    private static native int ubidi_getDirection(final long pBiDi);
-    private static native int ubidi_getLength(final long pBiDi);
-    private static native byte ubidi_getParaLevel(final long pBiDi);
-    private static native byte[] ubidi_getLevels(long pBiDi);
-    private static native int ubidi_countRuns(long pBiDi);
-    private static native Bidi.Run[] ubidi_getRuns(long pBidi);
-    private static native int[] ubidi_reorderVisual(byte[] levels, int length);
 }
diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp
index 71ac8bf..c32e675 100644
--- a/luni/src/main/native/Register.cpp
+++ b/luni/src/main/native/Register.cpp
@@ -47,7 +47,6 @@
     REGISTER(register_java_lang_StringToReal);
     REGISTER(register_java_lang_System);
     REGISTER(register_java_math_NativeBN);
-    REGISTER(register_java_text_Bidi);
     REGISTER(register_java_util_jar_StrictJarFile);
     REGISTER(register_java_util_regex_Matcher);
     REGISTER(register_java_util_regex_Pattern);
diff --git a/luni/src/main/native/java_text_Bidi.cpp b/luni/src/main/native/java_text_Bidi.cpp
deleted file mode 100644
index 6a3e751..0000000
--- a/luni/src/main/native/java_text_Bidi.cpp
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 "Bidi"
-
-#include "IcuUtilities.h"
-#include "JNIHelp.h"
-#include "JniConstants.h"
-#include "JniException.h"
-#include "ScopedPrimitiveArray.h"
-#include "unicode/ubidi.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <memory>
-
-struct BiDiData {
-    BiDiData(UBiDi* biDi) : mBiDi(biDi) {
-    }
-
-    ~BiDiData() {
-        ubidi_close(mBiDi);
-    }
-
-    UBiDiLevel* embeddingLevels() {
-        return reinterpret_cast<UBiDiLevel*>(&mEmbeddingLevels[0]);
-    }
-
-    void setEmbeddingLevels(jbyte* newEmbeddingLevels) {
-        mEmbeddingLevels.reset(newEmbeddingLevels);
-    }
-
-    UBiDi* uBiDi() {
-        return mBiDi;
-    }
-
-private:
-    UBiDi* mBiDi;
-    std::unique_ptr<jbyte[]> mEmbeddingLevels;
-
-    // Disallow copy and assignment.
-    BiDiData(const BiDiData&);
-    void operator=(const BiDiData&);
-};
-
-static BiDiData* biDiData(jlong ptr) {
-    return reinterpret_cast<BiDiData*>(static_cast<uintptr_t>(ptr));
-}
-
-static UBiDi* uBiDi(jlong ptr) {
-    return reinterpret_cast<BiDiData*>(static_cast<uintptr_t>(ptr))->uBiDi();
-}
-
-static jlong Bidi_ubidi_open(JNIEnv*, jclass) {
-    return reinterpret_cast<uintptr_t>(new BiDiData(ubidi_open()));
-}
-
-static void Bidi_ubidi_close(JNIEnv*, jclass, jlong ptr) {
-    delete biDiData(ptr);
-}
-
-static void Bidi_ubidi_setPara(JNIEnv* env, jclass, jlong ptr, jcharArray text, jint length, jint paraLevel, jbyteArray newEmbeddingLevels) {
-    BiDiData* data = biDiData(ptr);
-    // Copy the new embedding levels from the Java heap to the native heap.
-    if (newEmbeddingLevels != NULL) {
-        jbyte* dst;
-        data->setEmbeddingLevels(dst = new jbyte[length]);
-        env->GetByteArrayRegion(newEmbeddingLevels, 0, length, dst);
-    } else {
-        data->setEmbeddingLevels(NULL);
-    }
-    ScopedCharArrayRO chars(env, text);
-    if (chars.get() == NULL) {
-        return;
-    }
-    UErrorCode err = U_ZERO_ERROR;
-    ubidi_setPara(data->uBiDi(), chars.get(), length, paraLevel, data->embeddingLevels(), &err);
-    maybeThrowIcuException(env, "ubidi_setPara", err);
-}
-
-static jlong Bidi_ubidi_setLine(JNIEnv* env, jclass, jlong ptr, jint start, jint limit) {
-    UErrorCode status = U_ZERO_ERROR;
-    UBiDi* sized = ubidi_openSized(limit - start, 0, &status);
-    if (maybeThrowIcuException(env, "ubidi_openSized", status)) {
-        return 0;
-    }
-    std::unique_ptr<BiDiData> lineData(new BiDiData(sized));
-    ubidi_setLine(uBiDi(ptr), start, limit, lineData->uBiDi(), &status);
-    maybeThrowIcuException(env, "ubidi_setLine", status);
-    return reinterpret_cast<uintptr_t>(lineData.release());
-}
-
-static jint Bidi_ubidi_getDirection(JNIEnv*, jclass, jlong ptr) {
-    return ubidi_getDirection(uBiDi(ptr));
-}
-
-static jint Bidi_ubidi_getLength(JNIEnv*, jclass, jlong ptr) {
-    return ubidi_getLength(uBiDi(ptr));
-}
-
-static jbyte Bidi_ubidi_getParaLevel(JNIEnv*, jclass, jlong ptr) {
-    return ubidi_getParaLevel(uBiDi(ptr));
-}
-
-static jbyteArray Bidi_ubidi_getLevels(JNIEnv* env, jclass, jlong ptr) {
-    UErrorCode status = U_ZERO_ERROR;
-    const UBiDiLevel* levels = ubidi_getLevels(uBiDi(ptr), &status);
-    if (maybeThrowIcuException(env, "ubidi_getLevels", status)) {
-        return NULL;
-    }
-    int len = ubidi_getLength(uBiDi(ptr));
-    jbyteArray result = env->NewByteArray(len);
-    env->SetByteArrayRegion(result, 0, len, reinterpret_cast<const jbyte*>(levels));
-    return result;
-}
-
-static jint Bidi_ubidi_countRuns(JNIEnv* env, jclass, jlong ptr) {
-    UErrorCode status = U_ZERO_ERROR;
-    int count = ubidi_countRuns(uBiDi(ptr), &status);
-    maybeThrowIcuException(env, "ubidi_countRuns", status);
-    return count;
-}
-
-/**
- * TODO: if we care about performance, we might just want to use an int[] instead of a Run[].
- */
-static jobjectArray Bidi_ubidi_getRuns(JNIEnv* env, jclass, jlong ptr) {
-    UBiDi* ubidi = uBiDi(ptr);
-    UErrorCode status = U_ZERO_ERROR;
-    int runCount = ubidi_countRuns(ubidi, &status);
-    if (maybeThrowIcuException(env, "ubidi_countRuns", status)) {
-        return NULL;
-    }
-    static jmethodID bidiRunConstructor =
-            env->GetMethodID(JniConstants::bidiRunClass, "<init>", "(III)V");
-    jobjectArray runs = env->NewObjectArray(runCount, JniConstants::bidiRunClass, NULL);
-    UBiDiLevel level = 0;
-    int start = 0;
-    int limit = 0;
-    for (int i = 0; i < runCount; ++i) {
-        ubidi_getLogicalRun(ubidi, start, &limit, &level);
-        jobject run = env->NewObject(JniConstants::bidiRunClass, bidiRunConstructor, start, limit, level);
-        env->SetObjectArrayElement(runs, i, run);
-        start = limit;
-    }
-    return runs;
-}
-
-static jintArray Bidi_ubidi_reorderVisual(JNIEnv* env, jclass, jbyteArray javaLevels, jint length) {
-    ScopedByteArrayRO levelBytes(env, javaLevels);
-    if (levelBytes.get() == NULL) {
-        return NULL;
-    }
-
-    const UBiDiLevel* levels = reinterpret_cast<const UBiDiLevel*>(levelBytes.get());
-
-    std::unique_ptr<int[]> indexMap(new int[length]);
-    ubidi_reorderVisual(levels, length, &indexMap[0]);
-
-    jintArray result = env->NewIntArray(length);
-    env->SetIntArrayRegion(result, 0, length, &indexMap[0]);
-    return result;
-}
-
-static JNINativeMethod gMethods[] = {
-    NATIVE_METHOD(Bidi, ubidi_close, "(J)V"),
-    NATIVE_METHOD(Bidi, ubidi_countRuns, "(J)I"),
-    NATIVE_METHOD(Bidi, ubidi_getDirection, "(J)I"),
-    NATIVE_METHOD(Bidi, ubidi_getLength, "(J)I"),
-    NATIVE_METHOD(Bidi, ubidi_getLevels, "(J)[B"),
-    NATIVE_METHOD(Bidi, ubidi_getParaLevel, "(J)B"),
-    NATIVE_METHOD(Bidi, ubidi_getRuns, "(J)[Ljava/text/Bidi$Run;"),
-    NATIVE_METHOD(Bidi, ubidi_open, "()J"),
-    NATIVE_METHOD(Bidi, ubidi_reorderVisual, "([BI)[I"),
-    NATIVE_METHOD(Bidi, ubidi_setLine, "(JII)J"),
-    NATIVE_METHOD(Bidi, ubidi_setPara, "(J[CII[B)V"),
-};
-void register_java_text_Bidi(JNIEnv* env) {
-    jniRegisterNativeMethods(env, "java/text/Bidi", gMethods, NELEM(gMethods));
-}
diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk
index df918e4..b227793 100644
--- a/luni/src/main/native/sub.mk
+++ b/luni/src/main/native/sub.mk
@@ -27,7 +27,6 @@
     java_lang_StringToReal.cpp \
     java_lang_System.cpp \
     java_math_NativeBN.cpp \
-    java_text_Bidi.cpp \
     java_util_jar_StrictJarFile.cpp \
     java_util_regex_Matcher.cpp \
     java_util_regex_Pattern.cpp \
diff --git a/luni/src/test/java/libcore/java/text/OldBidiTest.java b/luni/src/test/java/libcore/java/text/OldBidiTest.java
index 45fe258..fbf68ea 100644
--- a/luni/src/test/java/libcore/java/text/OldBidiTest.java
+++ b/luni/src/test/java/libcore/java/text/OldBidiTest.java
@@ -70,4 +70,126 @@
         assertEquals(1, bd.getRunStart(1));
         assertEquals(2, bd.getRunStart(2));
     }
+
+    public void testReorderVisuallyIllegalArguments() {
+        // Negative index.
+        try {
+            Bidi.reorderVisually(new byte[] {}, -1, new Object[] {}, 0, 0);
+            fail();
+        } catch (IllegalArgumentException  expected) {
+            // Expected.
+        }
+
+        try {
+            Bidi.reorderVisually(new byte[] {}, 0, new Object[] {}, -1, 0);
+            fail();
+        } catch (IllegalArgumentException  expected) {
+            // Expected.
+        }
+
+        try {
+            Bidi.reorderVisually(new byte[] {}, 0, new Object[] {}, 0, -1);
+            fail();
+        } catch (IllegalArgumentException  expected) {
+            // Expected.
+        }
+
+        // Count > levels.length.
+        try {
+            Bidi.reorderVisually(new byte[] {}, 0, new Object[] {}, 0, 1);
+            fail();
+        } catch (IllegalArgumentException  expected) {
+            // Expected.
+        }
+
+        // Count > levels.length - levelStart.
+        try {
+            Bidi.reorderVisually(new byte[] {1, 2, 3}, 2, new Object[] {}, 0, 2);
+            fail();
+        } catch (IllegalArgumentException  expected) {
+            // Expected.
+        }
+    }
+
+    public void testRequiresBidiIllegalArguments() {
+        // Negative param.
+        try {
+            Bidi.requiresBidi(new char[] {}, 0, -1);
+        } catch (IllegalArgumentException expected) {
+            // Expected.
+        }
+
+        try {
+            Bidi.requiresBidi(new char[] {}, -1, 0);
+        } catch (IllegalArgumentException expected) {
+            // Expected.
+        }
+
+        // Limit > start.
+        try {
+            Bidi.requiresBidi(new char[] {}, 1, 0);
+        } catch (IllegalArgumentException expected) {
+            // Expected.
+        }
+
+        // Limit > text.length.
+        try {
+            Bidi.requiresBidi(new char[] {'a', 'b', 'c'}, 0, 4);
+        } catch (IllegalArgumentException expected) {
+            // Expected.
+        }
+    }
+
+    public void testCreateLineBidiIllegalArguments() {
+        Bidi bidi = new Bidi("test", Bidi.DIRECTION_LEFT_TO_RIGHT);
+
+        try {
+            bidi.createLineBidi(-1, 0);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Expected.
+        }
+
+        try {
+            bidi.createLineBidi(0, -1);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Expected.
+        }
+
+        // Linelimit > getLength().
+        try {
+            bidi.createLineBidi(0, 5);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Expected.
+        }
+
+        // lineStart > lineLimit.
+         try {
+            bidi.createLineBidi(2, 1);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Expected.
+        }
+    }
+
+    public void testConstructorIllegalArguments() {
+        try {
+            new Bidi(null, Bidi.DIRECTION_LEFT_TO_RIGHT);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Expected.
+        }
+
+        // text.length - textStart < paragraphLength.
+        try {
+            new Bidi(new char[] {'a', 'b', 'c', 'd', 'e'}, 1, new byte[] {}, 0, 5,
+                    Bidi.DIRECTION_LEFT_TO_RIGHT);
+            fail();
+        } catch (IllegalArgumentException expected) {
+            // Expected.
+        }
+    }
+
 }