resolved conflicts for merge of 18fb450a to dalvik-dev

Change-Id: I5f7884af56908adbdb9b9ab5799b5b7d0f7f51cc
diff --git a/NativeCode.mk b/NativeCode.mk
index 37cf8db..00bfba7 100644
--- a/NativeCode.mk
+++ b/NativeCode.mk
@@ -95,15 +95,16 @@
 # Define the rules.
 LOCAL_SRC_FILES := $(core_src_files)
 LOCAL_C_INCLUDES := $(core_c_includes) bionic/ bionic/libstdc++/include 
-
-ifneq ($(TARGET_SIMULATOR),true)
-LOCAL_C_INCLUDES += external/stlport/stlport
-endif
-
 LOCAL_SHARED_LIBRARIES := $(core_shared_libraries)
 LOCAL_STATIC_LIBRARIES := $(core_static_libraries)
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := libjavacore
+
+ifneq ($(TARGET_SIMULATOR),true)
+LOCAL_C_INCLUDES += external/stlport/stlport
+LOCAL_SHARED_LIBRARIES += libstlport
+endif
+
 include $(BUILD_STATIC_LIBRARY)
 
 # Deal with keystores required for security. Note: The path to this file
diff --git a/dalvik/src/main/java/dalvik/system/VMDebug.java b/dalvik/src/main/java/dalvik/system/VMDebug.java
index 0298680..1eb589e 100644
--- a/dalvik/src/main/java/dalvik/system/VMDebug.java
+++ b/dalvik/src/main/java/dalvik/system/VMDebug.java
@@ -397,9 +397,15 @@
     private static void startClassPrep() {}
 
     /**
-      * Returns a count of the extant instances of a class.
+     * Counts the instances of a class.
      *
+     * @param klass the class to be counted.
+     * @param assignable if false, direct instances of klass are
+     *                   counted.  If true, instances that are
+     *                   assignable to klass, as defined by
+     *                   {@link Class#isAssignableFrom} are counted.
+     * @returns the number of matching instances.
      * @hide
      */
-    public static native long countInstancesOfClass(Class cls);
+    public static native long countInstancesOfClass(Class klass, boolean assignable);
 }
diff --git a/dom/src/test/java/org/w3c/domts/JAXPDOMTestDocumentBuilderFactory.java b/dom/src/test/java/org/w3c/domts/JAXPDOMTestDocumentBuilderFactory.java
index c2d105e..0ca8f1f 100644
--- a/dom/src/test/java/org/w3c/domts/JAXPDOMTestDocumentBuilderFactory.java
+++ b/dom/src/test/java/org/w3c/domts/JAXPDOMTestDocumentBuilderFactory.java
@@ -12,6 +12,7 @@
 
 package org.w3c.domts;
 
+import java.io.InputStream;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
@@ -120,7 +121,9 @@
     try {
       LoadErrorHandler errorHandler = new LoadErrorHandler();
       builder.setErrorHandler(errorHandler);
-      doc = builder.parse(url.openStream(), url.toString());
+      InputStream stream = url.openStream();
+      doc = builder.parse(stream, url.toString());
+      stream.close();
       parseException = errorHandler.getFirstException();
     }
     catch (Exception ex) {
diff --git a/luni/src/main/java/java/io/BufferedWriter.java b/luni/src/main/java/java/io/BufferedWriter.java
index a91c0fa..e17f735 100644
--- a/luni/src/main/java/java/io/BufferedWriter.java
+++ b/luni/src/main/java/java/io/BufferedWriter.java
@@ -281,7 +281,8 @@
                 return;
             }
             if (offset > str.length() - count || offset < 0) {
-                throw new StringIndexOutOfBoundsException();
+                throw new StringIndexOutOfBoundsException("str.length=" + str.length()
+                        + " offset=" + offset + " count=" + count);
             }
             if (pos == 0 && count >= buf.length) {
                 char[] chars = new char[count];
diff --git a/luni/src/main/java/java/io/CharArrayWriter.java b/luni/src/main/java/java/io/CharArrayWriter.java
index 03cfeba..8ac1493 100644
--- a/luni/src/main/java/java/io/CharArrayWriter.java
+++ b/luni/src/main/java/java/io/CharArrayWriter.java
@@ -231,7 +231,8 @@
         // removed redundant check, used (offset | len) < 0
         // instead of (offset < 0) || (len < 0) to safe one operation
         if ((offset | len) < 0 || len > str.length() - offset) {
-            throw new StringIndexOutOfBoundsException();
+            throw new StringIndexOutOfBoundsException("str.length=" + str.length()
+                    + " offset=" + offset + " len=" + len);
         }
         // END android-changed
         synchronized (lock) {
diff --git a/luni/src/main/java/java/io/File.java b/luni/src/main/java/java/io/File.java
index ffc20aa..1e1d625 100644
--- a/luni/src/main/java/java/io/File.java
+++ b/luni/src/main/java/java/io/File.java
@@ -105,7 +105,7 @@
     /**
      * The path we return from getAbsolutePath, and pass down to native code.
      */
-    private String absolutePath;
+    private transient String absolutePath;
 
     static {
         // The default protection domain grants access to these properties.
@@ -262,8 +262,8 @@
     /**
      * Lists the file system roots. The Java platform may support zero or more
      * file systems, each with its own platform-dependent root. Further, the
-     * canonical pathname of any file on the system will always begin with one
-     * of the returned file system roots.
+     * {@link #getCanonicalPath canonical} path of any file on the system will
+     * always begin with one of the returned file system roots.
      *
      * @return the array of file system roots.
      */
@@ -436,9 +436,12 @@
     private static native boolean existsImpl(String path);
 
     /**
-     * Returns the absolute path of this file.
+     * Returns the absolute path of this file. An absolute path is a path that starts at a root
+     * of the file system. On Android, there is only one root: {@code /}.
      *
-     * @return the absolute file path.
+     * <p>A common use for absolute paths is when passing paths to a {@code Process} as
+     * command-line arguments, to remove the requirement implied by relative paths, that the
+     * child must have the same working directory as its parent.
      */
     public String getAbsolutePath() {
         return absolutePath;
@@ -446,191 +449,46 @@
 
     /**
      * Returns a new file constructed using the absolute path of this file.
-     *
-     * @return a new file from this file's absolute path.
-     * @see java.lang.SecurityManager#checkPropertyAccess
+     * Equivalent to {@code new File(this.getAbsolutePath())}.
      */
     public File getAbsoluteFile() {
         return new File(this.getAbsolutePath());
     }
 
     /**
-     * Returns the absolute path of this file with all references resolved. An
-     * <em>absolute</em> path is one that begins at the root of the file
-     * system. The canonical path is one in which all references have been
-     * resolved. For the cases of '..' and '.', where the file system supports
-     * parent and working directory respectively, these are removed and replaced
-     * with a direct directory reference. If the file does not exist,
-     * getCanonicalPath() may not resolve any references and simply returns an
-     * absolute path name or throws an IOException.
+     * Returns the canonical path of this file.
+     * An <i>absolute</i> path is one that begins at the root of the file system.
+     * A <i>canonical</i> path is an absolute path with symbolic links
+     * and references to "." or ".." resolved. If a path element does not exist (or
+     * is not searchable), there is a conflict between interpreting canonicalization
+     * as a textual operation (where "a/../b" is "b" even if "a" does not exist) .
+     *
+     * <p>Most callers should use {@link #getAbsolutePath} instead. A canonical path is
+     * significantly more expensive to compute, and not generally useful. The primary
+     * use for canonical paths is determining whether two paths point to the same file by
+     * comparing the canonicalized paths.
+     *
+     * <p>It can be actively harmful to use a canonical path, specifically because
+     * canonicalization removes symbolic links. It's wise to assume that a symbolic link
+     * is present for a reason, and that that reason is because the link may need to change.
+     * Canonicalization removes this layer of indirection. Good code should generally avoid
+     * caching canonical paths.
      *
      * @return the canonical path of this file.
      * @throws IOException
      *             if an I/O error occurs.
      */
     public String getCanonicalPath() throws IOException {
-        // BEGIN android-removed
-        //     Caching the canonical path is bogus. Users facing specific
-        //     performance problems can perform their own caching, with
-        //     eviction strategies that are appropriate for their application.
-        //     A VM-wide cache with no mechanism to evict stale elements is a
-        //     disservice to applications that need up-to-date data.
-        // String canonPath = FileCanonPathCache.get(absPath);
-        // if (canonPath != null) {
-        //     return canonPath;
-        // }
-        // END android-removed
-
-        // TODO: rewrite getCanonicalPath, resolve, and resolveLink.
-
-        String result = absolutePath;
-        if (separatorChar == '/') {
-            // resolve the full path first
-            result = resolveLink(result, result.length(), false);
-            // resolve the parent directories
-            result = resolve(result);
-        }
-        int numSeparators = 1;
-        for (int i = 0; i < result.length(); ++i) {
-            if (result.charAt(i) == separatorChar) {
-                numSeparators++;
-            }
-        }
-        int[] sepLocations = new int[numSeparators];
-        int rootLoc = 0;
-        if (separatorChar != '/') {
-            if (result.charAt(0) == '\\') {
-                rootLoc = (result.length() > 1 && result.charAt(1) == '\\') ? 1 : 0;
-            } else {
-                rootLoc = 2; // skip drive i.e. c:
-            }
-        }
-
-        char[] newResult = new char[result.length() + 1];
-        int newLength = 0, lastSlash = 0, foundDots = 0;
-        sepLocations[lastSlash] = rootLoc;
-        for (int i = 0; i <= result.length(); ++i) {
-            if (i < rootLoc) {
-                newResult[newLength++] = result.charAt(i);
-            } else {
-                if (i == result.length() || result.charAt(i) == separatorChar) {
-                    if (i == result.length() && foundDots == 0) {
-                        break;
-                    }
-                    if (foundDots == 1) {
-                        /* Don't write anything, just reset and continue */
-                        foundDots = 0;
-                        continue;
-                    }
-                    if (foundDots > 1) {
-                        /* Go back N levels */
-                        lastSlash = lastSlash > (foundDots - 1) ? lastSlash - (foundDots - 1) : 0;
-                        newLength = sepLocations[lastSlash] + 1;
-                        foundDots = 0;
-                        continue;
-                    }
-                    sepLocations[++lastSlash] = newLength;
-                    newResult[newLength++] = separatorChar;
-                    continue;
-                }
-                if (result.charAt(i) == '.') {
-                    foundDots++;
-                    continue;
-                }
-                /* Found some dots within text, write them out */
-                if (foundDots > 0) {
-                    for (int j = 0; j < foundDots; j++) {
-                        newResult[newLength++] = '.';
-                    }
-                }
-                newResult[newLength++] = result.charAt(i);
-                foundDots = 0;
-            }
-        }
-        // remove trailing slash
-        if (newLength > (rootLoc + 1) && newResult[newLength - 1] == separatorChar) {
-            newLength--;
-        }
-        return new String(newResult, 0, newLength);
+        return realpath(absolutePath);
     }
 
-    /*
-     * Resolve symbolic links in the parent directories.
+    /**
+     * TODO: move this stuff to libcore.os.
+     * @hide
      */
-    private static String resolve(String path) throws IOException {
-        int last = 1;
-        String linkPath = path;
-        String bytes;
-        boolean done;
-        for (int i = 1; i <= path.length(); i++) {
-            if (i == path.length() || path.charAt(i) == separatorChar) {
-                done = i >= path.length() - 1;
-                // if there is only one segment, do nothing
-                if (done && linkPath.length() == 1) {
-                    return path;
-                }
-                boolean inPlace = false;
-                if (linkPath.equals(path)) {
-                    bytes = path;
-                    // if there are no symbolic links, truncate the path instead of copying
-                    if (!done) {
-                        inPlace = true;
-                        path = path.substring(0, i);
-                    }
-                } else {
-                    int nextSize = i - last + 1;
-                    int linkSize = linkPath.length();
-                    if (linkPath.charAt(linkSize - 1) == separatorChar) {
-                        linkSize--;
-                    }
-                    bytes = linkPath.substring(0, linkSize) +
-                            path.substring(last - 1, last - 1 + nextSize);
-                    // the full path has already been resolved
-                }
-                if (done) {
-                    return bytes;
-                }
-                linkPath = resolveLink(bytes, inPlace ? i : bytes.length(), true);
-                if (inPlace) {
-                    // path[i] = '/';
-                    path = path.substring(0, i) + '/' + (i + 1 < path.length() ? path.substring(i + 1) : "");
-                }
-                last = i + 1;
-            }
-        }
-        throw new InternalError();
-    }
-
-    /*
-     * Resolve a symbolic link. While the path resolves to an existing path,
-     * keep resolving. If an absolute link is found, resolve the parent
-     * directories if resolveAbsolute is true.
-     */
-    private static String resolveLink(String path, int length, boolean resolveAbsolute) throws IOException {
-        boolean restart = false;
-        do {
-            String fragment = path.substring(0, length);
-            String target = readlink(fragment);
-            if (target.equals(fragment)) {
-                break;
-            }
-            if (target.charAt(0) == separatorChar) {
-                // The link target was an absolute path, so we may need to start again.
-                restart = resolveAbsolute;
-                path = target + path.substring(length);
-            } else {
-                path = path.substring(0, path.lastIndexOf(separatorChar, length - 1) + 1) + target;
-            }
-            length = path.length();
-        } while (existsImpl(path));
-        // resolve the parent directories
-        if (restart) {
-            return resolve(path);
-        }
-        return path;
-    }
-
-    private static native String readlink(String filePath);
+    public static native void symlink(String oldPath, String newPath);
+    private static native String realpath(String path);
+    private static native String readlink(String path);
 
     /**
      * Returns a new file created using the canonical path of this file.
diff --git a/luni/src/main/java/java/io/ObjectInputStream.java b/luni/src/main/java/java/io/ObjectInputStream.java
index 25e5574..aa9e7e1 100644
--- a/luni/src/main/java/java/io/ObjectInputStream.java
+++ b/luni/src/main/java/java/io/ObjectInputStream.java
@@ -26,6 +26,7 @@
 import java.io.EmulatedFields.ObjectSlot;
 import java.lang.reflect.Array;
 import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
@@ -35,6 +36,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
+import libcore.base.EmptyArray;
 import org.apache.harmony.luni.util.PriviAction;
 
 /**
@@ -53,8 +55,7 @@
     // BEGIN android-note
     // this is non-static to avoid sync contention. Would static be faster?
     // END android-note
-    private InputStream emptyStream = new ByteArrayInputStream(
-            new byte[0]);
+    private InputStream emptyStream = new ByteArrayInputStream(EmptyArray.BYTE);
 
     // To put into objectsRead when reading unsharedObject
     private static final Object UNSHARED_OBJ = new Object(); // $NON-LOCK-1$
@@ -392,20 +393,16 @@
                     .doPrivileged(new PrivilegedAction<Boolean>() {
                         public Boolean run() {
                             try {
-                                Method method = implementationClass
-                                        .getMethod(
-                                                "readFields",
-                                                ObjectStreamClass.EMPTY_CONSTRUCTOR_PARAM_TYPES);
+                                Method method = implementationClass.getMethod("readFields",
+                                        EmptyArray.CLASS);
                                 if (method.getDeclaringClass() != thisClass) {
                                     return Boolean.TRUE;
                                 }
                             } catch (NoSuchMethodException ignored) {
                             }
                             try {
-                                Method method = implementationClass
-                                        .getMethod(
-                                                "readUnshared",
-                                                ObjectStreamClass.EMPTY_CONSTRUCTOR_PARAM_TYPES);
+                                Method method = implementationClass.getMethod("readUnshared",
+                                        EmptyArray.CLASS);
                                 if (method.getDeclaringClass() != thisClass) {
                                     return Boolean.TRUE;
                                 }
@@ -471,8 +468,7 @@
                     primitiveData = new ByteArrayInputStream(readBlockData());
                     return;
                 case TC_BLOCKDATALONG:
-                    primitiveData = new ByteArrayInputStream(
-                            readBlockDataLong());
+                    primitiveData = new ByteArrayInputStream(readBlockDataLong());
                     return;
                 case TC_RESET:
                     resetState();
@@ -811,9 +807,8 @@
                 return readNewClassDesc(false);
             case TC_PROXYCLASSDESC:
                 Class<?> proxyClass = readNewProxyClassDesc();
-                ObjectStreamClass streamClass = ObjectStreamClass
-                        .lookup(proxyClass);
-                streamClass.setLoadFields(new ObjectStreamField[0]);
+                ObjectStreamClass streamClass = ObjectStreamClass.lookup(proxyClass);
+                streamClass.setLoadFields(ObjectStreamClass.NO_FIELDS);
                 registerObjectRead(streamClass, nextHandle(), false);
                 checkedSetSuperClassDesc(streamClass, readClassDesc());
                 return streamClass;
@@ -1116,8 +1111,7 @@
      * @throws NotActiveException
      *             if this stream is currently not reading an object.
      */
-    public GetField readFields() throws IOException, ClassNotFoundException,
-            NotActiveException {
+    public GetField readFields() throws IOException, ClassNotFoundException, NotActiveException {
         // We can't be called from just anywhere. There are rules.
         if (currentObject == null) {
             throw new NotActiveException();
@@ -1150,8 +1144,7 @@
      */
     private void readFieldValues(EmulatedFieldsForLoading emulatedFields)
             throws OptionalDataException, InvalidClassException, IOException {
-        EmulatedFields.ObjectSlot[] slots = emulatedFields.emulatedFields()
-                .slots();
+        EmulatedFields.ObjectSlot[] slots = emulatedFields.emulatedFields().slots();
         for (ObjectSlot element : slots) {
             element.defaulted = false;
             Class<?> type = element.field.getType();
@@ -1212,111 +1205,109 @@
      * @see #readFields
      * @see #readObject()
      */
-    private void readFieldValues(Object obj, ObjectStreamClass classDesc)
-            throws OptionalDataException, ClassNotFoundException, IOException {
+    private void readFieldValues(Object obj, ObjectStreamClass classDesc) throws OptionalDataException, ClassNotFoundException, IOException {
         // Now we must read all fields and assign them to the receiver
         ObjectStreamField[] fields = classDesc.getLoadFields();
-        fields = (null == fields ? new ObjectStreamField[] {} : fields);
+        fields = (null == fields ? ObjectStreamClass.NO_FIELDS : fields);
         Class<?> declaringClass = classDesc.forClass();
         if (declaringClass == null && mustResolve) {
             throw new ClassNotFoundException(classDesc.getName());
         }
 
         for (ObjectStreamField fieldDesc : fields) {
-
-            // BEGIN android-removed
-            // // get associated Field
-            // long fieldID = fieldDesc.getFieldID(accessor, declaringClass);
-            // END android-removed
-
-            // Code duplication starts, just because Java is typed
-            if (fieldDesc.isPrimitive()) {
-                try {
-                    // BEGIN android-changed
-                    switch (fieldDesc.getTypeCode()) {
-                        case 'B':
-                            setFieldByte(obj, declaringClass, fieldDesc.getName(),
-                                    input.readByte());
-                            break;
-                        case 'C':
-                            setFieldChar(obj, declaringClass, fieldDesc.getName(),
-                                    input.readChar());
-                            break;
-                        case 'D':
-                            setFieldDouble(obj, declaringClass, fieldDesc.getName(),
-                                    input.readDouble());
-                            break;
-                        case 'F':
-                            setFieldFloat(obj, declaringClass, fieldDesc.getName(),
-                                    input.readFloat());
-                            break;
-                        case 'I':
-                            setFieldInt(obj, declaringClass, fieldDesc.getName(),
-                                    input.readInt());
-                            break;
-                        case 'J':
-                            setFieldLong(obj, declaringClass, fieldDesc.getName(),
-                                    input.readLong());
-                            break;
-                        case 'S':
-                            setFieldShort(obj, declaringClass, fieldDesc.getName(),
-                                    input.readShort());
-                            break;
-                        case 'Z':
-                            setFieldBool(obj, declaringClass, fieldDesc.getName(),
-                                    input.readBoolean());
-                            break;
-                        default:
-                            throw new StreamCorruptedException("Invalid typecode: " +
-                                    fieldDesc.getTypeCode());
+            Field field = classDesc.getReflectionField(fieldDesc);
+            // We may not have been able to find the field, but we still need to read the value
+            // and do the other checking, so there's no null check on 'field' here.
+            try {
+                switch (fieldDesc.getTypeCode()) {
+                case 'B':
+                    byte b = input.readByte();
+                    if (field != null) {
+                        field.setByte(obj, b);
                     }
-                    // END android-changed
-                } catch (NoSuchFieldError ignored) {
-                }
-            } else {
-                // Object type (array included).
-                String fieldName = fieldDesc.getName();
-                boolean setBack = false;
-                // BEGIN android-added
-                ObjectStreamField field = classDesc.getField(fieldName);
-                // END android-added
-                if (mustResolve && fieldDesc == null) {
-                    setBack = true;
-                    mustResolve = false;
-                }
-                Object toSet;
-                if (fieldDesc != null && fieldDesc.isUnshared()) {
-                    toSet = readUnshared();
-                } else {
-                    toSet = readObject();
-                }
-                if (setBack) {
-                    mustResolve = true;
-                }
-                if (fieldDesc != null) {
-                    if (toSet != null) {
-                        // BEGIN android-changed
-                        // Get the field type from the local field rather than
-                        // from the stream's supplied data. That's the field
-                        // we'll be setting, so that's the one that needs to be
-                        // validated.
-                        Class<?> fieldType = field.getTypeInternal();
-                        // END android-added
-                        Class<?> valueType = toSet.getClass();
-                        if (!fieldType.isAssignableFrom(valueType)) {
-                            throw new ClassCastException(classDesc.getName() + "." + fieldName +
-                                    " - " + fieldType + " not compatible with " + valueType);
-                        }
-                        try {
+                    break;
+                case 'C':
+                    char c = input.readChar();
+                    if (field != null) {
+                        field.setChar(obj, c);
+                    }
+                    break;
+                case 'D':
+                    double d = input.readDouble();
+                    if (field != null) {
+                        field.setDouble(obj, d);
+                    }
+                    break;
+                case 'F':
+                    float f = input.readFloat();
+                    if (field != null) {
+                        field.setFloat(obj, f);
+                    }
+                    break;
+                case 'I':
+                    int i = input.readInt();
+                    if (field != null) {
+                        field.setInt(obj, i);
+                    }
+                    break;
+                case 'J':
+                    long j = input.readLong();
+                    if (field != null) {
+                        field.setLong(obj, j);
+                    }
+                    break;
+                case 'S':
+                    short s = input.readShort();
+                    if (field != null) {
+                        field.setShort(obj, s);
+                    }
+                    break;
+                case 'Z':
+                    boolean z = input.readBoolean();
+                    if (field != null) {
+                        field.setBoolean(obj, z);
+                    }
+                    break;
+                case 'L':
+                case '[':
+                    String fieldName = fieldDesc.getName();
+                    boolean setBack = false;
+                    ObjectStreamField localFieldDesc = classDesc.getField(fieldName);
+                    if (mustResolve && fieldDesc == null) {
+                        setBack = true;
+                        mustResolve = false;
+                    }
+                    boolean unshared = fieldDesc != null && fieldDesc.isUnshared();
+                    Object toSet = unshared ? readUnshared() : readObject();
+                    if (setBack) {
+                        mustResolve = true;
+                    }
+                    if (fieldDesc != null) {
+                        if (toSet != null) {
                             // BEGIN android-changed
-                            setFieldObject(obj, declaringClass, fieldName, field.getTypeString(),
-                                    toSet);
-                            // END android-changed
-                        } catch (NoSuchFieldError e) {
-                            // Ignored
+                            // Get the field type from the local field rather than
+                            // from the stream's supplied data. That's the field
+                            // we'll be setting, so that's the one that needs to be
+                            // validated.
+                            Class<?> fieldType = localFieldDesc.getTypeInternal();
+                            // END android-added
+                            Class<?> valueType = toSet.getClass();
+                            if (!fieldType.isAssignableFrom(valueType)) {
+                                throw new ClassCastException(classDesc.getName() + "." + fieldName + " - " + fieldType + " not compatible with " + valueType);
+                            }
+                            if (field != null) {
+                                field.set(obj, toSet);
+                            }
                         }
                     }
+                    break;
+                default:
+                    throw new StreamCorruptedException("Invalid typecode: " + fieldDesc.getTypeCode());
                 }
+            } catch (IllegalAccessException iae) {
+                // ObjectStreamField should have called setAccessible(true).
+                throw new AssertionError(iae);
+            } catch (NoSuchFieldError ignored) {
             }
         }
     }
@@ -1405,8 +1396,7 @@
             throw new NotActiveException();
         }
 
-        ArrayList<ObjectStreamClass> streamClassList = new ArrayList<ObjectStreamClass>(
-                32);
+        ArrayList<ObjectStreamClass> streamClassList = new ArrayList<ObjectStreamClass>(32);
         ObjectStreamClass nextStreamClass = classDesc;
         while (nextStreamClass != null) {
             streamClassList.add(0, nextStreamClass);
@@ -1827,7 +1817,7 @@
         // Resolve the field signatures using the class loader of the
         // resolved class
         ObjectStreamField[] fields = newClassDesc.getLoadFields();
-        fields = (null == fields ? new ObjectStreamField[] {} : fields);
+        fields = (null == fields ? ObjectStreamClass.NO_FIELDS : fields);
         ClassLoader loader = newClassDesc.forClass() == null ? callerClassLoader
                 : newClassDesc.forClass().getClassLoader();
         for (ObjectStreamField element : fields) {
@@ -1966,8 +1956,7 @@
             Constructor<?> constructor = null;
             if (constructorClass != null) {
                 try {
-                    constructor = constructorClass
-                            .getDeclaredConstructor(ObjectStreamClass.EMPTY_CONSTRUCTOR_PARAM_TYPES);
+                    constructor = constructorClass.getDeclaredConstructor(EmptyArray.CLASS);
                 } catch (NoSuchMethodException nsmEx) {
                     // Ignored
                 }
@@ -2052,15 +2041,7 @@
 
         Object result, registeredResult = null;
         if (objectClass != null) {
-
-            // BEGIN android-changed
-            // long constructor = classDesc.getConstructor();
-            // if (constructor == ObjectStreamClass.CONSTRUCTOR_IS_NOT_RESOLVED) {
-            //     constructor = accessor.getMethodID(resolveConstructorClass(objectClass, wasSerializable, wasExternalizable), null, new Class[0]);
-            //     classDesc.setConstructor(constructor);
-            // }
             Class constructorClass = resolveConstructorClass(objectClass, wasSerializable, wasExternalizable);
-            // END android-changed
 
             // Now we know which class to instantiate and which constructor to
             // run. We are allowed to run the constructor.
@@ -2582,60 +2563,6 @@
         return object;
     }
 
-    // BEGIN android-added
-
-    /*
-     * These methods set the value of a field named fieldName of instance. The
-     * field is declared by declaringClass. The field is the same type as the
-     * value parameter.
-     *
-     * these methods could be implemented non-natively on top of
-     * java.lang.reflect at the expense of extra object creation
-     * (java.lang.reflect.Field). Otherwise Serialization could not fetch
-     * private fields, except by the use of a native method like this one.
-     *
-     * @throws NoSuchFieldError If the field does not exist.
-     */
-    private static native void setFieldByte(Object instance,
-            Class<?> declaringClass, String fieldName, byte value)
-            throws NoSuchFieldError;
-
-
-    private static native void setFieldChar(Object instance,
-            Class<?> declaringClass, String fieldName, char value)
-            throws NoSuchFieldError;
-
-
-    private static native void setFieldDouble(Object instance,
-            Class<?> declaringClass, String fieldName, double value)
-            throws NoSuchFieldError;
-
-    private static native void setFieldFloat(Object instance,
-            Class<?> declaringClass, String fieldName, float value)
-            throws NoSuchFieldError;
-
-    private static native void setFieldInt(Object instance,
-            Class<?> declaringClass, String fieldName, int value)
-            throws NoSuchFieldError;
-
-    private static native void setFieldLong(Object instance,
-            Class<?> declaringClass, String fieldName, long value)
-            throws NoSuchFieldError;
-
-    private static native void setFieldObject(Object instance,
-            Class<?> declaringClass, String fieldName, String fieldTypeName,
-            Object value) throws NoSuchFieldError;
-
-    private static native void setFieldShort(Object instance,
-            Class<?> declaringClass, String fieldName, short value)
-            throws NoSuchFieldError;
-
-    private static native void setFieldBool(Object instance,
-            Class<?> declaringClass, String fieldName, boolean value)
-            throws NoSuchFieldError;
-
-    // END android-added
-
     /**
      * Skips {@code length} bytes on the source stream. This method should not
      * be used to skip bytes at any arbitrary position, just when reading
diff --git a/luni/src/main/java/java/io/ObjectOutputStream.java b/luni/src/main/java/java/io/ObjectOutputStream.java
index 2c5eb7a..e2e5c75 100644
--- a/luni/src/main/java/java/io/ObjectOutputStream.java
+++ b/luni/src/main/java/java/io/ObjectOutputStream.java
@@ -17,20 +17,17 @@
 
 package java.io;
 
+import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.nio.ByteOrder;
 import java.nio.charset.ModifiedUtf8;
 import java.util.IdentityHashMap;
+import libcore.base.EmptyArray;
 import libcore.io.SizeOf;
 import org.apache.harmony.luni.platform.OSMemory;
 
-// BEGIN android-note
-// Harmony uses ObjectAccessors to access fields through JNI. Android has not
-// yet migrated that API. As a consequence, there's a lot of changes here...
-// END android-note
-
 /**
  * A specialized {@link OutputStream} that is able to write (serialize) Java
  * objects as well as primitive data types (int, byte, char etc.). The data can
@@ -44,6 +41,8 @@
 public class ObjectOutputStream extends OutputStream implements ObjectOutput,
         ObjectStreamConstants {
 
+    private static final Class<?>[] WRITE_UNSHARED_PARAM_TYPES = new Class[] { Object.class };
+
     /*
      * Mask to zero SC_BLOC_DATA bit.
      */
@@ -289,16 +288,14 @@
         if (implementationClass != thisClass) {
             boolean mustCheck = false;
             try {
-                Method method = implementationClass.getMethod("putFields",
-                        ObjectStreamClass.EMPTY_CONSTRUCTOR_PARAM_TYPES);
+                Method method = implementationClass.getMethod("putFields", EmptyArray.CLASS);
                 mustCheck = method.getDeclaringClass() != thisClass;
             } catch (NoSuchMethodException e) {
             }
             if (!mustCheck) {
                 try {
-                    Method method = implementationClass.getMethod(
-                            "writeUnshared",
-                            ObjectStreamClass.UNSHARED_PARAM_TYPES);
+                    Method method = implementationClass.getMethod("writeUnshared",
+                            WRITE_UNSHARED_PARAM_TYPES);
                     mustCheck = method.getDeclaringClass() != thisClass;
                 } catch (NoSuchMethodException e) {
                 }
@@ -523,7 +520,6 @@
         output.flush();
     }
 
-    // BEGIN android-added
     /*
      * These methods get the value of a field named fieldName of object
      * instance. The field is declared by declaringClass. The field is the same
@@ -536,34 +532,7 @@
      *
      * @throws NoSuchFieldError If the field does not exist.
      */
-
-    private static native boolean getFieldBool(Object instance,
-            Class<?> declaringClass, String fieldName);
-
-    private static native byte getFieldByte(Object instance,
-            Class<?> declaringClass, String fieldName);
-
-    private static native char getFieldChar(Object instance,
-            Class<?> declaringClass, String fieldName);
-
-    private static native double getFieldDouble(Object instance,
-            Class<?> declaringClass, String fieldName);
-
-    private static native float getFieldFloat(Object instance,
-            Class<?> declaringClass, String fieldName);
-
-    private static native int getFieldInt(Object instance,
-            Class<?> declaringClass, String fieldName);
-
-    private static native long getFieldLong(Object instance,
-            Class<?> declaringClass, String fieldName);
-
-    private static native Object getFieldObj(Object instance,
-            Class<?> declaringClass, String fieldName, String fieldTypeName);
-
-    private static native short getFieldShort(Object instance,
-            Class<?> declaringClass, String fieldName);
-    // END android-added
+    private static native Object getFieldL(Object instance, Class<?> declaringClass, String fieldName, String fieldTypeName);
 
     /**
      * Return the next <code>Integer</code> handle to be used to indicate cyclic
@@ -1025,11 +994,9 @@
      * @see #writeFields
      * @see #writeObject(Object)
      */
-    private void writeFieldValues(EmulatedFieldsForDumping emulatedFields)
-            throws IOException {
-        EmulatedFields accessibleSimulatedFields = emulatedFields
-                .emulatedFields(); // Access internal fields which we can
-        // set/get. Users can't do this.
+    private void writeFieldValues(EmulatedFieldsForDumping emulatedFields) throws IOException {
+        // Access internal fields which we can set/get. Users can't do this.
+        EmulatedFields accessibleSimulatedFields = emulatedFields.emulatedFields();
         EmulatedFields.ObjectSlot[] slots = accessibleSimulatedFields.slots();
         for (int i = 0; i < slots.length; i++) {
             EmulatedFields.ObjectSlot slot = slots[i];
@@ -1037,29 +1004,21 @@
             Class<?> type = slot.getField().getType();
             // WARNING - default values exist for each primitive type
             if (type == Integer.TYPE) {
-                output.writeInt(fieldValue != null ? ((Integer) fieldValue)
-                        .intValue() : 0);
+                output.writeInt(fieldValue != null ? ((Integer) fieldValue).intValue() : 0);
             } else if (type == Byte.TYPE) {
-                output.writeByte(fieldValue != null ? ((Byte) fieldValue)
-                        .byteValue() : (byte) 0);
+                output.writeByte(fieldValue != null ? ((Byte) fieldValue).byteValue() : (byte) 0);
             } else if (type == Character.TYPE) {
-                output.writeChar(fieldValue != null ? ((Character) fieldValue)
-                        .charValue() : (char) 0);
+                output.writeChar(fieldValue != null ? ((Character) fieldValue).charValue() : (char) 0);
             } else if (type == Short.TYPE) {
-                output.writeShort(fieldValue != null ? ((Short) fieldValue)
-                        .shortValue() : (short) 0);
+                output.writeShort(fieldValue != null ? ((Short) fieldValue).shortValue() : (short) 0);
             } else if (type == Boolean.TYPE) {
-                output.writeBoolean(fieldValue != null ? ((Boolean) fieldValue)
-                        .booleanValue() : false);
+                output.writeBoolean(fieldValue != null ? ((Boolean) fieldValue).booleanValue() : false);
             } else if (type == Long.TYPE) {
-                output.writeLong(fieldValue != null ? ((Long) fieldValue)
-                        .longValue() : (long) 0);
+                output.writeLong(fieldValue != null ? ((Long) fieldValue).longValue() : (long) 0);
             } else if (type == Float.TYPE) {
-                output.writeFloat(fieldValue != null ? ((Float) fieldValue)
-                        .floatValue() : (float) 0);
+                output.writeFloat(fieldValue != null ? ((Float) fieldValue).floatValue() : (float) 0);
             } else if (type == Double.TYPE) {
-                output.writeDouble(fieldValue != null ? ((Double) fieldValue)
-                        .doubleValue() : (double) 0);
+                output.writeDouble(fieldValue != null ? ((Double) fieldValue).doubleValue() : (double) 0);
             } else {
                 // Either array or Object
                 writeObject(fieldValue);
@@ -1067,7 +1026,6 @@
         }
     }
 
-
     /**
      * Writes a collection of field values for the fields described by class
      * descriptor {@code classDesc} (an {@code ObjectStreamClass}).
@@ -1086,72 +1044,59 @@
      *
      * @see #writeObject(Object)
      */
-    private void writeFieldValues(Object obj, ObjectStreamClass classDesc)
-            throws IOException {
-        ObjectStreamField[] fields = classDesc.fields();
+    private void writeFieldValues(Object obj, ObjectStreamClass classDesc) throws IOException {
         Class<?> declaringClass = classDesc.forClass();
-        for(ObjectStreamField fieldDesc : fields) {
+        for (ObjectStreamField fieldDesc : classDesc.fields()) {
             try {
-
-                // BEGIN android-changed
-                // // get associated Field
-                // long fieldID = fieldDesc.getFieldID(accessor, declaringClass);
-
-                // Code duplication starts, just because Java is typed
-                if (fieldDesc.isPrimitive()) {
-                    switch (fieldDesc.getTypeCode()) {
-                        case 'B':
-                            output.writeByte(getFieldByte(obj, declaringClass,
-                                    fieldDesc.getName()));
-                            break;
-                        case 'C':
-                            output.writeChar(getFieldChar(obj, declaringClass,
-                                    fieldDesc.getName()));
-                            break;
-                        case 'D':
-                            output.writeDouble(getFieldDouble(obj,
-                                    declaringClass, fieldDesc.getName()));
-                            break;
-                        case 'F':
-                            output.writeFloat(getFieldFloat(obj,
-                                    declaringClass, fieldDesc.getName()));
-                            break;
-                        case 'I':
-                            output.writeInt(getFieldInt(obj, declaringClass,
-                                    fieldDesc.getName()));
-                            break;
-                        case 'J':
-                            output.writeLong(getFieldLong(obj, declaringClass,
-                                    fieldDesc.getName()));
-                            break;
-                        case 'S':
-                            output.writeShort(getFieldShort(obj,
-                                    declaringClass, fieldDesc.getName()));
-                            break;
-                        case 'Z':
-                            output.writeBoolean(getFieldBool(obj,
-                                    declaringClass, fieldDesc.getName()));
-                            break;
-                        default:
-                            throw new IOException("Invalid typecode: " +
-                                    fieldDesc.getTypeCode());
-                    }
-                } else {
-                    // Object type (array included).
-                    Object objField = getFieldObj(obj, declaringClass, fieldDesc
-                            .getName(), fieldDesc.getTypeString());
+                Field field = classDesc.getReflectionField(fieldDesc);
+                if (field == null) {
+                    throw new InvalidClassException(classDesc.getName() + " doesn't have a field " + fieldDesc.getName() + " of type " + fieldDesc.getTypeCode());
+                }
+                switch (fieldDesc.getTypeCode()) {
+                case 'B':
+                    output.writeByte(field.getByte(obj));
+                    break;
+                case 'C':
+                    output.writeChar(field.getChar(obj));
+                    break;
+                case 'D':
+                    output.writeDouble(field.getDouble(obj));
+                    break;
+                case 'F':
+                    output.writeFloat(field.getFloat(obj));
+                    break;
+                case 'I':
+                    output.writeInt(field.getInt(obj));
+                    break;
+                case 'J':
+                    output.writeLong(field.getLong(obj));
+                    break;
+                case 'S':
+                    output.writeShort(field.getShort(obj));
+                    break;
+                case 'Z':
+                    output.writeBoolean(field.getBoolean(obj));
+                    break;
+                case 'L':
+                case '[':
+                    // Reference types ('L' and '[').
+                    Object objField = field.get(obj);
                     if (fieldDesc.isUnshared()) {
                         writeUnshared(objField);
                     } else {
                         writeObject(objField);
                     }
+                    break;
+                default:
+                    throw new IOException("Invalid typecode: " + fieldDesc.getTypeCode());
                 }
-                // END android-changed
+            } catch (IllegalAccessException iae) {
+                // ObjectStreamField should have called setAccessible(true).
+                throw new AssertionError(iae);
             } catch (NoSuchFieldError nsf) {
                 // The user defined serialPersistentFields but did not provide
-                // the glue to transfer values,
-                // (in writeObject) so we end up using the default mechanism and
-                // fail to set the emulated field
+                // the glue to transfer values in writeObject, so we ended up using
+                // the default mechanism but failed to set the emulated field.
                 throw new InvalidClassException(classDesc.getName());
             }
         }
@@ -1673,40 +1618,39 @@
         writeObject(object, true);
     }
 
-    private void writeObject(Object object, boolean unshared)
-            throws IOException {
+    private void writeObject(Object object, boolean unshared) throws IOException {
         boolean setOutput = (primitiveTypes == output);
         if (setOutput) {
             primitiveTypes = null;
         }
-        // This is the spec'ed behavior in JDK 1.2. Very bizarre way to allow
+        // This is the specified behavior in JDK 1.2. Very bizarre way to allow
         // behavior overriding.
         if (subclassOverridingImplementation && !unshared) {
             writeObjectOverride(object);
-        } else {
+            return;
+        }
 
-            try {
-                // First we need to flush primitive types if they were written
-                drain();
-                // Actual work, and class-based replacement should be computed
-                // if needed.
-                writeObjectInternal(object, unshared, true, true);
-                if (setOutput) {
-                    primitiveTypes = output;
-                }
-            } catch (IOException ioEx1) {
-                // This will make it pass through until the top caller. It also
-                // lets it pass through the nested exception.
-                if (nestedLevels == 0 && ioEx1 != nestedException) {
-                    try {
-                        writeNewException(ioEx1);
-                    } catch (IOException ioEx2) {
-                        nestedException.fillInStackTrace();
-                        throw nestedException;
-                    }
-                }
-                throw ioEx1; // and then we propagate the original exception
+        try {
+            // First we need to flush primitive types if they were written
+            drain();
+            // Actual work, and class-based replacement should be computed
+            // if needed.
+            writeObjectInternal(object, unshared, true, true);
+            if (setOutput) {
+                primitiveTypes = output;
             }
+        } catch (IOException ioEx1) {
+            // This will make it pass through until the top caller. It also
+            // lets it pass through the nested exception.
+            if (nestedLevels == 0 && ioEx1 != nestedException) {
+                try {
+                    writeNewException(ioEx1);
+                } catch (IOException ioEx2) {
+                    nestedException.fillInStackTrace();
+                    throw nestedException;
+                }
+            }
+            throw ioEx1; // and then we propagate the original exception
         }
     }
 
@@ -1906,8 +1850,8 @@
         return classDesc;
     }
 
-    private Integer writeNewEnum(Object object, Class<?> theClass,
-            boolean unshared) throws IOException {
+    private Integer writeNewEnum(Object object, Class<?> theClass, boolean unshared)
+            throws IOException {
         // write new Enum
         EmulatedFieldsForDumping originalCurrentPutField = currentPutField; // save
         // null it, to make sure one will be computed if needed
@@ -1932,18 +1876,22 @@
         Class<?> declaringClass = classDesc.getSuperclass().forClass();
         // Only write field "name" for enum class, which is the second field of
         // enum, that is fields[1]. Ignore all non-fields and fields.length < 2
-        if (null != fields && fields.length > 1) {
-            // BEGIN android-changed
-            String str = (String) getFieldObj(object, declaringClass, fields[1]
-                    .getName(), fields[1].getTypeString());
-            // END android-changed
-
-            Integer strhandle = null;
-            if (!unshared) {
-                strhandle = dumpCycle(str);
+        if (fields != null && fields.length > 1) {
+            Field field = classDesc.getReflectionField(fields[1]);
+            if (field == null) {
+                throw new NoSuchFieldError();
             }
-            if (null == strhandle) {
-                writeNewString(str, unshared);
+            try {
+                String str = (String) field.get(object);
+                Integer strHandle = null;
+                if (!unshared) {
+                    strHandle = dumpCycle(str);
+                }
+                if (strHandle == null) {
+                    writeNewString(str, unshared);
+                }
+            } catch (IllegalAccessException iae) {
+                throw new AssertionError(iae);
             }
         }
 
diff --git a/luni/src/main/java/java/io/ObjectStreamClass.java b/luni/src/main/java/java/io/ObjectStreamClass.java
index 38bce57..7317d97 100644
--- a/luni/src/main/java/java/io/ObjectStreamClass.java
+++ b/luni/src/main/java/java/io/ObjectStreamClass.java
@@ -31,8 +31,10 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
 import java.util.WeakHashMap;
+import libcore.base.EmptyArray;
 import org.apache.harmony.luni.platform.OSMemory;
 import org.apache.harmony.luni.util.PriviAction;
 
@@ -57,42 +59,19 @@
 
     static final long CONSTRUCTOR_IS_NOT_RESOLVED = -1;
 
-    private static final int CLASS_MODIFIERS_MASK;
+    private static final int CLASS_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.FINAL |
+            Modifier.INTERFACE | Modifier.ABSTRACT;
 
-    private static final int FIELD_MODIFIERS_MASK;
+    private static final int FIELD_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.PRIVATE |
+            Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL | Modifier.VOLATILE |
+            Modifier.TRANSIENT;
 
-    private static final int METHOD_MODIFIERS_MASK;
+    private static final int METHOD_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.PRIVATE |
+            Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL | Modifier.SYNCHRONIZED |
+            Modifier.NATIVE | Modifier.ABSTRACT | Modifier.STRICT;
 
-    private static final Class<?>[] READ_PARAM_TYPES;
-
-    private static final Class<?>[] WRITE_PARAM_TYPES;
-
-    static final Class<?>[] EMPTY_CONSTRUCTOR_PARAM_TYPES;
-
-    private static final Class<Void> VOID_CLASS;
-
-    static final Class<?>[] UNSHARED_PARAM_TYPES;
-
-    static {
-        CLASS_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.FINAL
-                | Modifier.INTERFACE | Modifier.ABSTRACT;
-        FIELD_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.PRIVATE
-                | Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL
-                | Modifier.VOLATILE | Modifier.TRANSIENT;
-        METHOD_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.PRIVATE
-                | Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL
-                | Modifier.SYNCHRONIZED | Modifier.NATIVE | Modifier.ABSTRACT
-                | Modifier.STRICT;
-
-        READ_PARAM_TYPES = new Class[1];
-        WRITE_PARAM_TYPES = new Class[1];
-        READ_PARAM_TYPES[0] = ObjectInputStream.class;
-        WRITE_PARAM_TYPES[0] = ObjectOutputStream.class;
-        EMPTY_CONSTRUCTOR_PARAM_TYPES = new Class[0];
-        VOID_CLASS = Void.TYPE;
-        UNSHARED_PARAM_TYPES = new Class[1];
-        UNSHARED_PARAM_TYPES[0] = Object.class;
-    }
+    private static final Class<?>[] READ_PARAM_TYPES = new Class[] { ObjectInputStream.class };
+    private static final Class<?>[] WRITE_PARAM_TYPES = new Class[] { ObjectOutputStream.class };
 
     /**
      * Constant indicating that the class has no Serializable fields.
@@ -190,6 +169,11 @@
     // Array of ObjectStreamField describing the serialized fields of this class
     private transient ObjectStreamField[] loadFields;
 
+    // ObjectStreamField doesn't override hashCode or equals, so this is equivalent to an
+    // IdentityHashMap, which is fine for our purposes.
+    private transient HashMap<ObjectStreamField, Field> reflectionFields =
+            new HashMap<ObjectStreamField, Field>();
+
     // MethodID for deserialization constructor
     private transient long constructor = CONSTRUCTOR_IS_NOT_RESOLVED;
 
@@ -201,6 +185,28 @@
         return constructor;
     }
 
+    Field getReflectionField(ObjectStreamField osf) {
+        synchronized (reflectionFields) {
+            Field field = reflectionFields.get(osf);
+            if (field != null) {
+                return field;
+            }
+        }
+
+        try {
+            Class<?> declaringClass = forClass();
+            Field field = declaringClass.getDeclaredField(osf.getName());
+            field.setAccessible(true);
+            synchronized (reflectionFields) {
+                reflectionFields.put(osf, field);
+            }
+            return reflectionFields.get(osf);
+        } catch (NoSuchFieldException ex) {
+            // The caller messed up. We'll return null and won't try to resolve this again.
+            return null;
+        }
+    }
+
     /*
      * If an ObjectStreamClass describes an Externalizable class, it (the
      * descriptor) should not have field descriptors (ObjectStreamField) at all.
@@ -246,13 +252,12 @@
         Field[] declaredFields = null;
 
         // Compute the SUID
-        if(serializable || externalizable) {
+        if (serializable || externalizable) {
             if (result.isEnum() || result.isProxy()) {
                 result.setSerialVersionUID(0L);
             } else {
                 declaredFields = cl.getDeclaredFields();
-                result.setSerialVersionUID(computeSerialVersionUID(cl,
-                        declaredFields));
+                result.setSerialVersionUID(computeSerialVersionUID(cl, declaredFields));
             }
         }
 
@@ -294,12 +299,9 @@
         }
         result.methodWriteReplace = findMethod(cl, "writeReplace");
         result.methodReadResolve = findMethod(cl, "readResolve");
-        result.methodWriteObject = findPrivateMethod(cl, "writeObject",
-                WRITE_PARAM_TYPES);
-        result.methodReadObject = findPrivateMethod(cl, "readObject",
-                READ_PARAM_TYPES);
-        result.methodReadObjectNoData = findPrivateMethod(cl,
-                "readObjectNoData", EMPTY_CONSTRUCTOR_PARAM_TYPES);
+        result.methodWriteObject = findPrivateMethod(cl, "writeObject", WRITE_PARAM_TYPES);
+        result.methodReadObject = findPrivateMethod(cl, "readObject", READ_PARAM_TYPES);
+        result.methodReadObjectNoData = findPrivateMethod(cl, "readObjectNoData", EmptyArray.CLASS);
         if (result.hasMethodWriteObject()) {
             flags |= ObjectStreamConstants.SC_WRITE_METHOD;
         }
@@ -318,8 +320,7 @@
     void buildFieldDescriptors(Field[] declaredFields) {
         // We could find the field ourselves in the collection, but calling
         // reflect is easier. Optimize if needed.
-        final Field f = ObjectStreamClass.fieldSerialPersistentFields(this
-                .forClass());
+        final Field f = ObjectStreamClass.fieldSerialPersistentFields(this.forClass());
         // If we could not find the emulated fields, we'll have to compute
         // dumpable fields from reflect fields
         boolean useReflectFields = f == null; // Assume we will compute the
@@ -335,23 +336,18 @@
                 // static field, pass null
                 _fields = (ObjectStreamField[]) f.get(null);
             } catch (IllegalAccessException ex) {
-                // WARNING - what should we do if we have no access ? This
-                // should not happen.
-                throw new RuntimeException(ex);
+                throw new AssertionError(ex);
             }
         } else {
             // Compute collection of dumpable fields based on reflect fields
-            List<ObjectStreamField> serializableFields = new ArrayList<ObjectStreamField>(
-                    declaredFields.length);
+            List<ObjectStreamField> serializableFields =
+                    new ArrayList<ObjectStreamField>(declaredFields.length);
             // Filter, we are only interested in fields that are serializable
-            for (int i = 0; i < declaredFields.length; i++) {
-                Field declaredField = declaredFields[i];
+            for (Field declaredField : declaredFields) {
                 int modifiers = declaredField.getModifiers();
-                boolean shouldBeSerialized = !(Modifier.isStatic(modifiers) || Modifier
-                        .isTransient(modifiers));
-                if (shouldBeSerialized) {
-                    ObjectStreamField field = new ObjectStreamField(
-                            declaredField.getName(), declaredField.getType());
+                if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)) {
+                    ObjectStreamField field = new ObjectStreamField(declaredField.getName(),
+                            declaredField.getType());
                     serializableFields.add(field);
                 }
             }
@@ -360,12 +356,10 @@
                 _fields = NO_FIELDS; // If no serializable fields, share the
                 // special value so that users can test
             } else {
-                // Now convert from Vector to array
-                _fields = new ObjectStreamField[serializableFields.size()];
-                _fields = serializableFields.toArray(_fields);
+                _fields = serializableFields.toArray(new ObjectStreamField[serializableFields.size()]);
             }
         }
-        ObjectStreamField.sortFields(_fields);
+        Arrays.sort(_fields);
         // assign offsets
         int primOffset = 0, objectOffset = 0;
         for (int i = 0; i < _fields.length; i++) {
@@ -925,12 +919,7 @@
      */
     public static ObjectStreamClass lookup(Class<?> cl) {
         ObjectStreamClass osc = lookupStreamClass(cl);
-
-        if (osc.isSerializable() || osc.isExternalizable()) {
-            return osc;
-        }
-
-        return null;
+        return (osc.isSerializable() || osc.isExternalizable()) ? osc : null;
     }
 
     /**
@@ -944,7 +933,7 @@
      * @since 1.6
      */
     public static ObjectStreamClass lookupAny(Class<?> cl) {
-        return  lookupStreamClass(cl);
+        return lookupStreamClass(cl);
     }
 
     /**
@@ -958,8 +947,7 @@
      * @return the corresponding descriptor
      */
     static ObjectStreamClass lookupStreamClass(Class<?> cl) {
-        WeakHashMap<Class<?>,ObjectStreamClass> tlc = getCache();
-
+        WeakHashMap<Class<?>, ObjectStreamClass> tlc = getCache();
         ObjectStreamClass cachedValue = tlc.get(cl);
         if (cachedValue == null) {
             cachedValue = createClassDesc(cl);
@@ -1031,8 +1019,7 @@
             Class<?>[] param) {
         try {
             Method method = cl.getDeclaredMethod(methodName, param);
-            if (Modifier.isPrivate(method.getModifiers())
-                    && method.getReturnType() == VOID_CLASS) {
+            if (Modifier.isPrivate(method.getModifiers()) && method.getReturnType() == Void.TYPE) {
                 method.setAccessible(true);
                 return method;
             }
diff --git a/luni/src/main/java/java/io/ObjectStreamField.java b/luni/src/main/java/java/io/ObjectStreamField.java
index 686ea15..7e05f40 100644
--- a/luni/src/main/java/java/io/ObjectStreamField.java
+++ b/luni/src/main/java/java/io/ObjectStreamField.java
@@ -17,19 +17,10 @@
 
 package java.io;
 
-// BEGIN android-note
-// Harmony uses ObjectAccessors to access fields through JNI. Android has not
-// yet migrated that API.
-// END android-note
-
 import java.lang.ref.WeakReference;
 import java.util.Arrays;
 import java.util.Comparator;
 
-// BEGIN android-removed
-// import org.apache.harmony.misc.accessors.ObjectAccessor;
-// END android-removed
-
 /**
  * Describes a field for the purpose of serialization. Classes can define the
  * collection of fields that are serialized, which may be different from the set
@@ -40,9 +31,6 @@
  */
 public class ObjectStreamField implements Comparable<Object> {
 
-    static final int FIELD_IS_NOT_RESOLVED = -1;
-    static final int FIELD_IS_ABSENT = -2;
-
     // Declared name of the field
     private String name;
 
@@ -59,23 +47,6 @@
 
     private boolean isDeserialized;
 
-    private long assocFieldID = FIELD_IS_NOT_RESOLVED;
-
-    // BEGIN android-removed
-    // long getFieldID(ObjectAccessor accessor, Class<?> declaringClass) {
-    //     if (assocFieldID != FIELD_IS_NOT_RESOLVED) {
-    //         return assocFieldID;
-    //     } else {
-    //         try {
-    //             assocFieldID = accessor.getFieldID(declaringClass, name);
-    //         } catch(NoSuchFieldError e) {
-    //             assocFieldID = FIELD_IS_ABSENT;
-    //         }
-    //         return assocFieldID;
-    //     }
-    // }
-    // END android-removed
-
     /**
      * Constructs an ObjectStreamField with the specified name and type.
      *
@@ -114,8 +85,7 @@
             throw new NullPointerException();
         }
         this.name = name;
-        this.type = (cl.getClassLoader() == null) ? cl
-                : new WeakReference<Class<?>>(cl);
+        this.type = (cl.getClassLoader() == null) ? cl : new WeakReference<Class<?>>(cl);
         this.unshared = unshared;
     }
 
@@ -165,19 +135,6 @@
         return this.getName().compareTo(f.getName());
     }
 
-    // BEGIN android-removed
-    // There shouldn't be an implementation of these methods.
-    // @Override
-    // public boolean equals(Object arg0) {
-    //     return (arg0 instanceof ObjectStreamField) && (compareTo(arg0) == 0);
-    // }
-    //
-    // @Override
-    // public int hashCode() {
-    //     return getName().hashCode();
-    // }
-    // END android-removed
-
     /**
      * Gets the name of this field.
      *
@@ -325,27 +282,7 @@
      */
     @Override
     public String toString() {
-        return this.getClass().getName() + '(' + getName() + ':'
-                + getTypeInternal() + ')';
-    }
-
-    /**
-     * Sorts the fields for dumping. Primitive types come first, then regular
-     * types.
-     *
-     * @param fields
-     *            ObjectStreamField[] fields to be sorted
-     */
-    static void sortFields(ObjectStreamField[] fields) {
-        // Sort if necessary
-        if (fields.length > 1) {
-            Comparator<ObjectStreamField> fieldDescComparator = new Comparator<ObjectStreamField>() {
-                public int compare(ObjectStreamField f1, ObjectStreamField f2) {
-                    return f1.compareTo(f2);
-                }
-            };
-            Arrays.sort(fields, fieldDescComparator);
-        }
+        return this.getClass().getName() + '(' + getName() + ':' + getTypeInternal() + ')';
     }
 
     void resolve(ClassLoader loader) {
diff --git a/luni/src/main/java/java/io/OutputStreamWriter.java b/luni/src/main/java/java/io/OutputStreamWriter.java
index e1a515b..57c2d55 100644
--- a/luni/src/main/java/java/io/OutputStreamWriter.java
+++ b/luni/src/main/java/java/io/OutputStreamWriter.java
@@ -285,13 +285,14 @@
     public void write(String str, int offset, int count) throws IOException {
         synchronized (lock) {
             if (count < 0) {
-                throw new StringIndexOutOfBoundsException();
+                throw new StringIndexOutOfBoundsException(count);
             }
             if (str == null) {
                 throw new NullPointerException("str == null");
             }
             if (offset < 0 || offset > str.length() - count) {
-                throw new StringIndexOutOfBoundsException();
+                throw new StringIndexOutOfBoundsException("str.length=" + str.length()
+                        + " offset=" + offset + " count=" + count);
             }
             checkStatus();
             CharBuffer chars = CharBuffer.wrap(str, offset, count + offset);
diff --git a/luni/src/main/java/java/io/Writer.java b/luni/src/main/java/java/io/Writer.java
index ca909f3..89755ff 100644
--- a/luni/src/main/java/java/io/Writer.java
+++ b/luni/src/main/java/java/io/Writer.java
@@ -170,7 +170,7 @@
      */
     public void write(String str, int offset, int count) throws IOException {
         if (count < 0) { // other cases tested by getChars()
-            throw new StringIndexOutOfBoundsException();
+            throw new StringIndexOutOfBoundsException(count);
         }
         char[] buf = new char[count];
         str.getChars(offset, offset + count, buf, 0);
diff --git a/luni/src/main/java/java/lang/AbstractStringBuilder.java b/luni/src/main/java/java/lang/AbstractStringBuilder.java
index 2aa0e86..3e854b9 100644
--- a/luni/src/main/java/java/lang/AbstractStringBuilder.java
+++ b/luni/src/main/java/java/lang/AbstractStringBuilder.java
@@ -19,6 +19,7 @@
 
 import java.io.InvalidObjectException;
 import java.util.Arrays;
+import libcore.base.EmptyArray;
 
 /**
  * A modifiable {@link CharSequence sequence of characters} for use in creating
@@ -59,7 +60,7 @@
      */
     final void set(char[] val, int len) throws InvalidObjectException {
         if (val == null) {
-            val = new char[0];
+            val = EmptyArray.CHAR;
         }
         if (val.length < len) {
             throw new InvalidObjectException("count out of range");
@@ -211,7 +212,7 @@
      */
     public char charAt(int index) {
         if (index < 0 || index >= count) {
-            throw new StringIndexOutOfBoundsException(index);
+            throw new StringIndexOutOfBoundsException("index=" + index + " length=" + count);
         }
         return value[index];
     }
@@ -241,12 +242,14 @@
                 return;
             }
         }
-        throw new StringIndexOutOfBoundsException();
+        throw new StringIndexOutOfBoundsException(
+                "start=" + start + " end=" + end + " length=" + count);
     }
 
     final void deleteCharAt0(int location) {
         if (0 > location || location >= count) {
-            throw new StringIndexOutOfBoundsException(location);
+            throw new StringIndexOutOfBoundsException(
+                    "location=" + location + " length=" + count);
         }
         int length = count - location - 1;
         if (length > 0) {
@@ -305,14 +308,15 @@
      */
     public void getChars(int start, int end, char[] dst, int dstStart) {
         if (start > count || end > count || start > end) {
-            throw new StringIndexOutOfBoundsException();
+            throw new StringIndexOutOfBoundsException(
+                    "start=" + start + " end=" + end + " length=" + count);
         }
         System.arraycopy(value, start, dst, dstStart, end - start);
     }
 
     final void insert0(int index, char[] chars) {
         if (0 > index || index > count) {
-            throw new StringIndexOutOfBoundsException(index);
+            throw new StringIndexOutOfBoundsException("index=" + index + " length=" + count);
         }
         if (chars.length != 0) {
             move(chars.length, index);
@@ -332,11 +336,9 @@
                 }
                 return;
             }
-            throw new StringIndexOutOfBoundsException("offset " + start
-                    + ", length " + length
-                    + ", char[].length " + chars.length);
         }
-        throw new StringIndexOutOfBoundsException(index);
+        throw new StringIndexOutOfBoundsException("index=" + index + " chars.length=" + chars.length
+                + " start=" + start + " length=" + length + " this.length=" + count);
     }
 
     final void insert0(int index, char ch) {
@@ -363,7 +365,7 @@
                 count += min;
             }
         } else {
-            throw new StringIndexOutOfBoundsException(index);
+            throw new StringIndexOutOfBoundsException("index=" + index + " length=" + count);
         }
     }
 
@@ -450,7 +452,8 @@
                 return;
             }
         }
-        throw new StringIndexOutOfBoundsException();
+        throw new StringIndexOutOfBoundsException(
+                "start=" + start + " end=" + end + " length=" + count);
     }
 
     final void reverse0() {
@@ -543,7 +546,7 @@
      */
     public void setCharAt(int index, char ch) {
         if (0 > index || index >= count) {
-            throw new StringIndexOutOfBoundsException(index);
+            throw new StringIndexOutOfBoundsException("index=" + index + " length=" + count);
         }
         if (shared) {
             value = value.clone();
@@ -604,7 +607,7 @@
             // Remove String sharing for more performance
             return new String(value, start, count - start);
         }
-        throw new StringIndexOutOfBoundsException(start);
+        throw new StringIndexOutOfBoundsException("start=" + start + " length=" + count);
     }
 
     /**
@@ -629,7 +632,8 @@
             // Remove String sharing for more performance
             return new String(value, start, end - start);
         }
-        throw new StringIndexOutOfBoundsException();
+        throw new StringIndexOutOfBoundsException(
+                "start=" + start + " end=" + end + " length=" + count);
     }
 
     /**
@@ -834,7 +838,7 @@
      */
     public int codePointAt(int index) {
         if (index < 0 || index >= count) {
-            throw new StringIndexOutOfBoundsException(index);
+            throw new StringIndexOutOfBoundsException("index=" + index + " length=" + count);
         }
         return Character.codePointAt(value, index, count);
     }
@@ -854,7 +858,7 @@
      */
     public int codePointBefore(int index) {
         if (index < 1 || index > count) {
-            throw new StringIndexOutOfBoundsException(index);
+            throw new StringIndexOutOfBoundsException("index=" + index + " length=" + count);
         }
         return Character.codePointBefore(value, index);
     }
@@ -878,7 +882,8 @@
      */
     public int codePointCount(int beginIndex, int endIndex) {
         if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) {
-            throw new StringIndexOutOfBoundsException();
+            throw new StringIndexOutOfBoundsException("beginIndex=" + beginIndex
+                    + " endIndex" + endIndex + " length=" + count);
         }
         return Character.codePointCount(value, beginIndex, endIndex
                 - beginIndex);
diff --git a/luni/src/main/java/java/lang/Class.java b/luni/src/main/java/java/lang/Class.java
index 451da89..eb721b8 100644
--- a/luni/src/main/java/java/lang/Class.java
+++ b/luni/src/main/java/java/lang/Class.java
@@ -658,8 +658,7 @@
      *             access.
      * @see #getField(String)
      */
-    public Field getDeclaredField(String name)
-            throws NoSuchFieldException, SecurityException {
+    public Field getDeclaredField(String name) throws NoSuchFieldException, SecurityException {
         checkDeclaredMemberAccess();
 
         Field[] fields = getClassCache().getDeclaredFields();
@@ -871,6 +870,26 @@
     }
 
     /**
+     * Finds and returns a field with a given name and signature. Use
+     * this with one of the field lists returned by instances of ClassCache.
+     *
+     * @param list non-null; the list of fields to search through
+     * @return non-null; the matching field
+     * @throws NoSuchFieldException thrown if the field does not exist
+     */
+    private Field findFieldByName(Field[] list, String name) throws NoSuchFieldException {
+        if (name == null) {
+            throw new NullPointerException("name == null");
+        }
+        for (Field field : list) {
+            if (field.getName().equals(name)) {
+                return field;
+            }
+        }
+        throw new NoSuchFieldException("No field '" + name + "' in " + this);
+    }
+
+    /**
      * Returns an array containing {@code Field} objects for all public fields
      * for the class C represented by this {@code Class}. Fields may be declared
      * in C, the interfaces it implements or in the superclasses of C. The
@@ -1467,7 +1486,9 @@
         if (clazz.isAssignableFrom(this)) {
             return (Class<? extends U>)this;
         }
-        throw new ClassCastException();
+        String actualClassName = this.getName();
+        String desiredClassName = clazz.getName();
+        throw new ClassCastException(actualClassName + " cannot be cast to " + desiredClassName);
     }
 
     /**
@@ -1487,7 +1508,9 @@
         } else if (this.isInstance(obj)) {
             return (T)obj;
         }
-        throw new ClassCastException();
+        String actualClassName = obj.getClass().getName();
+        String desiredClassName = this.getName();
+        throw new ClassCastException(actualClassName + " cannot be cast to " + desiredClassName);
     }
 
     /**
diff --git a/luni/src/main/java/java/lang/ClassCache.java b/luni/src/main/java/java/lang/ClassCache.java
index 5588f62..55b787f 100644
--- a/luni/src/main/java/java/lang/ClassCache.java
+++ b/luni/src/main/java/java/lang/ClassCache.java
@@ -235,7 +235,7 @@
     public static Method findMethodByName(Method[] list, String name,
             Class<?>[] parameterTypes) throws NoSuchMethodException {
         if (name == null) {
-            throw new NullPointerException("Method name must not be null.");
+            throw new NullPointerException("name == null");
         }
         for (Method method : list) {
             if (method.getName().equals(name)
@@ -425,28 +425,6 @@
     }
 
     /**
-     * Finds and returns a field with a given name and signature. Use
-     * this with one of the field lists returned by instances of this class.
-     *
-     * @param list non-null; the list of fields to search through
-     * @return non-null; the matching field
-     * @throws NoSuchFieldException thrown if the field does not exist
-     */
-    public static Field findFieldByName(Field[] list, String name)
-            throws NoSuchFieldException {
-        if (name == null) {
-            throw new NullPointerException("Field name must not be null.");
-        }
-        for (Field field : list) {
-            if (field.getName().equals(name)) {
-                return field;
-            }
-        }
-
-        throw new NoSuchFieldException(name);
-    }
-
-    /**
      * Makes a deep copy of the given array of fields. This is useful
      * when handing out arrays from the public API.
      *
diff --git a/luni/src/main/java/java/lang/String.java b/luni/src/main/java/java/lang/String.java
index 605ea67..541d987 100644
--- a/luni/src/main/java/java/lang/String.java
+++ b/luni/src/main/java/java/lang/String.java
@@ -28,6 +28,7 @@
 import java.util.Formatter;
 import java.util.Locale;
 import java.util.regex.Pattern;
+import libcore.base.EmptyArray;
 
 /**
  * An immutable sequence of characters/code units ({@code char}s). A
@@ -94,8 +95,6 @@
      */
     public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();
 
-    private static final char[] EMPTY_CHAR_ARRAY = new char[0];
-
     private static final char[] ASCII;
     static {
         ASCII = new char[128];
@@ -116,7 +115,7 @@
      * Creates an empty string.
      */
     public String() {
-        value = EMPTY_CHAR_ARRAY;
+        value = EmptyArray.CHAR;
         offset = 0;
         count = 0;
     }
@@ -184,10 +183,11 @@
             if (count > 0) {
                 value = cb.array();
             } else {
-                value = EMPTY_CHAR_ARRAY;
+                value = EmptyArray.CHAR;
             }
         } else {
-            throw new StringIndexOutOfBoundsException();
+            throw new StringIndexOutOfBoundsException("data.length=" + data.length
+                    + " start=" + start + " length=" + length);
         }
     }
 
@@ -224,7 +224,8 @@
                     value[i] = (char) (high + (data[start++] & 0xff));
                 }
             } else {
-                throw new StringIndexOutOfBoundsException();
+                throw new StringIndexOutOfBoundsException(
+                        "data.length=" + data.length + " start=" + start + " length=" + length);
             }
         } else {
             throw new NullPointerException();
@@ -314,7 +315,8 @@
      */
     public String(byte[] data, int start, int length, Charset charset) {
         if (start < 0 || length < 0 || length > data.length - start) {
-            throw new StringIndexOutOfBoundsException();
+            throw new StringIndexOutOfBoundsException(
+                    "data.length=" + data.length + " start=" + start + " length=" + length);
         }
 
         // We inline UTF-8, ISO-8859-1, and US-ASCII decoders for speed and because 'count' and
@@ -446,7 +448,7 @@
                 this.value = new char[count];
                 System.arraycopy(cb.array(), 0, value, 0, count);
             } else {
-                value = EMPTY_CHAR_ARRAY;
+                value = EmptyArray.CHAR;
             }
         }
     }
@@ -514,7 +516,8 @@
             count = length;
             System.arraycopy(data, start, value, 0, count);
         } else {
-            throw new StringIndexOutOfBoundsException();
+            throw new StringIndexOutOfBoundsException(
+                    "data.length=" + data.length + " start=" + start + " length=" + length);
         }
     }
 
@@ -623,7 +626,8 @@
             throw new NullPointerException();
         }
         if (offset < 0 || count < 0 || (long) offset + (long) count > codePoints.length) {
-            throw new StringIndexOutOfBoundsException();
+            throw new StringIndexOutOfBoundsException("codePoints.length=" + codePoints.length
+                    + " offset=" + offset + " count=" + count);
         }
         this.offset = 0;
         this.value = new char[count * 2];
@@ -685,7 +689,7 @@
         if (0 <= index && index < count) {
             return value[offset + index];
         }
-        throw new StringIndexOutOfBoundsException();
+        throw new StringIndexOutOfBoundsException("index=" + index + " length=" + count);
     }
 
     // Optimized for ASCII
@@ -940,12 +944,11 @@
                 for (int i = offset + start; i < end; i++) {
                     data[index++] = (byte) value[i];
                 }
-            } catch (ArrayIndexOutOfBoundsException e) {
-                throw new StringIndexOutOfBoundsException();
+            } catch (ArrayIndexOutOfBoundsException ignored) {
             }
-        } else {
-            throw new StringIndexOutOfBoundsException();
         }
+        throw new StringIndexOutOfBoundsException("start=" + start + " end=" + end
+                + " data.length=" + data.length + " index=" + index + " length=" + count);
     }
 
     /**
@@ -1027,7 +1030,8 @@
         if (0 <= start && start <= end && end <= count) {
             System.arraycopy(value, start + offset, buffer, index, end - start);
         } else {
-            throw new StringIndexOutOfBoundsException();
+            throw new StringIndexOutOfBoundsException("start=" + start + " end=" + end +
+                    " buffer.length=" + buffer.length + " index=" + index + " length=" + count);
         }
     }
 
@@ -1618,7 +1622,7 @@
         if (0 <= start && start <= count) {
             return new String(offset + start, count - start, value);
         }
-        throw new StringIndexOutOfBoundsException(start);
+        throw new StringIndexOutOfBoundsException("start=" + start + " length=" + count);
     }
 
     /**
@@ -1644,7 +1648,8 @@
         if (0 <= start && start <= end && end <= count) {
             return new String(offset + start, end - start, value);
         }
-        throw new StringIndexOutOfBoundsException();
+        throw new StringIndexOutOfBoundsException(
+                "start=" + start + " end=" + end + " length=" + count);
     }
 
     /**
@@ -1894,6 +1899,23 @@
     }
 
     /**
+     * Equivalent to {@code equals(new String(chars, offset, length))}.
+     *
+     * @hide
+     */
+    public boolean contentEquals(char[] chars, int offset, int length) {
+        if (count != length) {
+            return false;
+        }
+        for (int i = 0; i < length; i++) {
+            if (chars[offset + i] != value[this.offset + i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
      * Compares a {@code CharSequence} to this {@code String} to determine if
      * their contents are equal.
      *
@@ -2045,7 +2067,7 @@
      */
     public int codePointAt(int index) {
         if (index < 0 || index >= count) {
-            throw new StringIndexOutOfBoundsException();
+            throw new StringIndexOutOfBoundsException("index=" + index + " length=" + count);
         }
         return Character.codePointAt(value, offset + index, offset + count);
     }
@@ -2059,7 +2081,7 @@
      */
     public int codePointBefore(int index) {
         if (index < 1 || index > count) {
-            throw new StringIndexOutOfBoundsException();
+            throw new StringIndexOutOfBoundsException("index=" + index + " length=" + count);
         }
         return Character.codePointBefore(value, offset + index, offset);
     }
@@ -2080,7 +2102,8 @@
      */
     public int codePointCount(int beginIndex, int endIndex) {
         if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) {
-            throw new StringIndexOutOfBoundsException();
+            throw new StringIndexOutOfBoundsException("beginIndex=" + beginIndex
+                    + " endIndex=" + endIndex + " length=" + count);
         }
         return Character.codePointCount(value, offset + beginIndex, endIndex - beginIndex);
     }
diff --git a/luni/src/main/java/java/lang/Thread.java b/luni/src/main/java/java/lang/Thread.java
index 980c841..e26ecf0 100644
--- a/luni/src/main/java/java/lang/Thread.java
+++ b/luni/src/main/java/java/lang/Thread.java
@@ -69,6 +69,7 @@
  *
  */
 public class Thread implements Runnable {
+    private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
 
     private static final int NANOS_PER_MILLI = 1000000;
 
@@ -735,7 +736,7 @@
         }
 
         StackTraceElement ste[] = VMStack.getThreadStackTrace(this);
-        return ste != null ? ste : new StackTraceElement[0];
+        return ste != null ? ste : EMPTY_STACK_TRACE;
     }
 
     /**
diff --git a/luni/src/main/java/java/lang/Void.java b/luni/src/main/java/java/lang/Void.java
index 56d7c74..75e616a 100644
--- a/luni/src/main/java/java/lang/Void.java
+++ b/luni/src/main/java/java/lang/Void.java
@@ -18,6 +18,7 @@
 package java.lang;
 
 import java.lang.reflect.Method;
+import libcore.base.EmptyArray;
 
 /**
  * Placeholder class for the Java keyword {@code void}.
@@ -38,7 +39,7 @@
     private static Class<Void> lookupType() {
         Class<?> voidType = null;
         try {
-            Method method = Runnable.class.getMethod("run", new Class[0]);
+            Method method = Runnable.class.getMethod("run", EmptyArray.CLASS);
             voidType = method.getReturnType();
         } catch (Exception e) {
             throw new RuntimeException(e);
diff --git a/luni/src/main/java/java/lang/reflect/Array.java b/luni/src/main/java/java/lang/reflect/Array.java
index 1e672d1..db0d426 100644
--- a/luni/src/main/java/java/lang/reflect/Array.java
+++ b/luni/src/main/java/java/lang/reflect/Array.java
@@ -47,14 +47,14 @@
     /**
      * Returns the element of the array at the specified index. This reproduces
      * the effect of {@code array[index]}. If the array component is a primitive
-     * type, the result is automatically wrapped.
+     * type, the result is automatically boxed.
      *
      * @param array
      *            the array
      * @param index
      *            the index
      *
-     * @return the requested element, possibly wrapped
+     * @return the requested element, possibly boxed
      *
      * @throws NullPointerException
      *             if the array is null
@@ -486,7 +486,7 @@
     /**
      * Sets the element of the array at the specified index to the value. This
      * reproduces the effect of {@code array[index] = value}. If the array
-     * component is a primitive type, the value is automatically unwrapped.
+     * component is a primitive type, the value is automatically unboxed.
      *
      * @param array
      *            the array
diff --git a/luni/src/main/java/java/lang/reflect/Constructor.java b/luni/src/main/java/java/lang/reflect/Constructor.java
index fba433e..f511687 100644
--- a/luni/src/main/java/java/lang/reflect/Constructor.java
+++ b/luni/src/main/java/java/lang/reflect/Constructor.java
@@ -34,6 +34,7 @@
 
 import dalvik.system.VMStack;
 import java.lang.annotation.Annotation;
+import libcore.base.EmptyArray;
 import org.apache.harmony.kernel.vm.StringUtils;
 import org.apache.harmony.luni.lang.reflect.GenericSignatureParser;
 import org.apache.harmony.luni.lang.reflect.ListOfTypes;
@@ -291,8 +292,9 @@
      * @return the declared exception classes
      */
     public Class<?>[] getExceptionTypes() {
-        if (exceptionTypes == null)
-            return new Class[0];
+        if (exceptionTypes == null) {
+            return EmptyArray.CLASS;
+        }
         return exceptionTypes.clone();
     }
 
@@ -381,7 +383,7 @@
      * <li>For each argument passed:
      * <ul>
      * <li>If the corresponding parameter type is a primitive type, the argument
-     * is unwrapped. If the unwrapping fails, an IllegalArgumentException is
+     * is unboxed. If the unboxing fails, an IllegalArgumentException is
      * thrown.</li>
      * <li>If the resulting argument cannot be converted to the parameter type
      * via a widening conversion, an IllegalArgumentException is thrown.</li>
diff --git a/luni/src/main/java/java/lang/reflect/Field.java b/luni/src/main/java/java/lang/reflect/Field.java
index ecdb93a..d7898b9 100644
--- a/luni/src/main/java/java/lang/reflect/Field.java
+++ b/luni/src/main/java/java/lang/reflect/Field.java
@@ -209,23 +209,22 @@
     /**
      * Returns the value of the field in the specified object. This reproduces
      * the effect of {@code object.fieldName}
-     * <p>
-     * If the type of this field is a primitive type, the field value is
-     * automatically wrapped.
-     * <p>
-     * If this field is static, the object argument is ignored.
+     *
+     * <p>If the type of this field is a primitive type, the field value is
+     * automatically boxed.
+     *
+     * <p>If this field is static, the object argument is ignored.
      * Otherwise, if the object is null, a NullPointerException is thrown. If
      * the object is not an instance of the declaring class of the method, an
      * IllegalArgumentException is thrown.
-     * <p>
-     * If this Field object is enforcing access control (see AccessibleObject)
+     *
+     * <p>If this Field object is enforcing access control (see AccessibleObject)
      * and this field is not accessible from the current context, an
      * IllegalAccessException is thrown.
-     * <p>
      *
      * @param object
      *            the object to access
-     * @return the field value, possibly wrapped
+     * @return the field value, possibly boxed
      * @throws NullPointerException
      *             if the object is {@code null} and the field is non-static
      * @throws IllegalArgumentException
@@ -527,18 +526,18 @@
     /**
      * Sets the value of the field in the specified object to the value. This
      * reproduces the effect of {@code object.fieldName = value}
-     * <p>
-     * If this field is static, the object argument is ignored.
+     *
+     * <p>If this field is static, the object argument is ignored.
      * Otherwise, if the object is {@code null}, a NullPointerException is
      * thrown. If the object is not an instance of the declaring class of the
      * method, an IllegalArgumentException is thrown.
-     * <p>
-     * If this Field object is enforcing access control (see AccessibleObject)
+     *
+     * <p>If this Field object is enforcing access control (see AccessibleObject)
      * and this field is not accessible from the current context, an
      * IllegalAccessException is thrown.
-     * <p>
-     * If the field type is a primitive type, the value is automatically
-     * unwrapped. If the unwrap fails, an IllegalArgumentException is thrown. If
+     *
+     * <p>If the field type is a primitive type, the value is automatically
+     * unboxed. If the unboxing fails, an IllegalArgumentException is thrown. If
      * the value cannot be converted to the field type via a widening
      * conversion, an IllegalArgumentException is thrown.
      *
diff --git a/luni/src/main/java/java/lang/reflect/InvocationHandler.java b/luni/src/main/java/java/lang/reflect/InvocationHandler.java
index c5bd7d2..330904e 100644
--- a/luni/src/main/java/java/lang/reflect/InvocationHandler.java
+++ b/luni/src/main/java/java/lang/reflect/InvocationHandler.java
@@ -46,10 +46,9 @@
      * @param args
      *            an array of objects containing the parameters passed to the
      *            method, or {@code null} if no arguments are expected.
-     *            Primitive types are wrapped in the appropriate wrapper type
+     *            Primitive types are boxed.
      *
-     * @return the result of executing the method. Primitive types need to be
-     *         wrapped in the appropriate wrapper type
+     * @return the result of executing the method. Primitive types are boxed.
      *
      * @throws Throwable
      *             the exception to throw from the invoked method on the proxy.
@@ -57,6 +56,5 @@
      *             of the invoked method or any unchecked exception type. If not
      *             then an {@code UndeclaredThrowableException} is thrown
      */
-    public Object invoke(Object proxy, Method method, Object[] args)
-            throws Throwable;
+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
 }
diff --git a/luni/src/main/java/java/lang/reflect/Method.java b/luni/src/main/java/java/lang/reflect/Method.java
index f46d76f..68fe219 100644
--- a/luni/src/main/java/java/lang/reflect/Method.java
+++ b/luni/src/main/java/java/lang/reflect/Method.java
@@ -34,6 +34,7 @@
 
 import java.lang.annotation.Annotation;
 import java.util.Comparator;
+import libcore.base.EmptyArray;
 import org.apache.harmony.kernel.vm.StringUtils;
 import org.apache.harmony.luni.lang.reflect.GenericSignatureParser;
 import org.apache.harmony.luni.lang.reflect.ListOfTypes;
@@ -378,9 +379,8 @@
      */
     public Class<?>[] getExceptionTypes() {
         if (exceptionTypes == null) {
-            return new Class[0];
+            return EmptyArray.CLASS;
         }
-
         return exceptionTypes.clone();
     }
 
@@ -444,73 +444,57 @@
     }
 
     /**
-     * Returns the result of dynamically invoking this method. This reproduces
-     * the effect of {@code receiver.methodName(arg1, arg2, ... , argN)} This
-     * method performs the following:
-     * <ul>
-     * <li>If this method is static, the receiver argument is ignored.</li>
-     * <li>Otherwise, if the receiver is null, a NullPointerException is thrown.
-     * </li>
-     * <li>If the receiver is not an instance of the declaring class of the
-     * method, an IllegalArgumentException is thrown.</li>
-     * <li>If this Method object is enforcing access control (see
-     * AccessibleObject) and this method is not accessible from the current
-     * context, an IllegalAccessException is thrown.</li>
-     * <li>If the number of arguments passed and the number of parameters do not
-     * match, an IllegalArgumentException is thrown.</li>
-     * <li>For each argument passed:
-     * <ul>
-     * <li>If the corresponding parameter type is a primitive type, the argument
-     * is unwrapped. If the unwrapping fails, an IllegalArgumentException is
-     * thrown.</li>
-     * <li>If the resulting argument cannot be converted to the parameter type
-     * via a widening conversion, an IllegalArgumentException is thrown.</li>
-     * </ul>
-     * <li>If this method is static, it is invoked directly. If it is
-     * non-static, this method and the receiver are then used to perform a
-     * standard dynamic method lookup. The resulting method is then invoked.</li>
-     * <li>If an exception is thrown during the invocation it is caught and
-     * wrapped in an InvocationTargetException. This exception is then thrown.</li>
-     * <li>If the invocation completes normally, the return value itself is
+     * Returns the result of dynamically invoking this method. Equivalent to
+     * {@code receiver.methodName(arg1, arg2, ... , argN)}.
+     *
+     * <p>If the method is static, the receiver argument is ignored (and may be null).
+     *
+     * <p>If the method takes no arguments, you can pass {@code (Object[]) null} instead of
+     * allocating an empty array.
+     *
+     * <p>If you're calling a varargs method, you need to pass an {@code Object[]} for the
+     * varargs parameter: that conversion is usually done in {@code javac}, not the VM, and
+     * the reflection machinery does not do this for you. (It couldn't, because it would be
+     * ambiguous.)
+     *
+     * <p>Reflective method invocation follows the usual process for method lookup.
+     *
+     * <p>If an exception is thrown during the invocation it is caught and
+     * wrapped in an InvocationTargetException. This exception is then thrown.
+     *
+     * <p>If the invocation completes normally, the return value itself is
      * returned. If the method is declared to return a primitive type, the
-     * return value is first wrapped. If the return type is void, null is
-     * returned.</li>
-     * </ul>
+     * return value is boxed. If the return type is void, null is returned.
      *
      * @param receiver
-     *            the object on which to call this method
+     *            the object on which to call this method (or null for static methods)
      * @param args
      *            the arguments to the method
-     *
-     * @return the new, initialized, object
+     * @return the result
      *
      * @throws NullPointerException
-     *             if the receiver is null for a non-static method
+     *             if {@code receiver == null} for a non-static method
      * @throws IllegalAccessException
-     *             if this method is not accessible
+     *             if this method is not accessible (see {@link AccessibleObject})
      * @throws IllegalArgumentException
-     *             if an incorrect number of arguments are passed, the receiver
-     *             is incompatible with the declaring class, or an argument
-     *             could not be converted by a widening conversion
+     *             if the number of arguments doesn't match the number of parameters, the receiver
+     *             is incompatible with the declaring class, or an argument could not be unboxed
+     *             or converted by a widening conversion to the corresponding parameter type
      * @throws InvocationTargetException
      *             if an exception was thrown by the invoked method
-     *
-     * @see AccessibleObject
      */
     public Object invoke(Object receiver, Object... args)
-            throws IllegalAccessException, IllegalArgumentException,
-            InvocationTargetException {
+            throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
         if (args == null) {
-            args = new Object[0];
+            args = EmptyArray.OBJECT;
         }
-
-        return invokeNative (receiver, args, declaringClass, parameterTypes, returnType, slot, flag);
+        return invokeNative(receiver, args, declaringClass, parameterTypes, returnType, slot, flag);
     }
 
-    private native Object invokeNative(Object obj, Object[] args, Class<?> declaringClass, Class<?>[] parameterTypes, Class<?> returnType, int slot, boolean noAccessCheck)
-    throws IllegalAccessException,
-             IllegalArgumentException,
-             InvocationTargetException;
+    private native Object invokeNative(Object obj, Object[] args, Class<?> declaringClass,
+            Class<?>[] parameterTypes, Class<?> returnType, int slot, boolean noAccessCheck)
+                    throws IllegalAccessException, IllegalArgumentException,
+                            InvocationTargetException;
 
     /**
      * Returns a string containing a concise, human-readable description of this
diff --git a/luni/src/main/java/java/net/DatagramPacket.java b/luni/src/main/java/java/net/DatagramPacket.java
index 36ec922..cde5d7b 100644
--- a/luni/src/main/java/java/net/DatagramPacket.java
+++ b/luni/src/main/java/java/net/DatagramPacket.java
@@ -277,8 +277,7 @@
      * @throws SocketException
      *             if an error in the underlying protocol occurs.
      */
-    public DatagramPacket(byte[] data, int length, SocketAddress sockAddr)
-            throws SocketException {
+    public DatagramPacket(byte[] data, int length, SocketAddress sockAddr) throws SocketException {
         this(data, 0, length);
         setSocketAddress(sockAddr);
     }
@@ -328,6 +327,9 @@
                     (sockAddr == null ? null : sockAddr.getClass()));
         }
         InetSocketAddress inetAddr = (InetSocketAddress) sockAddr;
+        if (inetAddr.isUnresolved()) {
+            throw new IllegalArgumentException("Socket address unresolved: " + sockAddr);
+        }
         port = inetAddr.getPort();
         address = inetAddr.getAddress();
     }
diff --git a/luni/src/main/java/java/net/DatagramSocket.java b/luni/src/main/java/java/net/DatagramSocket.java
index d1cc593..1bfe40e 100644
--- a/luni/src/main/java/java/net/DatagramSocket.java
+++ b/luni/src/main/java/java/net/DatagramSocket.java
@@ -429,18 +429,14 @@
         } else {
             // not connected so the target address is not allowed to be null
             if (packAddr == null) {
-                if (pack.getPort() == -1) {
-                    throw new NullPointerException("Destination address is null");
-                }
-                return;
+                throw new NullPointerException("Destination address is null");
             }
             SecurityManager security = System.getSecurityManager();
             if (security != null) {
                 if (packAddr.isMulticastAddress()) {
                     security.checkMulticast(packAddr);
                 } else {
-                    security.checkConnect(packAddr.getHostName(), pack
-                            .getPort());
+                    security.checkConnect(packAddr.getHostName(), pack.getPort());
                 }
             }
         }
diff --git a/luni/src/main/java/java/net/Inet6Address.java b/luni/src/main/java/java/net/Inet6Address.java
index 24a069f..f8920fc 100644
--- a/luni/src/main/java/java/net/Inet6Address.java
+++ b/luni/src/main/java/java/net/Inet6Address.java
@@ -23,6 +23,7 @@
 import java.io.ObjectStreamField;
 import java.util.Arrays;
 import java.util.Enumeration;
+import libcore.base.EmptyArray;
 
 /**
  * An IPv6 address. See {@link InetAddress}.
@@ -375,7 +376,7 @@
     }
 
     private static final ObjectStreamField[] serialPersistentFields = {
-            new ObjectStreamField("ipaddress", new byte[0].getClass()),
+            new ObjectStreamField("ipaddress", EmptyArray.BYTE.getClass()),
             new ObjectStreamField("scope_id", Integer.TYPE),
             new ObjectStreamField("scope_id_set", Boolean.TYPE),
             new ObjectStreamField("scope_ifname_set", Boolean.TYPE),
diff --git a/luni/src/main/java/java/net/InetSocketAddress.java b/luni/src/main/java/java/net/InetSocketAddress.java
index 8a33db8..f9f973b 100644
--- a/luni/src/main/java/java/net/InetSocketAddress.java
+++ b/luni/src/main/java/java/net/InetSocketAddress.java
@@ -28,10 +28,9 @@
 
     private static final long serialVersionUID = 5076001401234631237L;
 
-    private final String hostname;
-
+    // Exactly one of hostname or addr should be set.
     private final InetAddress addr;
-
+    private final String hostname;
     private final int port;
 
     /**
@@ -59,14 +58,10 @@
      */
     public InetSocketAddress(InetAddress address, int port) {
         if (port < 0 || port > 65535) {
-            throw new IllegalArgumentException();
+            throw new IllegalArgumentException("port=" + port);
         }
-        if (address == null) {
-            addr = Inet4Address.ANY;
-        } else {
-            addr = address;
-        }
-        hostname = addr.getHostName();
+        this.addr = (address == null) ? Inet4Address.ANY : address;
+        this.hostname = null;
         this.port = port;
     }
 
@@ -97,7 +92,6 @@
         if (hostname == null || port < 0 || port > 65535) {
             throw new IllegalArgumentException("host=" + hostname + ", port=" + port);
         }
-        this.port = port;
 
         InetAddress addr = null;
         if (needResolved) {
@@ -111,8 +105,9 @@
             } catch (UnknownHostException ignored) {
             }
         }
-        this.hostname = hostname;
         this.addr = addr;
+        this.hostname = hostname;
+        this.port = port;
     }
 
     /**
@@ -157,7 +152,7 @@
      * @return the socket endpoint hostname.
      */
     public final String getHostName() {
-        return (null != addr) ? addr.getHostName() : hostname;
+        return (addr != null) ? addr.getHostName() : hostname;
     }
 
     /**
@@ -178,13 +173,7 @@
      */
     @Override
     public String toString() {
-        String host;
-        if (addr != null) {
-            host = addr.toString();
-        } else {
-            host = hostname;
-        }
-        return host + ":" + port;
+        return ((addr != null) ? addr.toString() : hostname) + ":" + port;
     }
 
     /**
@@ -235,8 +224,7 @@
         return addr.hashCode() + port;
     }
 
-    private void readObject(ObjectInputStream stream) throws IOException,
-            ClassNotFoundException {
+    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
         stream.defaultReadObject();
     }
 }
diff --git a/luni/src/main/java/java/net/NetworkInterface.java b/luni/src/main/java/java/net/NetworkInterface.java
index 457b0e6..c5675b3 100644
--- a/luni/src/main/java/java/net/NetworkInterface.java
+++ b/luni/src/main/java/java/net/NetworkInterface.java
@@ -24,6 +24,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import libcore.base.EmptyArray;
 
 /**
  * This class is used to represent a network interface of the local device. An
@@ -471,7 +472,7 @@
      */
     public byte[] getHardwareAddress() throws SocketException {
         if (addresses.isEmpty()) {
-            return new byte[0];
+            return EmptyArray.BYTE;
         }
         return getHardwareAddressImpl(name);
     }
diff --git a/luni/src/main/java/java/net/Proxy.java b/luni/src/main/java/java/net/Proxy.java
index 752ea2b..b9d38ae 100644
--- a/luni/src/main/java/java/net/Proxy.java
+++ b/luni/src/main/java/java/net/Proxy.java
@@ -102,11 +102,15 @@
      */
     @Override
     public String toString() {
-        String proxyString = String.valueOf(type);
-        if (null != address) {
-            proxyString += "/" + address.toString();
+        StringBuilder builder = new StringBuilder();
+        if (type != null) {
+            builder.append(type.toString());
         }
-        return proxyString;
+        builder.append("@");
+        if (type != Proxy.Type.DIRECT && address != null) {
+            builder.append(address.toString());
+        }
+        return builder.toString();
     }
 
     /**
diff --git a/luni/src/main/java/java/net/SocketOptions.java b/luni/src/main/java/java/net/SocketOptions.java
index 71441d3..a1b95bf 100644
--- a/luni/src/main/java/java/net/SocketOptions.java
+++ b/luni/src/main/java/java/net/SocketOptions.java
@@ -101,10 +101,13 @@
     public static final int SO_KEEPALIVE = 8;
 
     /**
-     * This option specifies the value for the Type-of-Service (TOS) field of
-     * the IP header. This may be ignored by the underlying OS.
-     * Values must be between 0 and 255 inclusive.
-     * See <a href="http://www.ietf.org/rfc/rfc1349.txt">RFC 1349</a> for more information.
+     * This option specifies the value for the type-of-service field of the IPv4 header, or the
+     * traffic class field of the IPv6 header. These correspond to the IP_TOS and IPV6_TCLASS
+     * socket options. These may be ignored by the underlying OS. Values must be between 0 and 255
+     * inclusive.
+     *
+     * <p>See <a href="http://www.ietf.org/rfc/rfc1349.txt">RFC 1349</a> for more about IPv4
+     * and <a href="http://www.ietf.org/rfc/rfc2460.txt">RFC 2460</a> for more about IPv6.
      */
     public static final int IP_TOS = 3;
 
diff --git a/luni/src/main/java/java/nio/DatagramChannelImpl.java b/luni/src/main/java/java/nio/DatagramChannelImpl.java
index 7750830..6cc1217 100644
--- a/luni/src/main/java/java/nio/DatagramChannelImpl.java
+++ b/luni/src/main/java/java/nio/DatagramChannelImpl.java
@@ -34,6 +34,7 @@
 import java.nio.channels.IllegalBlockingModeException;
 import java.nio.channels.NotYetConnectedException;
 import java.nio.channels.spi.SelectorProvider;
+import libcore.base.EmptyArray;
 import org.apache.harmony.luni.net.PlainDatagramSocketImpl;
 import org.apache.harmony.luni.platform.FileDescriptorHandler;
 import org.apache.harmony.luni.platform.INetworkSystem;
@@ -43,8 +44,6 @@
  * The default implementation class of java.nio.channels.DatagramChannel.
  */
 class DatagramChannelImpl extends DatagramChannel implements FileDescriptorHandler {
-    private static final byte[] stubArray = new byte[0];
-
     // The fd to interact with native code
     private final FileDescriptor fd;
 
@@ -255,7 +254,7 @@
 
     private SocketAddress receiveDirectImpl(ByteBuffer target, boolean loop) throws IOException {
         SocketAddress retAddr = null;
-        DatagramPacket receivePacket = new DatagramPacket(stubArray, 0);
+        DatagramPacket receivePacket = new DatagramPacket(EmptyArray.BYTE, 0);
         int oldposition = target.position();
         int received = 0;
         do {
diff --git a/luni/src/main/java/java/nio/SelectorImpl.java b/luni/src/main/java/java/nio/SelectorImpl.java
index e01181a..bf673b8 100644
--- a/luni/src/main/java/java/nio/SelectorImpl.java
+++ b/luni/src/main/java/java/nio/SelectorImpl.java
@@ -35,6 +35,7 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
+import libcore.base.EmptyArray;
 import org.apache.harmony.luni.platform.FileDescriptorHandler;
 import org.apache.harmony.luni.platform.Platform;
 
@@ -43,9 +44,8 @@
  */
 final class SelectorImpl extends AbstractSelector {
 
-    private static final int[] EMPTY_INT_ARRAY = new int[0];
+    static final FileDescriptor[] EMPTY_FILE_DESCRIPTORS_ARRAY = new FileDescriptor[0];
 
-    private static final FileDescriptor[] EMPTY_FILE_DESCRIPTORS_ARRAY = new FileDescriptor[0];
     private static final SelectionKeyImpl[] EMPTY_SELECTION_KEY_IMPLS_ARRAY
             = new SelectionKeyImpl[0];
 
@@ -121,7 +121,7 @@
      * actively selecting, all elements are 0. Corresponds to the ready keys
      * set.
      */
-    private int[] flags = EMPTY_INT_ARRAY;
+    private int[] flags = EmptyArray.INT;
 
     public SelectorImpl(SelectorProvider selectorProvider) throws IOException {
         super(selectorProvider);
diff --git a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
index abea8cf..240a0ba 100644
--- a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
+++ b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
@@ -82,7 +82,7 @@
                     if (!isBlocking) {
                         int[] tryResult = new int[1];
                         boolean success = Platform.NETWORK.select(new FileDescriptor[] { fd },
-                                new FileDescriptor[0], 1, 0, 0, tryResult);
+                                SelectorImpl.EMPTY_FILE_DESCRIPTORS_ARRAY, 1, 0, 0, tryResult);
                         if (!success || 0 == tryResult[0]) {
                             // no pending connections, returns immediately.
                             return null;
diff --git a/luni/src/main/java/java/security/cert/X509CertSelector.java b/luni/src/main/java/java/security/cert/X509CertSelector.java
index 8745bf2..33e68c7 100644
--- a/luni/src/main/java/java/security/cert/X509CertSelector.java
+++ b/luni/src/main/java/java/security/cert/X509CertSelector.java
@@ -30,6 +30,7 @@
 import java.util.List;
 import java.util.Set;
 import javax.security.auth.x500.X500Principal;
+import libcore.base.EmptyArray;
 import org.apache.harmony.security.asn1.ASN1OctetString;
 import org.apache.harmony.security.x509.AlgorithmIdentifier;
 import org.apache.harmony.security.x509.CertificatePolicies;
@@ -1281,8 +1282,7 @@
                 // initialize the check map
                 for (int i=0; i<9; i++) {
                     map[i] = (subjectAltNames[i] == null)
-                                ? new boolean[0]
-                                : new boolean[subjectAltNames[i].size()];
+                            ? EmptyArray.BOOLEAN : new boolean[subjectAltNames[i].size()];
                 }
                 Iterator it = sans.iterator();
                 while (it.hasNext()) {
diff --git a/luni/src/main/java/java/text/ChoiceFormat.java b/luni/src/main/java/java/text/ChoiceFormat.java
index 4d689df..b594efc 100644
--- a/luni/src/main/java/java/text/ChoiceFormat.java
+++ b/luni/src/main/java/java/text/ChoiceFormat.java
@@ -21,6 +21,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
+import libcore.base.EmptyArray;
 
 /**
  * Returns a fixed string based on a numeric value. The class can be used in
@@ -147,8 +148,8 @@
             index = skipWhitespace(template, position.getIndex());
             if (position.getErrorIndex() != -1 || index >= length) {
                 // Fix Harmony 540
-                choiceLimits = new double[0];
-                choiceFormats = new String[0];
+                choiceLimits = EmptyArray.DOUBLE;
+                choiceFormats = EmptyArray.STRING;
                 return;
             }
             char ch = template.charAt(index++);
diff --git a/luni/src/main/java/java/text/MessageFormat.java b/luni/src/main/java/java/text/MessageFormat.java
index d740437..5abc9c8 100644
--- a/luni/src/main/java/java/text/MessageFormat.java
+++ b/luni/src/main/java/java/text/MessageFormat.java
@@ -27,6 +27,7 @@
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Vector;
+import libcore.base.EmptyArray;
 import libcore.base.Objects;
 
 /**
@@ -866,7 +867,7 @@
      */
     public Object[] parse(String string, ParsePosition position) {
         if (string == null) {
-            return new Object[0];
+            return EmptyArray.OBJECT;
         }
         ParsePosition internalPos = new ParsePosition(0);
         int offset = position.getIndex();
diff --git a/luni/src/main/java/java/util/AbstractQueue.java b/luni/src/main/java/java/util/AbstractQueue.java
index 8ec69f9..d368ac9 100644
--- a/luni/src/main/java/java/util/AbstractQueue.java
+++ b/luni/src/main/java/java/util/AbstractQueue.java
@@ -1,127 +1,163 @@
-/* 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.
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/licenses/publicdomain
  */
 
 package java.util;
 
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
+
 /**
- * AbstractQueue is an abstract class which implements some of the methods in
- * {@link Queue}. The provided implementations of {@code add, remove} and
- * {@code element} are based on {@code offer, poll}, and {@code peek} except
- * that they throw exceptions to indicate some error instead of returning true
- * or false.
+ * This class provides skeletal implementations of some {@link Queue}
+ * operations. The implementations in this class are appropriate when
+ * the base implementation does <em>not</em> allow <tt>null</tt>
+ * elements.  Methods {@link #add add}, {@link #remove remove}, and
+ * {@link #element element} are based on {@link #offer offer}, {@link
+ * #poll poll}, and {@link #peek peek}, respectively, but throw
+ * exceptions instead of indicating failure via <tt>false</tt> or
+ * <tt>null</tt> returns.
  *
- * @param <E>
- *            the type of the element in the collection.
+ * <p>A <tt>Queue</tt> implementation that extends this class must
+ * minimally define a method {@link Queue#offer} which does not permit
+ * insertion of <tt>null</tt> elements, along with methods {@link
+ * Queue#peek}, {@link Queue#poll}, {@link Collection#size}, and
+ * {@link Collection#iterator}.  Typically, additional methods will be
+ * overridden as well.  If these requirements cannot be met, consider
+ * instead subclassing {@link AbstractCollection}.
+ *
+ * @since 1.5
+ * @author Doug Lea
+ * @param <E> the type of elements held in this collection
  */
-public abstract class AbstractQueue<E> extends AbstractCollection<E> implements
-        Queue<E> {
+public abstract class AbstractQueue<E>
+    extends AbstractCollection<E>
+    implements Queue<E> {
 
     /**
-     * Constructor to be used by subclasses.
+     * Constructor for use by subclasses.
      */
     protected AbstractQueue() {
-        super();
     }
 
     /**
-     * Adds an element to the queue.
+     * Inserts the specified element into this queue if it is possible to do so
+     * immediately without violating capacity restrictions, returning
+     * <tt>true</tt> upon success and throwing an <tt>IllegalStateException</tt>
+     * if no space is currently available.
      *
-     * @param o
-     *            the element to be added to the queue.
-     * @return {@code true} if the operation succeeds, otherwise {@code false}.
-     * @throws IllegalStateException
-     *             if the element is not allowed to be added to the queue.
+     * <p>This implementation returns <tt>true</tt> if <tt>offer</tt> succeeds,
+     * else throws an <tt>IllegalStateException</tt>.
+     *
+     * @param e the element to add
+     * @return <tt>true</tt> (as specified by {@link Collection#add})
+     * @throws IllegalStateException if the element cannot be added at this
+     *         time due to capacity restrictions
+     * @throws ClassCastException if the class of the specified element
+     *         prevents it from being added to this queue
+     * @throws NullPointerException if the specified element is null and
+     *         this queue does not permit null elements
+     * @throws IllegalArgumentException if some property of this element
+     *         prevents it from being added to this queue
      */
-    @Override
-    public boolean add(E o) {
-        if (null == o) {
-            throw new NullPointerException();
-        }
-        if (offer(o)) {
+    public boolean add(E e) {
+        if (offer(e))
             return true;
-        }
-        throw new IllegalStateException();
+        else
+            throw new IllegalStateException("Queue full");
     }
 
     /**
-     * Adds all the elements of a collection to the queue. If the collection is
-     * the queue itself, then an IllegalArgumentException will be thrown. If
-     * during the process, some runtime exception is thrown, then those elements
-     * in the collection which have already successfully been added will remain
-     * in the queue. The result of the method is undefined if the collection is
-     * modified during the process of the method.
+     * Retrieves and removes the head of this queue.  This method differs
+     * from {@link #poll poll} only in that it throws an exception if this
+     * queue is empty.
      *
-     * @param c
-     *            the collection to be added to the queue.
-     * @return {@code true} if the operation succeeds, otherwise {@code false}.
-     * @throws NullPointerException
-     *             if the collection or any element of it is null.
-     * @throws IllegalArgumentException
-     *             If the collection to be added to the queue is the queue
-     *             itself.
-     */
-    @Override
-    public boolean addAll(Collection<? extends E> c) {
-        if (null == c) {
-            throw new NullPointerException();
-        }
-        if (this == c) {
-            throw new IllegalArgumentException();
-        }
-        return super.addAll(c);
-    }
-
-    /**
-     * Removes the element at the head of the queue and returns it.
+     * <p>This implementation returns the result of <tt>poll</tt>
+     * unless the queue is empty.
      *
-     * @return the element at the head of the queue.
-     * @throws NoSuchElementException
-     *             if the queue is empty.
+     * @return the head of this queue
+     * @throws NoSuchElementException if this queue is empty
      */
     public E remove() {
-        E o = poll();
-        if (null == o) {
+        E x = poll();
+        if (x != null)
+            return x;
+        else
             throw new NoSuchElementException();
-        }
-        return o;
     }
 
     /**
-     * Returns but does not remove the element at the head of the queue.
+     * Retrieves, but does not remove, the head of this queue.  This method
+     * differs from {@link #peek peek} only in that it throws an exception if
+     * this queue is empty.
      *
-     * @return the element at the head of the queue.
-     * @throws NoSuchElementException
-     *             if the queue is empty.
+     * <p>This implementation returns the result of <tt>peek</tt>
+     * unless the queue is empty.
+     *
+     * @return the head of this queue
+     * @throws NoSuchElementException if this queue is empty
      */
     public E element() {
-        E o = peek();
-        if (null == o) {
+        E x = peek();
+        if (x != null)
+            return x;
+        else
             throw new NoSuchElementException();
-        }
-        return o;
     }
 
     /**
-     * Removes all elements of the queue, leaving it empty.
+     * Removes all of the elements from this queue.
+     * The queue will be empty after this call returns.
+     *
+     * <p>This implementation repeatedly invokes {@link #poll poll} until it
+     * returns <tt>null</tt>.
      */
-    @Override
     public void clear() {
-        E o;
-        do {
-            o = poll();
-        } while (null != o);
+        while (poll() != null)
+            ;
     }
+
+    /**
+     * Adds all of the elements in the specified collection to this
+     * queue.  Attempts to addAll of a queue to itself result in
+     * <tt>IllegalArgumentException</tt>. Further, the behavior of
+     * this operation is undefined if the specified collection is
+     * modified while the operation is in progress.
+     *
+     * <p>This implementation iterates over the specified collection,
+     * and adds each element returned by the iterator to this
+     * queue, in turn.  A runtime exception encountered while
+     * trying to add an element (including, in particular, a
+     * <tt>null</tt> element) may result in only some of the elements
+     * having been successfully added when the associated exception is
+     * thrown.
+     *
+     * @param c collection containing elements to be added to this queue
+     * @return <tt>true</tt> if this queue changed as a result of the call
+     * @throws ClassCastException if the class of an element of the specified
+     *         collection prevents it from being added to this queue
+     * @throws NullPointerException if the specified collection contains a
+     *         null element and this queue does not permit null elements,
+     *         or if the specified collection is null
+     * @throws IllegalArgumentException if some property of an element of the
+     *         specified collection prevents it from being added to this
+     *         queue, or if the specified collection is this queue
+     * @throws IllegalStateException if not all the elements can be added at
+     *         this time due to insertion restrictions
+     * @see #add(Object)
+     */
+    public boolean addAll(Collection<? extends E> c) {
+        if (c == null)
+            throw new NullPointerException();
+        if (c == this)
+            throw new IllegalArgumentException();
+        boolean modified = false;
+        for (E e : c)
+            if (add(e))
+                modified = true;
+        return modified;
+    }
+
 }
diff --git a/luni/src/main/java/java/util/ArrayDeque.java b/luni/src/main/java/java/util/ArrayDeque.java
index feaac4d..fafcdb4 100644
--- a/luni/src/main/java/java/util/ArrayDeque.java
+++ b/luni/src/main/java/java/util/ArrayDeque.java
@@ -1,883 +1,838 @@
 /*
- *  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.
+ * Written by Josh Bloch of Google Inc. and released to the public domain,
+ * as explained at http://creativecommons.org/licenses/publicdomain.
  */
 
 package java.util;
 
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.io.ObjectOutputStream;
-import java.io.Serializable;
-import java.lang.reflect.Array;
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
+
+import java.io.*;
 
 /**
- * An implementation of Deque, backed by an array.
+ * Resizable-array implementation of the {@link Deque} interface.  Array
+ * deques have no capacity restrictions; they grow as necessary to support
+ * usage.  They are not thread-safe; in the absence of external
+ * synchronization, they do not support concurrent access by multiple threads.
+ * Null elements are prohibited.  This class is likely to be faster than
+ * {@link Stack} when used as a stack, and faster than {@link LinkedList}
+ * when used as a queue.
  *
- * ArrayDeques have no size limit, can not contain null element, and they are
- * not thread-safe.
+ * <p>Most <tt>ArrayDeque</tt> operations run in amortized constant time.
+ * Exceptions include {@link #remove(Object) remove}, {@link
+ * #removeFirstOccurrence removeFirstOccurrence}, {@link #removeLastOccurrence
+ * removeLastOccurrence}, {@link #contains contains}, {@link #iterator
+ * iterator.remove()}, and the bulk operations, all of which run in linear
+ * time.
  *
- * All optional operations are supported, and the elements can be any objects.
+ * <p>The iterators returned by this class's <tt>iterator</tt> method are
+ * <i>fail-fast</i>: If the deque is modified at any time after the iterator
+ * is created, in any way except through the iterator's own <tt>remove</tt>
+ * method, the iterator will generally throw a {@link
+ * ConcurrentModificationException}.  Thus, in the face of concurrent
+ * modification, the iterator fails quickly and cleanly, rather than risking
+ * arbitrary, non-deterministic behavior at an undetermined time in the
+ * future.
  *
- * @param <E>
- *            the type of elements in this collection
+ * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed
+ * as it is, generally speaking, impossible to make any hard guarantees in the
+ * presence of unsynchronized concurrent modification.  Fail-fast iterators
+ * throw <tt>ConcurrentModificationException</tt> on a best-effort basis.
+ * Therefore, it would be wrong to write a program that depended on this
+ * exception for its correctness: <i>the fail-fast behavior of iterators
+ * should be used only to detect bugs.</i>
  *
- * @since 1.6
+ * <p>This class and its iterator implement all of the
+ * <em>optional</em> methods of the {@link Collection} and {@link
+ * Iterator} interfaces.
+ *
+ * @author  Josh Bloch and Doug Lea
+ * @since   1.6
+ * @param <E> the type of elements held in this collection
  */
-public class ArrayDeque<E> extends AbstractCollection<E> implements Deque<E>,
-        Cloneable, Serializable {
-
-    private static final long serialVersionUID = 2340985798034038923L;
-
-    private static final int DEFAULT_SIZE = 16;
-
-    private enum DequeStatus {
-        Empty, Normal, Full;
-    }
-
-    private transient DequeStatus status;
-
-    private transient int modCount;
-
-    // the pointer of the head element
-    private transient int front;
-
-    // the pointer of the "next" position of the tail element
-    private transient int rear;
-
+public class ArrayDeque<E> extends AbstractCollection<E>
+                           implements Deque<E>, Cloneable, Serializable
+{
+    /**
+     * The array in which the elements of the deque are stored.
+     * The capacity of the deque is the length of this array, which is
+     * always a power of two. The array is never allowed to become
+     * full, except transiently within an addX method where it is
+     * resized (see doubleCapacity) immediately upon becoming full,
+     * thus avoiding head and tail wrapping around to equal each
+     * other.  We also guarantee that all array cells not holding
+     * deque elements are always null.
+     */
     private transient E[] elements;
 
-    @SuppressWarnings("hiding")
-    private class ArrayDequeIterator<E> implements Iterator<E> {
-        private int pos;
-
-        private final int expectedModCount;
-
-        private boolean canRemove;
-
-        @SuppressWarnings("synthetic-access")
-        ArrayDequeIterator() {
-            super();
-            pos = front;
-            expectedModCount = modCount;
-            canRemove = false;
-        }
-
-        @SuppressWarnings("synthetic-access")
-        public boolean hasNext() {
-            if (expectedModCount != modCount) {
-                return false;
-            }
-            return hasNextInternal();
-        }
-
-        private boolean hasNextInternal() {
-            // canRemove means "next" method is called, and the Full
-            // status can ensure that this method is not called just
-            // after "remove" method is call.(so, canRemove can keep
-            // true after "next" method called)
-            return (pos != rear)
-                    || ((status == DequeStatus.Full) && !canRemove);
-        }
-
-        @SuppressWarnings( { "synthetic-access", "unchecked" })
-        public E next() {
-            if (hasNextInternal()) {
-                E result = (E) elements[pos];
-                if (expectedModCount == modCount && null != result) {
-                    canRemove = true;
-                    pos = circularBiggerPos(pos);
-                    return result;
-                }
-                throw new ConcurrentModificationException();
-            }
-            throw new NoSuchElementException();
-        }
-
-        @SuppressWarnings("synthetic-access")
-        public void remove() {
-            if (canRemove) {
-                int removedPos = circularSmallerPos(pos);
-                if (expectedModCount == modCount
-                        && null != elements[removedPos]) {
-                    removeInternal(removedPos, true);
-                    canRemove = false;
-                    return;
-                }
-                throw new ConcurrentModificationException();
-            }
-            throw new IllegalStateException();
-        }
-    }
-
-    /*
-     * NOTES:descendingIterator is not fail-fast, according to the documentation
-     * and test case.
+    /**
+     * The index of the element at the head of the deque (which is the
+     * element that would be removed by remove() or pop()); or an
+     * arbitrary number equal to tail if the deque is empty.
      */
-    @SuppressWarnings("hiding")
-    private class ReverseArrayDequeIterator<E> implements Iterator<E> {
-        private int pos;
+    private transient int head;
 
-        private final int expectedModCount;
+    /**
+     * The index at which the next element would be added to the tail
+     * of the deque (via addLast(E), add(E), or push(E)).
+     */
+    private transient int tail;
 
-        private boolean canRemove;
+    /**
+     * The minimum capacity that we'll use for a newly created deque.
+     * Must be a power of 2.
+     */
+    private static final int MIN_INITIAL_CAPACITY = 8;
 
-        @SuppressWarnings("synthetic-access")
-        ReverseArrayDequeIterator() {
-            super();
-            expectedModCount = modCount;
-            pos = circularSmallerPos(rear);
-            canRemove = false;
+    // ******  Array allocation and resizing utilities ******
+
+    /**
+     * Allocate empty array to hold the given number of elements.
+     *
+     * @param numElements  the number of elements to hold
+     */
+    private void allocateElements(int numElements) {
+        int initialCapacity = MIN_INITIAL_CAPACITY;
+        // Find the best power of two to hold elements.
+        // Tests "<=" because arrays aren't kept full.
+        if (numElements >= initialCapacity) {
+            initialCapacity = numElements;
+            initialCapacity |= (initialCapacity >>>  1);
+            initialCapacity |= (initialCapacity >>>  2);
+            initialCapacity |= (initialCapacity >>>  4);
+            initialCapacity |= (initialCapacity >>>  8);
+            initialCapacity |= (initialCapacity >>> 16);
+            initialCapacity++;
+
+            if (initialCapacity < 0)   // Too many elements, must back off
+                initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
         }
-
-        @SuppressWarnings("synthetic-access")
-        public boolean hasNext() {
-            if (expectedModCount != modCount) {
-                return false;
-            }
-            return hasNextInternal();
-        }
-
-        private boolean hasNextInternal() {
-            // canRemove means "next" method is called, and the Full
-            // status can ensure that this method is not called just
-            // after "remove" method is call.(so, canRemove can keep
-            // true after "next" method called)
-            return (circularBiggerPos(pos) != front)
-                    || ((status == DequeStatus.Full) && !canRemove);
-        }
-
-        @SuppressWarnings( { "synthetic-access", "unchecked" })
-        public E next() {
-            if (hasNextInternal()) {
-                E result = (E) elements[pos];
-                canRemove = true;
-                pos = circularSmallerPos(pos);
-                return result;
-            }
-            throw new NoSuchElementException();
-        }
-
-        @SuppressWarnings("synthetic-access")
-        public void remove() {
-            if (canRemove) {
-                removeInternal(circularBiggerPos(pos), false);
-                canRemove = false;
-                return;
-            }
-            throw new IllegalStateException();
-        }
+        elements = (E[]) new Object[initialCapacity];
     }
 
     /**
-     * Constructs a new empty instance of ArrayDeque big enough for 16 elements.
+     * Double the capacity of this deque.  Call only when full, i.e.,
+     * when head and tail have wrapped around to become equal.
+     */
+    private void doubleCapacity() {
+        assert head == tail;
+        int p = head;
+        int n = elements.length;
+        int r = n - p; // number of elements to the right of p
+        int newCapacity = n << 1;
+        if (newCapacity < 0)
+            throw new IllegalStateException("Sorry, deque too big");
+        Object[] a = new Object[newCapacity];
+        System.arraycopy(elements, p, a, 0, r);
+        System.arraycopy(elements, 0, a, r, p);
+        elements = (E[])a;
+        head = 0;
+        tail = n;
+    }
+
+    /**
+     * Copies the elements from our element array into the specified array,
+     * in order (from first to last element in the deque).  It is assumed
+     * that the array is large enough to hold all elements in the deque.
+     *
+     * @return its argument
+     */
+    private <T> T[] copyElements(T[] a) {
+        if (head < tail) {
+            System.arraycopy(elements, head, a, 0, size());
+        } else if (head > tail) {
+            int headPortionLen = elements.length - head;
+            System.arraycopy(elements, head, a, 0, headPortionLen);
+            System.arraycopy(elements, 0, a, headPortionLen, tail);
+        }
+        return a;
+    }
+
+    /**
+     * Constructs an empty array deque with an initial capacity
+     * sufficient to hold 16 elements.
      */
     public ArrayDeque() {
-        this(DEFAULT_SIZE);
+        elements = (E[]) new Object[16];
     }
 
     /**
-     * Constructs a new empty instance of ArrayDeque big enough for specified
-     * number of elements.
+     * Constructs an empty array deque with an initial capacity
+     * sufficient to hold the specified number of elements.
      *
-     * @param minSize
-     *            the smallest size of the ArrayDeque
+     * @param numElements  lower bound on initial capacity of the deque
      */
-    @SuppressWarnings("unchecked")
-    public ArrayDeque(final int minSize) {
-        int size = countInitSize(minSize);
-        elements = (E[]) new Object[size];
-        front = rear = 0;
-        status = DequeStatus.Empty;
-        modCount = 0;
-    }
-
-    /*
-     * count out the size for a new deque, and ensure that size >= minSize
-     */
-    private int countInitSize(final int minSize) {
-        int size = Math.max(minSize, DEFAULT_SIZE);
-        // get the smallest number that not smaller than size,
-        // and is a power of 2.
-        size = Integer.highestOneBit(size - 1) << 1;
-        if (0 >= size) {
-            size = minSize;
-        }
-        return size;
+    public ArrayDeque(int numElements) {
+        allocateElements(numElements);
     }
 
     /**
-     * Constructs a new instance of ArrayDeque containing the elements of the
-     * specified collection, with the order returned by the collection's
-     * iterator.
+     * Constructs a deque containing the elements of the specified
+     * collection, in the order they are returned by the collection's
+     * iterator.  (The first element returned by the collection's
+     * iterator becomes the first element, or <i>front</i> of the
+     * deque.)
      *
-     * @param c
-     *            the source of the elements
-     * @throws NullPointerException
-     *             if the collection is null
+     * @param c the collection whose elements are to be placed into the deque
+     * @throws NullPointerException if the specified collection is null
      */
-    @SuppressWarnings("unchecked")
     public ArrayDeque(Collection<? extends E> c) {
-        elements = (E[]) new Object[countInitSize(c.size())];
-        front = rear = 0;
-        status = DequeStatus.Empty;
-        modCount = 0;
-        Iterator<? extends E> it = c.iterator();
-        while (it.hasNext()) {
-            addLastImpl(it.next());
-        }
+        allocateElements(c.size());
+        addAll(c);
     }
 
+    // The main insertion and extraction methods are addFirst,
+    // addLast, pollFirst, pollLast. The other methods are defined in
+    // terms of these.
+
     /**
-     * {@inheritDoc}
+     * Inserts the specified element at the front of this deque.
      *
-     * @param e
-     *            the element
-     * @throws NullPointerException
-     *             if the element is null
-     * @see java.util.Deque#addFirst(java.lang.Object)
+     * @param e the element to add
+     * @throws NullPointerException if the specified element is null
      */
     public void addFirst(E e) {
-        offerFirst(e);
+        if (e == null)
+            throw new NullPointerException();
+        elements[head = (head - 1) & (elements.length - 1)] = e;
+        if (head == tail)
+            doubleCapacity();
     }
 
     /**
-     * {@inheritDoc}
+     * Inserts the specified element at the end of this deque.
      *
-     * @param e
-     *            the element
-     * @throws NullPointerException
-     *             if the element is null
-     * @see java.util.Deque#addLast(java.lang.Object)
+     * <p>This method is equivalent to {@link #add}.
+     *
+     * @param e the element to add
+     * @throws NullPointerException if the specified element is null
      */
     public void addLast(E e) {
-        addLastImpl(e);
+        if (e == null)
+            throw new NullPointerException();
+        elements[tail] = e;
+        if ( (tail = (tail + 1) & (elements.length - 1)) == head)
+            doubleCapacity();
     }
 
     /**
-     * {@inheritDoc}
+     * Inserts the specified element at the front of this deque.
      *
-     * @param e
-     *            the element
-     * @return true
-     * @throws NullPointerException
-     *             if the element is null
-     * @see java.util.Deque#offerFirst(java.lang.Object)
+     * @param e the element to add
+     * @return <tt>true</tt> (as specified by {@link Deque#offerFirst})
+     * @throws NullPointerException if the specified element is null
      */
-    public  boolean offerFirst(E e) {
-        checkNull(e);
-        checkAndExpand();
-        front = circularSmallerPos(front);
-        elements[front] = e;
-        resetStatus(true);
-        modCount++;
+    public boolean offerFirst(E e) {
+        addFirst(e);
         return true;
     }
 
     /**
-     * {@inheritDoc}
+     * Inserts the specified element at the end of this deque.
      *
-     * @param e
-     *            the element
-     * @return true if the operation succeeds or false if it fails
-     * @throws NullPointerException
-     *             if the element is null
-     * @see java.util.Deque#offerLast(java.lang.Object)
+     * @param e the element to add
+     * @return <tt>true</tt> (as specified by {@link Deque#offerLast})
+     * @throws NullPointerException if the specified element is null
      */
     public boolean offerLast(E e) {
-        return addLastImpl(e);
+        addLast(e);
+        return true;
     }
 
     /**
-     * Inserts the element at the tail of the deque.
+     * @throws NoSuchElementException {@inheritDoc}
+     */
+    public E removeFirst() {
+        E x = pollFirst();
+        if (x == null)
+            throw new NoSuchElementException();
+        return x;
+    }
+
+    /**
+     * @throws NoSuchElementException {@inheritDoc}
+     */
+    public E removeLast() {
+        E x = pollLast();
+        if (x == null)
+            throw new NoSuchElementException();
+        return x;
+    }
+
+    public E pollFirst() {
+        int h = head;
+        E result = elements[h]; // Element is null if deque empty
+        if (result == null)
+            return null;
+        elements[h] = null;     // Must null out slot
+        head = (h + 1) & (elements.length - 1);
+        return result;
+    }
+
+    public E pollLast() {
+        int t = (tail - 1) & (elements.length - 1);
+        E result = elements[t];
+        if (result == null)
+            return null;
+        elements[t] = null;
+        tail = t;
+        return result;
+    }
+
+    /**
+     * @throws NoSuchElementException {@inheritDoc}
+     */
+    public E getFirst() {
+        E x = elements[head];
+        if (x == null)
+            throw new NoSuchElementException();
+        return x;
+    }
+
+    /**
+     * @throws NoSuchElementException {@inheritDoc}
+     */
+    public E getLast() {
+        E x = elements[(tail - 1) & (elements.length - 1)];
+        if (x == null)
+            throw new NoSuchElementException();
+        return x;
+    }
+
+    public E peekFirst() {
+        return elements[head]; // elements[head] is null if deque empty
+    }
+
+    public E peekLast() {
+        return elements[(tail - 1) & (elements.length - 1)];
+    }
+
+    /**
+     * Removes the first occurrence of the specified element in this
+     * deque (when traversing the deque from head to tail).
+     * If the deque does not contain the element, it is unchanged.
+     * More formally, removes the first element <tt>e</tt> such that
+     * <tt>o.equals(e)</tt> (if such an element exists).
+     * Returns <tt>true</tt> if this deque contained the specified element
+     * (or equivalently, if this deque changed as a result of the call).
      *
-     * @param e
-     *            the element
-     * @return true if the operation succeeds or false if it fails.
-     * @throws NullPointerException
-     *             if the element is null
-     * @see java.util.Queue#offer(java.lang.Object)
+     * @param o element to be removed from this deque, if present
+     * @return <tt>true</tt> if the deque contained the specified element
+     */
+    public boolean removeFirstOccurrence(Object o) {
+        if (o == null)
+            return false;
+        int mask = elements.length - 1;
+        int i = head;
+        E x;
+        while ( (x = elements[i]) != null) {
+            if (o.equals(x)) {
+                delete(i);
+                return true;
+            }
+            i = (i + 1) & mask;
+        }
+        return false;
+    }
+
+    /**
+     * Removes the last occurrence of the specified element in this
+     * deque (when traversing the deque from head to tail).
+     * If the deque does not contain the element, it is unchanged.
+     * More formally, removes the last element <tt>e</tt> such that
+     * <tt>o.equals(e)</tt> (if such an element exists).
+     * Returns <tt>true</tt> if this deque contained the specified element
+     * (or equivalently, if this deque changed as a result of the call).
+     *
+     * @param o element to be removed from this deque, if present
+     * @return <tt>true</tt> if the deque contained the specified element
+     */
+    public boolean removeLastOccurrence(Object o) {
+        if (o == null)
+            return false;
+        int mask = elements.length - 1;
+        int i = (tail - 1) & mask;
+        E x;
+        while ( (x = elements[i]) != null) {
+            if (o.equals(x)) {
+                delete(i);
+                return true;
+            }
+            i = (i - 1) & mask;
+        }
+        return false;
+    }
+
+    // *** Queue methods ***
+
+    /**
+     * Inserts the specified element at the end of this deque.
+     *
+     * <p>This method is equivalent to {@link #addLast}.
+     *
+     * @param e the element to add
+     * @return <tt>true</tt> (as specified by {@link Collection#add})
+     * @throws NullPointerException if the specified element is null
+     */
+    public boolean add(E e) {
+        addLast(e);
+        return true;
+    }
+
+    /**
+     * Inserts the specified element at the end of this deque.
+     *
+     * <p>This method is equivalent to {@link #offerLast}.
+     *
+     * @param e the element to add
+     * @return <tt>true</tt> (as specified by {@link Queue#offer})
+     * @throws NullPointerException if the specified element is null
      */
     public boolean offer(E e) {
-        return addLastImpl(e);
+        return offerLast(e);
     }
 
     /**
-     * Inserts the element to the tail of the deque.
+     * Retrieves and removes the head of the queue represented by this deque.
      *
-     * @param e
-     *            the element
-     * @return true
-     * @see java.util.AbstractCollection#add(java.lang.Object)
-     */
-    @Override
-    public boolean add(E e) {
-        return addLastImpl(e);
-    }
-
-    /**
-     * {@inheritDoc}
+     * This method differs from {@link #poll poll} only in that it throws an
+     * exception if this deque is empty.
      *
-     * @param e
-     *            the element to push
-     * @throws NullPointerException
-     *             if the element is null
-     * @see java.util.Deque#push(java.lang.Object)
-     */
-    public void push(E e) {
-        offerFirst(e);
-    }
-
-    /**
-     * {@inheritDoc}
+     * <p>This method is equivalent to {@link #removeFirst}.
      *
-     * @return the head element
-     * @throws NoSuchElementException
-     *             if the deque is empty
-     * @see java.util.Deque#removeFirst()
-     */
-    public  E removeFirst() {
-        checkEmpty();
-        return removePollFirstImpl();
-    }
-
-    /**
-     * Gets and removes the head element of this deque. This method throws an
-     * exception if the deque is empty.
-     *
-     * @return the head element
-     * @throws NoSuchElementException
-     *             if the deque is empty
-     * @see java.util.Queue#remove()
+     * @return the head of the queue represented by this deque
+     * @throws NoSuchElementException {@inheritDoc}
      */
     public E remove() {
         return removeFirst();
     }
 
     /**
-     * {@inheritDoc}
+     * Retrieves and removes the head of the queue represented by this deque
+     * (in other words, the first element of this deque), or returns
+     * <tt>null</tt> if this deque is empty.
      *
-     * @return the head element
-     * @throws NoSuchElementException
-     *             if the deque is empty
-     * @see java.util.Deque#pop()
-     */
-    public E pop() {
-        return removeFirst();
-    }
-
-    /**
-     * {@inheritDoc}
+     * <p>This method is equivalent to {@link #pollFirst}.
      *
-     * @return the tail element
-     * @throws NoSuchElementException
-     *             if the deque is empty
-     * @see java.util.Deque#removeLast()
-     */
-    public  E removeLast() {
-        checkEmpty();
-        return removeLastImpl();
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @return the head element or null if the deque is empty
-     * @see java.util.Deque#pollFirst()
-     */
-    public  E pollFirst() {
-        return (status == DequeStatus.Empty) ? null : removePollFirstImpl();
-    }
-
-    /**
-     * Gets and removes the head element of this deque. This method returns null
-     * if the deque is empty.
-     *
-     * @return the head element or null if the deque is empty
-     * @see java.util.Queue#poll()
+     * @return the head of the queue represented by this deque, or
+     *         <tt>null</tt> if this deque is empty
      */
     public E poll() {
         return pollFirst();
     }
 
     /**
-     * {@inheritDoc}
+     * Retrieves, but does not remove, the head of the queue represented by
+     * this deque.  This method differs from {@link #peek peek} only in
+     * that it throws an exception if this deque is empty.
      *
-     * @return the tail element or null if the deque is empty
-     * @see java.util.Deque#pollLast()
-     */
-    public  E pollLast() {
-        return (status == DequeStatus.Empty) ? null : removeLastImpl();
-    }
-
-    /**
-     * {@inheritDoc}
+     * <p>This method is equivalent to {@link #getFirst}.
      *
-     * @return the head element
-     * @throws NoSuchElementException
-     *             if the deque is empty
-     * @see java.util.Deque#getFirst()
-     */
-    public  E getFirst() {
-        checkEmpty();
-        return elements[front];
-    }
-
-    /**
-     * Gets but does not remove the head element of this deque. It throws an
-     * exception if the deque is empty.
-     *
-     * @return the head element
-     * @throws NoSuchElementException
-     *             if the deque is empty
-     * @see java.util.Queue#element()
+     * @return the head of the queue represented by this deque
+     * @throws NoSuchElementException {@inheritDoc}
      */
     public E element() {
         return getFirst();
     }
 
     /**
-     * {@inheritDoc}
+     * Retrieves, but does not remove, the head of the queue represented by
+     * this deque, or returns <tt>null</tt> if this deque is empty.
      *
-     * @return the tail element
-     * @throws NoSuchElementException
-     *             if the deque is empty
-     * @see java.util.Deque#getLast()
+     * <p>This method is equivalent to {@link #peekFirst}.
+     *
+     * @return the head of the queue represented by this deque, or
+     *         <tt>null</tt> if this deque is empty
      */
-    public  E getLast() {
-        checkEmpty();
-        return elements[circularSmallerPos(rear)];
+    public E peek() {
+        return peekFirst();
+    }
+
+    // *** Stack methods ***
+
+    /**
+     * Pushes an element onto the stack represented by this deque.  In other
+     * words, inserts the element at the front of this deque.
+     *
+     * <p>This method is equivalent to {@link #addFirst}.
+     *
+     * @param e the element to push
+     * @throws NullPointerException if the specified element is null
+     */
+    public void push(E e) {
+        addFirst(e);
     }
 
     /**
-     * {@inheritDoc}
+     * Pops an element from the stack represented by this deque.  In other
+     * words, removes and returns the first element of this deque.
      *
-     * @return the head element or null if the deque is empty
-     * @see java.util.Deque#peekFirst()
+     * <p>This method is equivalent to {@link #removeFirst()}.
+     *
+     * @return the element at the front of this deque (which is the top
+     *         of the stack represented by this deque)
+     * @throws NoSuchElementException {@inheritDoc}
      */
-    public  E peekFirst() {
-        return (status == DequeStatus.Empty) ? null : elements[front];
+    public E pop() {
+        return removeFirst();
+    }
+
+    private void checkInvariants() {
+        assert elements[tail] == null;
+        assert head == tail ? elements[head] == null :
+            (elements[head] != null &&
+             elements[(tail - 1) & (elements.length - 1)] != null);
+        assert elements[(head - 1) & (elements.length - 1)] == null;
     }
 
     /**
-     * Gets but not removes the head element of this deque. This method returns
-     * null if the deque is empty.
+     * Removes the element at the specified position in the elements array,
+     * adjusting head and tail as necessary.  This can result in motion of
+     * elements backwards or forwards in the array.
      *
-     * @return the head element or null if the deque is empty
-     * @see java.util.Queue#peek()
-     */
-    public  E peek() {
-        return (status == DequeStatus.Empty) ? null : elements[front];
-    }
-
-    /**
-     * {@inheritDoc}
+     * <p>This method is called delete rather than remove to emphasize
+     * that its semantics differ from those of {@link List#remove(int)}.
      *
-     * @return the tail element or null if the deque is empty
-     * @see java.util.Deque#peekLast()
+     * @return true if elements moved backwards
      */
-    public  E peekLast() {
-        return (status == DequeStatus.Empty) ? null
-                : elements[circularSmallerPos(rear)];
-    }
+    private boolean delete(int i) {
+        checkInvariants();
+        final E[] elements = this.elements;
+        final int mask = elements.length - 1;
+        final int h = head;
+        final int t = tail;
+        final int front = (i - h) & mask;
+        final int back  = (t - i) & mask;
 
-    private void checkNull(E e) {
-        if (null == e) {
-            throw new NullPointerException();
-        }
-    }
+        // Invariant: head <= i < tail mod circularity
+        if (front >= ((t - h) & mask))
+            throw new ConcurrentModificationException();
 
-    private void checkEmpty() {
-        if (status == DequeStatus.Empty) {
-            throw new NoSuchElementException();
-        }
-    }
-
-    private int circularSmallerPos(int current) {
-        return (current - 1 < 0) ? (elements.length - 1) : current - 1;
-    }
-
-    private int circularBiggerPos(int current) {
-        return (current + 1 >= elements.length) ? 0 : current + 1;
-    }
-
-    @SuppressWarnings("unchecked")
-    /*
-     * If array of elements is full, there will be a new bigger array to store
-     * the elements.
-     */
-    private void checkAndExpand() {
-        if (status != DequeStatus.Full) {
-            return;
-        }
-        if (Integer.MAX_VALUE == elements.length) {
-            throw new IllegalStateException();
-        }
-        int length = elements.length;
-        int newLength = length * 2;
-        // bigger than Integer.MAX_VALUE
-        if (newLength < 0) {
-            newLength = Integer.MAX_VALUE;
-        }
-        E[] newElements = (E[]) new Object[newLength];
-        System.arraycopy(elements, front, newElements, 0, length - front);
-        System.arraycopy(elements, 0, newElements, length - front, front);
-        front = 0;
-        rear = length;
-        status = DequeStatus.Normal;
-        elements = newElements;
-    }
-
-    /**
-     * Resets the status after adding or removing operation.
-     *
-     * @param adding
-     *            if the method is called after an "adding" operation
-     */
-    private void resetStatus(boolean adding) {
-        if (front == rear) {
-            status = adding ? DequeStatus.Full : DequeStatus.Empty;
+        // Optimize for least element motion
+        if (front < back) {
+            if (h <= i) {
+                System.arraycopy(elements, h, elements, h + 1, front);
+            } else { // Wrap around
+                System.arraycopy(elements, 0, elements, 1, i);
+                elements[0] = elements[mask];
+                System.arraycopy(elements, h, elements, h + 1, mask - h);
+            }
+            elements[h] = null;
+            head = (h + 1) & mask;
+            return false;
         } else {
-            status = DequeStatus.Normal;
-        }
-    }
-
-    private  boolean addLastImpl(E e) {
-        checkNull(e);
-        checkAndExpand();
-        elements[rear] = e;
-        rear = circularBiggerPos(rear);
-        resetStatus(true);
-        modCount++;
-        return true;
-    }
-
-    private E removePollFirstImpl() {
-        E element = elements[front];
-        elements[front] = null;
-        front = circularBiggerPos(front);
-        resetStatus(false);
-        modCount++;
-        return element;
-    }
-
-    private E removeLastImpl() {
-        int last = circularSmallerPos(rear);
-        E element = elements[last];
-        elements[last] = null;
-        rear = last;
-        resetStatus(false);
-        modCount++;
-        return element;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @param obj
-     *            the element to be removed
-     * @return true if the operation succeeds or false if the deque does not
-     *         contain the element
-     * @see java.util.Deque#removeFirstOccurrence(java.lang.Object)
-     */
-    public boolean removeFirstOccurrence(Object obj) {
-        return removeFirstOccurrenceImpl(obj);
-    }
-
-    /**
-     * Removes the first equivalent element of the specified object. If the
-     * deque does not contain the element, it is unchanged and returns false.
-     *
-     * @param obj
-     *            the element to be removed
-     * @return true if the operation succeeds or false if the deque does not
-     *         contain the element
-     * @see java.util.AbstractCollection#remove(java.lang.Object)
-     */
-    @Override
-    public boolean remove(Object obj) {
-        return removeFirstOccurrenceImpl(obj);
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @param obj
-     *            the element to be removed
-     * @return true if the operation succeeds or false if the deque does not
-     *         contain the element.
-     * @see java.util.Deque#removeLastOccurrence(java.lang.Object)
-     */
-    public  boolean removeLastOccurrence(final Object obj) {
-        if (null != obj) {
-            Iterator<E> iter = descendingIterator();
-            while (iter.hasNext()) {
-                if (iter.next().equals(obj)) {
-                    iter.remove();
-                    return true;
-                }
+            if (i < t) { // Copy the null tail as well
+                System.arraycopy(elements, i + 1, elements, i, back);
+                tail = t - 1;
+            } else { // Wrap around
+                System.arraycopy(elements, i + 1, elements, i, mask - i);
+                elements[mask] = elements[0];
+                System.arraycopy(elements, 1, elements, 0, t);
+                tail = (t - 1) & mask;
             }
+            return true;
         }
-        return false;
     }
 
-    private  boolean removeFirstOccurrenceImpl(final Object obj) {
-        if (null != obj) {
-            Iterator<E> iter = iterator();
-            while (iter.hasNext()) {
-                if (iter.next().equals(obj)) {
-                    iter.remove();
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
+    // *** Collection Methods ***
 
-    /*
-     * Removes the element in the cursor position and shifts front elements to
-     * fill the gap if frontShift is true, shifts rear elements otherwise.
+    /**
+     * Returns the number of elements in this deque.
      *
+     * @return the number of elements in this deque
      */
-    private void removeInternal(final int current, final boolean frontShift) {
-        int cursor = current;
-        if (frontShift) {
-            while (cursor != front) {
-                int next = circularSmallerPos(cursor);
-                elements[cursor] = elements[next];
-                cursor = next;
-            }
-            front = circularBiggerPos(front);
-        } else {
-            while (cursor != rear) {
-                int next = circularBiggerPos(cursor);
-                elements[cursor] = elements[next];
-                cursor = next;
-            }
-            rear = circularSmallerPos(rear);
-        }
-        elements[cursor] = null;
-        resetStatus(false);
+    public int size() {
+        return (tail - head) & (elements.length - 1);
     }
 
     /**
-     * Returns the size of the deque.
+     * Returns <tt>true</tt> if this deque contains no elements.
      *
-     * @return the size of the deque
-     * @see java.util.AbstractCollection#size()
+     * @return <tt>true</tt> if this deque contains no elements
      */
-    @Override
-    public  int size() {
-        if (status == DequeStatus.Full) {
-            return elements.length;
-        }
-        return (front <= rear) ? (rear - front)
-                : (rear + elements.length - front);
+    public boolean isEmpty() {
+        return head == tail;
     }
 
     /**
-     * Returns true if the deque has no elements.
+     * Returns an iterator over the elements in this deque.  The elements
+     * will be ordered from first (head) to last (tail).  This is the same
+     * order that elements would be dequeued (via successive calls to
+     * {@link #remove} or popped (via successive calls to {@link #pop}).
      *
-     * @return true if the deque has no elements, false otherwise
-     * @see java.util.AbstractCollection#isEmpty()
+     * @return an iterator over the elements in this deque
      */
-    @Override
-    public  boolean isEmpty() {
-        return 0 == size();
-    }
-
-    /**
-     * Returns true if the specified element is in the deque.
-     *
-     * @param obj
-     *            the element
-     * @return true if the element is in the deque, false otherwise
-     * @see java.util.AbstractCollection#contains(java.lang.Object)
-     */
-    @SuppressWarnings("cast")
-    @Override
-    public  boolean contains(final Object obj) {
-        if (null != obj) {
-            Iterator<E> it = new ArrayDequeIterator<E>();
-            while (it.hasNext()) {
-                if (obj.equals((E) it.next())) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Empty the deque.
-     *
-     * @see java.util.AbstractCollection#clear()
-     */
-    @SuppressWarnings("cast")
-    @Override
-    public  void clear() {
-        if (status != DequeStatus.Empty) {
-            int cursor = front;
-            do {
-                elements[cursor] = null;
-                cursor = circularBiggerPos(cursor);
-            } while (cursor != rear);
-            status = DequeStatus.Empty;
-        }
-        front = rear = 0;
-        modCount = 0;
-    }
-
-    /**
-     * Returns a clone of the deque.
-     *
-     * @return the clone of the deque
-     * @see java.lang.Object#clone()
-     * @see java.lang.Cloneable
-     */
-    @SuppressWarnings("unchecked")
-    @Override
-    public  ArrayDeque<E> clone() {
-        try {
-            ArrayDeque<E> newDeque = (ArrayDeque<E>) super.clone();
-            newDeque.elements = elements.clone();
-            return newDeque;
-        } catch (CloneNotSupportedException e) {
-            return null;
-        }
-    }
-
-    /**
-     * Returns all the elements in an array from head to tail. The result is a
-     * copy of all the elements.
-     *
-     * @return the Array of all the elements
-     * @see java.util.AbstractCollection#toArray()
-     */
-    @Override
-    public Object[] toArray() {
-        return newArray(new Object[size()]);
-    }
-
-    /**
-     * Returns all the elements in an array from head to tail, and the type of
-     * the result array is the type of the argument array. If the argument array
-     * is big enough, the elements from the deque will be stored in it(elements
-     * following the tail of the deque is set to null, if any); otherwise, it
-     * will return a new array with the size of the argument array and size of
-     * the deque.
-     *
-     * @param array
-     *            the array stores all the elements from the deque, if it has
-     *            enough space; otherwise, a new array of the same type and the
-     *            size of the deque will be used
-     * @return the Array of all the elements
-     * @throws ArrayStoreException
-     *             if the type of the argument array is not compatible with
-     *             every element in the deque
-     * @throws NullPointerException
-     *             if the argument array is null
-     * @see java.util.AbstractCollection#toArray
-     */
-    @Override
-    public <T> T[] toArray(T[] array) {
-        return newArray(array);
-    }
-
-    @SuppressWarnings("unchecked")
-    private  <T> T[] newArray(T[] array) {
-        int size = size();
-        if (size > array.length) {
-            Class<?> clazz = array.getClass().getComponentType();
-            array = (T[]) Array.newInstance(clazz, size);
-        }
-        if (front < rear) {
-            System.arraycopy(elements, front, array, 0, size);
-        } else if (size != 0) {
-            int length = elements.length;
-            System.arraycopy(elements, front, array, 0, length - front);
-            System.arraycopy(elements, 0, array, length - front, rear);
-        }
-        if (size < array.length) {
-            array[size] = null;
-        }
-        return array;
-    }
-
-    /**
-     * Returns the iterator of the deque. The elements will be ordered from head
-     * to tail.
-     *
-     * @return the iterator
-     * @see java.util.AbstractCollection#iterator()
-     */
-    @SuppressWarnings("synthetic-access")
-    @Override
     public Iterator<E> iterator() {
-        return new ArrayDequeIterator<E>();
+        return new DeqIterator();
     }
 
-    /**
-     * {@inheritDoc}
-     *
-     * @return the reverse order Iterator
-     * @see java.util.Deque#descendingIterator()
-     */
     public Iterator<E> descendingIterator() {
-        return new ReverseArrayDequeIterator<E>();
+        return new DescendingIterator();
     }
 
-    /**
-     * Deserialization method.
-     *
-     * @param stream
-     *            the ObjectInputStream
-     * @throws IOException
-     * @throws ClassNotFoundException
-     */
-    @SuppressWarnings("unchecked")
-    private void readObject(ObjectInputStream stream) throws IOException,
-            ClassNotFoundException {
-        stream.defaultReadObject();
-        int size = stream.readInt();
-        elements = (E[]) new Object[countInitSize(size)];
-        front = rear = 0;
-        status = DequeStatus.Empty;
-        modCount = 0;
-        for (int i = 0; i < size; i++) {
-            addLastImpl((E) stream.readObject());
+    private class DeqIterator implements Iterator<E> {
+        /**
+         * Index of element to be returned by subsequent call to next.
+         */
+        private int cursor = head;
+
+        /**
+         * Tail recorded at construction (also in remove), to stop
+         * iterator and also to check for comodification.
+         */
+        private int fence = tail;
+
+        /**
+         * Index of element returned by most recent call to next.
+         * Reset to -1 if element is deleted by a call to remove.
+         */
+        private int lastRet = -1;
+
+        public boolean hasNext() {
+            return cursor != fence;
+        }
+
+        public E next() {
+            if (cursor == fence)
+                throw new NoSuchElementException();
+            E result = elements[cursor];
+            // This check doesn't catch all possible comodifications,
+            // but does catch the ones that corrupt traversal
+            if (tail != fence || result == null)
+                throw new ConcurrentModificationException();
+            lastRet = cursor;
+            cursor = (cursor + 1) & (elements.length - 1);
+            return result;
+        }
+
+        public void remove() {
+            if (lastRet < 0)
+                throw new IllegalStateException();
+            if (delete(lastRet)) { // if left-shifted, undo increment in next()
+                cursor = (cursor - 1) & (elements.length - 1);
+                fence = tail;
+            }
+            lastRet = -1;
+        }
+    }
+
+    private class DescendingIterator implements Iterator<E> {
+        /*
+         * This class is nearly a mirror-image of DeqIterator, using
+         * tail instead of head for initial cursor, and head instead of
+         * tail for fence.
+         */
+        private int cursor = tail;
+        private int fence = head;
+        private int lastRet = -1;
+
+        public boolean hasNext() {
+            return cursor != fence;
+        }
+
+        public E next() {
+            if (cursor == fence)
+                throw new NoSuchElementException();
+            cursor = (cursor - 1) & (elements.length - 1);
+            E result = elements[cursor];
+            if (head != fence || result == null)
+                throw new ConcurrentModificationException();
+            lastRet = cursor;
+            return result;
+        }
+
+        public void remove() {
+            if (lastRet < 0)
+                throw new IllegalStateException();
+            if (!delete(lastRet)) {
+                cursor = (cursor + 1) & (elements.length - 1);
+                fence = head;
+            }
+            lastRet = -1;
         }
     }
 
     /**
-     * Serialization method.
+     * Returns <tt>true</tt> if this deque contains the specified element.
+     * More formally, returns <tt>true</tt> if and only if this deque contains
+     * at least one element <tt>e</tt> such that <tt>o.equals(e)</tt>.
      *
-     * @param stream
-     *            the ObjectOutputStream
-     * @serialData The current size of the deque, followed by all the elements
-     *             from head to tail.
-     * @throws IOException
-     *
+     * @param o object to be checked for containment in this deque
+     * @return <tt>true</tt> if this deque contains the specified element
      */
-    private void writeObject(ObjectOutputStream stream) throws IOException {
-        stream.defaultWriteObject();
-        stream.writeInt(size());
-        Iterator<?> it = new ArrayDequeIterator<E>();
-        while (it.hasNext()) {
-            stream.writeObject(it.next());
+    public boolean contains(Object o) {
+        if (o == null)
+            return false;
+        int mask = elements.length - 1;
+        int i = head;
+        E x;
+        while ( (x = elements[i]) != null) {
+            if (o.equals(x))
+                return true;
+            i = (i + 1) & mask;
+        }
+        return false;
+    }
+
+    /**
+     * Removes a single instance of the specified element from this deque.
+     * If the deque does not contain the element, it is unchanged.
+     * More formally, removes the first element <tt>e</tt> such that
+     * <tt>o.equals(e)</tt> (if such an element exists).
+     * Returns <tt>true</tt> if this deque contained the specified element
+     * (or equivalently, if this deque changed as a result of the call).
+     *
+     * <p>This method is equivalent to {@link #removeFirstOccurrence}.
+     *
+     * @param o element to be removed from this deque, if present
+     * @return <tt>true</tt> if this deque contained the specified element
+     */
+    public boolean remove(Object o) {
+        return removeFirstOccurrence(o);
+    }
+
+    /**
+     * Removes all of the elements from this deque.
+     * The deque will be empty after this call returns.
+     */
+    public void clear() {
+        int h = head;
+        int t = tail;
+        if (h != t) { // clear all cells
+            head = tail = 0;
+            int i = h;
+            int mask = elements.length - 1;
+            do {
+                elements[i] = null;
+                i = (i + 1) & mask;
+            } while (i != t);
         }
     }
 
+    /**
+     * Returns an array containing all of the elements in this deque
+     * in proper sequence (from first to last element).
+     *
+     * <p>The returned array will be "safe" in that no references to it are
+     * maintained by this deque.  (In other words, this method must allocate
+     * a new array).  The caller is thus free to modify the returned array.
+     *
+     * <p>This method acts as bridge between array-based and collection-based
+     * APIs.
+     *
+     * @return an array containing all of the elements in this deque
+     */
+    public Object[] toArray() {
+        return copyElements(new Object[size()]);
+    }
+
+    /**
+     * Returns an array containing all of the elements in this deque in
+     * proper sequence (from first to last element); the runtime type of the
+     * returned array is that of the specified array.  If the deque fits in
+     * the specified array, it is returned therein.  Otherwise, a new array
+     * is allocated with the runtime type of the specified array and the
+     * size of this deque.
+     *
+     * <p>If this deque fits in the specified array with room to spare
+     * (i.e., the array has more elements than this deque), the element in
+     * the array immediately following the end of the deque is set to
+     * <tt>null</tt>.
+     *
+     * <p>Like the {@link #toArray()} method, this method acts as bridge between
+     * array-based and collection-based APIs.  Further, this method allows
+     * precise control over the runtime type of the output array, and may,
+     * under certain circumstances, be used to save allocation costs.
+     *
+     * <p>Suppose <tt>x</tt> is a deque known to contain only strings.
+     * The following code can be used to dump the deque into a newly
+     * allocated array of <tt>String</tt>:
+     *
+     * <pre>
+     *     String[] y = x.toArray(new String[0]);</pre>
+     *
+     * Note that <tt>toArray(new Object[0])</tt> is identical in function to
+     * <tt>toArray()</tt>.
+     *
+     * @param a the array into which the elements of the deque are to
+     *          be stored, if it is big enough; otherwise, a new array of the
+     *          same runtime type is allocated for this purpose
+     * @return an array containing all of the elements in this deque
+     * @throws ArrayStoreException if the runtime type of the specified array
+     *         is not a supertype of the runtime type of every element in
+     *         this deque
+     * @throws NullPointerException if the specified array is null
+     */
+    public <T> T[] toArray(T[] a) {
+        int size = size();
+        if (a.length < size)
+            a = (T[])java.lang.reflect.Array.newInstance(
+                    a.getClass().getComponentType(), size);
+        copyElements(a);
+        if (a.length > size)
+            a[size] = null;
+        return a;
+    }
+
+    // *** Object methods ***
+
+    /**
+     * Returns a copy of this deque.
+     *
+     * @return a copy of this deque
+     */
+    public ArrayDeque<E> clone() {
+        try {
+            ArrayDeque<E> result = (ArrayDeque<E>) super.clone();
+            result.elements = Arrays.copyOf(elements, elements.length);
+            return result;
+
+        } catch (CloneNotSupportedException e) {
+            throw new AssertionError();
+        }
+    }
+
+    /**
+     * Appease the serialization gods.
+     */
+    private static final long serialVersionUID = 2340985798034038923L;
+
+    /**
+     * Serialize this deque.
+     *
+     * @serialData The current size (<tt>int</tt>) of the deque,
+     * followed by all of its elements (each an object reference) in
+     * first-to-last order.
+     */
+    private void writeObject(ObjectOutputStream s) throws IOException {
+        s.defaultWriteObject();
+
+        // Write out size
+        s.writeInt(size());
+
+        // Write out elements in order.
+        int mask = elements.length - 1;
+        for (int i = head; i != tail; i = (i + 1) & mask)
+            s.writeObject(elements[i]);
+    }
+
+    /**
+     * Deserialize this deque.
+     */
+    private void readObject(ObjectInputStream s)
+            throws IOException, ClassNotFoundException {
+        s.defaultReadObject();
+
+        // Read in size and allocate array
+        int size = s.readInt();
+        allocateElements(size);
+        head = 0;
+        tail = size;
+
+        // Read in all elements in the proper order.
+        for (int i = 0; i < size; i++)
+            elements[i] = (E)s.readObject();
+    }
 }
diff --git a/luni/src/main/java/java/util/ArrayList.java b/luni/src/main/java/java/util/ArrayList.java
index 19d2449..d07ca60 100644
--- a/luni/src/main/java/java/util/ArrayList.java
+++ b/luni/src/main/java/java/util/ArrayList.java
@@ -27,6 +27,7 @@
 import java.io.ObjectOutputStream;
 import java.io.Serializable;
 import java.lang.reflect.Array;
+import libcore.base.EmptyArray;
 
 /**
  * ArrayList is an implementation of {@link List}, backed by an array.
@@ -44,13 +45,7 @@
  * @param <E> The element type of this list.
  * @since 1.2
  */
-public class ArrayList<E> extends AbstractList<E>
-        implements Cloneable, Serializable, RandomAccess {
-    /**
-     * An empty array of objects (to be shared among all empty lists).
-     */
-    private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
-
+public class ArrayList<E> extends AbstractList<E> implements Cloneable, Serializable, RandomAccess {
     /**
      * The minimum amount by which the capacity of an ArrayList will increase.
      * This tuning parameter controls a time-space tradeoff. This value (12)
@@ -81,14 +76,14 @@
         if (capacity < 0) {
             throw new IllegalArgumentException();
         }
-        array = (capacity == 0 ? EMPTY_OBJECT_ARRAY : new Object[capacity]);
+        array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]);
     }
 
     /**
      * Constructs a new {@code ArrayList} instance with zero initial capacity.
      */
     public ArrayList() {
-        array = EMPTY_OBJECT_ARRAY;
+        array = EmptyArray.OBJECT;
     }
 
     /**
@@ -542,7 +537,7 @@
             return;
         }
         if (s == 0) {
-            array = EMPTY_OBJECT_ARRAY;
+            array = EmptyArray.OBJECT;
         } else {
             Object[] newArray = new Object[s];
             System.arraycopy(array, 0, newArray, 0, s);
@@ -652,15 +647,14 @@
         }
     }
 
-    private void readObject(ObjectInputStream stream) throws IOException,
-            ClassNotFoundException {
+    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
         stream.defaultReadObject();
         int cap = stream.readInt();
         if (cap < size) {
             throw new InvalidObjectException(
                     "Capacity: " + cap + " < size: " + size);
         }
-        array = (cap == 0 ? EMPTY_OBJECT_ARRAY : new Object[cap]);
+        array = (cap == 0 ? EmptyArray.OBJECT : new Object[cap]);
         for (int i = 0; i < size; i++) {
             array[i] = stream.readObject();
         }
diff --git a/luni/src/main/java/java/util/Arrays.java b/luni/src/main/java/java/util/Arrays.java
index d5bfbc8..5f38db3 100644
--- a/luni/src/main/java/java/util/Arrays.java
+++ b/luni/src/main/java/java/util/Arrays.java
@@ -114,15 +114,9 @@
 
         @Override
         public E set(int location, E object) {
-            try {
-                E result = a[location];
-                a[location] = object;
-                return result;
-            } catch (ArrayIndexOutOfBoundsException e) {
-                throw new IndexOutOfBoundsException();
-            } catch (ArrayStoreException e) {
-                throw new ClassCastException();
-            }
+            E result = a[location];
+            a[location] = object;
+            return result;
         }
 
         @Override
diff --git a/luni/src/main/java/java/util/Deque.java b/luni/src/main/java/java/util/Deque.java
index 7ccc6f4..cb6bd90 100644
--- a/luni/src/main/java/java/util/Deque.java
+++ b/luni/src/main/java/java/util/Deque.java
@@ -1,255 +1,548 @@
 /*
- *  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.
+ * Written by Doug Lea and Josh Bloch with assistance from members of
+ * JCP JSR-166 Expert Group and released to the public domain, as explained
+ * at http://creativecommons.org/licenses/publicdomain
  */
 
 package java.util;
 
-/**
- * A kind of collection that can insert or remove element at both ends("double
- * ended queue"). Mostly a deque has no limit of its size.
- *
- * <p>Extending from Queue, a deque can be used as a Queue which behavior is
- * first-in-first-out. Furthermore, a deque can also be used as a Stack(legacy
- * class) which behavior is last-in-first-out.
- *
- * <p>A typical deque does not allow null to be inserted as its element, while some
- * implementations allow it. But null should not be inserted even in these
- * implementations, since method poll return null to indicate that there is no
- * element left in the deque.
- *
- * <p>A deque can also remove interior elements by removeFirstOccurrence and
- * removeLastOccurrence methods. A deque can not access elements by index.
- *
- * @param <E>
- *            the type of elements in this collection
- * @since 1.6
- */
-public interface Deque<E> extends Queue<E> {
+// BEGIN android-note
+// removed link to collections framework docs
+// changed {@link #offer(Object)} to {@link #offer} to satisfy DroidDoc
+// END android-note
 
+/**
+ * A linear collection that supports element insertion and removal at
+ * both ends.  The name <i>deque</i> is short for "double ended queue"
+ * and is usually pronounced "deck".  Most <tt>Deque</tt>
+ * implementations place no fixed limits on the number of elements
+ * they may contain, but this interface supports capacity-restricted
+ * deques as well as those with no fixed size limit.
+ *
+ * <p>This interface defines methods to access the elements at both
+ * ends of the deque.  Methods are provided to insert, remove, and
+ * examine the element.  Each of these methods exists in two forms:
+ * one throws an exception if the operation fails, the other returns a
+ * special value (either <tt>null</tt> or <tt>false</tt>, depending on
+ * the operation).  The latter form of the insert operation is
+ * designed specifically for use with capacity-restricted
+ * <tt>Deque</tt> implementations; in most implementations, insert
+ * operations cannot fail.
+ *
+ * <p>The twelve methods described above are summarized in the
+ * following table:
+ *
+ * <p>
+ * <table BORDER CELLPADDING=3 CELLSPACING=1>
+ *  <tr>
+ *    <td></td>
+ *    <td ALIGN=CENTER COLSPAN = 2> <b>First Element (Head)</b></td>
+ *    <td ALIGN=CENTER COLSPAN = 2> <b>Last Element (Tail)</b></td>
+ *  </tr>
+ *  <tr>
+ *    <td></td>
+ *    <td ALIGN=CENTER><em>Throws exception</em></td>
+ *    <td ALIGN=CENTER><em>Special value</em></td>
+ *    <td ALIGN=CENTER><em>Throws exception</em></td>
+ *    <td ALIGN=CENTER><em>Special value</em></td>
+ *  </tr>
+ *  <tr>
+ *    <td><b>Insert</b></td>
+ *    <td>{@link #addFirst addFirst(e)}</td>
+ *    <td>{@link #offerFirst offerFirst(e)}</td>
+ *    <td>{@link #addLast addLast(e)}</td>
+ *    <td>{@link #offerLast offerLast(e)}</td>
+ *  </tr>
+ *  <tr>
+ *    <td><b>Remove</b></td>
+ *    <td>{@link #removeFirst removeFirst()}</td>
+ *    <td>{@link #pollFirst pollFirst()}</td>
+ *    <td>{@link #removeLast removeLast()}</td>
+ *    <td>{@link #pollLast pollLast()}</td>
+ *  </tr>
+ *  <tr>
+ *    <td><b>Examine</b></td>
+ *    <td>{@link #getFirst getFirst()}</td>
+ *    <td>{@link #peekFirst peekFirst()}</td>
+ *    <td>{@link #getLast getLast()}</td>
+ *    <td>{@link #peekLast peekLast()}</td>
+ *  </tr>
+ * </table>
+ *
+ * <p>This interface extends the {@link Queue} interface.  When a deque is
+ * used as a queue, FIFO (First-In-First-Out) behavior results.  Elements are
+ * added at the end of the deque and removed from the beginning.  The methods
+ * inherited from the <tt>Queue</tt> interface are precisely equivalent to
+ * <tt>Deque</tt> methods as indicated in the following table:
+ *
+ * <p>
+ * <table BORDER CELLPADDING=3 CELLSPACING=1>
+ *  <tr>
+ *    <td ALIGN=CENTER> <b><tt>Queue</tt> Method</b></td>
+ *    <td ALIGN=CENTER> <b>Equivalent <tt>Deque</tt> Method</b></td>
+ *  </tr>
+ *  <tr>
+ *    <td>{@link java.util.Queue#add add(e)}</td>
+ *    <td>{@link #addLast addLast(e)}</td>
+ *  </tr>
+ *  <tr>
+ *    <td>{@link java.util.Queue#offer offer(e)}</td>
+ *    <td>{@link #offerLast offerLast(e)}</td>
+ *  </tr>
+ *  <tr>
+ *    <td>{@link java.util.Queue#remove remove()}</td>
+ *    <td>{@link #removeFirst removeFirst()}</td>
+ *  </tr>
+ *  <tr>
+ *    <td>{@link java.util.Queue#poll poll()}</td>
+ *    <td>{@link #pollFirst pollFirst()}</td>
+ *  </tr>
+ *  <tr>
+ *    <td>{@link java.util.Queue#element element()}</td>
+ *    <td>{@link #getFirst getFirst()}</td>
+ *  </tr>
+ *  <tr>
+ *    <td>{@link java.util.Queue#peek peek()}</td>
+ *    <td>{@link #peek peekFirst()}</td>
+ *  </tr>
+ * </table>
+ *
+ * <p>Deques can also be used as LIFO (Last-In-First-Out) stacks.  This
+ * interface should be used in preference to the legacy {@link Stack} class.
+ * When a deque is used as a stack, elements are pushed and popped from the
+ * beginning of the deque.  Stack methods are precisely equivalent to
+ * <tt>Deque</tt> methods as indicated in the table below:
+ *
+ * <p>
+ * <table BORDER CELLPADDING=3 CELLSPACING=1>
+ *  <tr>
+ *    <td ALIGN=CENTER> <b>Stack Method</b></td>
+ *    <td ALIGN=CENTER> <b>Equivalent <tt>Deque</tt> Method</b></td>
+ *  </tr>
+ *  <tr>
+ *    <td>{@link #push push(e)}</td>
+ *    <td>{@link #addFirst addFirst(e)}</td>
+ *  </tr>
+ *  <tr>
+ *    <td>{@link #pop pop()}</td>
+ *    <td>{@link #removeFirst removeFirst()}</td>
+ *  </tr>
+ *  <tr>
+ *    <td>{@link #peek peek()}</td>
+ *    <td>{@link #peekFirst peekFirst()}</td>
+ *  </tr>
+ * </table>
+ *
+ * <p>Note that the {@link #peek peek} method works equally well when
+ * a deque is used as a queue or a stack; in either case, elements are
+ * drawn from the beginning of the deque.
+ *
+ * <p>This interface provides two methods to remove interior
+ * elements, {@link #removeFirstOccurrence removeFirstOccurrence} and
+ * {@link #removeLastOccurrence removeLastOccurrence}.
+ *
+ * <p>Unlike the {@link List} interface, this interface does not
+ * provide support for indexed access to elements.
+ *
+ * <p>While <tt>Deque</tt> implementations are not strictly required
+ * to prohibit the insertion of null elements, they are strongly
+ * encouraged to do so.  Users of any <tt>Deque</tt> implementations
+ * that do allow null elements are strongly encouraged <i>not</i> to
+ * take advantage of the ability to insert nulls.  This is so because
+ * <tt>null</tt> is used as a special return value by various methods
+ * to indicated that the deque is empty.
+ *
+ * <p><tt>Deque</tt> implementations generally do not define
+ * element-based versions of the <tt>equals</tt> and <tt>hashCode</tt>
+ * methods, but instead inherit the identity-based versions from class
+ * <tt>Object</tt>.
+ *
+ * @author Doug Lea
+ * @author Josh Bloch
+ * @since  1.6
+ * @param <E> the type of elements held in this collection
+ */
+
+public interface Deque<E> extends Queue<E> {
     /**
-     * Inserts an element at the head of this deque if it dose not violate size
-     * limit immediately. It is better to use offerFirst(E) if a deque is
-     * size-limited.
+     * Inserts the specified element at the front of this deque if it is
+     * possible to do so immediately without violating capacity restrictions.
+     * When using a capacity-restricted deque, it is generally preferable to
+     * use method {@link #offerFirst}.
      *
-     * @param e
-     *            the element
-     * @throws IllegalStateException
-     *             if it can not add now due to size limit
-     * @throws ClassCastException
-     *             if the class of element can not be added into this deque
-     * @throws NullPointerException
-     *             if the element is null and the deque can not contain null
-     *             element
-     * @throws IllegalArgumentException
-     *             if the element can not be added due to some property.
+     * @param e the element to add
+     * @throws IllegalStateException if the element cannot be added at this
+     *         time due to capacity restrictions
+     * @throws ClassCastException if the class of the specified element
+     *         prevents it from being added to this deque
+     * @throws NullPointerException if the specified element is null and this
+     *         deque does not permit null elements
+     * @throws IllegalArgumentException if some property of the specified
+     *         element prevents it from being added to this deque
      */
     void addFirst(E e);
 
     /**
-     * Inserts an element at the tail of this deque if it dose not violate size
-     * limit immediately. It is better to use offerLast(E) if a deque is
-     * size-limited.
+     * Inserts the specified element at the end of this deque if it is
+     * possible to do so immediately without violating capacity restrictions.
+     * When using a capacity-restricted deque, it is generally preferable to
+     * use method {@link #offerLast}.
      *
-     * @param e
-     *            the element
-     * @throws IllegalStateException
-     *             if it can not add now due to size limit
-     * @throws ClassCastException
-     *             if the class of element can not be added into this deque
-     * @throws NullPointerException
-     *             if the element is null and the deque can not contain null
-     *             element
-     * @throws IllegalArgumentException
-     *             if the element can not be added due to some property.
+     * <p>This method is equivalent to {@link #add}.
+     *
+     * @param e the element to add
+     * @throws IllegalStateException if the element cannot be added at this
+     *         time due to capacity restrictions
+     * @throws ClassCastException if the class of the specified element
+     *         prevents it from being added to this deque
+     * @throws NullPointerException if the specified element is null and this
+     *         deque does not permit null elements
+     * @throws IllegalArgumentException if some property of the specified
+     *         element prevents it from being added to this deque
      */
     void addLast(E e);
 
     /**
-     * Inserts an element at the head of this deque unless it would violate size
-     * limit. It is better than the addFirst(E) method in a size-limited deque,
-     * because the latter one may fail to add the element only by throwing an
-     * exception.
+     * Inserts the specified element at the front of this deque unless it would
+     * violate capacity restrictions.  When using a capacity-restricted deque,
+     * this method is generally preferable to the {@link #addFirst} method,
+     * which can fail to insert an element only by throwing an exception.
      *
-     * @param e
-     *            the element
-     * @return true if the operation succeeds or false if it fails.
-     * @throws ClassCastException
-     *             if the class of element can not be added into this deque
-     * @throws NullPointerException
-     *             if the element is null and the deque can not contain null
-     *             element
-     * @throws IllegalArgumentException
-     *             if the element can not be added due to some property.
+     * @param e the element to add
+     * @return <tt>true</tt> if the element was added to this deque, else
+     *         <tt>false</tt>
+     * @throws ClassCastException if the class of the specified element
+     *         prevents it from being added to this deque
+     * @throws NullPointerException if the specified element is null and this
+     *         deque does not permit null elements
+     * @throws IllegalArgumentException if some property of the specified
+     *         element prevents it from being added to this deque
      */
     boolean offerFirst(E e);
 
     /**
-     * Inserts an element at the tail of this deque unless it would violate size
-     * limit. It is better than the addLast(E) method in a size-limited deque,
-     * because the latter one may fail to add the element only by throwing an
-     * exception.
+     * Inserts the specified element at the end of this deque unless it would
+     * violate capacity restrictions.  When using a capacity-restricted deque,
+     * this method is generally preferable to the {@link #addLast} method,
+     * which can fail to insert an element only by throwing an exception.
      *
-     * @param e
-     *            the element
-     * @return true if the operation succeeds or false if it fails
-     * @throws ClassCastException
-     *             if the class of element can not be added into this deque
-     * @throws NullPointerException
-     *             if the element is null and the deque can not contain null
-     *             element
-     * @throws IllegalArgumentException
-     *             if the element can not be added due to some property
+     * @param e the element to add
+     * @return <tt>true</tt> if the element was added to this deque, else
+     *         <tt>false</tt>
+     * @throws ClassCastException if the class of the specified element
+     *         prevents it from being added to this deque
+     * @throws NullPointerException if the specified element is null and this
+     *         deque does not permit null elements
+     * @throws IllegalArgumentException if some property of the specified
+     *         element prevents it from being added to this deque
      */
     boolean offerLast(E e);
 
     /**
-     * Gets and removes the head element of this deque. This method throws an
-     * exception if the deque is empty.
+     * Retrieves and removes the first element of this deque.  This method
+     * differs from {@link #pollFirst pollFirst} only in that it throws an
+     * exception if this deque is empty.
      *
-     * @return the head element
-     * @throws NoSuchElementException
-     *             if the deque is empty
+     * @return the head of this deque
+     * @throws NoSuchElementException if this deque is empty
      */
     E removeFirst();
 
     /**
-     * Gets and removes the tail element of this deque. This method throws an
-     * exception if the deque is empty.
+     * Retrieves and removes the last element of this deque.  This method
+     * differs from {@link #pollLast pollLast} only in that it throws an
+     * exception if this deque is empty.
      *
-     * @return the tail element
-     * @throws NoSuchElementException
-     *             if the deque is empty
+     * @return the tail of this deque
+     * @throws NoSuchElementException if this deque is empty
      */
     E removeLast();
 
     /**
-     * Gets and removes the head element of this deque. This method returns null
-     * if the deque is empty.
+     * Retrieves and removes the first element of this deque,
+     * or returns <tt>null</tt> if this deque is empty.
      *
-     * @return the head element or null if the deque is empty
+     * @return the head of this deque, or <tt>null</tt> if this deque is empty
      */
     E pollFirst();
 
     /**
-     * Gets and removes the tail element of this deque. This method returns null
-     * if the deque is empty.
+     * Retrieves and removes the last element of this deque,
+     * or returns <tt>null</tt> if this deque is empty.
      *
-     * @return the tail element or null if the deque is empty
+     * @return the tail of this deque, or <tt>null</tt> if this deque is empty
      */
     E pollLast();
 
     /**
-     * Gets but not removes the head element of this deque. This method throws
-     * an exception if the deque is empty.
+     * Retrieves, but does not remove, the first element of this deque.
      *
-     * @return the head element
-     * @throws NoSuchElementException
-     *             if the deque is empty
+     * This method differs from {@link #peekFirst peekFirst} only in that it
+     * throws an exception if this deque is empty.
+     *
+     * @return the head of this deque
+     * @throws NoSuchElementException if this deque is empty
      */
     E getFirst();
 
     /**
-     * Gets but not removes the tail element of this deque. This method throws
-     * an exception if the deque is empty.
+     * Retrieves, but does not remove, the last element of this deque.
+     * This method differs from {@link #peekLast peekLast} only in that it
+     * throws an exception if this deque is empty.
      *
-     * @return the tail element
-     * @throws NoSuchElementException
-     *             if the deque is empty
+     * @return the tail of this deque
+     * @throws NoSuchElementException if this deque is empty
      */
     E getLast();
 
     /**
-     * Gets but not removes the head element of this deque. This method returns
-     * null if the deque is empty.
+     * Retrieves, but does not remove, the first element of this deque,
+     * or returns <tt>null</tt> if this deque is empty.
      *
-     * @return the head element or null if the deque is empty
+     * @return the head of this deque, or <tt>null</tt> if this deque is empty
      */
     E peekFirst();
 
     /**
-     * Gets but not removes the tail element of this deque. This method returns
-     * null if the deque is empty.
+     * Retrieves, but does not remove, the last element of this deque,
+     * or returns <tt>null</tt> if this deque is empty.
      *
-     * @return the tail element or null if the deque is empty
+     * @return the tail of this deque, or <tt>null</tt> if this deque is empty
      */
     E peekLast();
 
     /**
-     * Removes the first equivalent element of the specified object. If the
-     * deque does not contain the element, it is unchanged and returns false.
+     * Removes the first occurrence of the specified element from this deque.
+     * If the deque does not contain the element, it is unchanged.
+     * More formally, removes the first element <tt>e</tt> such that
+     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>
+     * (if such an element exists).
+     * Returns <tt>true</tt> if this deque contained the specified element
+     * (or equivalently, if this deque changed as a result of the call).
      *
-     * @param o
-     *            the element to be removed
-     * @return true if the operation succeeds or false if the deque does not
-     *         contain the element.
-     * @throws ClassCastException
-     *             if the class of the element is incompatible with the deque
-     * @throws NullPointerException
-     *             if the element is null and the deque can not contain null
-     *             element
+     * @param o element to be removed from this deque, if present
+     * @return <tt>true</tt> if an element was removed as a result of this call
+     * @throws ClassCastException if the class of the specified element
+     *         is incompatible with this deque (optional)
+     * @throws NullPointerException if the specified element is null and this
+     *         deque does not permit null elements (optional)
      */
     boolean removeFirstOccurrence(Object o);
 
     /**
-     * Removes the last equivalent element of the specified object. If the deque
-     * does not contain the element, it is unchanged and returns false.
+     * Removes the last occurrence of the specified element from this deque.
+     * If the deque does not contain the element, it is unchanged.
+     * More formally, removes the last element <tt>e</tt> such that
+     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>
+     * (if such an element exists).
+     * Returns <tt>true</tt> if this deque contained the specified element
+     * (or equivalently, if this deque changed as a result of the call).
      *
-     * @param o
-     *            the element to be removed
-     * @return true if the operation succeeds or false if the deque does not
-     *         contain the element.
-     * @throws ClassCastException
-     *             if the class of the element is incompatible with the deque
-     * @throws NullPointerException
-     *             if the element is null and the deque can not contain null
-     *             element
+     * @param o element to be removed from this deque, if present
+     * @return <tt>true</tt> if an element was removed as a result of this call
+     * @throws ClassCastException if the class of the specified element
+     *         is incompatible with this deque (optional)
+     * @throws NullPointerException if the specified element is null and this
+     *         deque does not permit null elements (optional)
      */
     boolean removeLastOccurrence(Object o);
 
+    // *** Queue methods ***
+
     /**
-     * Pushes the element to the deque(at the head of the deque), just same as
-     * addFirst(E).
+     * Inserts the specified element into the queue represented by this deque
+     * (in other words, at the tail of this deque) if it is possible to do so
+     * immediately without violating capacity restrictions, returning
+     * <tt>true</tt> upon success and throwing an
+     * <tt>IllegalStateException</tt> if no space is currently available.
+     * When using a capacity-restricted deque, it is generally preferable to
+     * use {@link #offer offer}.
      *
-     * @param e
-     *            the element
-     * @throws IllegalStateException
-     *             if it can not add now due to size limit
-     * @throws ClassCastException
-     *             if the class of element can not be added into this deque
-     * @throws NullPointerException
-     *             if the element is null and the deque can not contain null
-     *             element
-     * @throws IllegalArgumentException
-     *             if the element can not be added due to some property.
+     * <p>This method is equivalent to {@link #addLast}.
+     *
+     * @param e the element to add
+     * @return <tt>true</tt> (as specified by {@link Collection#add})
+     * @throws IllegalStateException if the element cannot be added at this
+     *         time due to capacity restrictions
+     * @throws ClassCastException if the class of the specified element
+     *         prevents it from being added to this deque
+     * @throws NullPointerException if the specified element is null and this
+     *         deque does not permit null elements
+     * @throws IllegalArgumentException if some property of the specified
+     *         element prevents it from being added to this deque
+     */
+    boolean add(E e);
+
+    /**
+     * Inserts the specified element into the queue represented by this deque
+     * (in other words, at the tail of this deque) if it is possible to do so
+     * immediately without violating capacity restrictions, returning
+     * <tt>true</tt> upon success and <tt>false</tt> if no space is currently
+     * available.  When using a capacity-restricted deque, this method is
+     * generally preferable to the {@link #add} method, which can fail to
+     * insert an element only by throwing an exception.
+     *
+     * <p>This method is equivalent to {@link #offerLast}.
+     *
+     * @param e the element to add
+     * @return <tt>true</tt> if the element was added to this deque, else
+     *         <tt>false</tt>
+     * @throws ClassCastException if the class of the specified element
+     *         prevents it from being added to this deque
+     * @throws NullPointerException if the specified element is null and this
+     *         deque does not permit null elements
+     * @throws IllegalArgumentException if some property of the specified
+     *         element prevents it from being added to this deque
+     */
+    boolean offer(E e);
+
+    /**
+     * Retrieves and removes the head of the queue represented by this deque
+     * (in other words, the first element of this deque).
+     * This method differs from {@link #poll poll} only in that it throws an
+     * exception if this deque is empty.
+     *
+     * <p>This method is equivalent to {@link #removeFirst()}.
+     *
+     * @return the head of the queue represented by this deque
+     * @throws NoSuchElementException if this deque is empty
+     */
+    E remove();
+
+    /**
+     * Retrieves and removes the head of the queue represented by this deque
+     * (in other words, the first element of this deque), or returns
+     * <tt>null</tt> if this deque is empty.
+     *
+     * <p>This method is equivalent to {@link #pollFirst()}.
+     *
+     * @return the first element of this deque, or <tt>null</tt> if
+     *         this deque is empty
+     */
+    E poll();
+
+    /**
+     * Retrieves, but does not remove, the head of the queue represented by
+     * this deque (in other words, the first element of this deque).
+     * This method differs from {@link #peek peek} only in that it throws an
+     * exception if this deque is empty.
+     *
+     * <p>This method is equivalent to {@link #getFirst()}.
+     *
+     * @return the head of the queue represented by this deque
+     * @throws NoSuchElementException if this deque is empty
+     */
+    E element();
+
+    /**
+     * Retrieves, but does not remove, the head of the queue represented by
+     * this deque (in other words, the first element of this deque), or
+     * returns <tt>null</tt> if this deque is empty.
+     *
+     * <p>This method is equivalent to {@link #peekFirst()}.
+     *
+     * @return the head of the queue represented by this deque, or
+     *         <tt>null</tt> if this deque is empty
+     */
+    E peek();
+
+
+    // *** Stack methods ***
+
+    /**
+     * Pushes an element onto the stack represented by this deque (in other
+     * words, at the head of this deque) if it is possible to do so
+     * immediately without violating capacity restrictions, returning
+     * <tt>true</tt> upon success and throwing an
+     * <tt>IllegalStateException</tt> if no space is currently available.
+     *
+     * <p>This method is equivalent to {@link #addFirst}.
+     *
+     * @param e the element to push
+     * @throws IllegalStateException if the element cannot be added at this
+     *         time due to capacity restrictions
+     * @throws ClassCastException if the class of the specified element
+     *         prevents it from being added to this deque
+     * @throws NullPointerException if the specified element is null and this
+     *         deque does not permit null elements
+     * @throws IllegalArgumentException if some property of the specified
+     *         element prevents it from being added to this deque
      */
     void push(E e);
 
     /**
-     * Pops the head element of the deque, just same as removeFirst().
+     * Pops an element from the stack represented by this deque.  In other
+     * words, removes and returns the first element of this deque.
      *
-     * @return the head element
-     * @throws NoSuchElementException
-     *             if the deque is empty
+     * <p>This method is equivalent to {@link #removeFirst()}.
+     *
+     * @return the element at the front of this deque (which is the top
+     *         of the stack represented by this deque)
+     * @throws NoSuchElementException if this deque is empty
      */
     E pop();
 
+
+    // *** Collection methods ***
+
     /**
-     * Returns the iterator in reverse order, from tail to head.
+     * Removes the first occurrence of the specified element from this deque.
+     * If the deque does not contain the element, it is unchanged.
+     * More formally, removes the first element <tt>e</tt> such that
+     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>
+     * (if such an element exists).
+     * Returns <tt>true</tt> if this deque contained the specified element
+     * (or equivalently, if this deque changed as a result of the call).
      *
-     * @return the iterator in reverse order
+     * <p>This method is equivalent to {@link #removeFirstOccurrence}.
+     *
+     * @param o element to be removed from this deque, if present
+     * @return <tt>true</tt> if an element was removed as a result of this call
+     * @throws ClassCastException if the class of the specified element
+     *         is incompatible with this deque (optional)
+     * @throws NullPointerException if the specified element is null and this
+     *         deque does not permit null elements (optional)
+     */
+    boolean remove(Object o);
+
+    /**
+     * Returns <tt>true</tt> if this deque contains the specified element.
+     * More formally, returns <tt>true</tt> if and only if this deque contains
+     * at least one element <tt>e</tt> such that
+     * <tt>(o==null&nbsp;?&nbsp;e==null&nbsp;:&nbsp;o.equals(e))</tt>.
+     *
+     * @param o element whose presence in this deque is to be tested
+     * @return <tt>true</tt> if this deque contains the specified element
+     * @throws ClassCastException if the type of the specified element
+     *         is incompatible with this deque (optional)
+     * @throws NullPointerException if the specified element is null and this
+     *         deque does not permit null elements (optional)
+     */
+    boolean contains(Object o);
+
+    /**
+     * Returns the number of elements in this deque.
+     *
+     * @return the number of elements in this deque
+     */
+    public int size();
+
+    /**
+     * Returns an iterator over the elements in this deque in proper sequence.
+     * The elements will be returned in order from first (head) to last (tail).
+     *
+     * @return an iterator over the elements in this deque in proper sequence
+     */
+    Iterator<E> iterator();
+
+    /**
+     * Returns an iterator over the elements in this deque in reverse
+     * sequential order.  The elements will be returned in order from
+     * last (tail) to first (head).
+     *
+     * @return an iterator over the elements in this deque in reverse
+     * sequence
      */
     Iterator<E> descendingIterator();
-}
\ No newline at end of file
+
+}
diff --git a/luni/src/main/java/java/util/EnumMap.java b/luni/src/main/java/java/util/EnumMap.java
index 2ccaff1..36ff03a 100644
--- a/luni/src/main/java/java/util/EnumMap.java
+++ b/luni/src/main/java/java/util/EnumMap.java
@@ -769,9 +769,7 @@
         if (null == key) {
             throw new NullPointerException();
         }
-        if (!isValidKeyType(key)) {
-            throw new ClassCastException();
-        }
+        keyType.cast(key); // Called to throw ClassCastException.
         int keyOrdinal = key.ordinal();
         if (!hasMapping[keyOrdinal]) {
             hasMapping[keyOrdinal] = true;
diff --git a/luni/src/main/java/java/util/EnumSet.java b/luni/src/main/java/java/util/EnumSet.java
index 48bfd3a..1b4723e 100644
--- a/luni/src/main/java/java/util/EnumSet.java
+++ b/luni/src/main/java/java/util/EnumSet.java
@@ -55,7 +55,7 @@
      */
     public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
         if (!elementType.isEnum()) {
-            throw new ClassCastException();
+            throw new ClassCastException(elementType.getClass().getName() + " is not an Enum");
         }
         // BEGIN android-changed
         E[] enums = SpecialAccess.LANG.getEnumValuesInOrder(elementType);
diff --git a/luni/src/main/java/java/util/HugeEnumSet.java b/luni/src/main/java/java/util/HugeEnumSet.java
index ef90d8e..2c6395f 100644
--- a/luni/src/main/java/java/util/HugeEnumSet.java
+++ b/luni/src/main/java/java/util/HugeEnumSet.java
@@ -121,10 +121,7 @@
 
     @Override
     public boolean add(E element) {
-        if (!isValidType(element.getDeclaringClass())) {
-            throw new ClassCastException();
-        }
-
+        elementClass.cast(element); // Called to throw ClassCastException.
         int ordinal = element.ordinal();
         int index = ordinal / BIT_IN_LONG;
         int inBits = ordinal % BIT_IN_LONG;
@@ -146,9 +143,7 @@
 
         if (collection instanceof EnumSet) {
             EnumSet<?> set = (EnumSet) collection; // raw type due to javac bug 6548436
-            if (!isValidType(set.elementClass)) {
-                throw new ClassCastException();
-            }
+            set.elementClass.asSubclass(elementClass); // Called to throw ClassCastException.
 
             HugeEnumSet<E> hugeSet = (HugeEnumSet<E>) set;
             boolean changed = false;
diff --git a/luni/src/main/java/java/util/IdentityHashMap.java b/luni/src/main/java/java/util/IdentityHashMap.java
index 53d3f09..827ffcf 100644
--- a/luni/src/main/java/java/util/IdentityHashMap.java
+++ b/luni/src/main/java/java/util/IdentityHashMap.java
@@ -83,8 +83,11 @@
     private static final Object NULL_OBJECT = new Object();  //$NON-LOCK-1$
 
     static class IdentityHashMapEntry<K, V> extends MapEntry<K, V> {
-        IdentityHashMapEntry(K theKey, V theValue) {
+        private final IdentityHashMap<K,V> map;
+
+        IdentityHashMapEntry(IdentityHashMap<K,V> map, K theKey, V theValue) {
             super(theKey, theValue);
+            this.map = map;
         }
 
         @Override
@@ -114,6 +117,13 @@
         public String toString() {
             return key + "=" + value;
         }
+
+        @Override
+        public V setValue(V object) {
+            V result = super.setValue(object);
+            map.put(key, object);
+            return result;
+        }
     }
 
     static class IdentityHashMapIterator<E, KT, VT> implements Iterator<E> {
@@ -407,7 +417,7 @@
             value = null;
         }
 
-        return new IdentityHashMapEntry<K, V>((K) key, (V) value);
+        return new IdentityHashMapEntry<K, V>(this, (K) key, (V) value);
     }
 
     /**
diff --git a/luni/src/main/java/java/util/MiniEnumSet.java b/luni/src/main/java/java/util/MiniEnumSet.java
index e19612a..c4c7364 100644
--- a/luni/src/main/java/java/util/MiniEnumSet.java
+++ b/luni/src/main/java/java/util/MiniEnumSet.java
@@ -108,10 +108,7 @@
 
     @Override
     public boolean add(E element) {
-        if (!isValidType(element.getDeclaringClass())) {
-            throw new ClassCastException();
-        }
-
+        elementClass.cast(element); // Called to throw ClassCastException.
         long oldBits = bits;
         long newBits = oldBits | (1L << element.ordinal());
         if (oldBits != newBits) {
@@ -129,9 +126,7 @@
         }
         if (collection instanceof EnumSet) {
             EnumSet<?> set = (EnumSet) collection; // raw type due to javac bug 6548436
-            if (!isValidType(set.elementClass)) {
-                throw new ClassCastException();
-            }
+            set.elementClass.asSubclass(elementClass); // Called to throw ClassCastException.
 
             MiniEnumSet<?> miniSet = (MiniEnumSet<?>) set;
             long oldBits = bits;
diff --git a/luni/src/main/java/java/util/NavigableMap.java b/luni/src/main/java/java/util/NavigableMap.java
index a87a8c0..29961c8 100644
--- a/luni/src/main/java/java/util/NavigableMap.java
+++ b/luni/src/main/java/java/util/NavigableMap.java
@@ -1,262 +1,396 @@
-/* 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.
+/*
+ * Written by Doug Lea and Josh Bloch with assistance from members of JCP
+ * JSR-166 Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/licenses/publicdomain
  */
 
 package java.util;
 
+// BEGIN android-note
+// removed link to collections framework docs
+// changed {@link #subMap(Object)} to {@link #subMap} to satisfy DroidDoc
+// END android-note
+
 /**
- * NavigableMap is a SortedMap with navigation methods answering the closest
- * matches for specified item.
+ * A {@link SortedMap} extended with navigation methods returning the
+ * closest matches for given search targets. Methods
+ * {@code lowerEntry}, {@code floorEntry}, {@code ceilingEntry},
+ * and {@code higherEntry} return {@code Map.Entry} objects
+ * associated with keys respectively less than, less than or equal,
+ * greater than or equal, and greater than a given key, returning
+ * {@code null} if there is no such key.  Similarly, methods
+ * {@code lowerKey}, {@code floorKey}, {@code ceilingKey}, and
+ * {@code higherKey} return only the associated keys. All of these
+ * methods are designed for locating, not traversing entries.
  *
- * @param <K>
- *            the type of key
- * @param <V>
- *            the type of value
+ * <p>A {@code NavigableMap} may be accessed and traversed in either
+ * ascending or descending key order.  The {@code descendingMap}
+ * method returns a view of the map with the senses of all relational
+ * and directional methods inverted. The performance of ascending
+ * operations and views is likely to be faster than that of descending
+ * ones.  Methods {@code subMap}, {@code headMap},
+ * and {@code tailMap} differ from the like-named {@code
+ * SortedMap} methods in accepting additional arguments describing
+ * whether lower and upper bounds are inclusive versus exclusive.
+ * Submaps of any {@code NavigableMap} must implement the {@code
+ * NavigableMap} interface.
+ *
+ * <p>This interface additionally defines methods {@code firstEntry},
+ * {@code pollFirstEntry}, {@code lastEntry}, and
+ * {@code pollLastEntry} that return and/or remove the least and
+ * greatest mappings, if any exist, else returning {@code null}.
+ *
+ * <p>Implementations of entry-returning methods are expected to
+ * return {@code Map.Entry} pairs representing snapshots of mappings
+ * at the time they were produced, and thus generally do <em>not</em>
+ * support the optional {@code Entry.setValue} method. Note however
+ * that it is possible to change mappings in the associated map using
+ * method {@code put}.
+ *
+ * <p>Methods
+ * {@link #subMap subMap(K, K)},
+ * {@link #headMap headMap(K)}, and
+ * {@link #tailMap tailMap(K)}
+ * are specified to return {@code SortedMap} to allow existing
+ * implementations of {@code SortedMap} to be compatibly retrofitted to
+ * implement {@code NavigableMap}, but extensions and implementations
+ * of this interface are encouraged to override these methods to return
+ * {@code NavigableMap}.  Similarly,
+ * {@link #keySet()} can be overriden to return {@code NavigableSet}.
+ *
+ * @author Doug Lea
+ * @author Josh Bloch
+ * @param <K> the type of keys maintained by this map
+ * @param <V> the type of mapped values
  * @since 1.6
  */
-public interface NavigableMap<K, V> extends SortedMap<K, V> {
+public interface NavigableMap<K,V> extends SortedMap<K,V> {
     /**
-     * Returns the entry with the smallest key, or null if the map is empty.
+     * Returns a key-value mapping associated with the greatest key
+     * strictly less than the given key, or {@code null} if there is
+     * no such key.
      *
-     * @return the entry with the smallest key, or null if the map is empty
+     * @param key the key
+     * @return an entry with the greatest key less than {@code key},
+     *         or {@code null} if there is no such key
+     * @throws ClassCastException if the specified key cannot be compared
+     *         with the keys currently in the map
+     * @throws NullPointerException if the specified key is null
+     *         and this map does not permit null keys
      */
-    Map.Entry<K, V> firstEntry();
+    Map.Entry<K,V> lowerEntry(K key);
 
     /**
-     * Returns the entry with the biggest key, or null if the map is empty.
+     * Returns the greatest key strictly less than the given key, or
+     * {@code null} if there is no such key.
      *
-     * @return the entry with the biggest key, or null if the map is empty
-     */
-    Map.Entry<K, V> lastEntry();
-
-    /**
-     * Deletes and returns the entry with the smallest key, or null if the map
-     * is empty.
-     *
-     * @return the entry with the smallest key, or null if the map is empty
-     */
-    Map.Entry<K, V> pollFirstEntry();
-
-    /**
-     * Deletes and returns the entry with the biggest key, or null if the map is
-     * empty.
-     *
-     * @return the entry with the biggest key, or null if the map is empty
-     */
-    Map.Entry<K, V> pollLastEntry();
-
-    /**
-     * Returns an entry related with the smallest key greater than or equal to
-     * the specified key, or null if no such key.
-     *
-     * @param key
-     *            the key
-     * @return the entry, or null if no such key
-     * @throws ClassCastException
-     *             if the key cannot be compared with the keys in the map
-     * @throws NullPointerException
-     *             if the key is null and the map can not contain null key
-     */
-    Map.Entry<K, V> ceilingEntry(K key);
-
-    /**
-     * Returns the smallest key greater than or equal to the specified key, or
-     * null if no such key.
-     *
-     * @param key
-     *            the key
-     * @return the smallest key greater than or equal to key, or null if no such
-     *         key
-     * @throws ClassCastException
-     *             if the key cannot be compared with the keys in the map
-     * @throws NullPointerException
-     *             if the key is null and the map can not contain null key
-     */
-    K ceilingKey(K key);
-
-    /**
-     * Returns an entry related with the smallest key greater than the specified
-     * key, or null if no such key.
-     *
-     * @param key
-     *            the key
-     * @return the entry, or null if no such key
-     * @throws ClassCastException
-     *             if the key cannot be compared with the keys in the map
-     * @throws NullPointerException
-     *             if the key is null and the map can not contain null key
-     */
-    Map.Entry<K, V> higherEntry(K key);
-
-    /**
-     * Returns the smallest key greater than the specified key, or null if no
-     * such key.
-     *
-     * @param key
-     *            the key
-     * @return the smallest key greater than key, or null if no such key
-     * @throws ClassCastException
-     *             if the key cannot be compared with the keys in the map
-     * @throws NullPointerException
-     *             if the key is null and the map can not contain null key
-     */
-    K higherKey(K key);
-
-    /**
-     * Returns an entry related with the biggest key less than or equal to the
-     * specified key, or null if no such key.
-     *
-     * @param key
-     *            the key
-     * @return the entry, or null if no such key
-     * @throws ClassCastException
-     *             if the key cannot be compared with the keys in the map
-     * @throws NullPointerException
-     *             if the key is null and the map can not contain null key
-     */
-    Map.Entry<K, V> floorEntry(K key);
-
-    /**
-     * Returns the biggest key less than or equal to the specified key, or null
-     * if no such key.
-     *
-     * @param key
-     *            the key
-     * @return the biggest key less than or equal to key, or null if no such key
-     * @throws ClassCastException
-     *             if the key cannot be compared with the keys in the map
-     * @throws NullPointerException
-     *             if the key is null and the map can not contain null key
-     */
-    K floorKey(K key);
-
-    /**
-     * Returns an entry related with the biggest key less than the specified
-     * key, or null if no such key.
-     *
-     * @param key
-     *            the key
-     * @return the entry, or null if no such key
-     * @throws ClassCastException
-     *             if the key cannot be compared with the keys in the map
-     * @throws NullPointerException
-     *             if the key is null and the map can not contain null key
-     */
-    Map.Entry<K, V> lowerEntry(K key);
-
-    /**
-     * Returns the biggest key less than the specified key, or null if no such
-     * key.
-     *
-     * @param key
-     *            the key
-     * @return the biggest key less than key, or null if no such key
-     * @throws ClassCastException
-     *             if the key cannot be compared with the keys in the map
-     * @throws NullPointerException
-     *             if the key is null and the map can not contain null key
+     * @param key the key
+     * @return the greatest key less than {@code key},
+     *         or {@code null} if there is no such key
+     * @throws ClassCastException if the specified key cannot be compared
+     *         with the keys currently in the map
+     * @throws NullPointerException if the specified key is null
+     *         and this map does not permit null keys
      */
     K lowerKey(K key);
 
     /**
-     * Returns a NavigableSet view of the keys in ascending order.
+     * Returns a key-value mapping associated with the greatest key
+     * less than or equal to the given key, or {@code null} if there
+     * is no such key.
      *
-     * @return the navigable set view
+     * @param key the key
+     * @return an entry with the greatest key less than or equal to
+     *         {@code key}, or {@code null} if there is no such key
+     * @throws ClassCastException if the specified key cannot be compared
+     *         with the keys currently in the map
+     * @throws NullPointerException if the specified key is null
+     *         and this map does not permit null keys
+     */
+    Map.Entry<K,V> floorEntry(K key);
+
+    /**
+     * Returns the greatest key less than or equal to the given key,
+     * or {@code null} if there is no such key.
+     *
+     * @param key the key
+     * @return the greatest key less than or equal to {@code key},
+     *         or {@code null} if there is no such key
+     * @throws ClassCastException if the specified key cannot be compared
+     *         with the keys currently in the map
+     * @throws NullPointerException if the specified key is null
+     *         and this map does not permit null keys
+     */
+    K floorKey(K key);
+
+    /**
+     * Returns a key-value mapping associated with the least key
+     * greater than or equal to the given key, or {@code null} if
+     * there is no such key.
+     *
+     * @param key the key
+     * @return an entry with the least key greater than or equal to
+     *         {@code key}, or {@code null} if there is no such key
+     * @throws ClassCastException if the specified key cannot be compared
+     *         with the keys currently in the map
+     * @throws NullPointerException if the specified key is null
+     *         and this map does not permit null keys
+     */
+    Map.Entry<K,V> ceilingEntry(K key);
+
+    /**
+     * Returns the least key greater than or equal to the given key,
+     * or {@code null} if there is no such key.
+     *
+     * @param key the key
+     * @return the least key greater than or equal to {@code key},
+     *         or {@code null} if there is no such key
+     * @throws ClassCastException if the specified key cannot be compared
+     *         with the keys currently in the map
+     * @throws NullPointerException if the specified key is null
+     *         and this map does not permit null keys
+     */
+    K ceilingKey(K key);
+
+    /**
+     * Returns a key-value mapping associated with the least key
+     * strictly greater than the given key, or {@code null} if there
+     * is no such key.
+     *
+     * @param key the key
+     * @return an entry with the least key greater than {@code key},
+     *         or {@code null} if there is no such key
+     * @throws ClassCastException if the specified key cannot be compared
+     *         with the keys currently in the map
+     * @throws NullPointerException if the specified key is null
+     *         and this map does not permit null keys
+     */
+    Map.Entry<K,V> higherEntry(K key);
+
+    /**
+     * Returns the least key strictly greater than the given key, or
+     * {@code null} if there is no such key.
+     *
+     * @param key the key
+     * @return the least key greater than {@code key},
+     *         or {@code null} if there is no such key
+     * @throws ClassCastException if the specified key cannot be compared
+     *         with the keys currently in the map
+     * @throws NullPointerException if the specified key is null
+     *         and this map does not permit null keys
+     */
+    K higherKey(K key);
+
+    /**
+     * Returns a key-value mapping associated with the least
+     * key in this map, or {@code null} if the map is empty.
+     *
+     * @return an entry with the least key,
+     *         or {@code null} if this map is empty
+     */
+    Map.Entry<K,V> firstEntry();
+
+    /**
+     * Returns a key-value mapping associated with the greatest
+     * key in this map, or {@code null} if the map is empty.
+     *
+     * @return an entry with the greatest key,
+     *         or {@code null} if this map is empty
+     */
+    Map.Entry<K,V> lastEntry();
+
+    /**
+     * Removes and returns a key-value mapping associated with
+     * the least key in this map, or {@code null} if the map is empty.
+     *
+     * @return the removed first entry of this map,
+     *         or {@code null} if this map is empty
+     */
+    Map.Entry<K,V> pollFirstEntry();
+
+    /**
+     * Removes and returns a key-value mapping associated with
+     * the greatest key in this map, or {@code null} if the map is empty.
+     *
+     * @return the removed last entry of this map,
+     *         or {@code null} if this map is empty
+     */
+    Map.Entry<K,V> pollLastEntry();
+
+    /**
+     * Returns a reverse order view of the mappings contained in this map.
+     * The descending map is backed by this map, so changes to the map are
+     * reflected in the descending map, and vice-versa.  If either map is
+     * modified while an iteration over a collection view of either map
+     * is in progress (except through the iterator's own {@code remove}
+     * operation), the results of the iteration are undefined.
+     *
+     * <p>The returned map has an ordering equivalent to
+     * <tt>{@link Collections#reverseOrder(Comparator) Collections.reverseOrder}(comparator())</tt>.
+     * The expression {@code m.descendingMap().descendingMap()} returns a
+     * view of {@code m} essentially equivalent to {@code m}.
+     *
+     * @return a reverse order view of this map
+     */
+    NavigableMap<K,V> descendingMap();
+
+    /**
+     * Returns a {@link NavigableSet} view of the keys contained in this map.
+     * The set's iterator returns the keys in ascending order.
+     * The set is backed by the map, so changes to the map are reflected in
+     * the set, and vice-versa.  If the map is modified while an iteration
+     * over the set is in progress (except through the iterator's own {@code
+     * remove} operation), the results of the iteration are undefined.  The
+     * set supports element removal, which removes the corresponding mapping
+     * from the map, via the {@code Iterator.remove}, {@code Set.remove},
+     * {@code removeAll}, {@code retainAll}, and {@code clear} operations.
+     * It does not support the {@code add} or {@code addAll} operations.
+     *
+     * @return a navigable set view of the keys in this map
      */
     NavigableSet<K> navigableKeySet();
 
     /**
-     * Returns a reverse order view of the map.
+     * Returns a reverse order {@link NavigableSet} view of the keys contained in this map.
+     * The set's iterator returns the keys in descending order.
+     * The set is backed by the map, so changes to the map are reflected in
+     * the set, and vice-versa.  If the map is modified while an iteration
+     * over the set is in progress (except through the iterator's own {@code
+     * remove} operation), the results of the iteration are undefined.  The
+     * set supports element removal, which removes the corresponding mapping
+     * from the map, via the {@code Iterator.remove}, {@code Set.remove},
+     * {@code removeAll}, {@code retainAll}, and {@code clear} operations.
+     * It does not support the {@code add} or {@code addAll} operations.
      *
-     * @return the reverse order view of the map
-     */
-    NavigableMap<K, V> descendingMap();
-
-    /**
-     * Returns a NavigableSet view of the keys in descending order.
-     *
-     * @return the navigable set view
+     * @return a reverse order navigable set view of the keys in this map
      */
     NavigableSet<K> descendingKeySet();
 
     /**
-     * Returns a view of part of the map whose keys is from startKey to endKey.
+     * Returns a view of the portion of this map whose keys range from
+     * {@code fromKey} to {@code toKey}.  If {@code fromKey} and
+     * {@code toKey} are equal, the returned map is empty unless
+     * {@code fromExclusive} and {@code toExclusive} are both true.  The
+     * returned map is backed by this map, so changes in the returned map are
+     * reflected in this map, and vice-versa.  The returned map supports all
+     * optional map operations that this map supports.
      *
-     * @param startKey
-     *            the start key
-     * @param startInclusive
-     *            true if the start key is in the returned map
-     * @param endKey
-     *            the end key
-     * @param endInclusive
-     *            true if the end key is in the returned map
-     * @return the sub-map view
+     * <p>The returned map will throw an {@code IllegalArgumentException}
+     * on an attempt to insert a key outside of its range, or to construct a
+     * submap either of whose endpoints lie outside its range.
      *
-     * @exception ClassCastException
-     *                when the class of the start or end key is inappropriate
-     *                for this SubMap
-     * @exception NullPointerException
-     *                when the start or end key is null and this SortedMap does
-     *                not support null keys
-     * @exception IllegalArgumentException
-     *                when the start key is greater than the end key
+     * @param fromKey low endpoint of the keys in the returned map
+     * @param fromInclusive {@code true} if the low endpoint
+     *        is to be included in the returned view
+     * @param toKey high endpoint of the keys in the returned map
+     * @param toInclusive {@code true} if the high endpoint
+     *        is to be included in the returned view
+     * @return a view of the portion of this map whose keys range from
+     *         {@code fromKey} to {@code toKey}
+     * @throws ClassCastException if {@code fromKey} and {@code toKey}
+     *         cannot be compared to one another using this map's comparator
+     *         (or, if the map has no comparator, using natural ordering).
+     *         Implementations may, but are not required to, throw this
+     *         exception if {@code fromKey} or {@code toKey}
+     *         cannot be compared to keys currently in the map.
+     * @throws NullPointerException if {@code fromKey} or {@code toKey}
+     *         is null and this map does not permit null keys
+     * @throws IllegalArgumentException if {@code fromKey} is greater than
+     *         {@code toKey}; or if this map itself has a restricted
+     *         range, and {@code fromKey} or {@code toKey} lies
+     *         outside the bounds of the range
      */
-    NavigableMap<K, V> subMap(K startKey, boolean startInclusive, K endKey,
-            boolean endInclusive);
+    NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive,
+                             K toKey,   boolean toInclusive);
 
     /**
-     * Returns a view of the head of the map whose keys are smaller than (or
-     * equal to, depends on inclusive argument) endKey.
+     * Returns a view of the portion of this map whose keys are less than (or
+     * equal to, if {@code inclusive} is true) {@code toKey}.  The returned
+     * map is backed by this map, so changes in the returned map are reflected
+     * in this map, and vice-versa.  The returned map supports all optional
+     * map operations that this map supports.
      *
-     * @param endKey
-     *            the end key
-     * @param inclusive
-     *            true if the end key is in the returned map
-     * @return the head-map view
+     * <p>The returned map will throw an {@code IllegalArgumentException}
+     * on an attempt to insert a key outside its range.
      *
-     * @exception ClassCastException
-     *                when the class of the end key is inappropriate for this
-     *                SubMap
-     * @exception NullPointerException
-     *                when the end key is null and this SortedMap does not
-     *                support null keys
-     * @exception IllegalArgumentException
-     *                when the map is range-limited and end key is out of the
-     *                range of the map
+     * @param toKey high endpoint of the keys in the returned map
+     * @param inclusive {@code true} if the high endpoint
+     *        is to be included in the returned view
+     * @return a view of the portion of this map whose keys are less than
+     *         (or equal to, if {@code inclusive} is true) {@code toKey}
+     * @throws ClassCastException if {@code toKey} is not compatible
+     *         with this map's comparator (or, if the map has no comparator,
+     *         if {@code toKey} does not implement {@link Comparable}).
+     *         Implementations may, but are not required to, throw this
+     *         exception if {@code toKey} cannot be compared to keys
+     *         currently in the map.
+     * @throws NullPointerException if {@code toKey} is null
+     *         and this map does not permit null keys
+     * @throws IllegalArgumentException if this map itself has a
+     *         restricted range, and {@code toKey} lies outside the
+     *         bounds of the range
      */
-    NavigableMap<K, V> headMap(K endKey, boolean inclusive);
+    NavigableMap<K,V> headMap(K toKey, boolean inclusive);
 
     /**
-     * Returns a view of the tail of the map whose keys are bigger than (or
-     * equal to, depends on inclusive argument) startKey.
+     * Returns a view of the portion of this map whose keys are greater than (or
+     * equal to, if {@code inclusive} is true) {@code fromKey}.  The returned
+     * map is backed by this map, so changes in the returned map are reflected
+     * in this map, and vice-versa.  The returned map supports all optional
+     * map operations that this map supports.
      *
-     * @param startKey
-     *            the start key
-     * @param inclusive
-     *            true if the start key is in the returned map
-     * @return the tail-map view
+     * <p>The returned map will throw an {@code IllegalArgumentException}
+     * on an attempt to insert a key outside its range.
      *
-     * @exception ClassCastException
-     *                when the class of the start key is inappropriate for this
-     *                SubMap
-     * @exception NullPointerException
-     *                when the start key is null and this SortedMap does not
-     *                support null keys
-     * @exception IllegalArgumentException
-     *                when the map is range-limited and start key is out of the
-     *                range of the map
+     * @param fromKey low endpoint of the keys in the returned map
+     * @param inclusive {@code true} if the low endpoint
+     *        is to be included in the returned view
+     * @return a view of the portion of this map whose keys are greater than
+     *         (or equal to, if {@code inclusive} is true) {@code fromKey}
+     * @throws ClassCastException if {@code fromKey} is not compatible
+     *         with this map's comparator (or, if the map has no comparator,
+     *         if {@code fromKey} does not implement {@link Comparable}).
+     *         Implementations may, but are not required to, throw this
+     *         exception if {@code fromKey} cannot be compared to keys
+     *         currently in the map.
+     * @throws NullPointerException if {@code fromKey} is null
+     *         and this map does not permit null keys
+     * @throws IllegalArgumentException if this map itself has a
+     *         restricted range, and {@code fromKey} lies outside the
+     *         bounds of the range
      */
-    NavigableMap<K, V> tailMap(K startKey, boolean inclusive);
-}
\ No newline at end of file
+    NavigableMap<K,V> tailMap(K fromKey, boolean inclusive);
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Equivalent to {@code subMap(fromKey, true, toKey, false)}.
+     *
+     * @throws ClassCastException       {@inheritDoc}
+     * @throws NullPointerException     {@inheritDoc}
+     * @throws IllegalArgumentException {@inheritDoc}
+     */
+    SortedMap<K,V> subMap(K fromKey, K toKey);
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Equivalent to {@code headMap(toKey, false)}.
+     *
+     * @throws ClassCastException       {@inheritDoc}
+     * @throws NullPointerException     {@inheritDoc}
+     * @throws IllegalArgumentException {@inheritDoc}
+     */
+    SortedMap<K,V> headMap(K toKey);
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Equivalent to {@code tailMap(fromKey, true)}.
+     *
+     * @throws ClassCastException       {@inheritDoc}
+     * @throws NullPointerException     {@inheritDoc}
+     * @throws IllegalArgumentException {@inheritDoc}
+     */
+    SortedMap<K,V> tailMap(K fromKey);
+}
diff --git a/luni/src/main/java/java/util/NavigableSet.java b/luni/src/main/java/java/util/NavigableSet.java
index ae94b77..cff0800 100644
--- a/luni/src/main/java/java/util/NavigableSet.java
+++ b/luni/src/main/java/java/util/NavigableSet.java
@@ -1,192 +1,291 @@
 /*
- *  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.
+ * Written by Doug Lea and Josh Bloch with assistance from members of JCP
+ * JSR-166 Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/licenses/publicdomain
  */
 
 package java.util;
 
+// BEGIN android-note
+// removed link to collections framework docs
+// changed {@link #subSet(Object)} to {@link #subSet} to satisfy DroidDoc
+// END android-note
+
 /**
- * NavigableSet is a SortedSet with navigation methods answering the closest
- * matches for specified item.
+ * A {@link SortedSet} extended with navigation methods reporting
+ * closest matches for given search targets. Methods {@code lower},
+ * {@code floor}, {@code ceiling}, and {@code higher} return elements
+ * respectively less than, less than or equal, greater than or equal,
+ * and greater than a given element, returning {@code null} if there
+ * is no such element.  A {@code NavigableSet} may be accessed and
+ * traversed in either ascending or descending order.  The {@code
+ * descendingSet} method returns a view of the set with the senses of
+ * all relational and directional methods inverted. The performance of
+ * ascending operations and views is likely to be faster than that of
+ * descending ones.  This interface additionally defines methods
+ * {@code pollFirst} and {@code pollLast} that return and remove the
+ * lowest and highest element, if one exists, else returning {@code
+ * null}.  Methods {@code subSet}, {@code headSet},
+ * and {@code tailSet} differ from the like-named {@code
+ * SortedSet} methods in accepting additional arguments describing
+ * whether lower and upper bounds are inclusive versus exclusive.
+ * Subsets of any {@code NavigableSet} must implement the {@code
+ * NavigableSet} interface.
  *
- * @param <E>
- *            the type of element
+ * <p> The return values of navigation methods may be ambiguous in
+ * implementations that permit {@code null} elements. However, even
+ * in this case the result can be disambiguated by checking
+ * {@code contains(null)}. To avoid such issues, implementations of
+ * this interface are encouraged to <em>not</em> permit insertion of
+ * {@code null} elements. (Note that sorted sets of {@link
+ * Comparable} elements intrinsically do not permit {@code null}.)
+ *
+ * <p>Methods
+ * {@link #subSet subSet(E, E)},
+ * {@link #headSet headSet(E)}, and
+ * {@link #tailSet tailSet(E)}
+ * are specified to return {@code SortedSet} to allow existing
+ * implementations of {@code SortedSet} to be compatibly retrofitted to
+ * implement {@code NavigableSet}, but extensions and implementations
+ * of this interface are encouraged to override these methods to return
+ * {@code NavigableSet}.
+ *
+ * @author Doug Lea
+ * @author Josh Bloch
+ * @param <E> the type of elements maintained by this set
  * @since 1.6
  */
 public interface NavigableSet<E> extends SortedSet<E> {
-
     /**
-     * Deletes and returns the smallest element, or null if the set is empty.
+     * Returns the greatest element in this set strictly less than the
+     * given element, or {@code null} if there is no such element.
      *
-     * @return the smallest element, or null if the set is empty
-     */
-    E pollFirst();
-
-    /**
-     * Deletes and returns the biggest element, or null if the set is empty.
-     *
-     * @return the biggest element, or null if the set is empty
-     */
-    E pollLast();
-
-    /**
-     * Returns the smallest element bigger than the specified one, or null if no
-     * such element.
-     *
-     * @param e
-     *            the specified element
-     * @return the smallest element bigger than the specified one, or null if no
-     *         such element
-     * @throws ClassCastException
-     *             if the element cannot be compared with the ones in the set
-     * @throws NullPointerException
-     *             if the element is null and the set can not contain null
-     */
-    E higher(E e);
-
-    /**
-     * Returns the smallest element bigger than or equal to the specified one,
-     * or null if no such element.
-     *
-     * @param e
-     *            the specified element
-     * @return the smallest element bigger than or equal to the specified one,
-     *         or null if no such element
-     * @throws ClassCastException
-     *             if the element cannot be compared with the ones in the set
-     * @throws NullPointerException
-     *             if the element is null and the set can not contain null
-     */
-    E ceiling(E e);
-
-    /**
-     * Returns the biggest element less than the specified one, or null if no
-     * such element.
-     *
-     * @param e
-     *            the specified element
-     * @return the biggest element less than the specified one, or null if no
-     *         such element
-     * @throws ClassCastException
-     *             if the element cannot be compared with the ones in the set
-     * @throws NullPointerException
-     *             if the element is null and the set can not contain null
+     * @param e the value to match
+     * @return the greatest element less than {@code e},
+     *         or {@code null} if there is no such element
+     * @throws ClassCastException if the specified element cannot be
+     *         compared with the elements currently in the set
+     * @throws NullPointerException if the specified element is null
+     *         and this set does not permit null elements
      */
     E lower(E e);
 
     /**
-     * Returns the biggest element less than or equal to the specified one, or
-     * null if no such element.
+     * Returns the greatest element in this set less than or equal to
+     * the given element, or {@code null} if there is no such element.
      *
-     * @param e
-     *            the specified element
-     * @return the biggest element less than or equal to the specified one, or
-     *         null if no such element
-     * @throws ClassCastException
-     *             if the element cannot be compared with the ones in the set
-     * @throws NullPointerException
-     *             if the element is null and the set can not contain null
+     * @param e the value to match
+     * @return the greatest element less than or equal to {@code e},
+     *         or {@code null} if there is no such element
+     * @throws ClassCastException if the specified element cannot be
+     *         compared with the elements currently in the set
+     * @throws NullPointerException if the specified element is null
+     *         and this set does not permit null elements
      */
     E floor(E e);
 
     /**
-     * Returns a descending iterator of this set.
+     * Returns the least element in this set greater than or equal to
+     * the given element, or {@code null} if there is no such element.
      *
-     * @return the descending iterator
+     * @param e the value to match
+     * @return the least element greater than or equal to {@code e},
+     *         or {@code null} if there is no such element
+     * @throws ClassCastException if the specified element cannot be
+     *         compared with the elements currently in the set
+     * @throws NullPointerException if the specified element is null
+     *         and this set does not permit null elements
      */
-    Iterator<E> descendingIterator();
+    E ceiling(E e);
 
     /**
-     * Returns a reverse order view of this set.
+     * Returns the least element in this set strictly greater than the
+     * given element, or {@code null} if there is no such element.
      *
-     * @return the reverse order view
+     * @param e the value to match
+     * @return the least element greater than {@code e},
+     *         or {@code null} if there is no such element
+     * @throws ClassCastException if the specified element cannot be
+     *         compared with the elements currently in the set
+     * @throws NullPointerException if the specified element is null
+     *         and this set does not permit null elements
+     */
+    E higher(E e);
+
+    /**
+     * Retrieves and removes the first (lowest) element,
+     * or returns {@code null} if this set is empty.
+     *
+     * @return the first element, or {@code null} if this set is empty
+     */
+    E pollFirst();
+
+    /**
+     * Retrieves and removes the last (highest) element,
+     * or returns {@code null} if this set is empty.
+     *
+     * @return the last element, or {@code null} if this set is empty
+     */
+    E pollLast();
+
+    /**
+     * Returns an iterator over the elements in this set, in ascending order.
+     *
+     * @return an iterator over the elements in this set, in ascending order
+     */
+    Iterator<E> iterator();
+
+    /**
+     * Returns a reverse order view of the elements contained in this set.
+     * The descending set is backed by this set, so changes to the set are
+     * reflected in the descending set, and vice-versa.  If either set is
+     * modified while an iteration over either set is in progress (except
+     * through the iterator's own {@code remove} operation), the results of
+     * the iteration are undefined.
+     *
+     * <p>The returned set has an ordering equivalent to
+     * <tt>{@link Collections#reverseOrder(Comparator) Collections.reverseOrder}(comparator())</tt>.
+     * The expression {@code s.descendingSet().descendingSet()} returns a
+     * view of {@code s} essentially equivalent to {@code s}.
+     *
+     * @return a reverse order view of this set
      */
     NavigableSet<E> descendingSet();
 
     /**
-     * Returns a NavigableSet of the specified portion of this set which
-     * contains elements greater (or equal to, depends on startInclusive) the
-     * start element but less than (or equal to, depends on endInclusive) the
-     * end element. The returned NavigableSet is backed by this set so changes
-     * to one are reflected by the other.
+     * Returns an iterator over the elements in this set, in descending order.
+     * Equivalent in effect to {@code descendingSet().iterator()}.
      *
-     * @param start
-     *            the start element
-     * @param startInclusive
-     *            true if the start element is in the returned set
-     * @param end
-     *            the end element
-     * @param endInclusive
-     *            true if the end element is in the returned set
-     * @return the subset
-     *
-     * @throws ClassCastException
-     *             when the start or end object cannot be compared with the
-     *             elements in this set
-     * @throws NullPointerException
-     *             when the start or end object is null and the set cannot
-     *             contain null
-     * @throws IllegalArgumentException
-     *             when the start is bigger than end; or start or end is out of
-     *             range and the set has a range
+     * @return an iterator over the elements in this set, in descending order
      */
-    NavigableSet<E> subSet(E start, boolean startInclusive, E end,
-            boolean endInclusive);
+    Iterator<E> descendingIterator();
 
     /**
-     * Returns a NavigableSet of the specified portion of this set which
-     * contains elements less than (or equal to, depends on endInclusive) the
-     * end element. The returned NavigableSet is backed by this set so changes
-     * to one are reflected by the other.
+     * Returns a view of the portion of this set whose elements range from
+     * {@code fromElement} to {@code toElement}.  If {@code fromElement} and
+     * {@code toElement} are equal, the returned set is empty unless {@code
+     * fromExclusive} and {@code toExclusive} are both true.  The returned set
+     * is backed by this set, so changes in the returned set are reflected in
+     * this set, and vice-versa.  The returned set supports all optional set
+     * operations that this set supports.
      *
-     * @param end
-     *            the end element
-     * @param endInclusive
-     *            true if the end element is in the returned set
-     * @return the subset
+     * <p>The returned set will throw an {@code IllegalArgumentException}
+     * on an attempt to insert an element outside its range.
      *
-     * @throws ClassCastException
-     *             when the end object cannot be compared with the elements in
-     *             this set
-     * @throws NullPointerException
-     *             when the end object is null and the set cannot contain handle
-     *             null
-     * @throws IllegalArgumentException
-     *             when end is out of range and the set has a range
+     * @param fromElement low endpoint of the returned set
+     * @param fromInclusive {@code true} if the low endpoint
+     *        is to be included in the returned view
+     * @param toElement high endpoint of the returned set
+     * @param toInclusive {@code true} if the high endpoint
+     *        is to be included in the returned view
+     * @return a view of the portion of this set whose elements range from
+     *         {@code fromElement}, inclusive, to {@code toElement}, exclusive
+     * @throws ClassCastException if {@code fromElement} and
+     *         {@code toElement} cannot be compared to one another using this
+     *         set's comparator (or, if the set has no comparator, using
+     *         natural ordering).  Implementations may, but are not required
+     *         to, throw this exception if {@code fromElement} or
+     *         {@code toElement} cannot be compared to elements currently in
+     *         the set.
+     * @throws NullPointerException if {@code fromElement} or
+     *         {@code toElement} is null and this set does
+     *         not permit null elements
+     * @throws IllegalArgumentException if {@code fromElement} is
+     *         greater than {@code toElement}; or if this set itself
+     *         has a restricted range, and {@code fromElement} or
+     *         {@code toElement} lies outside the bounds of the range.
      */
-    NavigableSet<E> headSet(E end, boolean endInclusive);
+    NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
+                           E toElement,   boolean toInclusive);
 
     /**
-     * Returns a NavigableSet of the specified portion of this set which
-     * contains elements greater (or equal to, depends on startInclusive) the
-     * start element. The returned NavigableSet is backed by this set so changes
-     * to one are reflected by the other.
+     * Returns a view of the portion of this set whose elements are less than
+     * (or equal to, if {@code inclusive} is true) {@code toElement}.  The
+     * returned set is backed by this set, so changes in the returned set are
+     * reflected in this set, and vice-versa.  The returned set supports all
+     * optional set operations that this set supports.
      *
-     * @param start
-     *            the start element
-     * @param startInclusive
-     *            true if the start element is in the returned set
-     * @return the subset
+     * <p>The returned set will throw an {@code IllegalArgumentException}
+     * on an attempt to insert an element outside its range.
      *
-     * @throws ClassCastException
-     *             when the start object cannot be compared with the elements in
-     *             this set
-     * @throws NullPointerException
-     *             when the start object is null and the set cannot contain null
-     * @throws IllegalArgumentException
-     *             when start is out of range and the set has a range
+     * @param toElement high endpoint of the returned set
+     * @param inclusive {@code true} if the high endpoint
+     *        is to be included in the returned view
+     * @return a view of the portion of this set whose elements are less than
+     *         (or equal to, if {@code inclusive} is true) {@code toElement}
+     * @throws ClassCastException if {@code toElement} is not compatible
+     *         with this set's comparator (or, if the set has no comparator,
+     *         if {@code toElement} does not implement {@link Comparable}).
+     *         Implementations may, but are not required to, throw this
+     *         exception if {@code toElement} cannot be compared to elements
+     *         currently in the set.
+     * @throws NullPointerException if {@code toElement} is null and
+     *         this set does not permit null elements
+     * @throws IllegalArgumentException if this set itself has a
+     *         restricted range, and {@code toElement} lies outside the
+     *         bounds of the range
      */
-    NavigableSet<E> tailSet(E start, boolean startInclusive);
-}
\ No newline at end of file
+    NavigableSet<E> headSet(E toElement, boolean inclusive);
+
+    /**
+     * Returns a view of the portion of this set whose elements are greater
+     * than (or equal to, if {@code inclusive} is true) {@code fromElement}.
+     * The returned set is backed by this set, so changes in the returned set
+     * are reflected in this set, and vice-versa.  The returned set supports
+     * all optional set operations that this set supports.
+     *
+     * <p>The returned set will throw an {@code IllegalArgumentException}
+     * on an attempt to insert an element outside its range.
+     *
+     * @param fromElement low endpoint of the returned set
+     * @param inclusive {@code true} if the low endpoint
+     *        is to be included in the returned view
+     * @return a view of the portion of this set whose elements are greater
+     *         than or equal to {@code fromElement}
+     * @throws ClassCastException if {@code fromElement} is not compatible
+     *         with this set's comparator (or, if the set has no comparator,
+     *         if {@code fromElement} does not implement {@link Comparable}).
+     *         Implementations may, but are not required to, throw this
+     *         exception if {@code fromElement} cannot be compared to elements
+     *         currently in the set.
+     * @throws NullPointerException if {@code fromElement} is null
+     *         and this set does not permit null elements
+     * @throws IllegalArgumentException if this set itself has a
+     *         restricted range, and {@code fromElement} lies outside the
+     *         bounds of the range
+     */
+    NavigableSet<E> tailSet(E fromElement, boolean inclusive);
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Equivalent to {@code subSet(fromElement, true, toElement, false)}.
+     *
+     * @throws ClassCastException       {@inheritDoc}
+     * @throws NullPointerException     {@inheritDoc}
+     * @throws IllegalArgumentException {@inheritDoc}
+     */
+    SortedSet<E> subSet(E fromElement, E toElement);
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Equivalent to {@code headSet(toElement, false)}.
+     *
+     * @throws ClassCastException       {@inheritDoc}
+     * @throws NullPointerException     {@inheritDoc}
+     * @throws IllegalArgumentException {@inheritDoc}
+na     */
+    SortedSet<E> headSet(E toElement);
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>Equivalent to {@code tailSet(fromElement, true)}.
+     *
+     * @throws ClassCastException       {@inheritDoc}
+     * @throws NullPointerException     {@inheritDoc}
+     * @throws IllegalArgumentException {@inheritDoc}
+     */
+    SortedSet<E> tailSet(E fromElement);
+}
diff --git a/luni/src/main/java/java/util/Queue.java b/luni/src/main/java/java/util/Queue.java
index 51899e3..5aef944 100644
--- a/luni/src/main/java/java/util/Queue.java
+++ b/luni/src/main/java/java/util/Queue.java
@@ -1,91 +1,188 @@
 /*
- *  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.
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/licenses/publicdomain
  */
+
 package java.util;
 
+// BEGIN android-note
+// removed link to collections framework docs
+// END android-note
+
 /**
- * This kind of collection provides advanced operations compared to basic
- * collections, such as insertion, extraction, and inspection.
+ * A collection designed for holding elements prior to processing.
+ * Besides basic {@link java.util.Collection Collection} operations,
+ * queues provide additional insertion, extraction, and inspection
+ * operations.  Each of these methods exists in two forms: one throws
+ * an exception if the operation fails, the other returns a special
+ * value (either <tt>null</tt> or <tt>false</tt>, depending on the
+ * operation).  The latter form of the insert operation is designed
+ * specifically for use with capacity-restricted <tt>Queue</tt>
+ * implementations; in most implementations, insert operations cannot
+ * fail.
+ *
  * <p>
- * Generally, a queue orders its elements by means of first-in-first-out.
- * However, a priority queue orders its elements according to a comparator
- * specified or the elements' natural order. Furthermore, a stack orders its
- * elements last-in-first out.
- * <p>
- * A typical queue does not allow {@code null} to be inserted as its element,
- * while some implementations such as {@code LinkedList} allow it. But {@code
- * null} should not be inserted even in these implementations, since the method
- * {@code poll} returns {@code null} to indicate that there is no element left
- * in the queue.
- * <p>
- * {@code Queue} does not provide blocking queue methods, which would block
- * until the operation of the method is allowed. See the
- * {@link java.util.concurrent.BlockingQueue} interface for information about
- * blocking queue methods.
+ * <table BORDER CELLPADDING=3 CELLSPACING=1>
+ *  <tr>
+ *    <td></td>
+ *    <td ALIGN=CENTER><em>Throws exception</em></td>
+ *    <td ALIGN=CENTER><em>Returns special value</em></td>
+ *  </tr>
+ *  <tr>
+ *    <td><b>Insert</b></td>
+ *    <td>{@link #add add(e)}</td>
+ *    <td>{@link #offer offer(e)}</td>
+ *  </tr>
+ *  <tr>
+ *    <td><b>Remove</b></td>
+ *    <td>{@link #remove remove()}</td>
+ *    <td>{@link #poll poll()}</td>
+ *  </tr>
+ *  <tr>
+ *    <td><b>Examine</b></td>
+ *    <td>{@link #element element()}</td>
+ *    <td>{@link #peek peek()}</td>
+ *  </tr>
+ * </table>
+ *
+ * <p>Queues typically, but do not necessarily, order elements in a
+ * FIFO (first-in-first-out) manner.  Among the exceptions are
+ * priority queues, which order elements according to a supplied
+ * comparator, or the elements' natural ordering, and LIFO queues (or
+ * stacks) which order the elements LIFO (last-in-first-out).
+ * Whatever the ordering used, the <em>head</em> of the queue is that
+ * element which would be removed by a call to {@link #remove() } or
+ * {@link #poll()}.  In a FIFO queue, all new elements are inserted at
+ * the <em> tail</em> of the queue. Other kinds of queues may use
+ * different placement rules.  Every <tt>Queue</tt> implementation
+ * must specify its ordering properties.
+ *
+ * <p>The {@link #offer offer} method inserts an element if possible,
+ * otherwise returning <tt>false</tt>.  This differs from the {@link
+ * java.util.Collection#add Collection.add} method, which can fail to
+ * add an element only by throwing an unchecked exception.  The
+ * <tt>offer</tt> method is designed for use when failure is a normal,
+ * rather than exceptional occurrence, for example, in fixed-capacity
+ * (or &quot;bounded&quot;) queues.
+ *
+ * <p>The {@link #remove()} and {@link #poll()} methods remove and
+ * return the head of the queue.
+ * Exactly which element is removed from the queue is a
+ * function of the queue's ordering policy, which differs from
+ * implementation to implementation. The <tt>remove()</tt> and
+ * <tt>poll()</tt> methods differ only in their behavior when the
+ * queue is empty: the <tt>remove()</tt> method throws an exception,
+ * while the <tt>poll()</tt> method returns <tt>null</tt>.
+ *
+ * <p>The {@link #element()} and {@link #peek()} methods return, but do
+ * not remove, the head of the queue.
+ *
+ * <p>The <tt>Queue</tt> interface does not define the <i>blocking queue
+ * methods</i>, which are common in concurrent programming.  These methods,
+ * which wait for elements to appear or for space to become available, are
+ * defined in the {@link java.util.concurrent.BlockingQueue} interface, which
+ * extends this interface.
+ *
+ * <p><tt>Queue</tt> implementations generally do not allow insertion
+ * of <tt>null</tt> elements, although some implementations, such as
+ * {@link LinkedList}, do not prohibit insertion of <tt>null</tt>.
+ * Even in the implementations that permit it, <tt>null</tt> should
+ * not be inserted into a <tt>Queue</tt>, as <tt>null</tt> is also
+ * used as a special return value by the <tt>poll</tt> method to
+ * indicate that the queue contains no elements.
+ *
+ * <p><tt>Queue</tt> implementations generally do not define
+ * element-based versions of methods <tt>equals</tt> and
+ * <tt>hashCode</tt> but instead inherit the identity based versions
+ * from class <tt>Object</tt>, because element-based equality is not
+ * always well-defined for queues with the same elements but different
+ * ordering properties.
+ *
+ * @see java.util.Collection
+ * @see LinkedList
+ * @see PriorityQueue
+ * @see java.util.concurrent.LinkedBlockingQueue
+ * @see java.util.concurrent.BlockingQueue
+ * @see java.util.concurrent.ArrayBlockingQueue
+ * @see java.util.concurrent.LinkedBlockingQueue
+ * @see java.util.concurrent.PriorityBlockingQueue
+ * @since 1.5
+ * @author Doug Lea
+ * @param <E> the type of elements held in this collection
  */
 public interface Queue<E> extends Collection<E> {
+    /**
+     * Inserts the specified element into this queue if it is possible to do so
+     * immediately without violating capacity restrictions, returning
+     * <tt>true</tt> upon success and throwing an <tt>IllegalStateException</tt>
+     * if no space is currently available.
+     *
+     * @param e the element to add
+     * @return <tt>true</tt> (as specified by {@link Collection#add})
+     * @throws IllegalStateException if the element cannot be added at this
+     *         time due to capacity restrictions
+     * @throws ClassCastException if the class of the specified element
+     *         prevents it from being added to this queue
+     * @throws NullPointerException if the specified element is null and
+     *         this queue does not permit null elements
+     * @throws IllegalArgumentException if some property of this element
+     *         prevents it from being added to this queue
+     */
+    boolean add(E e);
 
     /**
-     * Inserts the specified element into the queue provided that the condition
-     * allows such an operation. The method is generally preferable to
-     * {@link Collection#add}, since the latter might throw an exception if the
-     * operation fails.
+     * Inserts the specified element into this queue if it is possible to do
+     * so immediately without violating capacity restrictions.
+     * When using a capacity-restricted queue, this method is generally
+     * preferable to {@link #add}, which can fail to insert an element only
+     * by throwing an exception.
      *
-     * @param o
-     *            the specified element to insert into the queue.
-     * @return {@code true} if the operation succeeds and {@code false} if it
-     *         fails.
+     * @param e the element to add
+     * @return <tt>true</tt> if the element was added to this queue, else
+     *         <tt>false</tt>
+     * @throws ClassCastException if the class of the specified element
+     *         prevents it from being added to this queue
+     * @throws NullPointerException if the specified element is null and
+     *         this queue does not permit null elements
+     * @throws IllegalArgumentException if some property of this element
+     *         prevents it from being added to this queue
      */
-    public boolean offer(E o);
+    boolean offer(E e);
 
     /**
-     * Gets and removes the element at the head of the queue, or returns {@code
-     * null} if there is no element in the queue.
+     * Retrieves and removes the head of this queue.  This method differs
+     * from {@link #poll poll} only in that it throws an exception if this
+     * queue is empty.
      *
-     * @return the element at the head of the queue or {@code null} if there is
-     *         no element in the queue.
+     * @return the head of this queue
+     * @throws NoSuchElementException if this queue is empty
      */
-    public E poll();
+    E remove();
 
     /**
-     * Gets and removes the element at the head of the queue. Throws a
-     * NoSuchElementException if there is no element in the queue.
+     * Retrieves and removes the head of this queue,
+     * or returns <tt>null</tt> if this queue is empty.
      *
-     * @return the element at the head of the queue.
-     * @throws NoSuchElementException
-     *             if there is no element in the queue.
+     * @return the head of this queue, or <tt>null</tt> if this queue is empty
      */
-    public E remove();
+    E poll();
 
     /**
-     * Gets but does not remove the element at the head of the queue.
+     * Retrieves, but does not remove, the head of this queue.  This method
+     * differs from {@link #peek peek} only in that it throws an exception
+     * if this queue is empty.
      *
-     * @return the element at the head of the queue or {@code null} if there is
-     *         no element in the queue.
+     * @return the head of this queue
+     * @throws NoSuchElementException if this queue is empty
      */
-    public E peek();
+    E element();
 
     /**
-     * Gets but does not remove the element at the head of the queue. Throws a
-     * {@code NoSuchElementException} if there is no element in the queue.
+     * Retrieves, but does not remove, the head of this queue,
+     * or returns <tt>null</tt> if this queue is empty.
      *
-     * @return the element at the head of the queue.
-     * @throws NoSuchElementException
-     *             if there is no element in the queue.
+     * @return the head of this queue, or <tt>null</tt> if this queue is empty
      */
-    public E element();
-
+    E peek();
 }
diff --git a/luni/src/main/java/java/util/TreeMap.java b/luni/src/main/java/java/util/TreeMap.java
index ad0f182..59f240c 100644
--- a/luni/src/main/java/java/util/TreeMap.java
+++ b/luni/src/main/java/java/util/TreeMap.java
@@ -251,7 +251,7 @@
     Node<K, V> find(K key, Relation relation) {
         if (root == null) {
             if (comparator == NATURAL_ORDER && !(key instanceof Comparable)) {
-                throw new ClassCastException(key.getClass().getName()); // NullPointerException ok
+                throw new ClassCastException(key.getClass().getName() + " is not Comparable"); // NullPointerException ok
             }
             if (relation == Relation.CREATE) {
                 root = new Node<K, V>(null, key);
diff --git a/luni/src/main/java/java/util/concurrent/SynchronousQueue.java b/luni/src/main/java/java/util/concurrent/SynchronousQueue.java
index 153d449..85fd53e 100644
--- a/luni/src/main/java/java/util/concurrent/SynchronousQueue.java
+++ b/luni/src/main/java/java/util/concurrent/SynchronousQueue.java
@@ -14,6 +14,7 @@
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 import java.util.concurrent.locks.LockSupport;
 import java.util.concurrent.locks.ReentrantLock;
+import libcore.base.EmptyArray;
 
 // BEGIN android-note
 // removed link to collections framework docs
@@ -1011,7 +1012,7 @@
      * @return a zero-length array
      */
     public Object[] toArray() {
-        return new Object[0];
+        return EmptyArray.OBJECT;
     }
 
     /**
diff --git a/luni/src/main/java/java/util/jar/Attributes.java b/luni/src/main/java/java/util/jar/Attributes.java
index a0f6bf4..9041c26 100644
--- a/luni/src/main/java/java/util/jar/Attributes.java
+++ b/luni/src/main/java/java/util/jar/Attributes.java
@@ -387,7 +387,7 @@
      */
     public void putAll(Map<?, ?> attrib) {
         if (attrib == null || !(attrib instanceof Attributes)) {
-            throw new ClassCastException();
+            throw new ClassCastException(attrib.getClass().getName() + " not an Attributes");
         }
         this.map.putAll(attrib);
     }
diff --git a/luni/src/main/java/java/util/jar/JarFile.java b/luni/src/main/java/java/util/jar/JarFile.java
index 0f42d78..ed39f67 100644
--- a/luni/src/main/java/java/util/jar/JarFile.java
+++ b/luni/src/main/java/java/util/jar/JarFile.java
@@ -28,7 +28,6 @@
 import java.util.zip.ZipFile;
 import libcore.base.Streams;
 import org.apache.harmony.archive.util.Util;
-import org.apache.harmony.luni.util.InputStreamHelper;
 
 /**
  * {@code JarFile} is used to read jar entries and their associated data from
@@ -291,8 +290,11 @@
         try {
             InputStream is = super.getInputStream(manifestEntry);
             if (verifier != null) {
-                verifier.addMetaEntry(manifestEntry.getName(),
-                        InputStreamHelper.readFullyAndClose(is));
+                try {
+                    verifier.addMetaEntry(manifestEntry.getName(), Streams.readFully(is));
+                } finally {
+                    is.close();
+                }
                 is = super.getInputStream(manifestEntry);
             }
             try {
@@ -329,8 +331,7 @@
         for (ZipEntry entry : metaEntries) {
             String entryName = entry.getName();
             // Is this the entry for META-INF/MANIFEST.MF ?
-            if (manifestEntry == null
-                    && Util.asciiEqualsIgnoreCase(MANIFEST_NAME, entryName)) {
+            if (manifestEntry == null && Util.asciiEqualsIgnoreCase(MANIFEST_NAME, entryName)) {
                 manifestEntry = entry;
                 // If there is no verifier then we don't need to look any further.
                 if (verifier == null) {
@@ -344,8 +345,11 @@
                                 || Util.asciiEndsWithIgnoreCase(entryName, ".RSA"))) {
                     signed = true;
                     InputStream is = super.getInputStream(entry);
-                    byte[] buf = InputStreamHelper.readFullyAndClose(is);
-                    verifier.addMetaEntry(entryName, buf);
+                    try {
+                        verifier.addMetaEntry(entryName, Streams.readFully(is));
+                    } finally {
+                        is.close();
+                    }
                 }
             }
         }
diff --git a/luni/src/main/java/java/util/jar/Manifest.java b/luni/src/main/java/java/util/jar/Manifest.java
index e7476cf..621626179 100644
--- a/luni/src/main/java/java/util/jar/Manifest.java
+++ b/luni/src/main/java/java/util/jar/Manifest.java
@@ -17,10 +17,12 @@
 
 package java.util.jar;
 
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.lang.reflect.Field;
 import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
 import java.nio.charset.CharsetEncoder;
@@ -30,7 +32,7 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
-import org.apache.harmony.luni.util.InputStreamHelper;
+import libcore.base.Streams;
 
 /**
  * The {@code Manifest} class is used to obtain attribute information for a
@@ -45,6 +47,19 @@
 
     private static final Attributes.Name NAME_ATTRIBUTE = new Attributes.Name("Name");
 
+    private static final Field BAIS_BUF = getByteArrayInputStreamField("buf");
+    private static final Field BAIS_POS = getByteArrayInputStreamField("pos");
+
+    private static Field getByteArrayInputStreamField(String name) {
+        try {
+            Field f = ByteArrayInputStream.class.getDeclaredField(name);
+            f.setAccessible(true);
+            return f;
+        } catch (Exception ex) {
+            throw new AssertionError(ex);
+        }
+    }
+
     private Attributes mainAttributes = new Attributes();
 
     private HashMap<String, Attributes> entries = new HashMap<String, Attributes>();
@@ -185,11 +200,10 @@
      */
     public void read(InputStream is) throws IOException {
         byte[] buf;
-        // Try to read get a reference to the bytes directly
-        try {
-            buf = InputStreamHelper.expose(is);
-        } catch (UnsupportedOperationException uoe) {
-            buf = readFully(is);
+        if (is instanceof ByteArrayInputStream) {
+            buf = exposeByteArrayInputStreamBytes((ByteArrayInputStream) is);
+        } else {
+            buf = Streams.readFully(is);
         }
 
         if (buf.length == 0) {
@@ -211,53 +225,31 @@
         im.initEntries(entries, chunks);
     }
 
-    /*
-     * Helper to read the entire contents of the manifest from the
-     * given input stream.  Usually we can do this in a single read
-     * but we need to account for 'infinite' streams, by ensuring we
-     * have a line feed within a reasonable number of characters.
+    /**
+     * Returns a byte[] containing all the bytes from a ByteArrayInputStream.
+     * Where possible, this returns the actual array rather than a copy.
      */
-    private byte[] readFully(InputStream is) throws IOException {
-        // Initial read
-        byte[] buffer = new byte[4096];
-        int count = is.read(buffer);
-        int nextByte = is.read();
-
-        // Did we get it all in one read?
-        if (nextByte == -1) {
-            return Arrays.copyOf(buffer, count);
-        }
-
-        // Does it look like a manifest?
-        if (!containsLine(buffer, count)) {
-            throw new IOException("Manifest is too long");
-        }
-
-        // Requires additional reads
-        ByteArrayOutputStream baos = new ByteArrayOutputStream(count * 2);
-        baos.write(buffer, 0, count);
-        baos.write(nextByte);
-        while (true) {
-            count = is.read(buffer);
-            if (count == -1) {
-                return baos.toByteArray();
+    private static byte[] exposeByteArrayInputStreamBytes(ByteArrayInputStream bais) {
+        byte[] buffer;
+        synchronized (bais) {
+            byte[] buf;
+            int pos;
+            try {
+                buf = (byte[]) BAIS_BUF.get(bais);
+                pos = BAIS_POS.getInt(bais);
+            } catch (IllegalAccessException iae) {
+                throw new AssertionError(iae);
             }
-            baos.write(buffer, 0, count);
-        }
-    }
-
-    /*
-     * Check to see if the buffer contains a newline or carriage
-     * return character within the first 'length' bytes.  Used to
-     * check the validity of the manifest input stream.
-     */
-    private boolean containsLine(byte[] buffer, int length) {
-        for (int i = 0; i < length; i++) {
-            if (buffer[i] == 0x0A || buffer[i] == 0x0D) {
-                return true;
+            int available = bais.available();
+            if (pos == 0 && buf.length == available) {
+                buffer = buf;
+            } else {
+                buffer = new byte[available];
+                System.arraycopy(buf, pos, buffer, 0, available);
             }
+            bais.skip(available);
         }
-        return false;
+        return buffer;
     }
 
     /**
diff --git a/luni/src/main/java/java/util/prefs/AbstractPreferences.java b/luni/src/main/java/java/util/prefs/AbstractPreferences.java
index 34073a1..be768c5 100644
--- a/luni/src/main/java/java/util/prefs/AbstractPreferences.java
+++ b/luni/src/main/java/java/util/prefs/AbstractPreferences.java
@@ -30,6 +30,7 @@
 import java.util.Map;
 import java.util.TreeSet;
 import org.apache.harmony.luni.util.Base64;
+import libcore.base.EmptyArray;
 
 /**
  * This abstract class is a partial implementation of the abstract class
@@ -439,7 +440,7 @@
             return deflt;
         }
         if (svalue.length() == 0) {
-            return new byte[0];
+            return EmptyArray.BYTE;
         }
         try {
             byte[] bavalue = svalue.getBytes(Charsets.US_ASCII);
diff --git a/luni/src/main/java/java/util/prefs/XMLParser.java b/luni/src/main/java/java/util/prefs/XMLParser.java
index ed1fe0d..4934212 100644
--- a/luni/src/main/java/java/util/prefs/XMLParser.java
+++ b/luni/src/main/java/java/util/prefs/XMLParser.java
@@ -21,10 +21,13 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
+import java.io.Reader;
 import java.io.StringReader;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
@@ -39,6 +42,7 @@
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.FactoryConfigurationError;
 import javax.xml.parsers.ParserConfigurationException;
+import libcore.base.EmptyArray;
 import libcore.io.IoUtils;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
@@ -85,11 +89,6 @@
     static final String DOCTYPE = "<!DOCTYPE preferences SYSTEM";
 
     /*
-     * empty string array constant
-     */
-    private static final String[] EMPTY_SARRAY = new String[0];
-
-    /*
      * Constant - used by FilePreferencesImpl, which is default implementation of Linux platform
      */
     private static final String FILE_PREFS = "<!DOCTYPE map SYSTEM 'http://java.sun.com/dtd/preferences.dtd'>";
@@ -488,14 +487,14 @@
         if (!file.exists()) {
             file.getParentFile().mkdirs();
         } else if (file.canRead()) {
-            InputStream in = null;
+            Reader reader = null;
             FileLock lock = null;
             try {
-                FileInputStream istream = new FileInputStream(file);
-                in = new BufferedInputStream(istream);
-                FileChannel channel = istream.getChannel();
+                FileInputStream fileInputStream = new FileInputStream(file);
+                reader = new InputStreamReader(fileInputStream, "UTF-8");
+                FileChannel channel = fileInputStream.getChannel();
                 lock = channel.lock(0L, Long.MAX_VALUE, true);
-                Document doc = builder.parse(in);
+                Document doc = builder.parse(new InputSource(reader));
                 NodeList entries = selectNodeList(doc.getDocumentElement(), "entry");
                 int length = entries.getLength();
                 for (int i = 0; i < length; i++) {
@@ -509,7 +508,7 @@
             } catch (SAXException e) {
             } finally {
                 releaseQuietly(lock);
-                IoUtils.closeQuietly(in);
+                IoUtils.closeQuietly(reader);
             }
         } else {
             file.delete();
@@ -545,7 +544,7 @@
             out.write(FILE_PREFS);
             out.newLine();
             if (prefs.size() == 0) {
-                exportEntries(EMPTY_SARRAY, EMPTY_SARRAY, out);
+                exportEntries(EmptyArray.STRING, EmptyArray.STRING, out);
             } else {
                 String[] keys = prefs.keySet().toArray(new String[prefs.size()]);
                 int length = keys.length;
diff --git a/luni/src/main/java/java/util/zip/Adler32.java b/luni/src/main/java/java/util/zip/Adler32.java
index 7682ef9..504d272 100644
--- a/luni/src/main/java/java/util/zip/Adler32.java
+++ b/luni/src/main/java/java/util/zip/Adler32.java
@@ -22,7 +22,7 @@
  * of data. Compared to {@link CRC32} it trades reliability for speed.
  * Refer to RFC 1950 for the specification.
  */
-public class Adler32 implements java.util.zip.Checksum {
+public class Adler32 implements Checksum {
 
     private long adler = 1;
 
diff --git a/luni/src/main/java/java/util/zip/CRC32.java b/luni/src/main/java/java/util/zip/CRC32.java
index 8fc6bb7..1529dfe 100644
--- a/luni/src/main/java/java/util/zip/CRC32.java
+++ b/luni/src/main/java/java/util/zip/CRC32.java
@@ -19,9 +19,9 @@
 
 /**
  * The CRC32 class is used to compute a CRC32 checksum from data provided as
- * input value.
+ * input value. See also {@link Adler32} which is almost as good, but cheaper.
  */
-public class CRC32 implements java.util.zip.Checksum {
+public class CRC32 implements Checksum {
 
     private long crc = 0L;
 
diff --git a/luni/src/main/java/java/util/zip/Checksum.java b/luni/src/main/java/java/util/zip/Checksum.java
index fac7e4e..824b85a 100644
--- a/luni/src/main/java/java/util/zip/Checksum.java
+++ b/luni/src/main/java/java/util/zip/Checksum.java
@@ -18,8 +18,7 @@
 package java.util.zip;
 
 /**
- * Holds information about a checksum which was computed with the methods
- * implementing a checksum algorithm.
+ * The interface common to checksum classes such as {@link Adler32} and {@link CRC32}.
  */
 public interface Checksum {
 
diff --git a/luni/src/main/java/java/util/zip/Deflater.java b/luni/src/main/java/java/util/zip/Deflater.java
index 755db4e..4442333 100644
--- a/luni/src/main/java/java/util/zip/Deflater.java
+++ b/luni/src/main/java/java/util/zip/Deflater.java
@@ -18,6 +18,7 @@
 package java.util.zip;
 
 import dalvik.system.CloseGuard;
+import libcore.base.EmptyArray;
 
 /**
  * This class compresses data using the <i>DEFLATE</i> algorithm (see <a
@@ -109,10 +110,6 @@
      */
     private static final int FINISH = 4;
 
-    // A stub buffer used when deflate() called while inputBuffer has not been
-    // set.
-    private static final byte[] STUB_INPUT_BUFFER = new byte[0];
-
     private int flushParm = NO_FLUSH;
 
     private boolean finished;
@@ -237,7 +234,7 @@
             throw new ArrayIndexOutOfBoundsException();
         }
         if (inputBuffer == null) {
-            setInput(STUB_INPUT_BUFFER);
+            setInput(EmptyArray.BYTE);
         }
         return deflateImpl(buf, off, nbytes, streamHandle, flush);
     }
diff --git a/luni/src/main/java/java/util/zip/DeflaterOutputStream.java b/luni/src/main/java/java/util/zip/DeflaterOutputStream.java
index 7cb47db..fcbcb59 100644
--- a/luni/src/main/java/java/util/zip/DeflaterOutputStream.java
+++ b/luni/src/main/java/java/util/zip/DeflaterOutputStream.java
@@ -150,6 +150,7 @@
      */
     @Override
     public void close() throws IOException {
+        // everything closed here should also be closed in ZipOuputStream.close()
         if (!def.finished()) {
             finish();
         }
diff --git a/luni/src/main/java/java/util/zip/GZIPInputStream.java b/luni/src/main/java/java/util/zip/GZIPInputStream.java
index c52bf72..9107164 100644
--- a/luni/src/main/java/java/util/zip/GZIPInputStream.java
+++ b/luni/src/main/java/java/util/zip/GZIPInputStream.java
@@ -27,9 +27,23 @@
  * The {@code GZIPInputStream} class is used to read data stored in the GZIP
  * format, reading and decompressing GZIP data from the underlying stream into
  * its buffer.
+ *
+ * <h3>Example</h3>
+ * <p>Using {@code GZIPInputStream} is easier than {@link ZipInputStream}
+ * because GZIP is only for compression, and is not a container for multiple files.
+ * This code decompresses the data from a GZIP stream, similar to the {@code gunzip(1)} utility.
+ * <pre>
+ * InputStream is = ...
+ * GZIPInputStream zis = new GZIPInputStream(new BufferedInputStream(is));
+ * try {
+ *     // Reading from 'zis' gets you the uncompressed bytes...
+ *     processStream(zis);
+ * } finally {
+ *     zis.close();
+ * }
+ * </pre>
  */
 public class GZIPInputStream extends InflaterInputStream {
-
     private static final int FCOMMENT = 16;
 
     private static final int FEXTRA = 4;
diff --git a/luni/src/main/java/java/util/zip/GZIPOutputStream.java b/luni/src/main/java/java/util/zip/GZIPOutputStream.java
index 02510db..7d30ae8 100644
--- a/luni/src/main/java/java/util/zip/GZIPOutputStream.java
+++ b/luni/src/main/java/java/util/zip/GZIPOutputStream.java
@@ -23,6 +23,21 @@
 /**
  * The {@code GZIPOutputStream} class is used to write data to a stream in the
  * GZIP storage format.
+ *
+ * <h3>Example</h3>
+ * <p>Using {@code GZIPOutputStream} is a little easier than {@link ZipOutputStream}
+ * because GZIP is only for compression, and is not a container for multiple files.
+ * This code creates a GZIP stream, similar to the {@code gzip(1)} utility.
+ * <pre>
+ * OutputStream os = ...
+ * byte[] bytes = ...
+ * GZIPOutputStream zos = new GZIPOutputStream(new BufferedOutputStream(os));
+ * try {
+ *     zos.write(bytes);
+ * } finally {
+ *     zos.close();
+ * }
+ * </pre>
  */
 public class GZIPOutputStream extends DeflaterOutputStream {
 
diff --git a/luni/src/main/java/java/util/zip/ZipInputStream.java b/luni/src/main/java/java/util/zip/ZipInputStream.java
index a502796..bb9f20c 100644
--- a/luni/src/main/java/java/util/zip/ZipInputStream.java
+++ b/luni/src/main/java/java/util/zip/ZipInputStream.java
@@ -30,17 +30,43 @@
 
 /**
  * This class provides an implementation of {@code FilterInputStream} that
- * decompresses data from a <i>ZIP-archive</i> input stream.
- * <p>
- * A <i>ZIP-archive</i> is a collection of compressed (or uncompressed) files -
- * the so called ZIP entries. Therefore when reading from a {@code
- * ZipInputStream} first the entry's attributes will be retrieved with {@code
- * getNextEntry} before its data is read.
- * <p>
- * While {@code InflaterInputStream} can read a compressed <i>ZIP-archive</i>
- * entry, this extension can read uncompressed entries as well.
- * <p>
- * Use {@code ZipFile} if you can access the archive as a file directly.
+ * decompresses data from an {@code InputStream} containing a ZIP archive.
+ *
+ * <p>A ZIP archive is a collection of (possibly) compressed files.
+ * When reading from a {@code ZipInputStream}, you retrieve the
+ * entry's metadata with {@code getNextEntry} before you can read the userdata.
+ *
+ * <p>Although {@code InflaterInputStream} can only read compressed ZIP archive
+ * entries, this class can read non-compressed entries as well.
+ *
+ * <p>Use {@code ZipFile} if you can access the archive as a file directly,
+ * especially if you want random access to entries, rather than needing to
+ * iterate over all entries.
+ *
+ * <h3>Example</h3>
+ * <p>Using {@code ZipInputStream} is a little more complicated than {@link GZIPInputStream}
+ * because ZIP archives are containers that can contain multiple files. This code pulls all the
+ * files out of a ZIP archive, similar to the {@code unzip(1)} utility.
+ * <pre>
+ * InputStream is = ...
+ * ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is));
+ * try {
+ *     ZipEntry ze;
+ *     while ((ze = zis.getNextEntry()) != null) {
+ *         ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ *         byte[] buffer = new byte[1024];
+ *         int count;
+ *         while ((count = zis.read(buffer)) != -1) {
+ *             baos.write(buffer, 0, count);
+ *         }
+ *         String filename = ze.getName();
+ *         byte[] bytes = baos.toByteArray();
+ *         // do something with 'filename' and 'bytes'...
+ *     }
+ * } finally {
+ *     zis.close();
+ * }
+ * </pre>
  *
  * @see ZipEntry
  * @see ZipFile
diff --git a/luni/src/main/java/java/util/zip/ZipOutputStream.java b/luni/src/main/java/java/util/zip/ZipOutputStream.java
index 7586c84..067fc43 100644
--- a/luni/src/main/java/java/util/zip/ZipOutputStream.java
+++ b/luni/src/main/java/java/util/zip/ZipOutputStream.java
@@ -36,11 +36,31 @@
  * href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">file format
  * specification</a>.
  *
+ * <h3>Example</h3>
+ * <p>Using {@code ZipOutputStream} is a little more complicated than {@link GZIPOutputStream}
+ * because ZIP archives are containers that can contain multiple files. This code creates a ZIP
+ * archive containing several files, similar to the {@code zip(1)} utility.
+ * <pre>
+ * OutputStream os = ...
+ * ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(os));
+ * try {
+ *     for (int i = 0; i < fileCount; ++i) {
+ *         String filename = ...
+ *         byte[] bytes = ...
+ *         ZipEntry entry = new ZipEntry(filename);
+ *         zos.putNextEntry(entry);
+ *         zos.write(bytes);
+ *         zos.closeEntry();
+ *     }
+ * } finally {
+ *     zos.close();
+ * }
+ * </pre>
+ *
  * @see ZipEntry
  * @see ZipFile
  */
-public class ZipOutputStream extends DeflaterOutputStream implements
-        ZipConstants {
+public class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
 
     /**
      * Indicates deflated entries.
@@ -92,8 +112,10 @@
      */
     @Override
     public void close() throws IOException {
+        // don't call super.close() because that calls finish() conditionally
         if (out != null) {
             finish();
+            def.end();
             out.close();
             out = null;
         }
diff --git a/luni/src/main/java/javax/crypto/spec/PBEKeySpec.java b/luni/src/main/java/javax/crypto/spec/PBEKeySpec.java
index 1d67f68..c84cdcb 100644
--- a/luni/src/main/java/javax/crypto/spec/PBEKeySpec.java
+++ b/luni/src/main/java/javax/crypto/spec/PBEKeySpec.java
@@ -19,6 +19,7 @@
 
 import java.security.spec.KeySpec;
 import java.util.Arrays;
+import libcore.base.EmptyArray;
 
 /**
  * The key specification for a <i>password based encryption</i> key.
@@ -41,7 +42,7 @@
      */
     public PBEKeySpec(char[] password) {
         if (password == null) {
-            this.password = new char[0];
+            this.password = EmptyArray.CHAR;
         } else {
             this.password = new char[password.length];
             System.arraycopy(password, 0, this.password, 0, password.length);
@@ -85,7 +86,7 @@
         }
 
         if (password == null) {
-            this.password = new char[0];
+            this.password = EmptyArray.CHAR;
         } else {
             this.password = new char[password.length];
             System.arraycopy(password, 0, this.password, 0, password.length);
@@ -123,7 +124,7 @@
         }
 
         if (password == null) {
-            this.password = new char[0];
+            this.password = EmptyArray.CHAR;
         } else {
             this.password = new char[password.length];
             System.arraycopy(password, 0, this.password, 0, password.length);
diff --git a/luni/src/main/java/javax/crypto/spec/PSource.java b/luni/src/main/java/javax/crypto/spec/PSource.java
index a7f592c..5f520f3 100644
--- a/luni/src/main/java/javax/crypto/spec/PSource.java
+++ b/luni/src/main/java/javax/crypto/spec/PSource.java
@@ -17,6 +17,8 @@
 
 package javax.crypto.spec;
 
+import libcore.base.EmptyArray;
+
 /**
  * The source of the label <code>L</code> as specified in <a
  * href="http://www.ietf.org/rfc/rfc3447.txt"> PKCS #1</a>.
@@ -68,7 +70,7 @@
 
         private PSpecified() {
             super("PSpecified");
-            p = new byte[0];
+            p = EmptyArray.BYTE;
         }
 
         /**
diff --git a/luni/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java b/luni/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java
index 3e58897..267648c 100644
--- a/luni/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java
+++ b/luni/src/main/java/javax/net/ssl/DefaultSSLServerSocketFactory.java
@@ -21,6 +21,7 @@
 import java.net.InetAddress;
 import java.net.ServerSocket;
 import java.net.SocketException;
+import libcore.base.EmptyArray;
 
 /**
  * Default inoperative implementation of javax.net.ssl.SSLServerSocketFactory
@@ -35,12 +36,12 @@
 
     @Override
     public String[] getDefaultCipherSuites() {
-        return new String[0];
+        return EmptyArray.STRING;
     }
 
     @Override
     public String[] getSupportedCipherSuites() {
-        return new String[0];
+        return EmptyArray.STRING;
     }
 
     @Override
diff --git a/luni/src/main/java/javax/net/ssl/DefaultSSLSocketFactory.java b/luni/src/main/java/javax/net/ssl/DefaultSSLSocketFactory.java
index a44404fc..5b5025d 100644
--- a/luni/src/main/java/javax/net/ssl/DefaultSSLSocketFactory.java
+++ b/luni/src/main/java/javax/net/ssl/DefaultSSLSocketFactory.java
@@ -22,6 +22,7 @@
 import java.net.Socket;
 import java.net.SocketException;
 import java.net.UnknownHostException;
+import libcore.base.EmptyArray;
 
 /**
  * Default inoperative implementation of javax.net.ssl.SSLSocketFactory
@@ -37,12 +38,12 @@
 
     @Override
     public String[] getDefaultCipherSuites() {
-        return new String[0];
+        return EmptyArray.STRING;
     }
 
     @Override
     public String[] getSupportedCipherSuites() {
-        return new String[0];
+        return EmptyArray.STRING;
     }
 
     @Override
diff --git a/luni/src/main/java/libcore/base/EmptyArray.java b/luni/src/main/java/libcore/base/EmptyArray.java
new file mode 100644
index 0000000..6e97876
--- /dev/null
+++ b/luni/src/main/java/libcore/base/EmptyArray.java
@@ -0,0 +1,31 @@
+/*
+ * 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 libcore.base;
+
+public final class EmptyArray {
+    private EmptyArray() {}
+
+    public static final boolean[] BOOLEAN = new boolean[0];
+    public static final byte[] BYTE = new byte[0];
+    public static final char[] CHAR = new char[0];
+    public static final double[] DOUBLE = new double[0];
+    public static final int[] INT = new int[0];
+
+    public static final Class<?>[] CLASS = new Class[0];
+    public static final Object[] OBJECT = new Object[0];
+    public static final String[] STRING = new String[0];
+}
diff --git a/luni/src/main/java/libcore/base/Streams.java b/luni/src/main/java/libcore/base/Streams.java
index e746389..0ef5189 100644
--- a/luni/src/main/java/libcore/base/Streams.java
+++ b/luni/src/main/java/libcore/base/Streams.java
@@ -16,6 +16,7 @@
 
 package libcore.base;
 
+import java.io.ByteArrayOutputStream;
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
@@ -53,6 +54,22 @@
         }
     }
 
+    /**
+     * Returns a new byte[] containing the entire contents of the given InputStream.
+     * Useful when you don't know in advance how much data there is to be read.
+     */
+    public static byte[] readFully(InputStream in) throws IOException {
+        byte[] buffer = new byte[1024];
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        while (true) {
+            int byteCount = in.read(buffer);
+            if (byteCount == -1) {
+                return bytes.toByteArray();
+            }
+            bytes.write(buffer, 0, byteCount);
+        }
+    }
+
     public static void skipAll(InputStream in) throws IOException {
         do {
             in.skip(Long.MAX_VALUE);
diff --git a/luni/src/main/java/libcore/internal/StringPool.java b/luni/src/main/java/libcore/internal/StringPool.java
new file mode 100644
index 0000000..779a59a
--- /dev/null
+++ b/luni/src/main/java/libcore/internal/StringPool.java
@@ -0,0 +1,52 @@
+/*
+ * 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 libcore.internal;
+
+/**
+ * A pool of string instances. Unlike the {@link String#intern() VM's
+ * interned strings}, this pool provides no guarantee of reference equality.
+ * It is intended only to save allocations. This class is not thread safe.
+ */
+public final class StringPool {
+
+    private final String[] pool = new String[512];
+
+    /**
+     * Returns a string equal to {@code new String(array, start, length)}.
+     */
+    public String get(char[] array, int start, int length) {
+        // Compute an arbitrary hash of the content
+        int hashCode = 0;
+        for (int i = start; i < start + length; i++) {
+            hashCode = (hashCode * 31) + array[i];
+        }
+
+        // Pick a bucket using Doug Lea's supplemental secondaryHash function (from HashMap)
+        hashCode ^= (hashCode >>> 20) ^ (hashCode >>> 12);
+        hashCode ^= (hashCode >>> 7) ^ (hashCode >>> 4);
+        int index = hashCode & (pool.length - 1);
+
+        String pooled = pool[index];
+        if (pooled != null && pooled.contentEquals(array, start, length)) {
+            return pooled;
+        }
+
+        String result = new String(array, start, length);
+        pool[index] = result;
+        return result;
+    }
+}
diff --git a/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java b/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java
index 17d3864..8fa73e0 100644
--- a/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java
+++ b/luni/src/main/java/org/apache/harmony/luni/net/PlainDatagramSocketImpl.java
@@ -29,6 +29,7 @@
 import java.net.SocketAddress;
 import java.net.SocketException;
 import java.net.UnknownHostException;
+import libcore.base.EmptyArray;
 import org.apache.harmony.luni.platform.Platform;
 
 /**
@@ -154,8 +155,7 @@
     @Override
     protected int peek(InetAddress sender) throws IOException {
         // We don't actually want the data: we just want the DatagramPacket's filled-in address.
-        byte[] bytes = new byte[0];
-        DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
+        DatagramPacket packet = new DatagramPacket(EmptyArray.BYTE, 0);
         int result = peekData(packet);
         Platform.NETWORK.setInetAddress(sender, packet.getAddress().getAddress());
         return result;
diff --git a/luni/src/main/java/org/apache/harmony/luni/util/Base64.java b/luni/src/main/java/org/apache/harmony/luni/util/Base64.java
index 52aaa96..3521b2d 100644
--- a/luni/src/main/java/org/apache/harmony/luni/util/Base64.java
+++ b/luni/src/main/java/org/apache/harmony/luni/util/Base64.java
@@ -22,6 +22,7 @@
 package org.apache.harmony.luni.util;
 
 import java.nio.charset.Charset;
+import libcore.base.EmptyArray;
 
 /**
  * This class implements Base64 encoding/decoding functionality
@@ -40,7 +41,7 @@
         int length = len / 4 * 3;
         // return an empty array on empty or short input without padding
         if (length == 0) {
-            return new byte[0];
+            return EmptyArray.BYTE;
         }
         // temporary array
         byte[] out = new byte[length];
diff --git a/luni/src/main/java/org/apache/harmony/luni/util/InputStreamHelper.java b/luni/src/main/java/org/apache/harmony/luni/util/InputStreamHelper.java
deleted file mode 100644
index d1ceba5..0000000
--- a/luni/src/main/java/org/apache/harmony/luni/util/InputStreamHelper.java
+++ /dev/null
@@ -1,191 +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.
- */
-
-package org.apache.harmony.luni.util;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Field;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.Arrays;
-
-/**
- * The class contains static {@link java.io.InputStream} utilities.
- */
-public class InputStreamHelper {
-
-    /**
-     * Provides access to a protected underlying buffer of
-     * <code>ByteArrayInputStream</code>.
-     */
-    private static final Field BAIS_BUF;
-
-    /**
-     * Provides access to a protected position in the underlying buffer of
-     * <code>ByteArrayInputStream</code>.
-     */
-    private static final Field BAIS_POS;
-
-    static {
-        final Field[] f = new Field[2];
-        AccessController.doPrivileged(new PrivilegedAction<Object>() {
-            public Object run() {
-                try {
-                    f[0] = ByteArrayInputStream.class.getDeclaredField("buf");
-                    f[0].setAccessible(true);
-                    f[1] = ByteArrayInputStream.class.getDeclaredField("pos");
-                    f[1].setAccessible(true);
-                } catch (NoSuchFieldException nsfe) {
-                    throw new InternalError(nsfe.getLocalizedMessage());
-                }
-                return null;
-            }
-        });
-        BAIS_BUF = f[0];
-        BAIS_POS = f[1];
-    }
-
-    /**
-     * The extension of <code>ByteArrayInputStream</code> which exposes an
-     * underlying buffer.
-     */
-    static class ExposedByteArrayInputStream extends ByteArrayInputStream {
-        public ExposedByteArrayInputStream(byte[] buf) {
-            super(buf);
-        }
-
-        public ExposedByteArrayInputStream(byte[] buf, int offset, int length) {
-            super(buf, offset, length);
-        }
-
-        /**
-         * Reads the whole stream and returns the stream snapshot.
-         */
-        public synchronized byte[] expose() {
-            if (pos == 0 && count == buf.length) {
-                skip(count);
-                return buf;
-            }
-
-            final int available = available();
-            final byte[] buffer = new byte[available];
-            System.arraycopy(buf, pos, buffer, 0, available);
-            skip(available);
-            return buffer;
-        }
-    }
-
-    /**
-     * Reads all bytes from {@link java.io.ByteArrayInputStream} using its
-     * underlying buffer directly.
-     *
-     * @return an underlying buffer, if a current position is at the buffer
-     *         beginning, and an end position is at the buffer end, or a copy of
-     *         the underlying buffer part.
-     */
-    private static byte[] expose(ByteArrayInputStream bais) {
-        byte[] buffer, buf;
-        int pos;
-        synchronized (bais) {
-            int available = bais.available();
-            try {
-                buf = (byte[]) BAIS_BUF.get(bais);
-                pos = BAIS_POS.getInt(bais);
-            } catch (IllegalAccessException iae) {
-                throw new InternalError(iae.getLocalizedMessage());
-            }
-            if (pos == 0 && available == buf.length) {
-                buffer = buf;
-            } else {
-                buffer = new byte[available];
-                System.arraycopy(buf, pos, buffer, 0, available);
-            }
-            bais.skip(available);
-        }
-        return buffer;
-    }
-
-    /**
-     * The utility method for reading the whole input stream into a snapshot
-     * buffer. To speed up the access it works with an underlying buffer for a
-     * given {@link java.io.ByteArrayInputStream}.
-     *
-     * @param is
-     *            the stream to be read.
-     * @return the snapshot wrapping the buffer where the bytes are read to.
-     * @throws UnsupportedOperationException
-     *             if the input stream data cannot be exposed
-     */
-    public static byte[] expose(InputStream is) throws IOException,
-            UnsupportedOperationException {
-        if (is instanceof ExposedByteArrayInputStream) {
-            return ((ExposedByteArrayInputStream) is).expose();
-        }
-
-        if (is.getClass().equals(ByteArrayInputStream.class)) {
-            return expose((ByteArrayInputStream) is);
-        }
-
-        // We don't know how to do this
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * Reads all the bytes from the given input stream.
-     *
-     * Calls read multiple times on the given input stream until it receives an
-     * end of file marker. Returns the combined results as a byte array. Note
-     * that this method may block if the underlying stream read blocks.
-     *
-     * @param is
-     *            the input stream to be read.
-     * @return the content of the stream as a byte array.
-     * @throws IOException
-     *             if a read error occurs.
-     */
-    public static byte[] readFullyAndClose(InputStream is) throws IOException {
-
-        try {
-            // Initial read
-            byte[] buffer = new byte[1024];
-            int count = is.read(buffer);
-            int nextByte = is.read();
-
-            // Did we get it all in one read?
-            if (nextByte == -1) {
-                return Arrays.copyOf(buffer, count);
-            }
-
-            // Requires additional reads
-            ByteArrayOutputStream baos = new ByteArrayOutputStream(count * 2);
-            baos.write(buffer, 0, count);
-            baos.write(nextByte);
-            while (true) {
-                count = is.read(buffer);
-                if (count == -1) {
-                    return baos.toByteArray();
-                }
-                baos.write(buffer, 0, count);
-            }
-        } finally {
-            is.close();
-        }
-    }
-}
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1PRNG_SecureRandomImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1PRNG_SecureRandomImpl.java
index c07e2b1..1ffd55a 100644
--- a/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1PRNG_SecureRandomImpl.java
+++ b/luni/src/main/java/org/apache/harmony/security/provider/crypto/SHA1PRNG_SecureRandomImpl.java
@@ -24,6 +24,7 @@
 import java.io.Serializable;
 import java.security.InvalidParameterException;
 import java.security.SecureRandomSpi;
+import libcore.base.EmptyArray;
 
 /**
  * This class extends the SecureRandomSpi class implementing all its abstract methods. <BR>
@@ -234,13 +235,12 @@
             throw new NegativeArraySizeException(Integer.toString(numBytes));
         }
         if (numBytes == 0) {
-            return new byte[0];
+            return EmptyArray.BYTE;
         }
 
         if (myRandom == null) {
             myRandom = new SHA1PRNG_SecureRandomImpl();
-            myRandom.engineSetSeed(RandomBitsSupplier
-                    .getRandomBits(DIGEST_LENGTH));
+            myRandom.engineSetSeed(RandomBitsSupplier.getRandomBits(DIGEST_LENGTH));
         }
 
         myBytes = new byte[numBytes];
diff --git a/luni/src/main/java/org/apache/harmony/xml/ExpatPullParser.java b/luni/src/main/java/org/apache/harmony/xml/ExpatPullParser.java
index c48962a..2ddf464 100644
--- a/luni/src/main/java/org/apache/harmony/xml/ExpatPullParser.java
+++ b/luni/src/main/java/org/apache/harmony/xml/ExpatPullParser.java
@@ -763,7 +763,7 @@
             }
 
             if (length == 0) {
-                return;
+                return; // TODO: can't happen?
             }
 
             flush(parser, length);
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHello.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHello.java
index 6942894..b39b9e4 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHello.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientHello.java
@@ -20,6 +20,7 @@
 import java.io.IOException;
 import java.security.SecureRandom;
 import java.util.Arrays;
+import libcore.base.EmptyArray;
 
 /**
  * Represents Client Hello message
@@ -141,7 +142,7 @@
         if (challenge_length < 16) {
             fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect V2ClientHello, short challenge data");
         }
-        session_id = new byte[0];
+        session_id = EmptyArray.BYTE;
         cipher_suites = new CipherSuite[cipher_spec_length/3];
         for (int i = 0; i < cipher_suites.length; i++) {
             byte b0 = (byte) in.read();
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientKeyExchange.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientKeyExchange.java
index 4103cf4..3f2daeb 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientKeyExchange.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/ClientKeyExchange.java
@@ -19,6 +19,7 @@
 
 import java.io.IOException;
 import java.math.BigInteger;
+import libcore.base.EmptyArray;
 
 /**
  * Represents client key exchange message
@@ -79,7 +80,7 @@
      *
      */
     public ClientKeyExchange() {
-        exchange_keys = new byte[0];
+        exchange_keys = EmptyArray.BYTE;
         length = 0;
         isRSA = false;
     }
@@ -97,7 +98,7 @@
         this.isRSA = isRSA;
         if (length == 0) {
             this.length = 0;
-            exchange_keys = new byte[0];
+            exchange_keys = EmptyArray.BYTE;
         } else {
             int size;
             if (isRSA && !isTLS) {// SSL3.0 RSA
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/DigitalSignature.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/DigitalSignature.java
index e0ca33c..a100513 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/DigitalSignature.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/DigitalSignature.java
@@ -30,6 +30,7 @@
 import javax.crypto.IllegalBlockSizeException;
 import javax.crypto.NoSuchPaddingException;
 import javax.net.ssl.SSLException;
+import libcore.base.EmptyArray;
 
 /**
  * This class represents Signature type, as described in TLS v 1.0 Protocol
@@ -190,15 +191,15 @@
             } else if (cipher != null) {
                 return cipher.doFinal();
             }
-            return new byte[0];
+            return EmptyArray.BYTE;
         } catch (DigestException e){
-            return new byte[0];
+            return EmptyArray.BYTE;
         } catch (SignatureException e){
-            return new byte[0];
+            return EmptyArray.BYTE;
         } catch (BadPaddingException e){
-            return new byte[0];
+            return EmptyArray.BYTE;
         } catch (IllegalBlockSizeException e){
-            return new byte[0];
+            return EmptyArray.BYTE;
         }
     }
 
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerFactoryImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerFactoryImpl.java
index 7df12c0..b5ffa06 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerFactoryImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/KeyManagerFactoryImpl.java
@@ -30,6 +30,7 @@
 import javax.net.ssl.KeyManager;
 import javax.net.ssl.KeyManagerFactorySpi;
 import javax.net.ssl.ManagerFactoryParameters;
+import libcore.base.EmptyArray;
 
 /**
  * KeyManagerFactory implementation.
@@ -56,7 +57,7 @@
             if (password != null) {
                 pwd = password.clone();
             } else {
-                pwd = new char[0];
+                pwd = EmptyArray.CHAR;
             }
         } else {
             keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
@@ -85,7 +86,7 @@
                             }
                         });
                 if (keyStorePwd == null) {
-                    pwd = new char[0];
+                    pwd = EmptyArray.CHAR;
                 } else {
                     pwd = keyStorePwd.toCharArray();
                 }
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java
index 08c0822..da974f9 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/Logger.java
@@ -20,6 +20,7 @@
 import java.io.PrintStream;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import libcore.base.EmptyArray;
 
 /**
  * This class provides debug logging for JSSE provider implementation
@@ -106,7 +107,7 @@
                         }
                     });
         } catch (Exception e) {
-            names = new String[0];
+            names = EmptyArray.STRING;
         }
     }
 
@@ -119,4 +120,3 @@
         return null;
     }
 }
-
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
index ee2b33a..40724e7 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java
@@ -32,6 +32,7 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import javax.net.ssl.SSLException;
 
 /**
@@ -247,8 +248,11 @@
         // add(null, "PSK-RC4-SHA");
     }
 
-    private static final String[] SUPPORTED_CIPHER_SUITES
-            = STANDARD_TO_OPENSSL_CIPHER_SUITES.keySet().toArray(new String[0]);
+    private static final String[] SUPPORTED_CIPHER_SUITES;
+    static {
+        Set<String> suites = STANDARD_TO_OPENSSL_CIPHER_SUITES.keySet();
+        SUPPORTED_CIPHER_SUITES = suites.toArray(new String[suites.size()]);
+    }
 
     // SSL mode from ssl.h
     public static long SSL_MODE_HANDSHAKE_CUTTHROUGH = 0x00000040L;
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLServerSocketFactoryImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLServerSocketFactoryImpl.java
index 222ebec..1899342 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLServerSocketFactoryImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLServerSocketFactoryImpl.java
@@ -22,6 +22,7 @@
 import java.net.ServerSocket;
 import java.security.KeyManagementException;
 import javax.net.ssl.SSLServerSocketFactory;
+import libcore.base.EmptyArray;
 
 /**
  * Implementation of SSLServerSocketFactory.
@@ -61,7 +62,7 @@
     @Override
     public String[] getDefaultCipherSuites() {
         if (instantiationException != null) {
-            return new String[0];
+            return EmptyArray.STRING;
         }
         return sslParameters.getEnabledCipherSuites();
     }
@@ -72,7 +73,7 @@
     @Override
     public String[] getSupportedCipherSuites() {
         if (instantiationException != null) {
-            return new String[0];
+            return EmptyArray.STRING;
         }
         return CipherSuite.getSupportedCipherSuiteNames();
     }
@@ -127,4 +128,3 @@
                 (SSLParametersImpl) sslParameters.clone());
     }
 }
-
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java
index 582d316..0bf4007 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSessionImpl.java
@@ -33,6 +33,7 @@
 import javax.net.ssl.SSLSessionBindingEvent;
 import javax.net.ssl.SSLSessionBindingListener;
 import javax.net.ssl.SSLSessionContext;
+import libcore.base.EmptyArray;
 import libcore.base.Objects;
 
 /**
@@ -175,7 +176,7 @@
         lastAccessedTime = creationTime;
         if (cipher_suite == null) {
             this.cipherSuite = CipherSuite.SSL_NULL_WITH_NULL_NULL;
-            id = new byte[0];
+            id = EmptyArray.BYTE;
             isServer = false;
             isValid = false;
         } else {
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java
index 4e185fd..9cd9eeb 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/SSLSocketFactoryImpl.java
@@ -23,6 +23,7 @@
 import java.net.UnknownHostException;
 import java.security.KeyManagementException;
 import javax.net.ssl.SSLSocketFactory;
+import libcore.base.EmptyArray;
 
 /**
  * Implementation of SSLSocketFactory.
@@ -62,7 +63,7 @@
     @Override
     public String[] getDefaultCipherSuites() {
         if (instantiationException != null) {
-            return new String[0];
+            return EmptyArray.STRING;
         }
         return sslParameters.getEnabledCipherSuites();
     }
@@ -73,7 +74,7 @@
     @Override
     public String[] getSupportedCipherSuites() {
         if (instantiationException != null) {
-            return new String[0];
+            return EmptyArray.STRING;
         }
         return CipherSuite.getSupportedCipherSuiteNames();
     }
@@ -159,4 +160,3 @@
 
     // ------------------------------------------------------------------
 }
-
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerFactoryImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerFactoryImpl.java
index 149be0c..f894331 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerFactoryImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/TrustManagerFactoryImpl.java
@@ -32,6 +32,7 @@
 import javax.net.ssl.ManagerFactoryParameters;
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactorySpi;
+import libcore.base.EmptyArray;
 
 /**
  *
@@ -91,7 +92,7 @@
                         });
                 char[] pwd;
                 if (keyStorePwd == null) {
-                    pwd = new char[0];
+                    pwd = EmptyArray.CHAR;
                 } else {
                     pwd = keyStorePwd.toCharArray();
                 }
diff --git a/luni/src/main/java/org/apache/xml/serializer/ToHTMLStream.java b/luni/src/main/java/org/apache/xml/serializer/ToHTMLStream.java
index 4f2927a..9414875 100644
--- a/luni/src/main/java/org/apache/xml/serializer/ToHTMLStream.java
+++ b/luni/src/main/java/org/apache/xml/serializer/ToHTMLStream.java
@@ -25,6 +25,8 @@
 
 import javax.xml.transform.Result;
 
+import libcore.base.EmptyArray;
+
 import org.apache.xml.serializer.utils.MsgKey;
 import org.apache.xml.serializer.utils.Utils;
 import org.xml.sax.Attributes;
@@ -2054,7 +2056,7 @@
         final Node m_Root;
 
         /** helper buffer to convert Strings to char arrays */
-        private char[] m_charBuffer = new char[0];
+        private char[] m_charBuffer = EmptyArray.CHAR;
 
         /** true if the search for an object is lower case only with the key */
         private final boolean m_lowerCaseOnly;
diff --git a/luni/src/main/java/org/xml/sax/ext/Attributes2Impl.java b/luni/src/main/java/org/xml/sax/ext/Attributes2Impl.java
index 83cee5b..b3ec94a 100644
--- a/luni/src/main/java/org/xml/sax/ext/Attributes2Impl.java
+++ b/luni/src/main/java/org/xml/sax/ext/Attributes2Impl.java
@@ -7,7 +7,7 @@
 
 import org.xml.sax.Attributes;
 import org.xml.sax.helpers.AttributesImpl;
-
+import libcore.base.EmptyArray;
 
 /**
  * SAX2 extension helper for additional Attributes information,
@@ -46,8 +46,8 @@
      */
     public Attributes2Impl () {
         // BEGIN android-added
-        declared = new boolean[0];
-        specified = new boolean[0];
+        declared = EmptyArray.BOOLEAN;
+        specified = EmptyArray.BOOLEAN;
         // END android-added
     }
 
diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp
index 380bb7e..653c125 100644
--- a/luni/src/main/native/Register.cpp
+++ b/luni/src/main/native/Register.cpp
@@ -29,7 +29,6 @@
 extern int register_java_io_File(JNIEnv* env);
 extern int register_java_io_FileDescriptor(JNIEnv* env);
 extern int register_java_io_ObjectInputStream(JNIEnv* env);
-extern int register_java_io_ObjectOutputStream(JNIEnv* env);
 extern int register_java_io_ObjectStreamClass(JNIEnv* env);
 extern int register_java_lang_Character(JNIEnv* env);
 extern int register_java_lang_Double(JNIEnv* env);
@@ -80,7 +79,6 @@
             register_java_io_File(env) != -1 &&
             register_java_io_FileDescriptor(env) != -1 &&
             register_java_io_ObjectInputStream(env) != -1 &&
-            register_java_io_ObjectOutputStream(env) != -1 &&
             register_java_io_ObjectStreamClass(env) != -1 &&
             register_java_lang_Character(env) != -1 &&
             register_java_lang_Double(env) != -1 &&
diff --git a/luni/src/main/native/java_io_File.cpp b/luni/src/main/native/java_io_File.cpp
index ea8d12b..81794d8 100644
--- a/luni/src/main/native/java_io_File.cpp
+++ b/luni/src/main/native/java_io_File.cpp
@@ -25,6 +25,9 @@
 #include "ScopedPrimitiveArray.h"
 #include "ScopedUtfChars.h"
 #include "StaticAssert.h"
+#include "readlink.h"
+
+#include <string>
 
 #include <dirent.h>
 #include <errno.h>
@@ -32,8 +35,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/stat.h>
-#include <sys/vfs.h>
 #include <sys/types.h>
+#include <sys/vfs.h>
 #include <time.h>
 #include <unistd.h>
 #include <utime.h>
@@ -57,8 +60,7 @@
 static jlong File_lengthImpl(JNIEnv* env, jclass, jstring javaPath) {
     struct stat sb;
     if (!doStat(env, javaPath, sb)) {
-        // We must return 0 for files that don't exist.
-        // TODO: shouldn't we throw an IOException for ELOOP or EACCES?
+        // The RI returns 0 on error. (Even for errors like EACCES or ELOOP.)
         return 0;
     }
 
@@ -126,24 +128,27 @@
         return NULL;
     }
 
-    // We can't know how big a buffer readlink(2) will need, so we need to
-    // loop until it says "that fit".
-    size_t bufSize = 512;
-    while (true) {
-        LocalArray<512> buf(bufSize);
-        ssize_t len = readlink(path.c_str(), &buf[0], buf.size() - 1);
-        if (len == -1) {
-            // An error occurred.
-            return javaPath;
-        }
-        if (static_cast<size_t>(len) < buf.size() - 1) {
-            // The buffer was big enough.
-            buf[len] = '\0'; // readlink(2) doesn't NUL-terminate.
-            return env->NewStringUTF(&buf[0]);
-        }
-        // Try again with a bigger buffer.
-        bufSize *= 2;
+    std::string result;
+    if (!readlink(path.c_str(), result)) {
+        jniThrowIOException(env, errno);
+        return NULL;
     }
+    return env->NewStringUTF(result.c_str());
+}
+
+static jstring File_realpath(JNIEnv* env, jclass, jstring javaPath) {
+    ScopedUtfChars path(env, javaPath);
+    if (path.c_str() == NULL) {
+        return NULL;
+    }
+
+    extern bool realpath(const char* path, std::string& resolved);
+    std::string result;
+    if (!realpath(path.c_str(), result)) {
+        jniThrowIOException(env, errno);
+        return NULL;
+    }
+    return env->NewStringUTF(result.c_str());
 }
 
 static jboolean File_setLastModifiedImpl(JNIEnv* env, jclass, jstring javaPath, jlong ms) {
@@ -421,6 +426,22 @@
     return (rename(oldPath.c_str(), newPath.c_str()) == 0);
 }
 
+static void File_symlink(JNIEnv* env, jclass, jstring javaOldPath, jstring javaNewPath) {
+    ScopedUtfChars oldPath(env, javaOldPath);
+    if (oldPath.c_str() == NULL) {
+        return;
+    }
+
+    ScopedUtfChars newPath(env, javaNewPath);
+    if (newPath.c_str() == NULL) {
+        return;
+    }
+
+    if (symlink(oldPath.c_str(), newPath.c_str()) == -1) {
+        jniThrowIOException(env, errno);
+    }
+}
+
 static JNINativeMethod gMethods[] = {
     NATIVE_METHOD(File, canExecuteImpl, "(Ljava/lang/String;)Z"),
     NATIVE_METHOD(File, canReadImpl, "(Ljava/lang/String;)Z"),
@@ -438,11 +459,13 @@
     NATIVE_METHOD(File, listImpl, "(Ljava/lang/String;)[Ljava/lang/String;"),
     NATIVE_METHOD(File, mkdirImpl, "(Ljava/lang/String;)Z"),
     NATIVE_METHOD(File, readlink, "(Ljava/lang/String;)Ljava/lang/String;"),
+    NATIVE_METHOD(File, realpath, "(Ljava/lang/String;)Ljava/lang/String;"),
     NATIVE_METHOD(File, renameToImpl, "(Ljava/lang/String;Ljava/lang/String;)Z"),
     NATIVE_METHOD(File, setExecutableImpl, "(Ljava/lang/String;ZZ)Z"),
     NATIVE_METHOD(File, setLastModifiedImpl, "(Ljava/lang/String;J)Z"),
     NATIVE_METHOD(File, setReadableImpl, "(Ljava/lang/String;ZZ)Z"),
     NATIVE_METHOD(File, setWritableImpl, "(Ljava/lang/String;ZZ)Z"),
+    NATIVE_METHOD(File, symlink, "(Ljava/lang/String;Ljava/lang/String;)V"),
 };
 int register_java_io_File(JNIEnv* env) {
     return jniRegisterNativeMethods(env, "java/io/File", gMethods, NELEM(gMethods));
diff --git a/luni/src/main/native/java_io_ObjectInputStream.cpp b/luni/src/main/native/java_io_ObjectInputStream.cpp
index 1dd2f89..6d8aae7 100644
--- a/luni/src/main/native/java_io_ObjectInputStream.cpp
+++ b/luni/src/main/native/java_io_ObjectInputStream.cpp
@@ -17,53 +17,7 @@
 
 #define LOG_TAG "ObjectInputStream"
 
-#include "JNIHelp.h"
 #include "JniConstants.h"
-#include "ScopedUtfChars.h"
-
-#define SETTER(FUNCTION_NAME, JNI_C_TYPE, JNI_TYPE_STRING, JNI_SETTER_FUNCTION) \
-    static void FUNCTION_NAME(JNIEnv* env, jclass, jobject instance, \
-            jclass declaringClass, jstring javaFieldName, JNI_C_TYPE newValue) { \
-        if (instance == NULL) { \
-            return; \
-        } \
-        ScopedUtfChars fieldName(env, javaFieldName); \
-        if (fieldName.c_str() == NULL) { \
-            return; \
-        } \
-        jfieldID fid = env->GetFieldID(declaringClass, fieldName.c_str(), JNI_TYPE_STRING); \
-        if (fid != 0) { \
-            env->JNI_SETTER_FUNCTION(instance, fid, newValue); \
-        } \
-    }
-
-SETTER(ObjectInputStream_setFieldBool,   jboolean, "Z", SetBooleanField)
-SETTER(ObjectInputStream_setFieldByte,   jbyte,    "B", SetByteField)
-SETTER(ObjectInputStream_setFieldChar,   jchar,    "C", SetCharField)
-SETTER(ObjectInputStream_setFieldDouble, jdouble,  "D", SetDoubleField)
-SETTER(ObjectInputStream_setFieldFloat,  jfloat,   "F", SetFloatField)
-SETTER(ObjectInputStream_setFieldInt,    jint,     "I", SetIntField)
-SETTER(ObjectInputStream_setFieldLong,   jlong,    "J", SetLongField)
-SETTER(ObjectInputStream_setFieldShort,  jshort,   "S", SetShortField)
-
-static void ObjectInputStream_setFieldObject(JNIEnv* env, jclass, jobject instance,
-        jclass declaringClass, jstring javaFieldName, jstring javaFieldTypeName, jobject newValue) {
-    if (instance == NULL) {
-        return;
-    }
-    ScopedUtfChars fieldName(env, javaFieldName);
-    if (fieldName.c_str() == NULL) {
-        return;
-    }
-    ScopedUtfChars fieldTypeName(env, javaFieldTypeName);
-    if (fieldTypeName.c_str() == NULL) {
-        return;
-    }
-    jfieldID fid = env->GetFieldID(declaringClass, fieldName.c_str(), fieldTypeName.c_str());
-    if (fid != 0) {
-        env->SetObjectField(instance, fid, newValue);
-    }
-}
 
 static jobject ObjectInputStream_newInstance(JNIEnv* env, jclass,
         jclass instantiationClass, jclass constructorClass) {
@@ -76,15 +30,6 @@
 
 static JNINativeMethod gMethods[] = {
     NATIVE_METHOD(ObjectInputStream, newInstance, "(Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/Object;"),
-    NATIVE_METHOD(ObjectInputStream, setFieldObject, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V"),
-    NATIVE_METHOD(ObjectInputStream, setFieldByte, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;B)V"),
-    NATIVE_METHOD(ObjectInputStream, setFieldChar, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;C)V"),
-    NATIVE_METHOD(ObjectInputStream, setFieldDouble, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;D)V"),
-    NATIVE_METHOD(ObjectInputStream, setFieldFloat, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;F)V"),
-    NATIVE_METHOD(ObjectInputStream, setFieldInt, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;I)V"),
-    NATIVE_METHOD(ObjectInputStream, setFieldLong, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;J)V"),
-    NATIVE_METHOD(ObjectInputStream, setFieldShort, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;S)V"),
-    NATIVE_METHOD(ObjectInputStream, setFieldBool, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;Z)V"),
 };
 int register_java_io_ObjectInputStream(JNIEnv* env) {
     return jniRegisterNativeMethods(env, "java/io/ObjectInputStream", gMethods, NELEM(gMethods));
diff --git a/luni/src/main/native/java_io_ObjectOutputStream.cpp b/luni/src/main/native/java_io_ObjectOutputStream.cpp
deleted file mode 100644
index e99f9b2..0000000
--- a/luni/src/main/native/java_io_ObjectOutputStream.cpp
+++ /dev/null
@@ -1,80 +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 "ObjectOutputStream"
-
-#include "JNIHelp.h"
-#include "JniConstants.h"
-#include "ScopedUtfChars.h"
-
-#define GETTER(FUNCTION_NAME, JNI_C_TYPE, JNI_TYPE_STRING, JNI_GETTER_FUNCTION) \
-    static JNI_C_TYPE FUNCTION_NAME(JNIEnv* env, jclass, jobject instance, jclass declaringClass, \
-            jstring javaFieldName) { \
-        if (instance == NULL) { \
-            return JNI_C_TYPE(); \
-        } \
-        ScopedUtfChars fieldName(env, javaFieldName); \
-        if (fieldName.c_str() == NULL) { \
-            return JNI_C_TYPE(); \
-        } \
-        jfieldID fid = env->GetFieldID(declaringClass, fieldName.c_str(), JNI_TYPE_STRING); \
-        if (fid == 0) { \
-            return JNI_C_TYPE(); \
-        } \
-        return env->JNI_GETTER_FUNCTION(instance, fid); \
-    }
-
-GETTER(ObjectOutputStream_getFieldBool,   jboolean, "Z", GetBooleanField)
-GETTER(ObjectOutputStream_getFieldByte,   jbyte,    "B", GetByteField)
-GETTER(ObjectOutputStream_getFieldChar,   jchar,    "C", GetCharField)
-GETTER(ObjectOutputStream_getFieldDouble, jdouble,  "D", GetDoubleField)
-GETTER(ObjectOutputStream_getFieldFloat,  jfloat,   "F", GetFloatField)
-GETTER(ObjectOutputStream_getFieldInt,    jint,     "I", GetIntField)
-GETTER(ObjectOutputStream_getFieldLong,   jlong,    "J", GetLongField)
-GETTER(ObjectOutputStream_getFieldShort,  jshort,   "S", GetShortField)
-
-static jobject ObjectOutputStream_getFieldObj(JNIEnv* env, jclass, jobject instance,
-        jclass declaringClass, jstring javaFieldName, jstring javaFieldTypeName) {
-    ScopedUtfChars fieldName(env, javaFieldName);
-    if (fieldName.c_str() == NULL) {
-        return NULL;
-    }
-    ScopedUtfChars fieldTypeName(env, javaFieldTypeName);
-    if (fieldTypeName.c_str() == NULL) {
-        return NULL;
-    }
-    jfieldID fid = env->GetFieldID(declaringClass, fieldName.c_str(), fieldTypeName.c_str());
-    if (fid == 0) {
-        return NULL;
-    }
-    return env->GetObjectField(instance, fid);
-}
-
-static JNINativeMethod gMethods[] = {
-    NATIVE_METHOD(ObjectOutputStream, getFieldBool, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)Z"),
-    NATIVE_METHOD(ObjectOutputStream, getFieldByte, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)B"),
-    NATIVE_METHOD(ObjectOutputStream, getFieldChar, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)C"),
-    NATIVE_METHOD(ObjectOutputStream, getFieldDouble, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)D"),
-    NATIVE_METHOD(ObjectOutputStream, getFieldFloat, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)F"),
-    NATIVE_METHOD(ObjectOutputStream, getFieldInt, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)I"),
-    NATIVE_METHOD(ObjectOutputStream, getFieldLong, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)J"),
-    NATIVE_METHOD(ObjectOutputStream, getFieldObj, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;"),
-    NATIVE_METHOD(ObjectOutputStream, getFieldShort, "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/String;)S"),
-};
-int register_java_io_ObjectOutputStream(JNIEnv* env) {
-    return jniRegisterNativeMethods(env, "java/io/ObjectOutputStream", gMethods, NELEM(gMethods));
-}
diff --git a/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp b/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
index c41eb82..a5ee710 100644
--- a/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
+++ b/luni/src/main/native/org_apache_harmony_xml_ExpatParser.cpp
@@ -1000,9 +1000,11 @@
 }
 
 /**
- * Expat decides for itself what character encoding it's looking at. The interface is in terms of
- * bytes, which may point to UTF-8, UTF-16, ISO-8859-1, or US-ASCII. appendBytes, appendCharacters,
- * and appendString thus all call through to this method, strange though that appears.
+ * Decodes the bytes as characters and parse the characters as XML. This
+ * performs character decoding using the charset specified at XML_Parser
+ * creation. For Java chars, that charset must be UTF-16 so that a Java char[]
+ * can be reinterpreted as a UTF-16 encoded byte[]. appendBytes, appendChars
+ * and appendString all call through this method.
  */
 static void append(JNIEnv* env, jobject object, jint pointer,
         const char* bytes, size_t byteOffset, size_t byteCount, jboolean isFinal) {
diff --git a/luni/src/main/native/readlink.cpp b/luni/src/main/native/readlink.cpp
new file mode 100644
index 0000000..555d515
--- /dev/null
+++ b/luni/src/main/native/readlink.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "LocalArray.h"
+#include "readlink.h"
+
+#include <string>
+#include <unistd.h>
+
+bool readlink(const char* path, std::string& result) {
+    // We can't know how big a buffer readlink(2) will need, so we need to
+    // loop until it says "that fit".
+    size_t bufSize = 512;
+    while (true) {
+        LocalArray<512> buf(bufSize);
+        ssize_t len = readlink(path, &buf[0], buf.size());
+        if (len == -1) {
+            // An error occurred.
+            return false;
+        }
+        if (static_cast<size_t>(len) < buf.size()) {
+            // The buffer was big enough.
+            result.assign(&buf[0], len);
+            return true;
+        }
+        // Try again with a bigger buffer.
+        bufSize *= 2;
+    }
+}
diff --git a/luni/src/main/native/readlink.h b/luni/src/main/native/readlink.h
new file mode 100644
index 0000000..14031dc
--- /dev/null
+++ b/luni/src/main/native/readlink.h
@@ -0,0 +1,25 @@
+/*
+ * 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 <string>
+
+/**
+ * Fills 'result' with the contents of the symbolic link 'path'. Sets errno and returns false on
+ * failure, returns true on success. The contents of 'result' on failure are undefined. Possible
+ * errors are those defined for readlink(2), except that this function takes care of sizing the
+ * buffer appropriately.
+ */
+bool readlink(const char* path, std::string& result);
diff --git a/luni/src/main/native/realpath.cpp b/luni/src/main/native/realpath.cpp
new file mode 100644
index 0000000..d1960a4
--- /dev/null
+++ b/luni/src/main/native/realpath.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "readlink.h"
+
+#include <string>
+
+#include <errno.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+/**
+ * This differs from realpath(3) mainly in its behavior when a path element does not exist or can
+ * not be searched. realpath(3) treats that as an error and gives up, but we have Java-compatible
+ * behavior where we just assume the path element was not a symbolic link. This leads to a textual
+ * treatment of ".." from that point in the path, which may actually lead us back to a path we
+ * can resolve (as in "/tmp/does-not-exist/../blah.txt" which would be an error for realpath(3)
+ * but "/tmp/blah.txt" under the traditional Java interpretation).
+ *
+ * This implementation also removes all the fixed-length buffers of the C original.
+ */
+bool realpath(const char* path, std::string& resolved) {
+    // 'path' must be an absolute path.
+    if (path[0] != '/') {
+        errno = EINVAL;
+        return false;
+    }
+
+    resolved = "/";
+    if (path[1] == '\0') {
+        return true;
+    }
+
+    // Iterate over path components in 'left'.
+    int symlinkCount = 0;
+    std::string left(path + 1);
+    while (!left.empty()) {
+        // Extract the next path component.
+        size_t nextSlash = left.find('/');
+        std::string nextPathComponent = left.substr(0, nextSlash);
+        if (nextSlash != std::string::npos) {
+            left.erase(0, nextSlash + 1);
+        } else {
+            left.clear();
+        }
+        if (nextPathComponent.empty()) {
+            continue;
+        } else if (nextPathComponent == ".") {
+            continue;
+        } else if (nextPathComponent == "..") {
+            // Strip the last path component except when we have single "/".
+            if (resolved.size() > 1) {
+                resolved.erase(resolved.rfind('/'));
+            }
+            continue;
+        }
+
+        // Append the next path component.
+        if (resolved[resolved.size() - 1] != '/') {
+            resolved += '/';
+        }
+        resolved += nextPathComponent;
+
+        // See if we've got a symbolic link, and resolve it if so.
+        struct stat sb;
+        if (lstat(resolved.c_str(), &sb) == 0 && S_ISLNK(sb.st_mode)) {
+            if (symlinkCount++ > MAXSYMLINKS) {
+                errno = ELOOP;
+                return false;
+            }
+
+            std::string symlink;
+            if (!readlink(resolved.c_str(), symlink)) {
+                return false;
+            }
+            if (symlink[0] == '/') {
+                // The symbolic link is absolute, so we need to start from scratch.
+                resolved = "/";
+            } else if (resolved.size() > 1) {
+                // The symbolic link is relative, so we just lose the last path component (which
+                // was the link).
+                resolved.erase(resolved.rfind('/'));
+            }
+
+            if (!left.empty()) {
+                const char* maybeSlash = (symlink[symlink.size() - 1] != '/') ? "/" : "";
+                left = symlink + maybeSlash + left;
+            } else {
+                left = symlink;
+            }
+        }
+    }
+
+    // Remove trailing slash except when the resolved pathname is a single "/".
+    if (resolved.size() > 1 && resolved[resolved.size() - 1] == '/') {
+        resolved.erase(resolved.size() - 1, 1);
+    }
+    return true;
+}
diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk
index 774e8cb..43127d0 100644
--- a/luni/src/main/native/sub.mk
+++ b/luni/src/main/native/sub.mk
@@ -26,7 +26,6 @@
 	java_io_File.cpp \
 	java_io_FileDescriptor.cpp \
 	java_io_ObjectInputStream.cpp \
-	java_io_ObjectOutputStream.cpp \
 	java_io_ObjectStreamClass.cpp \
 	java_lang_Character.cpp \
 	java_lang_Double.cpp \
@@ -52,6 +51,8 @@
 	org_apache_harmony_luni_platform_OSNetworkSystem.cpp \
 	org_apache_harmony_luni_util_FloatingPointParser.cpp \
 	org_apache_harmony_xml_ExpatParser.cpp \
+	readlink.cpp \
+	realpath.cpp \
 	valueOf.cpp
 
 LOCAL_C_INCLUDES += \
diff --git a/luni/src/test/java/libcore/internal/StringPoolTest.java b/luni/src/test/java/libcore/internal/StringPoolTest.java
new file mode 100644
index 0000000..82c7c4e
--- /dev/null
+++ b/luni/src/test/java/libcore/internal/StringPoolTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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 libcore.internal;
+
+import junit.framework.TestCase;
+import libcore.internal.StringPool;
+
+public final class StringPoolTest extends TestCase {
+
+    public void testStringPool() {
+      StringPool stringPool = new StringPool();
+      String bcd = stringPool.get(new char[] { 'a', 'b', 'c', 'd', 'e' }, 1, 3);
+      assertEquals("bcd", bcd);
+      assertSame(bcd, stringPool.get(new char[] { 'a', 'b', 'c', 'd', 'e' }, 1, 3));
+    }
+
+    public void testHashCollision() {
+      StringPool stringPool = new StringPool();
+      char[] a = { (char) 1, (char) 0 };
+      char[] b = { (char) 0, (char) 31 };
+      assertEquals(new String(a).hashCode(), new String(b).hashCode());
+
+      String aString = stringPool.get(a, 0, 2);
+      assertEquals(new String(a), aString);
+      String bString = stringPool.get(b, 0, 2);
+      assertEquals(new String(b), bString);
+      assertSame(bString, stringPool.get(b, 0, 2));
+      assertNotSame(aString, stringPool.get(a, 0, 2));
+    }
+}
diff --git a/luni/src/test/java/libcore/java/io/FileTest.java b/luni/src/test/java/libcore/java/io/FileTest.java
index cd9b877..535975d6 100644
--- a/luni/src/test/java/libcore/java/io/FileTest.java
+++ b/luni/src/test/java/libcore/java/io/FileTest.java
@@ -186,18 +186,6 @@
     }
 
     private static void ln_s(String target, String linkName) throws Exception {
-        String[] args = new String[] { "ln", "-s", target, linkName };
-        // System.err.println("ln -s " + target + " " + linkName);
-        Process p = Runtime.getRuntime().exec(args);
-        int result = p.waitFor();
-        if (result != 0) {
-            BufferedReader r = new BufferedReader(new InputStreamReader(p.getErrorStream()));
-            String line;
-            while ((line = r.readLine()) != null) {
-                System.err.println(line);
-            }
-            fail("ln -s " + target + " " + linkName + " failed. " +
-                    "Does that file system support symlinks?");
-        }
+        File.symlink(target, linkName);
     }
 }
diff --git a/luni/src/test/java/libcore/java/lang/ArrayIndexOutOfBoundsExceptionTest.java b/luni/src/test/java/libcore/java/lang/ArrayIndexOutOfBoundsExceptionTest.java
index ee7f6e8..041b931 100644
--- a/luni/src/test/java/libcore/java/lang/ArrayIndexOutOfBoundsExceptionTest.java
+++ b/luni/src/test/java/libcore/java/lang/ArrayIndexOutOfBoundsExceptionTest.java
@@ -24,7 +24,7 @@
         try {
             bs[2] = 0;
             fail();
-        } catch (Exception ex) {
+        } catch (ArrayIndexOutOfBoundsException ex) {
             assertEquals("index=2 length=1", ex.getMessage());
         }
     }
@@ -34,7 +34,7 @@
         try {
             byte b = bs[2];
             fail();
-        } catch (Exception ex) {
+        } catch (ArrayIndexOutOfBoundsException ex) {
             assertEquals("index=2 length=1", ex.getMessage());
         }
     }
@@ -44,7 +44,7 @@
         try {
             ds[2] = 0.0;
             fail();
-        } catch (Exception ex) {
+        } catch (ArrayIndexOutOfBoundsException ex) {
             assertEquals("index=2 length=1", ex.getMessage());
         }
     }
@@ -54,7 +54,7 @@
         try {
             double d = ds[2];
             fail();
-        } catch (Exception ex) {
+        } catch (ArrayIndexOutOfBoundsException ex) {
             assertEquals("index=2 length=1", ex.getMessage());
         }
     }
@@ -64,7 +64,7 @@
         try {
             os[2] = null;
             fail();
-        } catch (Exception ex) {
+        } catch (ArrayIndexOutOfBoundsException ex) {
             assertEquals("index=2 length=1", ex.getMessage());
         }
     }
@@ -74,7 +74,7 @@
         try {
             Object o = os[2];
             fail();
-        } catch (Exception ex) {
+        } catch (ArrayIndexOutOfBoundsException ex) {
             assertEquals("index=2 length=1", ex.getMessage());
         }
     }
diff --git a/luni/src/test/java/libcore/java/lang/ClassCastExceptionTest.java b/luni/src/test/java/libcore/java/lang/ClassCastExceptionTest.java
new file mode 100644
index 0000000..780f620
--- /dev/null
+++ b/luni/src/test/java/libcore/java/lang/ClassCastExceptionTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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 libcore.java.lang;
+
+import java.util.EnumMap;
+import java.util.EnumSet;
+import junit.framework.TestCase;
+
+public final class ClassCastExceptionTest extends TestCase {
+    public void testCast() throws Exception {
+        Object o = new Exception();
+        try {
+            String s = (String) o;
+            fail();
+        } catch (ClassCastException ex) {
+            assertEquals("java.lang.Exception cannot be cast to java.lang.String", ex.getMessage());
+        }
+    }
+
+    public void testClassCast() throws Exception {
+        Object o = new Exception();
+        try {
+            String.class.cast(o);
+            fail();
+        } catch (ClassCastException ex) {
+            assertEquals("java.lang.Exception cannot be cast to java.lang.String", ex.getMessage());
+        }
+    }
+
+    public void testClassAsSubclass() throws Exception {
+        try {
+            Exception.class.asSubclass(String.class);
+            fail();
+        } catch (ClassCastException ex) {
+            assertEquals("java.lang.Exception cannot be cast to java.lang.String", ex.getMessage());
+        }
+    }
+
+    enum E { A, B, C };
+    enum F { A, B, C };
+
+    public void testEnumMapPut() throws Exception {
+        EnumMap m = new EnumMap(E.class);
+        try {
+            m.put(F.A, "world");
+            fail();
+        } catch (ClassCastException ex) {
+            ex.printStackTrace();
+            assertNotNull(ex.getMessage());
+        }
+    }
+
+    public void testMiniEnumSetAdd() throws Exception {
+        EnumSet m = EnumSet.noneOf(E.class);
+        try {
+            m.add(F.A);
+            fail();
+        } catch (ClassCastException ex) {
+            ex.printStackTrace();
+            assertNotNull(ex.getMessage());
+        }
+    }
+
+    public void testMiniEnumSetAddAll() throws Exception {
+        EnumSet m = EnumSet.noneOf(E.class);
+        EnumSet n = EnumSet.allOf(F.class);
+        try {
+            m.addAll(n);
+            fail();
+        } catch (ClassCastException ex) {
+            ex.printStackTrace();
+            assertNotNull(ex.getMessage());
+        }
+    }
+
+    enum HugeE {
+        A0, B0, C0, D0, E0, F0, G0, H0, I0, J0, K0, L0, M0, N0, O0, P0, Q0, R0, S0, T0, U0, V0, W0, X0, Y0, Z0,
+        A1, B1, C1, D1, E1, F1, G1, H1, I1, J1, K1, L1, M1, N1, O1, P1, Q1, R1, S1, T1, U1, V1, W1, X1, Y1, Z1,
+        A2, B2, C2, D2, E2, F2, G2, H2, I2, J2, K2, L2, M2, N2, O2, P2, Q2, R2, S2, T2, U2, V2, W2, X2, Y2, Z2,
+    };
+    enum HugeF {
+        A0, B0, C0, D0, E0, F0, G0, H0, I0, J0, K0, L0, M0, N0, O0, P0, Q0, R0, S0, T0, U0, V0, W0, X0, Y0, Z0,
+        A1, B1, C1, D1, E1, F1, G1, H1, I1, J1, K1, L1, M1, N1, O1, P1, Q1, R1, S1, T1, U1, V1, W1, X1, Y1, Z1,
+        A2, B2, C2, D2, E2, F2, G2, H2, I2, J2, K2, L2, M2, N2, O2, P2, Q2, R2, S2, T2, U2, V2, W2, X2, Y2, Z2,
+    };
+
+    public void testHugeEnumSetAdd() throws Exception {
+        EnumSet m = EnumSet.noneOf(HugeE.class);
+        try {
+            m.add(HugeF.A0);
+            fail();
+        } catch (ClassCastException ex) {
+            ex.printStackTrace();
+            assertNotNull(ex.getMessage());
+        }
+    }
+
+    public void testHugeEnumSetAddAll() throws Exception {
+        EnumSet m = EnumSet.noneOf(HugeE.class);
+        EnumSet n = EnumSet.allOf(HugeF.class);
+        try {
+            m.addAll(n);
+            fail();
+        } catch (ClassCastException ex) {
+            ex.printStackTrace();
+            assertNotNull(ex.getMessage());
+        }
+    }
+}
diff --git a/luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java b/luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java
index 5968fdf..726b05a 100644
--- a/luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java
+++ b/luni/src/test/java/libcore/java/net/ConcurrentCloseTest.java
@@ -28,6 +28,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
+import tests.net.StuckServer;
 
 /**
  * Test that Socket.close called on another thread interrupts a thread that's blocked doing
@@ -47,24 +48,28 @@
     }
 
     public void test_connect() throws Exception {
+        StuckServer ss = new StuckServer();
         Socket s = new Socket();
         new Killer(s).start();
         try {
             System.err.println("connect...");
-            s.connect(new InetSocketAddress("10.0.0.1", 7));
+            s.connect(ss.getLocalSocketAddress());
             fail("connect returned!");
         } catch (SocketException expected) {
             assertEquals("Socket closed", expected.getMessage());
+        } finally {
+            ss.close();
         }
     }
 
     public void test_connect_nonBlocking() throws Exception {
+        StuckServer ss = new StuckServer();
         SocketChannel s = SocketChannel.open();
         new Killer(s.socket()).start();
         try {
             System.err.println("connect (non-blocking)...");
             s.configureBlocking(false);
-            s.connect(new InetSocketAddress("10.0.0.2", 7));
+            s.connect(ss.getLocalSocketAddress());
             while (!s.finishConnect()) {
                 // Spin like a mad thing!
             }
@@ -76,6 +81,8 @@
         } catch (ClosedChannelException alsoOkay) {
             // For now, I'm assuming that we're happy as long as we get any reasonable exception.
             // It may be that we're supposed to guarantee only one or the other.
+        } finally {
+            ss.close();
         }
     }
 
diff --git a/luni/src/test/java/libcore/java/net/URLConnectionTest.java b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
index a1f99e9b..1a11bd9 100644
--- a/luni/src/test/java/libcore/java/net/URLConnectionTest.java
+++ b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
@@ -68,6 +68,7 @@
 import tests.http.MockResponse;
 import tests.http.MockWebServer;
 import tests.http.RecordedRequest;
+import tests.net.StuckServer;
 
 public class URLConnectionTest extends junit.framework.TestCase {
 
@@ -1393,27 +1394,16 @@
     }
 
     public void testConnectTimeouts() throws IOException {
-        // Set a backlog and use it up so that we can expect the
-        // URLConnection to properly timeout. According to Steven's
-        // 4.5 "listen function", linux adds 3 to the specified
-        // backlog, so we need to connect 4 times before it will hang.
-        ServerSocket serverSocket = new ServerSocket(0, 1);
-        int serverPort = serverSocket.getLocalPort();
-        Socket[] sockets = new Socket[4];
-        for (int i = 0; i < sockets.length; i++) {
-            sockets[i] = new Socket("localhost", serverPort);
-        }
-
+        StuckServer ss = new StuckServer();
+        int serverPort = ss.getLocalPort();
         URLConnection urlConnection = new URL("http://localhost:" + serverPort).openConnection();
         urlConnection.setConnectTimeout(1000);
         try {
             urlConnection.getInputStream();
             fail();
         } catch (SocketTimeoutException expected) {
-        }
-
-        for (Socket s : sockets) {
-            s.close();
+        } finally {
+            ss.close();
         }
     }
 
diff --git a/luni/src/test/java/libcore/java/util/zip/GzipTest.java b/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java
similarity index 71%
rename from luni/src/test/java/libcore/java/util/zip/GzipTest.java
rename to luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java
index f7e03dc..a28fae5 100644
--- a/luni/src/test/java/libcore/java/util/zip/GzipTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/GZIPInputStreamTest.java
@@ -27,23 +27,25 @@
 import java.util.zip.GZIPOutputStream;
 import junit.framework.TestCase;
 
-public final class GzipTest extends TestCase {
-
-    public void testRoundtripShortMessage() throws IOException {
-        byte[] data = gzip(("Hello World").getBytes("UTF-8"));
-        assertTrue(Arrays.equals(data, gunzip(gzip(data))));
+public final class GZIPInputStreamTest extends TestCase {
+    public void testShortMessage() throws IOException {
+        byte[] data = new byte[] {
+            31, -117, 8, 0, 0, 0, 0, 0, 0, 0, -13, 72, -51, -55, -55, 87, 8, -49,
+            47, -54, 73, 1, 0, 86, -79, 23, 74, 11, 0, 0, 0
+        };
+        assertEquals("Hello World", new String(gunzip(data), "UTF-8"));
     }
 
-    public void testRoundtripLongMessage() throws IOException {
+    public void testLongMessage() throws IOException {
         byte[] data = new byte[1024 * 1024];
         new Random().nextBytes(data);
-        assertTrue(Arrays.equals(data, gunzip(gzip(data))));
+        assertTrue(Arrays.equals(data, gunzip(GZIPOutputStreamTest.gzip(data))));
     }
 
     /** http://b/3042574 GzipInputStream.skip() causing CRC failures */
     public void testSkip() throws IOException {
         byte[] data = new byte[1024 * 1024];
-        byte[] gzipped = gzip(data);
+        byte[] gzipped = GZIPOutputStreamTest.gzip(data);
         GZIPInputStream in = new GZIPInputStream(new ByteArrayInputStream(gzipped));
         long totalSkipped = 0;
 
@@ -53,19 +55,11 @@
             totalSkipped += count;
         } while (count > 0);
 
-
         assertEquals(data.length, totalSkipped);
+        in.close();
     }
 
-    public byte[] gzip(byte[] bytes) throws IOException {
-        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
-        OutputStream gzippedOut = new GZIPOutputStream(bytesOut);
-        gzippedOut.write(bytes);
-        gzippedOut.close();
-        return bytesOut.toByteArray();
-    }
-
-    public byte[] gunzip(byte[] bytes) throws IOException {
+    public static byte[] gunzip(byte[] bytes) throws IOException {
         InputStream in = new GZIPInputStream(new ByteArrayInputStream(bytes));
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         byte[] buffer = new byte[1024];
@@ -73,6 +67,7 @@
         while ((count = in.read(buffer)) != -1) {
             out.write(buffer, 0, count);
         }
+        in.close();
         return out.toByteArray();
     }
 }
diff --git a/luni/src/test/java/libcore/java/util/zip/GZIPOutputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/GZIPOutputStreamTest.java
new file mode 100644
index 0000000..a61880f
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/zip/GZIPOutputStreamTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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 libcore.java.util.zip;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.GZIPOutputStream;
+import junit.framework.TestCase;
+
+public final class GZIPOutputStreamTest extends TestCase {
+    public void testShortMessage() throws IOException {
+        byte[] data = gzip(("Hello World").getBytes("UTF-8"));
+        assertEquals("[31, -117, 8, 0, 0, 0, 0, 0, 0, 0, -13, 72, -51, -55, -55, 87, 8, -49, " +
+                "47, -54, 73, 1, 0, 86, -79, 23, 74, 11, 0, 0, 0]", Arrays.toString(data));
+    }
+
+    public void testLongMessage() throws IOException {
+        byte[] data = new byte[1024 * 1024];
+        new Random().nextBytes(data);
+        assertTrue(Arrays.equals(data, GZIPInputStreamTest.gunzip(gzip(data))));
+    }
+
+    public static byte[] gzip(byte[] bytes) throws IOException {
+        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+        OutputStream gzippedOut = new GZIPOutputStream(bytesOut);
+        gzippedOut.write(bytes);
+        gzippedOut.close();
+        return bytesOut.toByteArray();
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipInputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/ZipInputStreamTest.java
new file mode 100644
index 0000000..cb98322
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/zip/ZipInputStreamTest.java
@@ -0,0 +1,62 @@
+/*
+ * 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 libcore.java.util.zip;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+import junit.framework.TestCase;
+
+public final class ZipInputStreamTest extends TestCase {
+    public void testShortMessage() throws IOException {
+        byte[] data = "Hello World".getBytes("UTF-8");
+        byte[] zipped = ZipOutputStreamTest.zip("short", data);
+        assertEquals(Arrays.toString(data), Arrays.toString(unzip("short", zipped)));
+    }
+
+    public void testLongMessage() throws IOException {
+        byte[] data = new byte[1024 * 1024];
+        new Random().nextBytes(data);
+        assertTrue(Arrays.equals(data, unzip("r", ZipOutputStreamTest.zip("r", data))));
+    }
+
+    public static byte[] unzip(String name, byte[] bytes) throws IOException {
+        ZipInputStream in = new ZipInputStream(new ByteArrayInputStream(bytes));
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+
+        ZipEntry entry = in.getNextEntry();
+        assertEquals(name, entry.getName());
+
+        byte[] buffer = new byte[1024];
+        int count;
+        while ((count = in.read(buffer)) != -1) {
+            out.write(buffer, 0, count);
+        }
+
+        assertNull(in.getNextEntry()); // There's only one entry in the Zip files we create.
+
+        in.close();
+        return out.toByteArray();
+    }
+}
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java b/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java
new file mode 100644
index 0000000..e7c518f
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/zip/ZipOutputStreamTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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 libcore.java.util.zip;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Random;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+import junit.framework.TestCase;
+
+public final class ZipOutputStreamTest extends TestCase {
+    public void testShortMessage() throws IOException {
+        byte[] data = "Hello World".getBytes("UTF-8");
+        byte[] zipped = zip("short", data);
+        assertEquals(Arrays.toString(data), Arrays.toString(ZipInputStreamTest.unzip("short", zipped)));
+    }
+
+    // http://b/3181430 --- a sign-extension bug on CRCs with the top bit set.
+    public void test3181430() throws IOException {
+        byte[] data = new byte[1]; // CRC32({ 0 }) == 0xd202ef8d
+        byte[] zipped = zip("z", data);
+        assertEquals(Arrays.toString(data), Arrays.toString(ZipInputStreamTest.unzip("z", zipped)));
+    }
+
+    public void testLongMessage() throws IOException {
+        byte[] data = new byte[1024 * 1024];
+        new Random().nextBytes(data);
+        assertTrue(Arrays.equals(data, ZipInputStreamTest.unzip("r", zip("r", data))));
+    }
+
+    public static byte[] zip(String name, byte[] bytes) throws IOException {
+        ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+        ZipOutputStream zippedOut = new ZipOutputStream(bytesOut);
+
+        ZipEntry entry = new ZipEntry(name);
+        zippedOut.putNextEntry(entry);
+        zippedOut.write(bytes);
+        zippedOut.closeEntry();
+
+        zippedOut.close();
+        return bytesOut.toByteArray();
+    }
+}
diff --git a/luni/src/test/java/org/apache/harmony/xml/ExpatPullParserTest.java b/luni/src/test/java/org/apache/harmony/xml/ExpatPullParserTest.java
index 71e2671..42c4108 100644
--- a/luni/src/test/java/org/apache/harmony/xml/ExpatPullParserTest.java
+++ b/luni/src/test/java/org/apache/harmony/xml/ExpatPullParserTest.java
@@ -21,7 +21,6 @@
 public final class ExpatPullParserTest extends PullParserTest {
 
     @Override XmlPullParser newPullParser() {
-        ExpatPullParser parser = new ExpatPullParser();
-        return parser;
+        return new ExpatPullParser();
     }
 }
diff --git a/luni/src/test/java/org/apache/harmony/xml/ExpatSaxParserTest.java b/luni/src/test/java/org/apache/harmony/xml/ExpatSaxParserTest.java
index 51804f2..2a3bf06 100644
--- a/luni/src/test/java/org/apache/harmony/xml/ExpatSaxParserTest.java
+++ b/luni/src/test/java/org/apache/harmony/xml/ExpatSaxParserTest.java
@@ -45,6 +45,19 @@
 
     private static final String SNIPPET = "<dagny dad=\"bob\">hello</dagny>";
 
+    public void testGlobalReferenceTableOverflow() throws Exception {
+        // We used to use a JNI global reference per interned string.
+        // Framework apps have a limit of 2000 JNI global references per VM.
+        StringBuilder xml = new StringBuilder();
+        xml.append("<root>");
+        for (int i = 0; i < 4000; ++i) {
+            xml.append("<tag" + i + ">");
+            xml.append("</tag" + i + ">");
+        }
+        xml.append("</root>");
+        parse(xml.toString(), new DefaultHandler());
+    }
+
     public void testExceptions() {
         // From startElement().
         ContentHandler contentHandler = new DefaultHandler() {
@@ -675,8 +688,7 @@
             XMLReader reader = new ExpatReader();
             reader.setContentHandler(contentHandler);
             reader.parse(new InputSource(new StringReader(xml)));
-        }
-        catch (IOException e) {
+        } catch (IOException e) {
             throw new AssertionError(e);
         }
     }
diff --git a/luni/src/test/java/org/apache/harmony/xml/PullParserTest.java b/luni/src/test/java/org/apache/harmony/xml/PullParserTest.java
index d6d5370..fa6c54e 100644
--- a/luni/src/test/java/org/apache/harmony/xml/PullParserTest.java
+++ b/luni/src/test/java/org/apache/harmony/xml/PullParserTest.java
@@ -24,70 +24,477 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 public abstract class PullParserTest extends TestCase {
-    private static final String SNIPPET = "<dagny dad=\"bob\">hello</dagny>";
 
-    public void testPullParser() {
+    public void testAttributeNoValueWithRelaxed() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setFeature("http://xmlpull.org/v1/doc/features.html#relaxed", true);
+        parser.setInput(new StringReader("<input checked></input>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals("input", parser.getName());
+        assertEquals("checked", parser.getAttributeName(0));
+        assertEquals("checked", parser.getAttributeValue(0));
+    }
+
+    public void testAttributeUnquotedValueWithRelaxed() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setFeature("http://xmlpull.org/v1/doc/features.html#relaxed", true);
+        parser.setInput(new StringReader("<input checked=true></input>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals("input", parser.getName());
+        assertEquals("checked", parser.getAttributeName(0));
+        assertEquals("true", parser.getAttributeValue(0));
+    }
+
+    public void testUnterminatedEntityWithRelaxed() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setFeature("http://xmlpull.org/v1/doc/features.html#relaxed", true);
+        parser.setInput(new StringReader("<foo bar='A&W'>mac&cheese</foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals("foo", parser.getName());
+        assertEquals("bar", parser.getAttributeName(0));
+        assertEquals("A&W", parser.getAttributeValue(0));
+        assertEquals(XmlPullParser.TEXT, parser.next());
+        assertEquals("mac&cheese", parser.getText());
+    }
+
+    public void testEntitiesAndNamespaces() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setFeature("http://xmlpull.org/v1/doc/features.html#process-namespaces", true);
+        parser.setInput(new StringReader(
+                "<foo:a xmlns:foo='http://foo' xmlns:bar='http://bar'><bar:b/></foo:a>"));
+        testNamespace(parser);
+    }
+
+    public void testEntitiesAndNamespacesWithRelaxed() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setFeature("http://xmlpull.org/v1/doc/features.html#process-namespaces", true);
+        parser.setFeature("http://xmlpull.org/v1/doc/features.html#relaxed", true);
+        parser.setInput(new StringReader(
+                "<foo:a xmlns:foo='http://foo' xmlns:bar='http://bar'><bar:b/></foo:a>"));
+        testNamespace(parser); // TODO: end tag fails on gingerbread for relaxed mode
+    }
+
+    private void testNamespace(XmlPullParser parser) throws XmlPullParserException, IOException {
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals("http://foo", parser.getNamespace());
+        assertEquals("a", parser.getName());
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals("http://bar", parser.getNamespace());
+        assertEquals("b", parser.getName());
+        assertEquals(XmlPullParser.END_TAG, parser.next());
+        assertEquals("http://bar", parser.getNamespace());
+        assertEquals("b", parser.getName());
+        assertEquals(XmlPullParser.END_TAG, parser.next());
+        assertEquals("http://foo", parser.getNamespace());
+        assertEquals("a", parser.getName());
+    }
+
+    public void testLargeNumericEntities() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader(
+                "<foo>&#2147483647; &#-2147483648;</foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals(XmlPullParser.TEXT, parser.next());
+        // TODO: this can't possibly be what the spec wants; it should refuse too-large characters
+        assertEquals(new String(new char[] { (char) 2147483647, ' ', (char) -2147483648}),
+                parser.getText());
+    }
+
+    public void testVeryLargeNumericEntities() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader(
+                "<foo>&#2147483648;</foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        // TODO: this can't possibly be what the spec wants; it should throw another type
+        assertNextFails(parser);
+    }
+
+    public void testOmittedNumericEntities() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo>&#;</foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        // TODO: this can't possibly be what the spec wants; it should throw another type
+        assertNextFails(parser);
+    }
+
+    /**
+     * Carriage returns followed by line feeds are silently discarded.
+     */
+    public void testCarriageReturnLineFeed() throws Exception {
+        testLineEndings("\r\n<foo\r\na='b\r\nc'\r\n>d\r\ne</foo\r\n>\r\n");
+    }
+
+    /**
+     * Lone carriage returns are treated like newlines.
+     */
+    public void testLoneCarriageReturn() throws Exception {
+        testLineEndings("\r<foo\ra='b\rc'\r>d\re</foo\r>\r");
+    }
+
+    public void testLoneNewLine() throws Exception {
+        testLineEndings("\n<foo\na='b\nc'\n>d\ne</foo\n>\n");
+    }
+
+    private void testLineEndings(String xml) throws XmlPullParserException, IOException {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader(xml));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals("foo", parser.getName());
+        assertEquals("b c", parser.getAttributeValue(0));
+        assertEquals(XmlPullParser.TEXT, parser.next());
+        assertEquals("d\ne", parser.getText());
+        assertEquals(XmlPullParser.END_TAG, parser.next());
+        assertEquals("foo", parser.getName());
+        assertEquals(XmlPullParser.END_DOCUMENT, parser.next());
+    }
+
+    public void testXmlDeclaration() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader(
+                "<?xml version='1.0' encoding='UTF-8' standalone='no'?><foo/>"));
+        assertEquals(XmlPullParser.START_TAG, parser.nextToken());
+        assertEquals("1.0", parser.getProperty(
+                "http://xmlpull.org/v1/doc/properties.html#xmldecl-version"));
+        assertEquals(Boolean.FALSE, parser.getProperty(
+                "http://xmlpull.org/v1/doc/properties.html#xmldecl-standalone"));
+        assertEquals("UTF-8", parser.getInputEncoding());
+    }
+
+    public void testXmlDeclarationExtraAttributes() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader(
+                "<?xml version='1.0' encoding='UTF-8' standalone='no' a='b'?><foo/>"));
         try {
-            XmlPullParser parser = newPullParser();
-            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
-
-            // Test reader.
-            parser.setInput(new StringReader(SNIPPET));
-            validate(parser);
-
-            // Test input stream.
-            parser.setInput(new ByteArrayInputStream(SNIPPET.getBytes()),
-                    "UTF-8");
-            validate(parser);
-        } catch (XmlPullParserException e) {
-            throw new RuntimeException(e);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
+            parser.nextToken();
+            fail();
+        } catch (XmlPullParserException expected) {
         }
     }
 
+    public void testCustomEntitiesUsingNext() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader(
+                "<foo a='cd&aaaaaaaaaa;ef'>wx&aaaaaaaaaa;yz</foo>"));
+        parser.defineEntityReplacementText("aaaaaaaaaa", "b");
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals("cdbef", parser.getAttributeValue(0));
+        assertEquals(XmlPullParser.TEXT, parser.next());
+        assertEquals("wxbyz", parser.getText());
+    }
+
+    public void testCustomEntitiesUsingNextToken() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader(
+                "<foo a='cd&aaaaaaaaaa;ef'>wx&aaaaaaaaaa;yz</foo>"));
+        parser.defineEntityReplacementText("aaaaaaaaaa", "b");
+        assertEquals(XmlPullParser.START_TAG, parser.nextToken());
+        assertEquals("cdbef", parser.getAttributeValue(0));
+        assertEquals(XmlPullParser.TEXT, parser.nextToken());
+        assertEquals("wx", parser.getText());
+        assertEquals(XmlPullParser.ENTITY_REF, parser.nextToken());
+        assertEquals("aaaaaaaaaa", parser.getName());
+        assertEquals("b", parser.getText());
+        assertEquals(XmlPullParser.TEXT, parser.nextToken());
+        assertEquals("yz", parser.getText());
+    }
+
+    public void testGreaterThanInText() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo>></foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals(XmlPullParser.TEXT, parser.next());
+        assertEquals(">", parser.getText()); // TODO: this should probably fail?
+    }
+
+    public void testGreaterThanInAttribute() throws Exception{
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo a='>'></foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals(">", parser.getAttributeValue(0)); // TODO: this should probably fail?
+    }
+
+    public void testLessThanInText() throws Exception{
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo><</foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertNextFails(parser);
+    }
+
+    public void testLessThanInAttribute() throws Exception{
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo a='<'></foo>"));
+        assertNextFails(parser);
+    }
+
+    public void testQuotesInAttribute() throws Exception{
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo a='\"' b=\"'\"></foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals("\"", parser.getAttributeValue(0));
+        assertEquals("'", parser.getAttributeValue(1));
+    }
+
+    public void testQuotesInText() throws Exception{
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo>\" '</foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals(XmlPullParser.TEXT, parser.next());
+        assertEquals("\" '", parser.getText());
+    }
+
+    public void testCdataDelimiterInAttribute() throws Exception{
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo a=']]>'></foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals("]]>", parser.getAttributeValue(0));
+    }
+
+    public void testCdataDelimiterInText() throws Exception{
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo>]]></foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertNextFails(parser);
+    }
+
+    public void testUnexpectedEof() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo><![C"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertNextFails(parser);
+    }
+
+    public void testUnexpectedSequence() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo><![Cdata[bar]]></foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertNextFails(parser);
+    }
+
+    public void testThreeDashCommentDelimiter() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo><!--a---></foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertNextFails(parser);
+    }
+
+    public void testTwoDashesInComment() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo><!-- -- --></foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        // TODO: confirm with the spec that this should fail
+        assertNextFails(parser);
+    }
+
+    public void testEmptyComment() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo><!----></foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals(XmlPullParser.COMMENT, parser.nextToken());
+        assertEquals("", parser.getText());
+    }
+
+    /**
+     * Close braces require lookaheads because we need to defend against "]]>".
+     */
+    public void testManyCloseBraces() throws Exception{
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo>]]]]]]]]]]]]]]]]]]]]]]]</foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals(XmlPullParser.TEXT, parser.next());
+        assertEquals("]]]]]]]]]]]]]]]]]]]]]]]", parser.getText());
+    }
+
+    public void testCommentWithNext() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo>ab<!-- comment! -->cd</foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals(XmlPullParser.TEXT, parser.next());
+        assertEquals("abcd", parser.getText());
+    }
+
+    public void testCommentWithNextToken() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo>ab<!-- comment! -->cd</foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals(XmlPullParser.TEXT, parser.nextToken());
+        assertEquals("ab", parser.getText());
+        assertEquals(XmlPullParser.COMMENT, parser.nextToken());
+        assertEquals(" comment! ", parser.getText());
+        assertEquals(XmlPullParser.TEXT, parser.nextToken());
+        assertEquals("cd", parser.getText());
+    }
+
+    public void testCdataWithNext() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo>ab<![CDATA[cdef]]gh&amp;i]]>jk</foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals(XmlPullParser.TEXT, parser.next());
+        assertEquals("abcdef]]gh&amp;ijk", parser.getText());
+        assertEquals(XmlPullParser.END_TAG, parser.nextToken());
+    }
+
+    public void testCdataWithNextToken() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo>ab<![CDATA[cdef]]gh&amp;i]]>jk</foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals(XmlPullParser.TEXT, parser.nextToken());
+        assertEquals("ab", parser.getText());
+        assertEquals(XmlPullParser.CDSECT, parser.nextToken());
+        assertEquals("cdef]]gh&amp;i", parser.getText());
+        assertEquals(XmlPullParser.TEXT, parser.nextToken());
+        assertEquals("jk", parser.getText());
+        assertEquals(XmlPullParser.END_TAG, parser.nextToken());
+    }
+
+    public void testEntityLooksLikeCdataClose() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo>&#93;&#93;></foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals(XmlPullParser.TEXT, parser.next());
+        assertEquals("]]>", parser.getText());
+    }
+
+    public void testDoctypeWithNext() throws Exception {
+        String s = "<!DOCTYPE foo ["
+            + "  <!ENTITY bb \"bar baz\">"
+            + "  <!NOTATION png SYSTEM \"image/png\">"
+            + "]><foo>a&bb;c</foo>";
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader(
+                "<!DOCTYPE foo [<!ENTITY bb \"bar baz\">]><foo>a&bb;c</foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals(XmlPullParser.TEXT, parser.next());
+        assertEquals("abar bazc", parser.getText()); // TODO: this fails on gingerbread
+        assertEquals(XmlPullParser.END_TAG, parser.next());
+    }
+
+    public void testDoctypeWithNextToken() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader(
+                "<!DOCTYPE foo [<!ENTITY bb \"bar baz\">]><foo>a&bb;c</foo>"));
+        assertEquals(XmlPullParser.DOCDECL, parser.nextToken());
+        assertEquals(" foo [<!ENTITY bb \"bar baz\">]", parser.getText());
+        assertNull(parser.getName());
+
+        assertEquals(XmlPullParser.START_TAG, parser.nextToken());
+        assertEquals(XmlPullParser.TEXT, parser.nextToken());
+        assertEquals("a", parser.getText());
+        assertEquals(XmlPullParser.ENTITY_REF, parser.nextToken());
+        assertEquals("bb", parser.getName());
+        assertEquals("bar baz", parser.getText()); // TODO: this fails on gingerbread
+        assertEquals(XmlPullParser.TEXT, parser.nextToken());
+        assertEquals("c", parser.getText());
+        assertEquals(XmlPullParser.END_TAG, parser.next());
+    }
+
+    public void testProcessingInstructionWithNext() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo>ab<?cd efg hij?>kl</foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals(XmlPullParser.TEXT, parser.next());
+        assertEquals("abkl", parser.getText());
+        assertEquals(XmlPullParser.END_TAG, parser.next());
+    }
+
+    public void testProcessingInstructionWithNextToken() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo>ab<?cd efg hij?>kl</foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.nextToken());
+        assertEquals(XmlPullParser.TEXT, parser.nextToken());
+        assertEquals("ab", parser.getText());
+        assertEquals(XmlPullParser.PROCESSING_INSTRUCTION, parser.nextToken());
+        assertEquals("cd efg hij", parser.getText());
+        assertEquals(XmlPullParser.TEXT, parser.nextToken());
+        assertEquals("kl", parser.getText());
+        assertEquals(XmlPullParser.END_TAG, parser.next());
+    }
+
+    public void testLinesAndColumns() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("\n"
+                + "  <foo><bar a='\n"
+                + "' b='cde'></bar\n"
+                + "><!--\n"
+                + "\n"
+                + "--><baz/>fg\n"
+                + "</foo>"));
+        assertEquals("1,1", parser.getLineNumber() + "," + parser.getColumnNumber());
+        assertEquals(XmlPullParser.IGNORABLE_WHITESPACE, parser.nextToken());
+        assertEquals("2,3", parser.getLineNumber() + "," + parser.getColumnNumber());
+        assertEquals(XmlPullParser.START_TAG, parser.nextToken());
+        assertEquals("2,8", parser.getLineNumber() + "," + parser.getColumnNumber());
+        assertEquals(XmlPullParser.START_TAG, parser.nextToken());
+        assertEquals("3,11", parser.getLineNumber() + "," + parser.getColumnNumber());
+        assertEquals(XmlPullParser.END_TAG, parser.nextToken());
+        assertEquals("4,2", parser.getLineNumber() + "," + parser.getColumnNumber());
+        assertEquals(XmlPullParser.COMMENT, parser.nextToken());
+        assertEquals("6,4", parser.getLineNumber() + "," + parser.getColumnNumber());
+        assertEquals(XmlPullParser.START_TAG, parser.nextToken());
+        assertEquals("6,10", parser.getLineNumber() + "," + parser.getColumnNumber());
+        assertEquals(XmlPullParser.END_TAG, parser.nextToken());
+        assertEquals("6,10", parser.getLineNumber() + "," + parser.getColumnNumber());
+        assertEquals(XmlPullParser.TEXT, parser.nextToken());
+        assertEquals("7,1", parser.getLineNumber() + "," + parser.getColumnNumber());
+        assertEquals(XmlPullParser.END_TAG, parser.nextToken());
+        assertEquals("7,7", parser.getLineNumber() + "," + parser.getColumnNumber());
+        assertEquals(XmlPullParser.END_DOCUMENT, parser.nextToken());
+        assertEquals("7,7", parser.getLineNumber() + "," + parser.getColumnNumber());
+    }
+
+    public void testEmptyCdata() throws Exception {
+        XmlPullParser parser = newPullParser();
+        parser.setInput(new StringReader("<foo><![CDATA[]]></foo>"));
+        assertEquals(XmlPullParser.START_TAG, parser.next());
+        assertEquals(XmlPullParser.TEXT, parser.next()); // TODO: This should probably fail!
+        assertEquals("", parser.getText());
+        assertEquals(XmlPullParser.END_TAG, parser.next());
+    }
+
+    public void testParseReader() throws Exception {
+        String snippet = "<dagny dad=\"bob\">hello</dagny>";
+        XmlPullParser parser = newPullParser();
+        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
+        parser.setInput(new StringReader(snippet));
+        validate(parser);
+    }
+
+    public void testParseInputStream() throws Exception {
+        String snippet = "<dagny dad=\"bob\">hello</dagny>";
+        XmlPullParser parser = newPullParser();
+        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
+        parser.setInput(new ByteArrayInputStream(snippet.getBytes()), "UTF-8");
+        validate(parser);
+    }
+
     static void validate(XmlPullParser parser)
             throws XmlPullParserException, IOException {
         assertEquals(XmlPullParser.START_DOCUMENT, parser.getEventType());
-
         assertEquals(0, parser.getDepth());
-
         assertEquals(XmlPullParser.START_TAG, parser.next());
-
         assertEquals(1, parser.getDepth());
-
         assertEquals("dagny", parser.getName());
         assertEquals(1, parser.getAttributeCount());
         assertEquals("dad", parser.getAttributeName(0));
         assertEquals("bob", parser.getAttributeValue(0));
         assertEquals("bob", parser.getAttributeValue(null, "dad"));
-
         assertEquals(XmlPullParser.TEXT, parser.next());
-
         assertEquals(1, parser.getDepth());
-
         assertEquals("hello", parser.getText());
-
         assertEquals(XmlPullParser.END_TAG, parser.next());
-
         assertEquals(1, parser.getDepth());
-
         assertEquals("dagny", parser.getName());
-
         assertEquals(XmlPullParser.END_DOCUMENT, parser.next());
-
         assertEquals(0, parser.getDepth());
     }
 
-    static final String XML =
-        "<one xmlns='ns:default' xmlns:n1='ns:1' a='b'>\n"
-              + "  <n1:two c='d' n1:e='f' xmlns:n2='ns:2'>text</n1:two>\n"
-              + "</one>";
+    public void testNamespaces() throws Exception {
+        String xml = "<one xmlns='ns:default' xmlns:n1='ns:1' a='b'>\n"
+                + "  <n1:two c='d' n1:e='f' xmlns:n2='ns:2'>text</n1:two>\n"
+                + "</one>";
 
-    public void testExpatPullParserNamespaces() throws Exception {
         XmlPullParser parser = newPullParser();
         parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-        parser.setInput(new StringReader(XML));
+        parser.setInput(new StringReader(xml));
 
         assertEquals(0, parser.getDepth());
         assertEquals(0, parser.getNamespaceCount(0));
@@ -174,6 +581,14 @@
         assertEquals("ns:default", parser.getNamespaceUri(0));
     }
 
+    private void assertNextFails(XmlPullParser parser) throws IOException {
+        try {
+            parser.next();
+            fail();
+        } catch (XmlPullParserException expected) {
+        }
+    }
+
     /**
      * Creates a new pull parser with namespace support.
      */
diff --git a/support/src/test/java/tests/net/StuckServer.java b/support/src/test/java/tests/net/StuckServer.java
new file mode 100644
index 0000000..4230f17
--- /dev/null
+++ b/support/src/test/java/tests/net/StuckServer.java
@@ -0,0 +1,57 @@
+/*
+ * 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 tests.net;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.ArrayList;
+
+/**
+ * A test ServerSocket that you can't connect to --- connects will time out.
+ */
+public final class StuckServer {
+    private ServerSocket serverSocket;
+    private ArrayList<Socket> clients = new ArrayList<Socket>();
+
+    public StuckServer() throws IOException {
+        // Set a backlog and use it up so that we can expect the
+        // connection to time out. According to Steven's
+        // 4.5 "listen function", Linux adds 3 to the specified
+        // backlog, so we need to connect 4 times before it will hang.
+        serverSocket = new ServerSocket(0, 1);
+        for (int i = 0; i < 4; i++) {
+            clients.add(new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort()));
+        }
+    }
+
+    public InetSocketAddress getLocalSocketAddress() {
+        return (InetSocketAddress) serverSocket.getLocalSocketAddress();
+    }
+
+    public int getLocalPort() {
+        return serverSocket.getLocalPort();
+    }
+
+    public void close() throws IOException {
+        serverSocket.close();
+        for (Socket client : clients) {
+            client.close();
+        }
+    }
+}
diff --git a/support/src/test/java/tests/resources/GZIPInputStream/hyts_gInput.txt.gz b/support/src/test/java/tests/resources/GZIPInputStream/hyts_gInput.txt.gz
deleted file mode 100644
index e0f5a00..0000000
--- a/support/src/test/java/tests/resources/GZIPInputStream/hyts_gInput.txt.gz
+++ /dev/null
Binary files differ
diff --git a/xml/src/main/java/org/kxml2/io/KXmlParser.java b/xml/src/main/java/org/kxml2/io/KXmlParser.java
index dd73f0b..9c20e0d 100644
--- a/xml/src/main/java/org/kxml2/io/KXmlParser.java
+++ b/xml/src/main/java/org/kxml2/io/KXmlParser.java
@@ -22,31 +22,44 @@
 
 package org.kxml2.io;
 
-import java.io.*;
-import java.util.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.HashMap;
+import java.util.Map;
+import libcore.internal.StringPool;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
 
-import org.xmlpull.v1.*;
-
-/** A simple, pull based XML parser. This classe replaces the kXML 1
-    XmlParser class and the corresponding event classes. */
-
+/**
+ * A pull based XML parser.
+ */
 public class KXmlParser implements XmlPullParser {
 
-    private Object location;
+    private static final char[] START_COMMENT = { '<', '!', '-', '-' };
+    private static final char[] END_COMMENT = { '-', '-', '>' };
+    private static final char[] START_CDATA = { '<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[' };
+    private static final char[] END_CDATA = { ']', ']', '>' };
+    private static final char[] START_PROCESSING_INSTRUCTION = { '<', '?' };
+    private static final char[] END_PROCESSING_INSTRUCTION = { '?', '>' };
+    private static final char[] START_DOCTYPE = { '<', '!', 'D', 'O', 'C', 'T', 'Y', 'P', 'E' };
+    // no END_DOCTYPE because doctype must be parsed
+
     static final private String UNEXPECTED_EOF = "Unexpected EOF";
     static final private String ILLEGAL_TYPE = "Wrong event type";
-    static final private int LEGACY = 999;
-    static final private int XML_DECL = 998;
+    static final private int XML_DECLARATION = 998;
 
     // general
+    private String location;
 
     private String version;
     private Boolean standalone;
 
     private boolean processNsp;
     private boolean relaxed;
-    private boolean keepNamespaceAttributes; // android-added
-    private Hashtable entityMap;
+    private boolean keepNamespaceAttributes;
+    private Map<String, String> entityMap;
     private int depth;
     private String[] elementStack = new String[16];
     private String[] nspStack = new String[8];
@@ -56,33 +69,31 @@
 
     private Reader reader;
     private String encoding;
-    private char[] srcBuf;
+    private final char[] buffer = new char[8192];
+    private int position = 0;
+    private int limit = 0;
 
-    private int srcPos;
-    private int srcCount;
+    /*
+     * Track the number of newlines and columns preceding the current buffer. To
+     * compute the line and column of a position in the buffer, compute the line
+     * and column in the buffer and add the preceding values.
+     */
+    private int bufferStartLine;
+    private int bufferStartColumn;
 
-    private int line;
-    private int column;
-
-    // txtbuffer
-
-    /** Target buffer for storing incoming text (including aggregated resolved entities) */
-    private char[] txtBuf = new char[128];
-    /** Write position  */
-    private int txtPos;
-
-    // Event-related
+    // the current token
 
     private int type;
     private boolean isWhitespace;
     private String namespace;
     private String prefix;
     private String name;
+    private String text;
 
     private boolean degenerated;
     private int attributeCount;
 
-    /**
+    /*
      * The current element's attributes arranged in groups of 4:
      * i + 0 = attribute namespace URI
      * i + 1 = attribute namespace prefix
@@ -90,54 +101,39 @@
      * i + 3 = attribute value
      */
     private String[] attributes = new String[16];
-//    private int stackMismatch = 0;
+
     private String error;
 
-    /**
-     * A separate peek buffer seems simpler than managing
-     * wrap around in the first level read buffer */
-
-    private int[] peek = new int[2];
-    private int peekCount;
-    private boolean wasCR;
-
     private boolean unresolved;
     private boolean token;
 
-    public KXmlParser() {
-        srcBuf =
-            new char[Runtime.getRuntime().freeMemory() >= 1048576 ? 8192 : 128];
-    }
+    public final StringPool stringPool = new StringPool();
 
-    // BEGIN android-added
     /**
-     * Retains namespace attributes like {@code xmlns="http://foo"} or {@code
-     * xmlns:foo="http:foo"} in pulled elements. Most applications will only be
-     * interested in the effective namespaces of their elements, so these
-     * attributes aren't useful. But for structure preserving wrappers like DOM,
-     * it is necessary to keep the namespace data around.
+     * Retains namespace attributes like {@code xmlns="http://foo"} or {@code xmlns:foo="http:foo"}
+     * in pulled elements. Most applications will only be interested in the effective namespaces of
+     * their elements, so these attributes aren't useful. But for structure preserving wrappers like
+     * DOM, it is necessary to keep the namespace data around.
      */
     public void keepNamespaceAttributes() {
         this.keepNamespaceAttributes = true;
     }
-    // END android-added
 
-    private final boolean isProp(String n1, boolean prop, String n2) {
-        if (!n1.startsWith("http://xmlpull.org/v1/doc/"))
+    private boolean isProp(String n1, boolean prop, String n2) {
+        if (!n1.startsWith("http://xmlpull.org/v1/doc/")) {
             return false;
-        if (prop)
+        }
+        if (prop) {
             return n1.substring(42).equals(n2);
-        else
+        } else {
             return n1.substring(40).equals(n2);
+        }
     }
 
-    private final boolean adjustNsp() throws XmlPullParserException {
-
+    private boolean adjustNsp() throws XmlPullParserException {
         boolean any = false;
 
         for (int i = 0; i < attributeCount << 2; i += 4) {
-            // * 4 - 4; i >= 0; i -= 4) {
-
             String attrName = attributes[i + 2];
             int cut = attrName.indexOf(':');
             String prefix;
@@ -145,32 +141,26 @@
             if (cut != -1) {
                 prefix = attrName.substring(0, cut);
                 attrName = attrName.substring(cut + 1);
-            }
-            else if (attrName.equals("xmlns")) {
+            } else if (attrName.equals("xmlns")) {
                 prefix = attrName;
                 attrName = null;
-            }
-            else
+            } else {
                 continue;
+            }
 
             if (!prefix.equals("xmlns")) {
                 any = true;
-            }
-            else {
+            } else {
                 int j = (nspCounts[depth]++) << 1;
 
                 nspStack = ensureCapacity(nspStack, j + 2);
                 nspStack[j] = attrName;
                 nspStack[j + 1] = attributes[i + 3];
 
-                if (attrName != null && attributes[i + 3].isEmpty())
-                    error("illegal empty namespace");
+                if (attrName != null && attributes[i + 3].isEmpty()) {
+                    checkRelaxed("illegal empty namespace");
+                }
 
-                //  prefixMap = new PrefixMap (prefixMap, attrName, attr.getValue ());
-
-                //System.out.println (prefixMap);
-
-                // BEGIN android-changed
                 if (keepNamespaceAttributes) {
                     // explicitly set the namespace for unprefixed attributes
                     // such as xmlns="http://foo"
@@ -186,7 +176,6 @@
 
                     i -= 4;
                 }
-                // END android-changed
             }
         }
 
@@ -196,45 +185,33 @@
                 String attrName = attributes[i + 2];
                 int cut = attrName.indexOf(':');
 
-                if (cut == 0 && !relaxed)
+                if (cut == 0 && !relaxed) {
                     throw new RuntimeException(
-                        "illegal attribute name: " + attrName + " at " + this);
-
-                else if (cut != -1) {
+                            "illegal attribute name: " + attrName + " at " + this);
+                } else if (cut != -1) {
                     String attrPrefix = attrName.substring(0, cut);
 
                     attrName = attrName.substring(cut + 1);
 
                     String attrNs = getNamespace(attrPrefix);
 
-                    if (attrNs == null && !relaxed)
+                    if (attrNs == null && !relaxed) {
                         throw new RuntimeException(
-                            "Undefined Prefix: " + attrPrefix + " in " + this);
+                                "Undefined Prefix: " + attrPrefix + " in " + this);
+                    }
 
                     attributes[i] = attrNs;
                     attributes[i + 1] = attrPrefix;
                     attributes[i + 2] = attrName;
-
-                    /*
-                                        if (!relaxed) {
-                                            for (int j = (attributeCount << 2) - 4; j > i; j -= 4)
-                                                if (attrName.equals(attributes[j + 2])
-                                                    && attrNs.equals(attributes[j]))
-                                                    exception(
-                                                        "Duplicate Attribute: {"
-                                                            + attrNs
-                                                            + "}"
-                                                            + attrName);
-                                        }
-                        */
                 }
             }
         }
 
         int cut = name.indexOf(':');
 
-        if (cut == 0)
-            error("illegal tag name: " + name);
+        if (cut == 0) {
+            checkRelaxed("illegal tag name: " + name);
+        }
 
         if (cut != -1) {
             prefix = name.substring(0, cut);
@@ -244,49 +221,45 @@
         this.namespace = getNamespace(prefix);
 
         if (this.namespace == null) {
-            if (prefix != null)
-                error("undefined prefix: " + prefix);
+            if (prefix != null) {
+                checkRelaxed("undefined prefix: " + prefix);
+            }
             this.namespace = NO_NAMESPACE;
         }
 
         return any;
     }
 
-    private final String[] ensureCapacity(String[] arr, int required) {
-        if (arr.length >= required)
+    private String[] ensureCapacity(String[] arr, int required) {
+        if (arr.length >= required) {
             return arr;
+        }
         String[] bigger = new String[required + 16];
         System.arraycopy(arr, 0, bigger, 0, arr.length);
         return bigger;
     }
 
-    private final void error(String desc) throws XmlPullParserException {
-        if (relaxed) {
-            if (error == null)
-                error = "ERR: " + desc;
+    private void checkRelaxed(String errorMessage) throws XmlPullParserException {
+        if (!relaxed) {
+            throw new XmlPullParserException(errorMessage, this, null);
         }
-        else
-            exception(desc);
-    }
-
-    private final void exception(String desc) throws XmlPullParserException {
-        throw new XmlPullParserException(
-            desc.length() < 100 ? desc : desc.substring(0, 100) + "\n",
-            this,
-            null);
+        if (error == null) {
+            error = "Error: " + errorMessage;
+        }
     }
 
     /**
-     * common base for next and nextToken. Clears the state, except from
-     * txtPos and whitespace. Does not set the type variable */
+     * Common base for next() and nextToken(). Clears the state, except from txtPos and whitespace.
+     * Does not set the type variable.
+     */
+    private void nextImpl() throws IOException, XmlPullParserException {
+        if (reader == null) {
+            throw new XmlPullParserException("setInput() must be called first.", this, null);
+        }
 
-    private final void nextImpl() throws IOException, XmlPullParserException {
-
-        if (reader == null)
-            exception("No Input specified");
-
-        if (type == END_TAG)
+        if (type == END_TAG) {
             depth--;
+        }
 
         while (true) {
             attributeCount = -1;
@@ -300,432 +273,398 @@
                 return;
             }
 
-
             if (error != null) {
-                for (int i = 0; i < error.length(); i++)
-                    push(error.charAt(i));
-                //text = error;
+                text = error;
                 error = null;
                 type = COMMENT;
                 return;
             }
 
-
-//            if (relaxed
-//                && (stackMismatch > 0 || (peek(0) == -1 && depth > 0))) {
-//                int sp = (depth - 1) << 2;
-//                type = END_TAG;
-//                namespace = elementStack[sp];
-//                prefix = elementStack[sp + 1];
-//                name = elementStack[sp + 2];
-//                if (stackMismatch != 1)
-//                    error = "missing end tag /" + name + " inserted";
-//                if (stackMismatch > 0)
-//                    stackMismatch--;
-//                return;
-//            }
-
             prefix = null;
             name = null;
             namespace = null;
-            //            text = null;
 
             type = peekType();
 
             switch (type) {
 
-                case ENTITY_REF :
-                    pushEntity();
+            case ENTITY_REF:
+                if (token) {
+                    StringBuilder entityTextBuilder = new StringBuilder();
+                    readEntity(entityTextBuilder);
+                    text = entityTextBuilder.toString();
                     return;
+                }
+                // fall-through
+            case TEXT:
+                text = readValue('<', !token, false);
+                if (depth == 0 && isWhitespace) {
+                    type = IGNORABLE_WHITESPACE;
+                }
+                return;
 
-                case START_TAG :
-                    parseStartTag(false);
-                    return;
+            case START_TAG:
+                text = null; // TODO: fix next()/nextToken() so this is handled there
+                parseStartTag(false);
+                return;
 
-                case END_TAG :
-                    parseEndTag();
-                    return;
+            case END_TAG:
+                readEndTag();
+                return;
 
-                case END_DOCUMENT :
-                    return;
+            case END_DOCUMENT:
+                return;
 
-                case TEXT :
-                    // BEGIN android-changed: distinguish attribute values from normal text.
-                    pushText('<', !token, false);
-                    // END android-changed
-                    if (depth == 0) {
-                        if (isWhitespace)
-                            type = IGNORABLE_WHITESPACE;
-                        // make exception switchable for instances.chg... !!!!
-                        // else
-                        //    exception ("text '"+getText ()+"' not allowed outside root element");
-                    }
-                    return;
+            case XML_DECLARATION:
+                readXmlDeclaration();
+                continue;
 
-                default :
-                    type = parseLegacy(token);
-                    if (type != XML_DECL)
-                        return;
+            case PROCESSING_INSTRUCTION:
+                read(START_PROCESSING_INSTRUCTION);
+                if (token) {
+                    text = readUntil(END_PROCESSING_INSTRUCTION, true);
+                } else {
+                    readUntil(END_PROCESSING_INSTRUCTION, false);
+                }
+                return;
+
+            case DOCDECL:
+                readDoctype(token);
+                return;
+
+            case CDSECT:
+                String oldText = text;
+                read(START_CDATA);
+                text = readUntil(END_CDATA, true);
+                if (oldText != null) {
+                    text = oldText + text; // TODO: fix next()/nextToken() so this is handled there
+                }
+                return;
+
+            case COMMENT:
+                read(START_COMMENT);
+                if (token) {
+                    text = readUntil(END_COMMENT, true);
+                } else {
+                    readUntil(END_COMMENT, false);
+                }
+                return;
             }
         }
     }
 
-    private final int parseLegacy(boolean push)
-        throws IOException, XmlPullParserException {
+    /**
+     * Reads text until the specified delimiter is encountered. Consumes the
+     * text and the delimiter.
+     *
+     * @param returnText true to return the read text excluding the delimiter;
+     *     false to return null.
+     */
+    private String readUntil(char[] delimiter, boolean returnText)
+            throws IOException, XmlPullParserException {
+        int previous = -1;
+        int start = position;
+        StringBuilder result = null;
 
-        String req = "";
-        int term;
-        int result;
-        int prev = 0;
-
-        read(); // <
-        int c = read();
-
-        if (c == '?') {
-            if ((peek(0) == 'x' || peek(0) == 'X')
-                && (peek(1) == 'm' || peek(1) == 'M')) {
-
-                if (push) {
-                    push(peek(0));
-                    push(peek(1));
-                }
-                read();
-                read();
-
-                if ((peek(0) == 'l' || peek(0) == 'L') && peek(1) <= ' ') {
-
-                    if (line != 1 || column > 4)
-                        error("PI must not start with xml");
-
-                    parseStartTag(true);
-
-                    if (attributeCount < 1 || !"version".equals(attributes[2]))
-                        error("version expected");
-
-                    version = attributes[3];
-
-                    int pos = 1;
-
-                    if (pos < attributeCount
-                        && "encoding".equals(attributes[2 + 4])) {
-                        encoding = attributes[3 + 4];
-                        pos++;
+        search:
+        while (true) {
+            if (position + delimiter.length >= limit) {
+                if (start < position && returnText) {
+                    if (result == null) {
+                        result = new StringBuilder();
                     }
+                    result.append(buffer, start, position - start);
+                }
+                if (!fillBuffer(delimiter.length)) {
+                    checkRelaxed(UNEXPECTED_EOF);
+                    type = COMMENT;
+                    return null;
+                }
+                start = position;
+            }
 
-                    if (pos < attributeCount
-                        && "standalone".equals(attributes[4 * pos + 2])) {
-                        String st = attributes[3 + 4 * pos];
-                        if ("yes".equals(st))
-                            standalone = new Boolean(true);
-                        else if ("no".equals(st))
-                            standalone = new Boolean(false);
-                        else
-                            error("illegal standalone value: " + st);
-                        pos++;
-                    }
-
-                    if (pos != attributeCount)
-                        error("illegal xmldecl");
-
-                    isWhitespace = true;
-                    txtPos = 0;
-
-                    return XML_DECL;
+            // TODO: replace with Arrays.equals(buffer, position, delimiter, 0, delimiter.length)
+            // when the VM has better method inlining
+            for (int i = 0; i < delimiter.length; i++) {
+                if (buffer[position + i] != delimiter[i]) {
+                    previous = buffer[position];
+                    position++;
+                    continue search;
                 }
             }
 
-            /*            int c0 = read ();
-                        int c1 = read ();
-                        int */
-
-            term = '?';
-            result = PROCESSING_INSTRUCTION;
-        }
-        else if (c == '!') {
-            if (peek(0) == '-') {
-                result = COMMENT;
-                req = "--";
-                term = '-';
-            }
-            else if (peek(0) == '[') {
-                result = CDSECT;
-                req = "[CDATA[";
-                term = ']';
-                push = true;
-            }
-            else {
-                result = DOCDECL;
-                req = "DOCTYPE";
-                term = -1;
-            }
-        }
-        else {
-            error("illegal: <" + c);
-            return COMMENT;
+            break;
         }
 
-        for (int i = 0; i < req.length(); i++)
-            read(req.charAt(i));
-
-        if (result == DOCDECL)
-            parseDoctype(push);
-        else {
-            while (true) {
-                c = read();
-                if (c == -1){
-                    error(UNEXPECTED_EOF);
-                    return COMMENT;
-                }
-
-                if (push)
-                    push(c);
-
-                if ((term == '?' || c == term)
-                    && peek(0) == term
-                    && peek(1) == '>')
-                    break;
-
-                prev = c;
-            }
-
-            if (term == '-' && prev == '-' && !relaxed)
-                error("illegal comment delimiter: --->");
-
-            read();
-            read();
-
-            if (push && term != '?')
-                txtPos--;
-
+        if (delimiter == END_COMMENT && previous == '-') {
+            checkRelaxed("illegal comment delimiter: --->");
         }
-        return result;
+
+        int end = position;
+        position += delimiter.length;
+
+        if (!returnText) {
+            return null;
+        } else if (result == null) {
+            return stringPool.get(buffer, start, end - start);
+        } else {
+            result.append(buffer, start, end - start);
+            return result.toString();
+        }
     }
 
-    /** precondition: &lt! consumed */
+    /**
+     * Returns true if an XML declaration was read.
+     */
+    private boolean readXmlDeclaration() throws IOException, XmlPullParserException {
+        if (bufferStartLine != 0 || bufferStartColumn != 0 || position != 0) {
+            checkRelaxed("processing instructions must not start with xml");
+        }
 
-    private final void parseDoctype(boolean push)
-        throws IOException, XmlPullParserException {
+        read(START_PROCESSING_INSTRUCTION);
+        parseStartTag(true);
 
+        if (attributeCount < 1 || !"version".equals(attributes[2])) {
+            checkRelaxed("version expected");
+        }
+
+        version = attributes[3];
+
+        int pos = 1;
+
+        if (pos < attributeCount && "encoding".equals(attributes[2 + 4])) {
+            encoding = attributes[3 + 4];
+            pos++;
+        }
+
+        if (pos < attributeCount && "standalone".equals(attributes[4 * pos + 2])) {
+            String st = attributes[3 + 4 * pos];
+            if ("yes".equals(st)) {
+                standalone = Boolean.TRUE;
+            } else if ("no".equals(st)) {
+                standalone = Boolean.FALSE;
+            } else {
+                checkRelaxed("illegal standalone value: " + st);
+            }
+            pos++;
+        }
+
+        if (pos != attributeCount) {
+            checkRelaxed("unexpected attributes in XML declaration");
+        }
+
+        isWhitespace = true;
+        text = null;
+        return true;
+    }
+
+    private void readDoctype(boolean assignText) throws IOException, XmlPullParserException {
+        read(START_DOCTYPE);
+
+        int start = position;
+        StringBuilder result = null;
         int nesting = 1;
         boolean quoted = false;
 
-        // read();
-
         while (true) {
-            int i = read();
-            switch (i) {
-
-                case -1 :
-                    error(UNEXPECTED_EOF);
-                    return;
-
-                case '\'' :
-                    quoted = !quoted;
-                    break;
-
-                case '<' :
-                    if (!quoted)
-                        nesting++;
-                    break;
-
-                case '>' :
-                    if (!quoted) {
-                        if ((--nesting) == 0)
-                            return;
+            if (position >= limit) {
+                if (start < position && assignText) {
+                    if (result == null) {
+                        result = new StringBuilder();
                     }
-                    break;
+                    result.append(buffer, start, position - start);
+                }
+                if (!fillBuffer(1)) {
+                    checkRelaxed(UNEXPECTED_EOF);
+                    return;
+                }
+                start = position;
             }
-            if (push)
-                push(i);
+
+            char i = buffer[position++];
+
+            if (i == '\'') {
+                quoted = !quoted; // TODO: should this include a double quote as well?
+            } else if (i == '<') {
+                if (!quoted) {
+                    nesting++;
+                }
+            } else if (i == '>') {
+                if (!quoted && --nesting == 0) {
+                    break;
+                }
+            }
+        }
+
+        if (assignText) {
+            if (result == null) {
+                text = stringPool.get(buffer, start, position - start - 1); // omit the '>'
+            } else {
+                result.append(buffer, start, position - start - 1); // omit the '>'
+                text = result.toString();
+            }
         }
     }
 
-    /* precondition: &lt;/ consumed */
-
-    private final void parseEndTag()
-        throws IOException, XmlPullParserException {
-
-        read(); // '<'
-        read(); // '/'
-        name = readName();
+    private void readEndTag() throws IOException, XmlPullParserException {
+        read('<');
+        read('/');
+        name = readName(); // TODO: pass the expected name in as a hint?
         skip();
         read('>');
 
-        int sp = (depth - 1) << 2;
+        int sp = (depth - 1) * 4;
 
         if (depth == 0) {
-            error("element stack empty");
+            checkRelaxed("read end tag " + name + " with no tags open");
             type = COMMENT;
             return;
         }
 
         if (!relaxed) {
-          if (!name.equals(elementStack[sp + 3])) {
-            error("expected: /" + elementStack[sp + 3] + " read: " + name);
+            if (!name.equals(elementStack[sp + 3])) {
+                throw new XmlPullParserException(
+                        "expected: /" + elementStack[sp + 3] + " read: " + name, this, null);
+            }
 
-              // become case insensitive in relaxed mode
-
-//            int probe = sp;
-//            while (probe >= 0 && !name.toLowerCase().equals(elementStack[probe + 3].toLowerCase())) {
-//                stackMismatch++;
-//                probe -= 4;
-//            }
-//
-//            if (probe < 0) {
-//                stackMismatch = 0;
-//                // text = "unexpected end tag ignored";
-//                type = COMMENT;
-//                return;
-//            }
-        }
-
-        namespace = elementStack[sp];
-        prefix = elementStack[sp + 1];
-        name = elementStack[sp + 2];
+            namespace = elementStack[sp];
+            prefix = elementStack[sp + 1];
+            name = elementStack[sp + 2];
         }
     }
 
-    private final int peekType() throws IOException {
-        switch (peek(0)) {
-            case -1 :
-                return END_DOCUMENT;
-            case '&' :
-                return ENTITY_REF;
-            case '<' :
-                switch (peek(1)) {
-                    case '/' :
-                        return END_TAG;
-                    case '?' :
-                    case '!' :
-                        return LEGACY;
-                    default :
-                        return START_TAG;
+    /**
+     * Returns the type of the next token.
+     */
+    private int peekType() throws IOException, XmlPullParserException {
+        if (position >= limit && !fillBuffer(1)) {
+            return END_DOCUMENT;
+        }
+
+        if (buffer[position] == '&') {
+            return ENTITY_REF;
+
+        } else if (buffer[position] == '<') {
+            if (position + 2 >= limit && !fillBuffer(3)) {
+                throw new XmlPullParserException("Dangling <", this, null);
+            }
+
+            if (buffer[position + 1] == '/') {
+                return END_TAG;
+            } else if (buffer[position + 1] == '?') {
+                // we're looking for "<?xml " with case insensitivity
+                if ((position + 5 < limit || fillBuffer(6))
+                        && (buffer[position + 2] == 'x' || buffer[position + 2] == 'X')
+                        && (buffer[position + 3] == 'm' || buffer[position + 3] == 'M')
+                        && (buffer[position + 4] == 'l' || buffer[position + 4] == 'L')
+                        && (buffer[position + 5] == ' ')) {
+                    return XML_DECLARATION;
+                } else {
+                    return PROCESSING_INSTRUCTION;
                 }
-            default :
-                return TEXT;
+            } else if (buffer[position + 1] == '!') {
+                if (buffer[position + 2] == START_DOCTYPE[2]) {
+                    return DOCDECL;
+                } else if (buffer[position + 2] == START_CDATA[2]) {
+                    return CDSECT;
+                } else if (buffer[position + 2] == START_COMMENT[2]) {
+                    return COMMENT;
+                } else {
+                    throw new XmlPullParserException("Unexpected <!", this, null);
+                }
+            } else {
+                return START_TAG;
+            }
+        } else {
+            return TEXT;
         }
     }
 
-    private final String get(int pos) {
-        return new String(txtBuf, pos, txtPos - pos);
-    }
-
-    /*
-    private final String pop (int pos) {
-    String result = new String (txtBuf, pos, txtPos - pos);
-    txtPos = pos;
-    return result;
-    }
-    */
-
-    private final void push(int c) {
-
-        isWhitespace &= c <= ' ';
-
-        if (txtPos == txtBuf.length) {
-            char[] bigger = new char[txtPos * 4 / 3 + 4];
-            System.arraycopy(txtBuf, 0, bigger, 0, txtPos);
-            txtBuf = bigger;
+    /**
+     * Sets name and attributes
+     */
+    private void parseStartTag(boolean xmldecl) throws IOException, XmlPullParserException {
+        if (!xmldecl) {
+            read('<');
         }
-
-        txtBuf[txtPos++] = (char) c;
-    }
-
-    /** Sets name and attributes */
-
-    private final void parseStartTag(boolean xmldecl)
-        throws IOException, XmlPullParserException {
-
-        if (!xmldecl)
-            read();
         name = readName();
         attributeCount = 0;
 
         while (true) {
             skip();
 
-            int c = peek(0);
+            if (position >= limit && !fillBuffer(1)) {
+                checkRelaxed(UNEXPECTED_EOF);
+                return;
+            }
+
+            int c = buffer[position];
 
             if (xmldecl) {
                 if (c == '?') {
-                    read();
+                    position++;
                     read('>');
                     return;
                 }
-            }
-            else {
+            } else {
                 if (c == '/') {
                     degenerated = true;
-                    read();
+                    position++;
                     skip();
                     read('>');
                     break;
-                }
-
-                if (c == '>' && !xmldecl) {
-                    read();
+                } else if (c == '>') {
+                    position++;
                     break;
                 }
             }
 
-            if (c == -1) {
-                error(UNEXPECTED_EOF);
-                //type = COMMENT;
-                return;
-            }
-
             String attrName = readName();
 
-            if (attrName.length() == 0) {
-                error("attr name expected");
-               //type = COMMENT;
-                break;
-            }
-
-            int i = (attributeCount++) << 2;
-
+            int i = (attributeCount++) * 4;
             attributes = ensureCapacity(attributes, i + 4);
-
             attributes[i++] = "";
             attributes[i++] = null;
             attributes[i++] = attrName;
 
             skip();
+            if (position >= limit && !fillBuffer(1)) {
+                checkRelaxed(UNEXPECTED_EOF);
+                return;
+            }
 
-            if (peek(0) != '=') {
-                if(!relaxed){
-                    error("Attr.value missing f. "+attrName);
-                }
-                attributes[i] = attrName;
-            } else {
-                read('=');
+            if (buffer[position] == '=') {
+                position++;
+
                 skip();
-                int delimiter = peek(0);
+                if (position >= limit && !fillBuffer(1)) {
+                    checkRelaxed(UNEXPECTED_EOF);
+                    return;
+                }
+                char delimiter = buffer[position];
 
-                if (delimiter != '\'' && delimiter != '"') {
-                    if(!relaxed){
-                        error("attr value delimiter missing!");
-                    }
+                if (delimiter == '\'' || delimiter == '"') {
+                    position++;
+                } else if (relaxed) {
                     delimiter = ' ';
                 } else {
-                    read();
+                    throw new XmlPullParserException("attr value delimiter missing!", this, null);
                 }
 
-                int p = txtPos;
-                // BEGIN android-changed: distinguish attribute values from normal text.
-                pushText(delimiter, true, true);
-                // END android-changed
+                attributes[i] = readValue(delimiter, true, true);
 
-                attributes[i] = get(p);
-                txtPos = p;
-
-                if (delimiter != ' ')
-                    read(); // skip endquote
+                if (delimiter != ' ') {
+                    position++; // end quote
+                }
+            } else if (relaxed) {
+                attributes[i] = attrName;
+            } else {
+                checkRelaxed("Attr.value missing f. " + attrName);
+                attributes[i] = attrName;
             }
         }
 
-        int sp = depth++ << 2;
-
+        int sp = depth++ * 4;
         elementStack = ensureCapacity(elementStack, sp + 4);
         elementStack[sp + 3] = name;
 
@@ -737,20 +676,11 @@
 
         nspCounts[depth] = nspCounts[depth - 1];
 
-        /*
-            if(!relaxed){
-                for (int i = attributeCount - 1; i > 0; i--) {
-                    for (int j = 0; j < i; j++) {
-                        if (getAttributeName(i).equals(getAttributeName(j)))
-                            exception("Duplicate Attribute: " + getAttributeName(i));
-                    }
-                }
-            }
-        */
-        if (processNsp)
+        if (processNsp) {
             adjustNsp();
-        else
+        } else {
             namespace = "";
+        }
 
         elementStack[sp] = namespace;
         elementStack[sp + 1] = prefix;
@@ -758,236 +688,352 @@
     }
 
     /**
-     * result: isWhitespace; if the setName parameter is set,
-     * the name of the entity is stored in "name" */
+     * Reads an entity reference from the buffer, resolves it, and writes the
+     * resolved entity to {@code out}. If the entity cannot be read or resolved,
+     * {@code out} will contain the partial entity reference.
+     */
+    private void readEntity(StringBuilder out) throws IOException, XmlPullParserException {
+        int start = out.length();
 
-    private final void pushEntity()
-        throws IOException, XmlPullParserException {
-
-        push(read()); // &
-
-
-        int pos = txtPos;
-
-        while (true) {
-            int c = peek(0);
-            if (c == ';') {
-              read();
-              break;
-            }
-            if (c < 128 && (c < '0' || c > '9') && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z')
-                    && c != '_' && c != '-' && c != '#') {
-                if(!relaxed){
-                    error("unterminated entity ref");
-                }
-
-                // BEGIN android-removed: avoid log spam.
-                // System.out.println("broken entitiy: "+get(pos-1));
-                // END android-removed
-
-                //; ends with:"+(char)c);
-//                if (c != -1)
-//                    push(c);
-                return;
-            }
-
-            push(read());
+        if (buffer[position++] != '&') {
+            throw new AssertionError();
         }
 
-        String code = get(pos);
-        txtPos = pos - 1;
-        if (token && type == ENTITY_REF){
+        out.append('&');
+
+        while (true) {
+            int c = peekCharacter();
+
+            if (c == ';') {
+                position++;
+                break;
+
+            } else if (c >= 128
+                    || (c >= '0' && c <= '9')
+                    || (c >= 'a' && c <= 'z')
+                    || (c >= 'A' && c <= 'Z')
+                    || c == '_'
+                    || c == '-'
+                    || c == '#') {
+                position++;
+                out.append((char) c);
+
+            } else if (relaxed) {
+                // intentionally leave the partial reference in 'out'
+                return;
+
+            } else {
+                throw new XmlPullParserException("unterminated entity ref", this, null);
+            }
+        }
+
+        String code = out.substring(start + 1);
+        out.delete(start, out.length());
+
+        if (token && type == ENTITY_REF) {
             name = code;
         }
 
         if (code.charAt(0) == '#') {
-            int c =
-                (code.charAt(1) == 'x'
+            // TODO: check IndexOutOfBoundsException?
+            // TODO: save an intermediate string for 'code' if unneeded?
+            int c = code.charAt(1) == 'x'
                     ? Integer.parseInt(code.substring(2), 16)
-                    : Integer.parseInt(code.substring(1)));
-            push(c);
+                    : Integer.parseInt(code.substring(1));
+            // TODO: set unresolved to false?
+            out.append((char) c);
             return;
         }
 
-        String result = (String) entityMap.get(code);
-
-        unresolved = result == null;
-
-        if (unresolved) {
-            if (!token)
-                error("unresolved: &" + code + ";");
+        String resolved = entityMap.get(code);
+        if (resolved != null) {
+            unresolved = false;
+            out.append(resolved);
+            return;
         }
-        else {
-            for (int i = 0; i < result.length(); i++)
-                push(result.charAt(i));
+
+        unresolved = true;
+        if (!token) {
+            checkRelaxed("unresolved: &" + code + ";");
+            // TODO: should the &code; show up in the text in relaxed mode?
         }
     }
 
-    /** types:
-    '<': parse to any token (for nextToken ())
-    '"': parse to quote
-    ' ': parse to whitespace or '>'
-    */
+    /**
+     * Returns the current text or attribute value. This also has the side
+     * effect of setting isWhitespace to false if a non-whitespace character is
+     * encountered.
+     *
+     * @param delimiter {@code >} for text, {@code "} and {@code '} for quoted
+     *     attributes, or a space for unquoted attributes.
+     */
+    private String readValue(char delimiter, boolean resolveEntities,
+            boolean inAttributeValue) throws IOException, XmlPullParserException {
 
-    private final void pushText(int delimiter, boolean resolveEntities, boolean inAttributeValue)
-        throws IOException, XmlPullParserException {
+        /*
+         * This method returns all of the characters from the current position
+         * through to an appropriate delimiter.
+         *
+         * If we're lucky (which we usually are), we'll return a single slice of
+         * the buffer. This fast path avoids allocating a string builder.
+         *
+         * There are 5 unlucky characters we could encounter:
+         *  - "&":  entities must be resolved.
+         *  - "<":  this isn't permitted in attributes unless relaxed.
+         *  - "]":  this requires a lookahead to defend against the forbidden
+         *          CDATA section delimiter "]]>".
+         *  - "\r": If a "\r" is followed by a "\n", we discard the "\r". If it
+         *          isn't followed by "\n", we replace "\r" with either a "\n"
+         *          in text nodes or a space in attribute values.
+         *  - "\n": In attribute values, "\n" must be replaced with a space.
+         *
+         * We could also get unlucky by needing to refill the buffer midway
+         * through the text.
+         */
 
-        int next = peek(0);
-        int cbrCount = 0;
+        int start = position;
+        StringBuilder result = null;
 
-        while (next != -1 && next != delimiter) { // covers eof, '<', '"'
-
-            if (delimiter == ' ')
-                if (next <= ' ' || next == '>')
-                    break;
-
-            // BEGIN android-changed: "<" is not allowed in attribute values.
-            if (next == '&') {
-                if (!resolveEntities)
-                    break;
-
-                pushEntity();
-            }
-            else if (next == '<' && inAttributeValue) {
-                error("Illegal: \"<\" inside attribute value");
-            }
-            else if (next == '\n' && type == START_TAG) {
-                read();
-                push(' ');
-            }
-            else
-                push(read());
-            // END android-changed
-
-            // BEGIN android-changed: "]]>" *is* allowed in attribute values, but
-            // is not allowed in regular text between markup.
-            final boolean allowCloseCdata = inAttributeValue;
-            if (!allowCloseCdata && (next == '>' && cbrCount >= 2 && delimiter != ']')) {
-                error("Illegal: \"]]>\" outside CDATA section");
-            }
-            // END android-changed
-
-            if (next == ']')
-                cbrCount++;
-            else
-                cbrCount = 0;
-
-            next = peek(0);
+        // if a text section was already started, prefix the start
+        if (text != null) {
+            result = new StringBuilder();
+            result.append(text);
         }
-    }
-
-    private final void read(char c)
-        throws IOException, XmlPullParserException {
-        int a = read();
-        if (a != c)
-            error("expected: '" + c + "' actual: '" + ((char) a) + "'");
-    }
-
-    private final int read() throws IOException {
-        int result;
-
-        if (peekCount == 0)
-            result = peek(0);
-        else {
-            result = peek[0];
-            peek[0] = peek[1];
-        }
-        // else {
-        //     result = peek[0];
-        //     System.arraycopy (peek, 1, peek, 0, peekCount-1);
-        // }
-        peekCount--;
-
-        column++;
-
-        if (result == '\n') {
-
-            line++;
-            column = 1;
-        }
-
-        return result;
-    }
-
-    /** Does never read more than needed */
-
-    private final int peek(int pos) throws IOException {
-
-        while (pos >= peekCount) {
-
-            int nw;
-
-            if (srcBuf.length <= 1)
-                nw = reader.read();
-            else if (srcPos < srcCount)
-                nw = srcBuf[srcPos++];
-            else {
-                srcCount = reader.read(srcBuf, 0, srcBuf.length);
-                if (srcCount <= 0)
-                    nw = -1;
-                else
-                    nw = srcBuf[0];
-
-                srcPos = 1;
-            }
-
-            if (nw == '\r') {
-                wasCR = true;
-                peek[peekCount++] = '\n';
-            }
-            else {
-                if (nw == '\n') {
-                    if (!wasCR)
-                        peek[peekCount++] = '\n';
-                }
-                else
-                    peek[peekCount++] = nw;
-
-                wasCR = false;
-            }
-        }
-
-        return peek[pos];
-    }
-
-    private final String readName()
-        throws IOException, XmlPullParserException {
-
-        int pos = txtPos;
-        int c = peek(0);
-        if ((c < 'a' || c > 'z')
-            && (c < 'A' || c > 'Z')
-            && c != '_'
-            && c != ':'
-            && c < 0x0c0
-            && !relaxed)
-            error("name expected");
-
-        do {
-            push(read());
-            c = peek(0);
-        }
-        while ((c >= 'a' && c <= 'z')
-            || (c >= 'A' && c <= 'Z')
-            || (c >= '0' && c <= '9')
-            || c == '_'
-            || c == '-'
-            || c == ':'
-            || c == '.'
-            || c >= 0x0b7);
-
-        String result = get(pos);
-        txtPos = pos;
-        return result;
-    }
-
-    private final void skip() throws IOException {
 
         while (true) {
-            int c = peek(0);
-            if (c > ' ' || c == -1)
+
+            /*
+             * Make sure we have at least a single character to read from the
+             * buffer. This mutates the buffer, so save the partial result
+             * to the slow path string builder first.
+             */
+            if (position >= limit) {
+                if (start < position) {
+                    if (result == null) {
+                        result = new StringBuilder();
+                    }
+                    result.append(buffer, start, position - start);
+                }
+                if (!fillBuffer(1)) {
+                    return result != null ? result.toString() : "";
+                }
+                start = position;
+            }
+
+            char c = buffer[position];
+
+            if (c == delimiter
+                    || (delimiter == ' ' && (c <= ' ' || c == '>'))
+                    || c == '&' && !resolveEntities) {
                 break;
-            read();
+            }
+
+            if (c != '\r'
+                    && (c != '\n' || !inAttributeValue)
+                    && c != '&'
+                    && c != '<'
+                    && (c != ']' || inAttributeValue)) {
+                isWhitespace &= (c <= ' ');
+                position++;
+                continue;
+            }
+
+            /*
+             * We've encountered an unlucky character! Convert from fast
+             * path to slow path if we haven't done so already.
+             */
+            if (result == null) {
+                result = new StringBuilder();
+            }
+            result.append(buffer, start, position - start);
+
+            if (c == '\r') {
+                if ((position + 1 < limit || fillBuffer(2)) && buffer[position + 1] == '\n') {
+                    position++;
+                }
+                c = inAttributeValue ? ' ' : '\n';
+
+            } else if (c == '\n') {
+                c = ' ';
+
+            } else if (c == '&') {
+                isWhitespace = false; // TODO: what if the entity resolves to whitespace?
+                readEntity(result);
+                start = position;
+                continue;
+
+            } else if (c == '<') {
+                if (inAttributeValue) {
+                    checkRelaxed("Illegal: \"<\" inside attribute value");
+                }
+                isWhitespace = false;
+
+            } else if (c == ']') {
+                if ((position + 2 < limit || fillBuffer(3))
+                        && buffer[position + 1] == ']' && buffer[position + 2] == '>') {
+                    checkRelaxed("Illegal: \"]]>\" outside CDATA section");
+                }
+                isWhitespace = false;
+
+            } else {
+                throw new AssertionError();
+            }
+
+            position++;
+            result.append(c);
+            start = position;
+        }
+
+        if (result == null) {
+            return stringPool.get(buffer, start, position - start);
+        } else {
+            result.append(buffer, start, position - start);
+            return result.toString();
+        }
+    }
+
+    private void read(char expected) throws IOException, XmlPullParserException {
+        int c = peekCharacter();
+        if (c != expected) {
+            checkRelaxed("expected: '" + expected + "' actual: '" + ((char) c) + "'");
+        }
+        position++;
+    }
+
+    private void read(char[] chars) throws IOException, XmlPullParserException {
+        if (position + chars.length >= limit && !fillBuffer(chars.length)) {
+            checkRelaxed("expected: '" + new String(chars) + "' but was EOF");
+            return;
+        }
+
+        // TODO: replace with Arrays.equals(buffer, position, delimiter, 0, delimiter.length)
+        // when the VM has better method inlining
+        for (int i = 0; i < chars.length; i++) {
+            if (buffer[position + i] != chars[i]) {
+                checkRelaxed("expected: \"" + new String(chars) + "\" but was \""
+                        + new String(buffer, position, chars.length) + "...\"");
+            }
+        }
+
+        position += chars.length;
+    }
+
+    private int peekCharacter() throws IOException, XmlPullParserException {
+        if (position < limit || fillBuffer(1)) {
+            return buffer[position];
+        }
+        return -1;
+    }
+
+    /**
+     * Returns true once {@code limit - position >= minimum}. If the data is
+     * exhausted before that many characters are available, this returns
+     * false.
+     */
+    private boolean fillBuffer(int minimum) throws IOException {
+        // Before clobbering the old characters, update where buffer starts
+        for (int i = 0; i < position; i++) {
+            if (buffer[i] == '\n') {
+                bufferStartLine++;
+                bufferStartColumn = 0;
+            } else {
+                bufferStartColumn++;
+            }
+        }
+
+        if (limit != position) {
+            limit -= position;
+            System.arraycopy(buffer, position, buffer, 0, limit);
+        } else {
+            limit = 0;
+        }
+
+        position = 0;
+        int total;
+        while ((total = reader.read(buffer, limit, buffer.length - limit)) != -1) {
+            limit += total;
+            if (limit >= minimum) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns an element or attribute name. This is always non-empty for
+     * non-relaxed parsers.
+     */
+    private String readName() throws IOException, XmlPullParserException {
+        if (position >= limit && !fillBuffer(1)) {
+            checkRelaxed("name expected");
+            return "";
+        }
+
+        int start = position;
+        StringBuilder result = null;
+
+        // read the first character
+        char c = buffer[position];
+        if ((c >= 'a' && c <= 'z')
+                || (c >= 'A' && c <= 'Z')
+                || c == '_'
+                || c == ':'
+                || c >= '\u00c0' // TODO: check the XML spec
+                || relaxed) {
+            position++;
+        } else {
+            checkRelaxed("name expected");
+            return "";
+        }
+
+        while (true) {
+            /*
+             * Make sure we have at least a single character to read from the
+             * buffer. This mutates the buffer, so save the partial result
+             * to the slow path string builder first.
+             */
+            if (position >= limit) {
+                if (result == null) {
+                    result = new StringBuilder();
+                }
+                result.append(buffer, start, position - start);
+                if (!fillBuffer(1)) {
+                    return result.toString();
+                }
+                start = position;
+            }
+
+            // read another character
+            c = buffer[position];
+            if ((c >= 'a' && c <= 'z')
+                    || (c >= 'A' && c <= 'Z')
+                    || (c >= '0' && c <= '9')
+                    || c == '_'
+                    || c == '-'
+                    || c == ':'
+                    || c == '.'
+                    || c >= '\u00b7') {  // TODO: check the XML spec
+                position++;
+                continue;
+            }
+
+            // we encountered a non-name character. done!
+            if (result == null) {
+                return stringPool.get(buffer, start, position - start);
+            } else {
+                result.append(buffer, start, position - start);
+                return result.toString();
+            }
+        }
+    }
+
+    private void skip() throws IOException {
+        while (position < limit || fillBuffer(1)) {
+            int c = buffer[position];
+            if (c > ' ') {
+                break;
+            }
+            position++;
         }
     }
 
@@ -996,8 +1042,6 @@
     public void setInput(Reader reader) throws XmlPullParserException {
         this.reader = reader;
 
-        line = 1;
-        column = 0;
         type = START_DOCUMENT;
         name = null;
         namespace = null;
@@ -1007,15 +1051,17 @@
         version = null;
         standalone = null;
 
-        if (reader == null)
+        if (reader == null) {
             return;
+        }
 
-        srcPos = 0;
-        srcCount = 0;
-        peekCount = 0;
+        position = 0;
+        limit = 0;
+        bufferStartLine = 0;
+        bufferStartColumn = 0;
         depth = 0;
 
-        entityMap = new Hashtable();
+        entityMap = new HashMap<String, String>();
         entityMap.put("amp", "&");
         entityMap.put("apos", "'");
         entityMap.put("gt", ">");
@@ -1023,82 +1069,81 @@
         entityMap.put("quot", "\"");
     }
 
-    public void setInput(InputStream is, String _enc)
-        throws XmlPullParserException {
-
-        srcPos = 0;
-        srcCount = 0;
+    public void setInput(InputStream is, String _enc) throws XmlPullParserException {
+        position = 0;
+        limit = 0;
         String enc = _enc;
 
-        if (is == null)
+        if (is == null) {
             throw new IllegalArgumentException();
+        }
 
         try {
-
             if (enc == null) {
-                // read four bytes
-
-                int chk = 0;
-
-                while (srcCount < 4) {
+                // read the four bytes looking for an indication of the encoding in use
+                int firstFourBytes = 0;
+                while (limit < 4) {
                     int i = is.read();
-                    if (i == -1)
+                    if (i == -1) {
                         break;
-                    chk = (chk << 8) | i;
-                    srcBuf[srcCount++] = (char) i;
+                    }
+                    firstFourBytes = (firstFourBytes << 8) | i;
+                    buffer[limit++] = (char) i;
                 }
 
-                if (srcCount == 4) {
-                    switch (chk) {
-                        case 0x00000FEFF :
+                if (limit == 4) {
+                    switch (firstFourBytes) {
+                        case 0x00000FEFF: // UTF-32BE BOM
                             enc = "UTF-32BE";
-                            srcCount = 0;
+                            limit = 0;
                             break;
 
-                        case 0x0FFFE0000 :
+                        case 0x0FFFE0000: // UTF-32LE BOM
                             enc = "UTF-32LE";
-                            srcCount = 0;
+                            limit = 0;
                             break;
 
-                        case 0x03c :
+                        case 0x0000003c: // '>' in UTF-32BE
                             enc = "UTF-32BE";
-                            srcBuf[0] = '<';
-                            srcCount = 1;
+                            buffer[0] = '<';
+                            limit = 1;
                             break;
 
-                        case 0x03c000000 :
+                        case 0x03c000000: // '<' in UTF-32LE
                             enc = "UTF-32LE";
-                            srcBuf[0] = '<';
-                            srcCount = 1;
+                            buffer[0] = '<';
+                            limit = 1;
                             break;
 
-                        case 0x0003c003f :
+                        case 0x0003c003f: // "<?" in UTF-16BE
                             enc = "UTF-16BE";
-                            srcBuf[0] = '<';
-                            srcBuf[1] = '?';
-                            srcCount = 2;
+                            buffer[0] = '<';
+                            buffer[1] = '?';
+                            limit = 2;
                             break;
 
-                        case 0x03c003f00 :
+                        case 0x03c003f00: // "<?" in UTF-16LE
                             enc = "UTF-16LE";
-                            srcBuf[0] = '<';
-                            srcBuf[1] = '?';
-                            srcCount = 2;
+                            buffer[0] = '<';
+                            buffer[1] = '?';
+                            limit = 2;
                             break;
 
-                        case 0x03c3f786d :
+                        case 0x03c3f786d: // "<?xm" in ASCII etc.
                             while (true) {
                                 int i = is.read();
-                                if (i == -1)
+                                if (i == -1) {
                                     break;
-                                srcBuf[srcCount++] = (char) i;
+                                }
+                                buffer[limit++] = (char) i;
                                 if (i == '>') {
-                                    String s = new String(srcBuf, 0, srcCount);
+                                    String s = new String(buffer, 0, limit);
                                     int i0 = s.indexOf("encoding");
                                     if (i0 != -1) {
                                         while (s.charAt(i0) != '"'
-                                            && s.charAt(i0) != '\'')
+                                                && s.charAt(i0) != '\'') {
                                             i0++;
+                                        }
                                         char deli = s.charAt(i0++);
                                         int i1 = s.indexOf(deli, i0);
                                         enc = s.substring(i0, i1);
@@ -1107,51 +1152,46 @@
                                 }
                             }
 
-                        default :
-                            if ((chk & 0x0ffff0000) == 0x0FEFF0000) {
+                        default:
+                            // handle a byte order mark followed by something other than <?
+                            if ((firstFourBytes & 0x0ffff0000) == 0x0FEFF0000) {
                                 enc = "UTF-16BE";
-                                srcBuf[0] =
-                                    (char) ((srcBuf[2] << 8) | srcBuf[3]);
-                                srcCount = 1;
-                            }
-                            else if ((chk & 0x0ffff0000) == 0x0fffe0000) {
+                                buffer[0] = (char) ((buffer[2] << 8) | buffer[3]);
+                                limit = 1;
+                            } else if ((firstFourBytes & 0x0ffff0000) == 0x0fffe0000) {
                                 enc = "UTF-16LE";
-                                srcBuf[0] =
-                                    (char) ((srcBuf[3] << 8) | srcBuf[2]);
-                                srcCount = 1;
-                            }
-                            else if ((chk & 0x0ffffff00) == 0x0EFBBBF00) {
+                                buffer[0] = (char) ((buffer[3] << 8) | buffer[2]);
+                                limit = 1;
+                            } else if ((firstFourBytes & 0x0ffffff00) == 0x0EFBBBF00) {
                                 enc = "UTF-8";
-                                srcBuf[0] = srcBuf[3];
-                                srcCount = 1;
+                                buffer[0] = buffer[3];
+                                limit = 1;
                             }
                     }
                 }
             }
 
-            if (enc == null)
+            if (enc == null) {
                 enc = "UTF-8";
+            }
 
-            int sc = srcCount;
+            int sc = limit;
             setInput(new InputStreamReader(is, enc));
             encoding = _enc;
-            srcCount = sc;
-        }
-        catch (Exception e) {
-            throw new XmlPullParserException(
-                "Invalid stream or encoding: " + e.toString(),
-                this,
-                e);
+            limit = sc;
+        } catch (Exception e) {
+            throw new XmlPullParserException("Invalid stream or encoding: " + e, this, e);
         }
     }
 
     public boolean getFeature(String feature) {
-        if (XmlPullParser.FEATURE_PROCESS_NAMESPACES.equals(feature))
+        if (XmlPullParser.FEATURE_PROCESS_NAMESPACES.equals(feature)) {
             return processNsp;
-        else if (isProp(feature, false, "relaxed"))
+        } else if (isProp(feature, false, "relaxed")) {
             return relaxed;
-        else
+        } else {
             return false;
+        }
     }
 
     public String getInputEncoding() {
@@ -1159,50 +1199,58 @@
     }
 
     public void defineEntityReplacementText(String entity, String value)
-        throws XmlPullParserException {
-        if (entityMap == null)
+            throws XmlPullParserException {
+        if (entityMap == null) {
             throw new RuntimeException("entity replacement text must be defined after setInput!");
+        }
         entityMap.put(entity, value);
     }
 
     public Object getProperty(String property) {
-        if (isProp(property, true, "xmldecl-version"))
+        if (isProp(property, true, "xmldecl-version")) {
             return version;
-        if (isProp(property, true, "xmldecl-standalone"))
+        }
+        if (isProp(property, true, "xmldecl-standalone")) {
             return standalone;
-        if (isProp(property, true, "location"))
+        }
+        if (isProp(property, true, "location")) {
             return location != null ? location : reader.toString();
+        }
         return null;
     }
 
     public int getNamespaceCount(int depth) {
-        if (depth > this.depth)
+        if (depth > this.depth) {
             throw new IndexOutOfBoundsException();
+        }
         return nspCounts[depth];
     }
 
     public String getNamespacePrefix(int pos) {
-        return nspStack[pos << 1];
+        return nspStack[pos * 2];
     }
 
     public String getNamespaceUri(int pos) {
-        return nspStack[(pos << 1) + 1];
+        return nspStack[(pos * 2) + 1];
     }
 
     public String getNamespace(String prefix) {
 
-        if ("xml".equals(prefix))
+        if ("xml".equals(prefix)) {
             return "http://www.w3.org/XML/1998/namespace";
-        if ("xmlns".equals(prefix))
+        }
+        if ("xmlns".equals(prefix)) {
             return "http://www.w3.org/2000/xmlns/";
+        }
 
         for (int i = (getNamespaceCount(depth) << 1) - 2; i >= 0; i -= 2) {
             if (prefix == null) {
-                if (nspStack[i] == null)
+                if (nspStack[i] == null) {
                     return nspStack[i + 1];
-            }
-            else if (prefix.equals(nspStack[i]))
+                }
+            } else if (prefix.equals(nspStack[i])) {
                 return nspStack[i + 1];
+            }
         }
         return null;
     }
@@ -1212,50 +1260,52 @@
     }
 
     public String getPositionDescription() {
-
-        StringBuffer buf =
-            new StringBuffer(type < TYPES.length ? TYPES[type] : "unknown");
+        StringBuilder buf = new StringBuilder(type < TYPES.length ? TYPES[type] : "unknown");
         buf.append(' ');
 
         if (type == START_TAG || type == END_TAG) {
-            if (degenerated)
+            if (degenerated) {
                 buf.append("(empty) ");
+            }
             buf.append('<');
-            if (type == END_TAG)
+            if (type == END_TAG) {
                 buf.append('/');
+            }
 
-            if (prefix != null)
+            if (prefix != null) {
                 buf.append("{" + namespace + "}" + prefix + ":");
+            }
             buf.append(name);
 
-            int cnt = attributeCount << 2;
+            int cnt = attributeCount * 4;
             for (int i = 0; i < cnt; i += 4) {
                 buf.append(' ');
-                if (attributes[i + 1] != null)
-                    buf.append(
-                        "{" + attributes[i] + "}" + attributes[i + 1] + ":");
+                if (attributes[i + 1] != null) {
+                    buf.append("{" + attributes[i] + "}" + attributes[i + 1] + ":");
+                }
                 buf.append(attributes[i + 2] + "='" + attributes[i + 3] + "'");
             }
 
             buf.append('>');
-        }
-        else if (type == IGNORABLE_WHITESPACE);
-        else if (type != TEXT)
+        } else if (type == IGNORABLE_WHITESPACE) {
+            ;
+        } else if (type != TEXT) {
             buf.append(getText());
-        else if (isWhitespace)
+        } else if (isWhitespace) {
             buf.append("(whitespace)");
-        else {
+        } else {
             String text = getText();
-            if (text.length() > 16)
+            if (text.length() > 16) {
                 text = text.substring(0, 16) + "...";
+            }
             buf.append(text);
         }
 
-        buf.append("@"+line + ":" + column);
-        if(location != null){
+        buf.append("@" + getLineNumber() + ":" + getColumnNumber());
+        if (location != null) {
             buf.append(" in ");
             buf.append(location);
-        } else if(reader != null){
+        } else if (reader != null) {
             buf.append(" in ");
             buf.append(reader.toString());
         }
@@ -1263,39 +1313,55 @@
     }
 
     public int getLineNumber() {
-        return line;
+        int result = bufferStartLine;
+        for (int i = 0; i < position; i++) {
+            if (buffer[i] == '\n') {
+                result++;
+            }
+        }
+        return result + 1; // the first line is '1'
     }
 
     public int getColumnNumber() {
-        return column;
+        int result = bufferStartColumn;
+        for (int i = 0; i < position; i++) {
+            if (buffer[i] == '\n') {
+                result = 0;
+            } else {
+                result++;
+            }
+        }
+        return result + 1; // the first column is '1'
     }
 
     public boolean isWhitespace() throws XmlPullParserException {
-        if (type != TEXT && type != IGNORABLE_WHITESPACE && type != CDSECT)
-            exception(ILLEGAL_TYPE);
+        if (type != TEXT && type != IGNORABLE_WHITESPACE && type != CDSECT) {
+            throw new XmlPullParserException(ILLEGAL_TYPE, this, null);
+        }
         return isWhitespace;
     }
 
     public String getText() {
-        return type < TEXT
-            || (type == ENTITY_REF && unresolved) ? null : get(0);
+        if (type < TEXT || (type == ENTITY_REF && unresolved)) {
+            return null;
+        } else if (text == null) {
+            return "";
+        } else {
+            return text;
+        }
     }
 
     public char[] getTextCharacters(int[] poslen) {
-        if (type >= TEXT) {
-            if (type == ENTITY_REF) {
-                poslen[0] = 0;
-                poslen[1] = name.length();
-                return name.toCharArray();
-            }
-            poslen[0] = 0;
-            poslen[1] = txtPos;
-            return txtBuf;
+        String text = getText();
+        if (text == null) {
+            poslen[0] = -1;
+            poslen[1] = -1;
+            return null;
         }
-
-        poslen[0] = -1;
-        poslen[1] = -1;
-        return null;
+        char[] result = text.toCharArray();
+        poslen[0] = 0;
+        poslen[1] = result.length;
+        return result;
     }
 
     public String getNamespace() {
@@ -1311,8 +1377,9 @@
     }
 
     public boolean isEmptyElementTag() throws XmlPullParserException {
-        if (type != START_TAG)
-            exception(ILLEGAL_TYPE);
+        if (type != START_TAG) {
+            throw new XmlPullParserException(ILLEGAL_TYPE, this, null);
+        }
         return degenerated;
     }
 
@@ -1329,35 +1396,39 @@
     }
 
     public String getAttributeNamespace(int index) {
-        if (index >= attributeCount)
+        if (index >= attributeCount) {
             throw new IndexOutOfBoundsException();
-        return attributes[index << 2];
+        }
+        return attributes[index * 4];
     }
 
     public String getAttributeName(int index) {
-        if (index >= attributeCount)
+        if (index >= attributeCount) {
             throw new IndexOutOfBoundsException();
-        return attributes[(index << 2) + 2];
+        }
+        return attributes[(index * 4) + 2];
     }
 
     public String getAttributePrefix(int index) {
-        if (index >= attributeCount)
+        if (index >= attributeCount) {
             throw new IndexOutOfBoundsException();
-        return attributes[(index << 2) + 1];
+        }
+        return attributes[(index * 4) + 1];
     }
 
     public String getAttributeValue(int index) {
-        if (index >= attributeCount)
+        if (index >= attributeCount) {
             throw new IndexOutOfBoundsException();
-        return attributes[(index << 2) + 3];
+        }
+        return attributes[(index * 4) + 3];
     }
 
     public String getAttributeValue(String namespace, String name) {
-
-        for (int i = (attributeCount << 2) - 4; i >= 0; i -= 4) {
+        for (int i = (attributeCount * 4) - 4; i >= 0; i -= 4) {
             if (attributes[i + 2].equals(name)
-                && (namespace == null || attributes[i].equals(namespace)))
+                    && (namespace == null || attributes[i].equals(namespace))) {
                 return attributes[i + 3];
+            }
         }
 
         return null;
@@ -1368,122 +1439,100 @@
     }
 
     public int next() throws XmlPullParserException, IOException {
-
-        txtPos = 0;
+        text = null;
         isWhitespace = true;
         int minType = 9999;
         token = false;
 
         do {
             nextImpl();
-            if (type < minType)
+            if (type < minType) {
                 minType = type;
-            //    if (curr <= TEXT) type = curr;
-        }
-        while (minType > ENTITY_REF // ignorable
-            || (minType >= TEXT && peekType() >= TEXT));
+            }
+        } while (minType > ENTITY_REF // ignorable
+                || (minType >= TEXT && peekType() >= TEXT));
 
         type = minType;
-        if (type > TEXT)
+        if (type > TEXT) {
             type = TEXT;
+        }
 
         return type;
     }
 
     public int nextToken() throws XmlPullParserException, IOException {
-
         isWhitespace = true;
-        txtPos = 0;
+        text = null;
 
         token = true;
         nextImpl();
         return type;
     }
 
-    //
     // utility methods to make XML parsing easier ...
 
     public int nextTag() throws XmlPullParserException, IOException {
-
         next();
-        if (type == TEXT && isWhitespace)
+        if (type == TEXT && isWhitespace) {
             next();
+        }
 
-        if (type != END_TAG && type != START_TAG)
-            exception("unexpected type");
+        if (type != END_TAG && type != START_TAG) {
+            throw new XmlPullParserException("unexpected type", this, null);
+        }
 
         return type;
     }
 
     public void require(int type, String namespace, String name)
-        throws XmlPullParserException, IOException {
+            throws XmlPullParserException, IOException {
 
         if (type != this.type
-            || (namespace != null && !namespace.equals(getNamespace()))
-            || (name != null && !name.equals(getName())))
-            exception(
-                "expected: " + TYPES[type] + " {" + namespace + "}" + name);
+                || (namespace != null && !namespace.equals(getNamespace()))
+                || (name != null && !name.equals(getName()))) {
+            throw new XmlPullParserException(
+                    "expected: " + TYPES[type] + " {" + namespace + "}" + name, this, null);
+        }
     }
 
     public String nextText() throws XmlPullParserException, IOException {
-        if (type != START_TAG)
-            exception("precondition: START_TAG");
+        if (type != START_TAG) {
+            throw new XmlPullParserException("precondition: START_TAG", this, null);
+        }
 
         next();
 
         String result;
-
         if (type == TEXT) {
             result = getText();
             next();
-        }
-        else
+        } else {
             result = "";
+        }
 
-        if (type != END_TAG)
-            exception("END_TAG expected");
+        if (type != END_TAG) {
+            throw new XmlPullParserException("END_TAG expected", this, null);
+        }
 
         return result;
     }
 
-    public void setFeature(String feature, boolean value)
-        throws XmlPullParserException {
-        if (XmlPullParser.FEATURE_PROCESS_NAMESPACES.equals(feature))
+    public void setFeature(String feature, boolean value) throws XmlPullParserException {
+        if (XmlPullParser.FEATURE_PROCESS_NAMESPACES.equals(feature)) {
             processNsp = value;
-        else if (isProp(feature, false, "relaxed"))
+        } else if (isProp(feature, false, "relaxed")) {
+            // "http://xmlpull.org/v1/doc/features.html#relaxed"
             relaxed = value;
-        else
-            exception("unsupported feature: " + feature);
-    }
-
-    public void setProperty(String property, Object value)
-        throws XmlPullParserException {
-        if(isProp(property, true, "location")) {
-            location = value;
         } else {
-            throw new XmlPullParserException("unsupported property: " + property);
+            throw new XmlPullParserException("unsupported feature: " + feature, this, null);
         }
     }
 
-    /**
-      * Skip sub tree that is currently porser positioned on.
-      * <br>NOTE: parser must be on START_TAG and when funtion returns
-      * parser will be positioned on corresponding END_TAG.
-      */
-
-    // Implementation copied from Alek's mail...
-
-    public void skipSubTree() throws XmlPullParserException, IOException {
-        require(START_TAG, null, null);
-        int level = 1;
-        while (level > 0) {
-            int eventType = next();
-            if (eventType == END_TAG) {
-                --level;
-            }
-            else if (eventType == START_TAG) {
-                ++level;
-            }
+    public void setProperty(String property, Object value) throws XmlPullParserException {
+        if (isProp(property, true, "location")) {
+            location = String.valueOf(value);
+        } else {
+            throw new XmlPullParserException("unsupported property: " + property);
         }
     }
 }
diff --git a/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java b/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java
index 66c8f4d..36e6025 100644
--- a/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java
+++ b/xml/src/main/java/org/xmlpull/v1/XmlPullParser.java
@@ -59,7 +59,7 @@
  *  getProperty(&quot;<a href="http://xmlpull.org/v1/doc/properties.html#xmldecl-version">http://xmlpull.org/v1/doc/properties.html#xmldecl-version</a>&quot;)
  *       returns String ("1.0") or null if XMLDecl was not read or if property is not supported
  * <li><b>standalone</b>:
- *  getProperty(&quot;<a href="http://xmlpull.org/v1/doc/features.html#xmldecl-standalone">http://xmlpull.org/v1/doc/features.html#xmldecl-standalone</a>&quot;)
+ *  getProperty(&quot;<a href="http://xmlpull.org/v1/doc/properties.html#xmldecl-standalone">http://xmlpull.org/v1/doc/properties.html#xmldecl-standalone</a>&quot;)
  *       returns Boolean: null if there was no standalone declaration
  *  or if property is not supported
  *         otherwise returns Boolean(true) if standalone="yes" and Boolean(false) when standalone="no"
@@ -74,7 +74,7 @@
  * import java.io.StringReader;
  *
  * import org.xmlpull.v1.XmlPullParser;
- * import org.xmlpull.v1.<a href="XmlPullParserException.html">XmlPullParserException.html</a>;
+ * import org.xmlpull.v1.<a href="XmlPullParserException.html">XmlPullParserException</a>;
  * import org.xmlpull.v1.<a href="XmlPullParserFactory.html">XmlPullParserFactory</a>;
  *
  * public class SimpleXmlPullApp
@@ -92,8 +92,6 @@
  *         while (eventType != XmlPullParser.END_DOCUMENT) {
  *          if(eventType == XmlPullParser.START_DOCUMENT) {
  *              System.out.println("Start document");
- *          } else if(eventType == XmlPullParser.END_DOCUMENT) {
- *              System.out.println("End document");
  *          } else if(eventType == XmlPullParser.START_TAG) {
  *              System.out.println("Start tag "+xpp.<a href="#getName()">getName()</a>);
  *          } else if(eventType == XmlPullParser.END_TAG) {
@@ -103,6 +101,7 @@
  *          }
  *          eventType = xpp.next();
  *         }
+ *         System.out.println("End document");
  *     }
  * }
  * </pre>