Updating archive to Harmony r772995.

Squashed commit of the following:
    Adding @TestTargetNew tags

    Initial merge of branch 'archive_772995' into archive_dalvik.

    Fixed some problems, including InflaterInputStream.available()
    and JarFile.skip() bugs.

    Conflicts:
    	libcore/archive/.classpath
    	libcore/archive/META-INF/MANIFEST.MF
    	libcore/archive/build.xml
    	libcore/archive/make/hyproperties.xml
    	libcore/archive/src/main/java/java/util/jar/Attributes.java
    	libcore/archive/src/main/java/java/util/jar/JarEntry.java
    	libcore/archive/src/main/java/java/util/jar/JarException.java
    	libcore/archive/src/main/java/java/util/jar/JarFile.java
    	libcore/archive/src/main/java/java/util/jar/JarInputStream.java
    	libcore/archive/src/main/java/java/util/jar/JarVerifier.java
    	libcore/archive/src/main/java/java/util/jar/Manifest.java
    	libcore/archive/src/main/java/java/util/jar/Pack200.java
    	libcore/archive/src/main/java/java/util/zip/Adler32.java
    	libcore/archive/src/main/java/java/util/zip/CRC32.java
    	libcore/archive/src/main/java/java/util/zip/Checksum.java
    	libcore/archive/src/main/java/java/util/zip/DataFormatException.java
    	libcore/archive/src/main/java/java/util/zip/Deflater.java
    	libcore/archive/src/main/java/java/util/zip/DeflaterOutputStream.java
    	libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java
    	libcore/archive/src/main/java/java/util/zip/GZIPOutputStream.java
    	libcore/archive/src/main/java/java/util/zip/Inflater.java
    	libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java
    	libcore/archive/src/main/java/java/util/zip/ZipEntry.java
    	libcore/archive/src/main/java/java/util/zip/ZipException.java
    	libcore/archive/src/main/java/java/util/zip/ZipFile.java
    	libcore/archive/src/main/java/java/util/zip/ZipInputStream.java
    	libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java
    	libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java
    	libcore/archive/src/main/native/archive/shared/archiveglob.c
    	libcore/archive/src/main/native/archive/shared/jarfile.c
    	libcore/archive/src/main/native/archive/shared/zip.c
    	libcore/archive/src/main/native/archive/shared/zip.h
    	libcore/archive/src/main/native/archive/unix/makefile
    	libcore/archive/src/main/native/archive/windows/makefile
    	libcore/archive/src/main/native/java_util_zip_Adler32.c
    	libcore/archive/src/main/native/java_util_zip_CRC32.c
    	libcore/archive/src/main/native/java_util_zip_Deflater.c
    	libcore/archive/src/main/native/java_util_zip_Inflater.c
    	libcore/archive/src/main/native/zip/shared/hyzip.nls
    	libcore/archive/src/main/native/zip/unix/makefile
    	libcore/archive/src/main/native/zipsup.h
    	libcore/archive/src/main/native/zlib/unix/makefile
    	libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesNameTest.java
    	libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java
    	libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java
    	libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java
    	libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarInputStreamTest.java
    	libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java
    	libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java
    	libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java
    	libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java
    	libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java
    	libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java

commit 946f165f5b592f4453fd8f2c19766921544d38dd
Author: Jesse Wilson <jessewilson@google.com>
Date:   Tue May 12 15:12:55 2009 -0700

    Strip @Since Android 1.0 from Archive

commit 3498f216d7e826bfc9c4cc7c0da35830ca239367
Author: Jesse Wilson <jessewilson@google.com>
Date:   Tue May 12 15:09:05 2009 -0700

    Dalvik archive

commit 62e9db90bc6aa6b5d1c897cccdd616d812672677
Author: Jesse Wilson <jessewilson@google.com>
Date:   Tue May 12 15:07:51 2009 -0700

    Archive 772995

commit d1bf618681d6badf1b50edaf204a083d3912213d
Author: Jesse Wilson <jessewilson@google.com>
Date:   Tue May 12 15:06:36 2009 -0700

    Archive 527399
diff --git a/libcore/archive/src/main/java/java/util/jar/Attributes.java b/libcore/archive/src/main/java/java/util/jar/Attributes.java
index 5a4d923..4ee94df 100644
--- a/libcore/archive/src/main/java/java/util/jar/Attributes.java
+++ b/libcore/archive/src/main/java/java/util/jar/Attributes.java
@@ -17,6 +17,7 @@
 
 package java.util.jar;
 
+import java.io.UnsupportedEncodingException;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
@@ -28,7 +29,6 @@
  * The {@code Attributes} class is used to store values for manifest entries.
  * Attribute keys are generally instances of {@code Attributes.Name}. Values
  * associated with attribute keys are of type {@code String}.
- * @since Android 1.0
  */
 public class Attributes implements Cloneable, Map<Object, Object> {
 
@@ -37,8 +37,6 @@
      * {@link Attributes.Name}) of a JAR file manifest to arbitrary values. The
      * attribute names thus are obtained from the {@link Manifest} for
      * convenience.
-     * 
-     * @since Android 1.0
      */
     protected Map<Object, Object> map;
 
@@ -46,46 +44,35 @@
      * The name part of the name/value pairs constituting an attribute as
      * defined by the specification of the JAR manifest. May be composed of the
      * following ASCII signs as defined in the EBNF below:
-     * 
+     *
      * <pre>
      * name       = alphanum *headerchar
      * headerchar = alphanum | - | _
-     * alphanum   = {A-Z} | {a-z} | {0-9} 
+     * alphanum   = {A-Z} | {a-z} | {0-9}
      * </pre>
-     * 
-     * @since Android 1.0
      */
     public static class Name {
-        private final String name;
+        private final byte[] name;
 
         private int hashCode;
 
         /**
          * The class path (a main attribute).
-         * 
-         * @since Android 1.0
          */
         public static final Name CLASS_PATH = new Name("Class-Path"); //$NON-NLS-1$
 
         /**
          * The version of the manifest file (a main attribute).
-         * 
-         * @since Android 1.0
          */
-        public static final Name MANIFEST_VERSION = new Name(
-                "Manifest-Version"); //$NON-NLS-1$
+        public static final Name MANIFEST_VERSION = new Name("Manifest-Version"); //$NON-NLS-1$
 
         /**
          * The main class's name (for stand-alone applications).
-         * 
-         * @since Android 1.0
          */
         public static final Name MAIN_CLASS = new Name("Main-Class"); //$NON-NLS-1$
 
         /**
          * Defines the signature version of the JAR file.
-         * 
-         * @since Android 1.0
          */
         public static final Name SIGNATURE_VERSION = new Name(
                 "Signature-Version"); //$NON-NLS-1$
@@ -98,16 +85,12 @@
         /**
          * The {@code Sealed} manifest attribute which may have the value
          * {@code true} for sealed archives.
-         * 
-         * @since Android 1.0
          */
         public static final Name SEALED = new Name("Sealed"); //$NON-NLS-1$
 
         /**
          * The {@code Implementation-Title} attribute whose value is a string
          * that defines the title of the extension implementation.
-         * 
-         * @since Android 1.0
          */
         public static final Name IMPLEMENTATION_TITLE = new Name(
                 "Implementation-Title"); //$NON-NLS-1$
@@ -115,8 +98,6 @@
         /**
          * The {@code Implementation-Version} attribute defining the version of
          * the extension implementation.
-         * 
-         * @since Android 1.0
          */
         public static final Name IMPLEMENTATION_VERSION = new Name(
                 "Implementation-Version"); //$NON-NLS-1$
@@ -124,8 +105,6 @@
         /**
          * The {@code Implementation-Vendor} attribute defining the organization
          * that maintains the extension implementation.
-         * 
-         * @since Android 1.0
          */
         public static final Name IMPLEMENTATION_VENDOR = new Name(
                 "Implementation-Vendor"); //$NON-NLS-1$
@@ -133,8 +112,6 @@
         /**
          * The {@code Specification-Title} attribute defining the title of the
          * extension specification.
-         * 
-         * @since Android 1.0
          */
         public static final Name SPECIFICATION_TITLE = new Name(
                 "Specification-Title"); //$NON-NLS-1$
@@ -142,8 +119,6 @@
         /**
          * The {@code Specification-Version} attribute defining the version of
          * the extension specification.
-         * 
-         * @since Android 1.0
          */
         public static final Name SPECIFICATION_VERSION = new Name(
                 "Specification-Version"); //$NON-NLS-1$
@@ -151,8 +126,6 @@
         /**
          * The {@code Specification-Vendor} attribute defining the organization
          * that maintains the extension specification.
-         * 
-         * @since Android 1.0
          */
         public static final Name SPECIFICATION_VENDOR = new Name(
                 "Specification-Vendor"); //$NON-NLS-1$
@@ -160,23 +133,17 @@
         /**
          * The {@code Extension-List} attribute defining the extensions that are
          * needed by the applet.
-         * 
-         * @since Android 1.0
          */
         public static final Name EXTENSION_LIST = new Name("Extension-List"); //$NON-NLS-1$
 
         /**
          * The {@code Extension-Name} attribute which defines the unique name of
          * the extension.
-         * 
-         * @since Android 1.0
          */
         public static final Name EXTENSION_NAME = new Name("Extension-Name"); //$NON-NLS-1$
 
         /**
          * The {@code Extension-Installation} attribute.
-         * 
-         * @since Android 1.0
          */
         public static final Name EXTENSION_INSTALLATION = new Name(
                 "Extension-Installation"); //$NON-NLS-1$
@@ -185,8 +152,6 @@
          * The {@code Implementation-Vendor-Id} attribute specifies the vendor
          * of an extension implementation if the applet requires an
          * implementation from a specific vendor.
-         * 
-         * @since Android 1.0
          */
         public static final Name IMPLEMENTATION_VENDOR_ID = new Name(
                 "Implementation-Vendor-Id"); //$NON-NLS-1$
@@ -195,91 +160,112 @@
          * The {@code Implementation-URL} attribute specifying a URL that can be
          * used to obtain the most recent version of the extension if the
          * required version is not already installed.
-         * 
-         * @since Android 1.0
          */
         public static final Name IMPLEMENTATION_URL = new Name(
                 "Implementation-URL"); //$NON-NLS-1$
 
+        static final Name NAME = new Name("Name");
+
         /**
          * A String which must satisfy the following EBNF grammar to specify an
          * additional attribute:
-         * 
+         *
          * <pre>
          * name       = alphanum *headerchar
          * headerchar = alphanum | - | _
          * alphanum   = {A-Z} | {a-z} | {0-9}
          * </pre>
-         * 
+         *
          * @param s
          *            The Attribute string.
          * @exception IllegalArgumentException
          *                if the string does not satisfy the EBNF grammar.
-         * @since Android 1.0
          */
         public Name(String s) {
             int i = s.length();
-            if (i == 0 || i > 70) {
+            if (i == 0 || i > Manifest.LINE_LENGTH_LIMIT - 2) {
                 throw new IllegalArgumentException();
             }
+
+            name = new byte[i];
+
             for (; --i >= 0;) {
                 char ch = s.charAt(i);
                 if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
                         || ch == '_' || ch == '-' || (ch >= '0' && ch <= '9'))) {
                     throw new IllegalArgumentException(s);
                 }
+                name[i] = (byte) ch;
             }
-            name = s;
+        }
+
+        /**
+         * A private constructor for a trusted attribute name.
+         */
+        Name(byte[] buf) {
+            name = buf;
+        }
+
+        byte[] getBytes() {
+            return name;
         }
 
         /**
          * Returns this attribute name.
-         * 
+         *
          * @return the attribute name.
-         * @since Android 1.0
          */
         @Override
         public String toString() {
-            return name;
+            try {
+                return new String(name, "ISO-8859-1");
+            } catch (UnsupportedEncodingException iee) {
+                throw new InternalError(iee.getLocalizedMessage());
+            }
         }
 
         /**
          * returns whether the argument provided is the same as the attribute
          * name.
-         * 
+         *
          * @return if the attribute names correspond.
-         * @param an
+         * @param object
          *            An attribute name to be compared with this name.
-         * @since Android 1.0
          */
         @Override
-        public boolean equals(Object an) {
-            if (an == null) {
+        public boolean equals(Object object) {
+            if (object == null || object.getClass() != getClass()
+                    || object.hashCode() != hashCode()) {
                 return false;
             }
-            return an.getClass() == this.getClass()
-                    && name.equalsIgnoreCase(((Name) an).name);
+
+            return Util.equalsIgnoreCase(name, ((Name) object).name);
         }
 
         /**
          * Computes a hash code of the name.
-         * 
+         *
          * @return the hash value computed from the name.
-         * @since Android 1.0
          */
         @Override
         public int hashCode() {
             if (hashCode == 0) {
-                hashCode = Util.toASCIILowerCase("name").hashCode();
+                int hash = 0, multiplier = 1;
+                for (int i = name.length - 1; i >= 0; i--) {
+                    // 'A' & 0xDF == 'a' & 0xDF, ..., 'Z' & 0xDF == 'z' & 0xDF
+                    hash += (name[i] & 0xDF) * multiplier;
+                    int shifted = multiplier << 5;
+                    multiplier = shifted - multiplier;
+                }
+                hashCode = hash;
             }
             return hashCode;
         }
+
     }
 
     /**
      * Constructs an {@code Attributes} instance.
-     * 
-     * @since Android 1.0
      */
     public Attributes() {
         map = new HashMap<Object, Object>();
@@ -288,23 +274,21 @@
     /**
      * Constructs an {@code Attributes} instance obtaining keys and values from
      * the parameter {@code attrib}.
-     * 
+     *
      * @param attrib
      *            The attributes to obtain entries from.
-     * @since Android 1.0
      */
     @SuppressWarnings("unchecked")
     public Attributes(Attributes attrib) {
-        map = (Map<Object, Object>)((HashMap) attrib.map).clone();
+        map = (Map<Object, Object>) ((HashMap) attrib.map).clone();
     }
 
     /**
      * Constructs an {@code Attributes} instance with initial capacity of size
      * {@code size}.
-     * 
+     *
      * @param size
      *            Initial size of this {@code Attributes} instance.
-     * @since Android 1.0
      */
     public Attributes(int size) {
         map = new HashMap<Object, Object>(size);
@@ -312,8 +296,6 @@
 
     /**
      * Removes all key/value pairs from this {@code Attributes}.
-     * 
-     * @since Android 1.0
      */
     public void clear() {
         map.clear();
@@ -321,11 +303,10 @@
 
     /**
      * Determines whether this {@code Attributes} contains the specified key.
-     * 
+     *
      * @param key
      *            The key to search for.
      * @return {@code true} if the key is found, {@code false} otherwise.
-     * @since Android 1.0
      */
     public boolean containsKey(Object key) {
         return map.containsKey(key);
@@ -333,11 +314,10 @@
 
     /**
      * Determines whether this {@code Attributes} contains the specified value.
-     * 
+     *
      * @param value
      *            the value to search for.
      * @return {@code true} if the value is found, {@code false} otherwise.
-     * @since Android 1.0
      */
     public boolean containsValue(Object value) {
         return map.containsValue(value);
@@ -346,9 +326,8 @@
     /**
      * Returns a set containing map entries for each of the key/value pair
      * contained in this {@code Attributes}.
-     * 
+     *
      * @return a set of Map.Entry's
-     * @since Android 1.0
      */
     public Set<Map.Entry<Object, Object>> entrySet() {
         return map.entrySet();
@@ -356,12 +335,11 @@
 
     /**
      * Returns the value associated with the parameter key.
-     * 
+     *
      * @param key
      *            the key to search for.
      * @return Object associated with key, or {@code null} if key does not
      *         exist.
-     * @since Android 1.0
      */
     public Object get(Object key) {
         return map.get(key);
@@ -369,9 +347,8 @@
 
     /**
      * Determines whether this {@code Attributes} contains any keys.
-     * 
+     *
      * @return {@code true} if one or more keys exist, {@code false} otherwise.
-     * @since Android 1.0
      */
     public boolean isEmpty() {
         return map.isEmpty();
@@ -380,9 +357,8 @@
     /**
      * Returns a {@code Set} containing all the keys found in this {@code
      * Attributes}.
-     * 
+     *
      * @return a {@code Set} of all keys.
-     * @since Android 1.0
      */
     public Set<Object> keySet() {
         return map.keySet();
@@ -390,7 +366,7 @@
 
     /**
      * Stores key/value pairs in this {@code Attributes}.
-     * 
+     *
      * @param key
      *            the key to associate with value.
      * @param value
@@ -399,21 +375,20 @@
      * @exception ClassCastException
      *                when key is not an {@code Attributes.Name} or value is not
      *                a {@code String}.
-     *@since Android 1.0
      */
-    @SuppressWarnings("cast") // Require cast to force ClassCastException
+    @SuppressWarnings("cast")
+    // Require cast to force ClassCastException
     public Object put(Object key, Object value) {
-        return map.put((Name)key, (String)value);
+        return map.put((Name) key, (String) value);
     }
 
     /**
-     * Stores all the key/value pairs in the argument in this {@code Attributes}
-     * .
-     * 
+     * Stores all the key/value pairs in the argument in this {@code
+     * Attributes}.
+     *
      * @param attrib
-     *            the associations to store (must be of type {@code Attributes}
-     *            ).
-     * @since Android 1.0
+     *            the associations to store (must be of type {@code
+     *            Attributes}).
      */
     public void putAll(Map<?, ?> attrib) {
         if (attrib == null || !(attrib instanceof Attributes)) {
@@ -425,12 +400,11 @@
     /**
      * Deletes the key/value pair with key {@code key} from this {@code
      * Attributes}.
-     * 
+     *
      * @param key
      *            the key to remove.
      * @return the values associated with the removed key, {@code null} if not
      *         present.
-     * @since Android 1.0
      */
     public Object remove(Object key) {
         return map.remove(key);
@@ -439,20 +413,18 @@
     /**
      * Returns the number of key/value pairs associated with this {@code
      * Attributes}.
-     * 
+     *
      * @return the size of this {@code Attributes}.
-     * @since Android 1.0
      */
     public int size() {
         return map.size();
     }
 
     /**
-     * Returns a collection of all the values present in this {@code Attributes}
-     * .
-     * 
+     * Returns a collection of all the values present in this {@code
+     * Attributes}.
+     *
      * @return a collection of all values present.
-     * @since Android 1.0
      */
     public Collection<Object> values() {
         return map.values();
@@ -473,9 +445,8 @@
 
     /**
      * Returns the hash code of this {@code Attributes}.
-     * 
+     *
      * @return the hash code of this object.
-     * @since Android 1.0
      */
     @Override
     public int hashCode() {
@@ -486,12 +457,11 @@
      * Determines if this {@code Attributes} and the parameter {@code
      * Attributes} are equal. Two {@code Attributes} instances are equal if they
      * contain the same keys and values.
-     * 
+     *
      * @param obj
      *            the object with which this {@code Attributes} is compared.
      * @return {@code true} if the {@code Attributes} are equal, {@code false}
      *         otherwise.
-     * @since Android 1.0
      */
     @Override
     public boolean equals(Object obj) {
@@ -507,12 +477,11 @@
     /**
      * Returns the value associated with the parameter {@code Attributes.Name}
      * key.
-     * 
+     *
      * @param name
      *            the key to obtain the value for.
      * @return the {@code String} associated with name, or {@code null} if name
      *         is not a valid key.
-     * @since Android 1.0
      */
     public String getValue(Attributes.Name name) {
         return (String) map.get(name);
@@ -520,12 +489,11 @@
 
     /**
      * Returns the string associated with the parameter name.
-     * 
+     *
      * @param name
      *            the key to obtain the value for.
      * @return the string associated with name, or {@code null} if name is not a
      *         valid key.
-     * @since Android 1.0
      */
     public String getValue(String name) {
         return (String) map.get(new Attributes.Name(name));
@@ -534,13 +502,12 @@
     /**
      * Stores the value {@code val} associated with the key {@code name} in this
      * {@code Attributes}.
-     * 
+     *
      * @param name
      *            the key to store.
      * @param val
      *            the value to store in this {@code Attributes}.
      * @return the value being stored.
-     * @since Android 1.0
      */
     public String putValue(String name, String val) {
         return (String) map.put(new Attributes.Name(name), val);
diff --git a/libcore/archive/src/main/java/java/util/jar/InitManifest.java b/libcore/archive/src/main/java/java/util/jar/InitManifest.java
index 47fdf1c..bf9c397 100644
--- a/libcore/archive/src/main/java/java/util/jar/InitManifest.java
+++ b/libcore/archive/src/main/java/java/util/jar/InitManifest.java
@@ -17,279 +17,206 @@
 
 package java.util.jar;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
-import java.io.UTFDataFormatException;
-import java.security.AccessController;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CoderResult;
 import java.util.Map;
 
 import org.apache.harmony.archive.internal.nls.Messages;
-import org.apache.harmony.luni.util.PriviAction;
-import org.apache.harmony.luni.util.Util;
+import org.apache.harmony.luni.util.ThreadLocalCache;
 
 class InitManifest {
-    private final byte[] inbuf = new byte[1024];
 
-    private int inbufCount = 0, inbufPos = 0;
+    private byte[] buf;
 
-    private byte[] buffer = new byte[5];
+    private int pos;
 
-    private char[] charbuf = new char[0];
+    Attributes.Name name;
 
-    private final ByteArrayOutputStream out = new ByteArrayOutputStream(256);
+    String value;
 
-    private String encoding;
+    CharsetDecoder decoder = ThreadLocalCache.utf8Decoder.get();
+    CharBuffer cBuf = ThreadLocalCache.charBuffer.get();
 
-    private boolean usingUTF8 = true;
+    InitManifest(byte[] buf, Attributes main, Attributes.Name ver)
+            throws IOException {
 
-    private final Map<String, Attributes.Name> attributeNames = new HashMap<String, Attributes.Name>();
+        this.buf = buf;
 
-    private final byte[] mainAttributesChunk;
-
-    InitManifest(InputStream is, Attributes main, Map<String, Attributes> entries, Map<String, byte[]> chunks,
-            String verString) throws IOException {
-        encoding = AccessController.doPrivileged(new PriviAction<String>(
-                "manifest.read.encoding")); //$NON-NLS-1$
-        if ("".equals(encoding)) { //$NON-NLS-1$
-            encoding = null;
+        // check a version attribute
+        if (!readHeader() || (ver != null && !name.equals(ver))) {
+            throw new IOException(Messages.getString(
+                    "archive.2D", ver)); //$NON-NLS-1$
         }
 
-        Attributes current = main;
-        ArrayList<String> list = new ArrayList<String>();
-
-        // Return the chunk of main attributes in the manifest.
-        mainAttributesChunk = nextChunk(is, list);
-
-        Iterator<String> it = list.iterator();
-        while (it.hasNext()) {
-            addAttribute(it.next(), current);
+        main.put(name, value);
+        while (readHeader()) {
+            main.put(name, value);
         }
+    }
 
-        // Check for version attribute
-        if (verString != null && main.getValue(verString) == null) {
-            throw new IOException(Messages.getString("archive.2D", verString)); //$NON-NLS-1$
-        }
+    void initEntries(Map<String, Attributes> entries,
+            Map<String, Manifest.Chunk> chunks) throws IOException {
 
-        list.clear();
-        byte[] chunk = null;
-        while (chunks == null ? readLines(is, list) : (chunk = nextChunk(is,
-                list)) != null) {
-            it = list.iterator();
-            String line = it.next();
-            if (line.length() < 7
-                    || !Util.toASCIILowerCase(line.substring(0, 5)).equals("name:")) { //$NON-NLS-1$
+        int mark = pos;
+        while (readHeader()) {
+            if (!Attributes.Name.NAME.equals(name)) {
                 throw new IOException(Messages.getString("archive.23")); //$NON-NLS-1$
             }
-            // Name: length required space char
-            String name = line.substring(6, line.length());
-            current = new Attributes(12);
+            String entryNameValue = value;
+
+            Attributes entry = entries.get(entryNameValue);
+            if (entry == null) {
+                entry = new Attributes(12);
+            }
+
+            while (readHeader()) {
+                entry.put(name, value);
+            }
+
             if (chunks != null) {
-                chunks.put(name, chunk);
-            }
-            entries.put(name, current);
-            while (it.hasNext()) {
-                addAttribute(it.next(), current);
-            }
-            list.clear();
-        }
-
-    }
-
-    byte[] getMainAttributesChunk() {
-        return mainAttributesChunk;
-    }
-
-    private void addLine(int length, List<String> lines) throws IOException {
-        if (encoding != null) {
-            lines.add(new String(buffer, 0, length, encoding));
-        } else {
-            if (usingUTF8) {
-                try {
-                    if (charbuf.length < length) {
-                        charbuf = new char[length];
-                    }
-                    lines.add(Util.convertUTF8WithBuf(buffer, charbuf, 0,
-                            length));
-                } catch (UTFDataFormatException e) {
-                    usingUTF8 = false;
+                if (chunks.get(entryNameValue) != null) {
+                    // TODO A bug: there might be several verification chunks for
+                    // the same name. I believe they should be used to update
+                    // signature in order of appearance; there are two ways to fix
+                    // this: either use a list of chunks, or decide on used
+                    // signature algorithm in advance and reread the chunks while
+                    // updating the signature; for now a defensive error is thrown
+                    throw new IOException(Messages.getString("archive.34")); //$NON-NLS-1$
                 }
+                chunks.put(entryNameValue, new Manifest.Chunk(mark, pos));
+                mark = pos;
             }
-            if (!usingUTF8) {
-                if (charbuf.length < length) {
-                    charbuf = new char[length];
-                }
-                // If invalid UTF8, convert bytes to chars setting the upper
-                // bytes to zeros
-                int charOffset = 0;
-                int offset = 0;
-                for (int i = length; --i >= 0;) {
-                    charbuf[charOffset++] = (char) (buffer[offset++] & 0xff);
-                }
-                lines.add(new String(charbuf, 0, length));
-            }
+
+            entries.put(entryNameValue, entry);
         }
     }
 
-    private byte[] nextChunk(InputStream in, List<String> lines)
-            throws IOException {
-        if (inbufCount == -1) {
-            return null;
-        }
-        byte next;
-        int pos = 0;
-        boolean blankline = false, lastCr = false;
-        out.reset();
-        while (true) {
-            if (inbufPos == inbufCount) {
-                if ((inbufCount = in.read(inbuf)) == -1) {
-                    if (out.size() == 0) {
-                        return null;
-                    }
-                    if (blankline) {
-                        addLine(pos, lines);
-                    }
-                    return out.toByteArray();
-                }
-                if (inbufCount == inbuf.length && in.available() == 0) {
-                    /* archive.2E = "line too long" */
-                    throw new IOException(Messages.getString("archive.2E")); //$NON-NLS-1$
-                }
-                inbufPos = 0;
-            }
-            next = inbuf[inbufPos++];
-            if (lastCr) {
-                if (next != '\n') {
-                    inbufPos--;
-                    next = '\r';
-                } else {
-                    if (out.size() == 0) {
-                        continue;
-                    }
-                    out.write('\r');
-                }
-                lastCr = false;
-            } else if (next == '\r') {
-                lastCr = true;
-                continue;
-            }
-            if (blankline) {
-                if (next == ' ') {
-                    out.write(next);
-                    blankline = false;
-                    continue;
-                }
-                addLine(pos, lines);
-                if (next == '\n') {
-                    out.write(next);
-                    return out.toByteArray();
-                }
-                pos = 0;
-            } else if (next == '\n') {
-                if (out.size() == 0) {
-                    continue;
-                }
-                out.write(next);
-                blankline = true;
-                continue;
-            }
-            blankline = false;
-            out.write(next);
-            if (pos == buffer.length) {
-                byte[] newBuf = new byte[buffer.length * 2];
-                System.arraycopy(buffer, 0, newBuf, 0, buffer.length);
-                buffer = newBuf;
-            }
-            buffer[pos++] = next;
-        }
+    int getPos() {
+        return pos;
     }
 
-    private boolean readLines(InputStream in, List<String> lines)
-            throws IOException {
-        if (inbufCount == -1) {
+    /**
+     * Number of subsequent line breaks.
+     */
+    int linebreak = 0;
+
+    /**
+     * Read a single line from the manifest buffer.
+     */
+    private boolean readHeader() throws IOException {
+        if (linebreak > 1) {
+            // break a section on an empty line
+            linebreak = 0;
             return false;
         }
-        byte next;
-        int pos = 0;
-        boolean blankline = false, lastCr = false;
-        while (true) {
-            if (inbufPos == inbufCount) {
-                if ((inbufCount = in.read(inbuf)) == -1) {
-                    if (blankline) {
-                        addLine(pos, lines);
-                    }
-                    return lines.size() != 0;
+        readName();
+        linebreak = 0;
+        readValue();
+        // if the last line break is missed, the line
+        // is ignored by the reference implementation
+        return linebreak > 0;
+    }
+
+    private byte[] wrap(int mark, int pos) {
+        byte[] buffer = new byte[pos - mark];
+        System.arraycopy(buf, mark, buffer, 0, pos - mark);
+        return buffer;
+    }
+
+    private void readName() throws IOException {
+        int i = 0;
+        int mark = pos;
+
+        while (pos < buf.length) {
+            byte b = buf[pos++];
+
+            if (b == ':') {
+                byte[] nameBuffer = wrap(mark, pos - 1);
+
+                if (buf[pos++] != ' ') {
+                    throw new IOException(Messages.getString(
+                            "archive.30", nameBuffer)); //$NON-NLS-1$
                 }
-                if (inbufCount == inbuf.length && in.available() == 0) {
-                    /* archive.2E = "line too long" */
-                    throw new IOException(Messages.getString("archive.2E")); //$NON-NLS-1$
-                }
-                inbufPos = 0;
+
+                name = new Attributes.Name(nameBuffer);
+                return;
             }
-            next = inbuf[inbufPos++];
-            if (lastCr) {
-                if (next != '\n') {
-                    inbufPos--;
-                    next = '\r';
-                }
-                lastCr = false;
-            } else if (next == '\r') {
-                lastCr = true;
-                continue;
+
+            if (!((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || b == '_'
+                    || b == '-' || (b >= '0' && b <= '9'))) {
+                throw new IOException(Messages.getString("archive.30", b)); //$NON-NLS-1$
             }
-            if (blankline) {
-                if (next == ' ') {
-                    blankline = false;
-                    continue;
-                }
-                addLine(pos, lines);
-                if (next == '\n') {
-                    return true;
-                }
-                pos = 0;
-            } else if (next == '\n') {
-                if (pos == 0 && lines.size() == 0) {
-                    continue;
-                }
-                blankline = true;
-                continue;
-            }
-            blankline = false;
-            if (pos == buffer.length) {
-                byte[] newBuf = new byte[buffer.length * 2];
-                System.arraycopy(buffer, 0, newBuf, 0, buffer.length);
-                buffer = newBuf;
-            }
-            buffer[pos++] = next;
+        }
+        if (i > 0) {
+            throw new IOException(Messages.getString(
+                    "archive.30", wrap(mark, buf.length))); //$NON-NLS-1$
         }
     }
 
-    /* Get the next attribute and add it */
-    private void addAttribute(String line, Attributes current)
-            throws IOException {
-        String header;
-        int hdrIdx = line.indexOf(':');
-        if (hdrIdx < 1) {
-            throw new IOException(Messages.getString("archive.2F", line)); //$NON-NLS-1$
-        }
-        header = line.substring(0, hdrIdx);
-        Attributes.Name name = attributeNames.get(header);
-        if (name == null) {
-            try {
-                name = new Attributes.Name(header);
-            } catch (IllegalArgumentException e) {
-                throw new IOException(e.toString());
+    private void readValue() throws IOException {
+        byte next;
+        boolean lastCr = false;
+        int mark = pos;
+        int last = pos;
+
+        decoder.reset();
+        cBuf.clear();
+
+        while (pos < buf.length) {
+            next = buf[pos++];
+
+            switch (next) {
+            case 0:
+                throw new IOException(Messages.getString("archive.2F")); //$NON-NLS-1$
+            case '\n':
+                if (lastCr) {
+                    lastCr = false;
+                } else {
+                    linebreak++;
+                }
+                continue;
+            case '\r':
+                lastCr = true;
+                linebreak++;
+                continue;
+            case ' ':
+                if (linebreak == 1) {
+                    decode(mark, last, false);
+                    mark = pos;
+                    linebreak = 0;
+                    continue;
+                }
             }
-            attributeNames.put(header, name);
+
+            if (linebreak >= 1) {
+                pos--;
+                break;
+            }
+            last = pos;
         }
-        if (hdrIdx + 1 >= line.length() || line.charAt(hdrIdx + 1) != ' ') {
-            throw new IOException(Messages.getString("archive.2F", line)); //$NON-NLS-1$
+
+        decode(mark, last, true);
+        while (CoderResult.OVERFLOW == decoder.flush(cBuf)) {
+            enlargeBuffer();
         }
-        // +2 due to required SPACE char
-        current.put(name, line.substring(hdrIdx + 2, line.length()));
+        value = new String(cBuf.array(), cBuf.arrayOffset(), cBuf.position());
+    }
+
+    private void decode(int mark, int pos, boolean endOfInput)
+            throws IOException {
+        ByteBuffer bBuf = ByteBuffer.wrap(buf, mark, pos - mark);
+        while (CoderResult.OVERFLOW == decoder.decode(bBuf, cBuf, endOfInput)) {
+            enlargeBuffer();
+        }
+    }
+
+    private void enlargeBuffer() {
+        CharBuffer newBuf = CharBuffer.allocate(cBuf.capacity() * 2);
+        newBuf.put(cBuf.array(), cBuf.arrayOffset(), cBuf.position());
+        cBuf = newBuf;
+        ThreadLocalCache.charBuffer.set(cBuf);
     }
 }
diff --git a/libcore/archive/src/main/java/java/util/jar/JarEntry.java b/libcore/archive/src/main/java/java/util/jar/JarEntry.java
index b8dabee..869e4b4 100644
--- a/libcore/archive/src/main/java/java/util/jar/JarEntry.java
+++ b/libcore/archive/src/main/java/java/util/jar/JarEntry.java
@@ -33,27 +33,27 @@
 /**
  * Represents a single file in a JAR archive together with the manifest
  * attributes and digital signatures associated with it.
- * 
- * @since Android 1.0
+ *
+ * @see JarFile
+ * @see JarInputStream
  */
 public class JarEntry extends ZipEntry {
     private Attributes attributes;
 
     JarFile parentJar;
-   
+
     CodeSigner signers[];
 
     // Cached factory used to build CertPath-s in <code>getCodeSigners()</code>.
     private CertificateFactory factory;
 
-    private boolean isFactoryChecked = false;     
+    private boolean isFactoryChecked = false;
 
     /**
      * Creates a new {@code JarEntry} named name.
      * 
      * @param name
      *            The name of the new {@code JarEntry}.
-     * @since Android 1.0
      */
     public JarEntry(String name) {
         super(name);
@@ -64,7 +64,6 @@
      * 
      * @param entry
      *            The ZipEntry to obtain values from.
-     * @since Android 1.0
      */
     public JarEntry(ZipEntry entry) {
         super(entry);
@@ -78,7 +77,6 @@
      * @exception IOException
      *                If an error occurs obtaining the {@code Attributes}.
      * @see Attributes
-     * @since Android 1.0
      */
     public Attributes getAttributes() throws IOException {
         if (attributes != null || parentJar == null) {
@@ -99,7 +97,6 @@
      * 
      * @return the certificate for this entry.
      * @see java.security.cert.Certificate
-     * @since Android 1.0
      */
     public Certificate[] getCertificates() {
         if (null == parentJar) {
@@ -122,12 +119,11 @@
      * 
      * @param je
      *            The {@code JarEntry} to obtain values from.
-     * @since Android 1.0
      */
     public JarEntry(JarEntry je) {
         super(je);
         parentJar = je.parentJar;
-        attributes = je.attributes;        
+        attributes = je.attributes;
         signers = je.signers;
     }
 
@@ -139,7 +135,6 @@
      * 
      * @return the code signers for the JAR entry.
      * @see CodeSigner
-     * @since Android 1.0
      */
     public CodeSigner[] getCodeSigners() {
         if (null == signers) {
@@ -155,7 +150,7 @@
     }
 
     private CodeSigner[] getCodeSigners(Certificate[] certs) {
-        if(null == certs) {
+        if (null == certs) {
             return null;
         }
 
diff --git a/libcore/archive/src/main/java/java/util/jar/JarException.java b/libcore/archive/src/main/java/java/util/jar/JarException.java
index f18d639..d6943c4 100644
--- a/libcore/archive/src/main/java/java/util/jar/JarException.java
+++ b/libcore/archive/src/main/java/java/util/jar/JarException.java
@@ -22,8 +22,6 @@
 /**
  * This runtime exception is thrown when a problem occurs while reading a JAR
  * file.
- * 
- * @since Android 1.0
  */
 public class JarException extends ZipException {
 
@@ -31,8 +29,6 @@
 
     /**
      * Constructs a new {@code JarException} instance.
-     * 
-     * @since Android 1.0
      */
     public JarException() {
         super();
@@ -41,10 +37,9 @@
     /**
      * Constructs a new {@code JarException} instance with the specified
      * message.
-     * 
+     *
      * @param detailMessage
      *            the detail message for the exception.
-     * @since Android 1.0
      */
     public JarException(String detailMessage) {
         super(detailMessage);
diff --git a/libcore/archive/src/main/java/java/util/jar/JarFile.java b/libcore/archive/src/main/java/java/util/jar/JarFile.java
index 9af9056..d6e8339 100644
--- a/libcore/archive/src/main/java/java/util/jar/JarFile.java
+++ b/libcore/archive/src/main/java/java/util/jar/JarFile.java
@@ -29,7 +29,6 @@
 import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.security.MessageDigest;
 import java.util.Enumeration;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
@@ -39,17 +38,14 @@
 /**
  * {@code JarFile} is used to read jar entries and their associated data from
  * jar files.
- * 
+ *
  * @see JarInputStream
  * @see JarEntry
- * @since Android 1.0
  */
 public class JarFile extends ZipFile {
 
     /**
      * The MANIFEST file name.
-     * 
-     * @since Android 1.0
      */
     public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF"; //$NON-NLS-1$
 
@@ -70,63 +66,69 @@
 
         private ZipEntry zipEntry;
 
-        private JarVerifier verifier;
-
         private JarVerifier.VerifierEntry entry;
 
-        private MessageDigest digest;
-
-        JarFileInputStream(InputStream is, ZipEntry ze, JarVerifier ver) {
+        JarFileInputStream(InputStream is, ZipEntry ze,
+                JarVerifier.VerifierEntry e) {
             super(is);
-            if (ver != null) {
-                zipEntry = ze;
-                verifier = ver;
-                count = zipEntry.getSize();
-                entry = verifier.initEntry(ze.getName());
-                if (entry != null) {
-                    digest = entry.digest;
-                }
-            }
+            zipEntry = ze;
+            count = zipEntry.getSize();
+            entry = e;
         }
 
         @Override
         public int read() throws IOException {
-            int r = super.read();
-            if (entry != null) {
+            if (count > 0) {
+                int r = super.read();
                 if (r != -1) {
-                    digest.update((byte) r);
+                    entry.write(r);
                     count--;
+                } else {
+                    count = 0;
                 }
-                if (r == -1 || count <= 0) {
-                    JarVerifier.VerifierEntry temp = entry;
-                    entry = null;
-                    verifier.verifySignatures(temp, zipEntry);
+                if (count == 0) {
+                    entry.verify();
                 }
+                return r;
+            } else {
+                return -1;
             }
-            return r;
         }
 
         @Override
         public int read(byte[] buf, int off, int nbytes) throws IOException {
-            int r = super.read(buf, off, nbytes);
-            if (entry != null) {
+            if (count > 0) {
+                int r = super.read(buf, off, nbytes);
                 if (r != -1) {
                     int size = r;
                     if (count < size) {
                         size = (int) count;
                     }
-                    digest.update(buf, off, size);
-                    count -= r;
+                    entry.write(buf, off, size);
+                    count -= size;
+                } else {
+                    count = 0;
                 }
-                if (r == -1 || count <= 0) {
-                    JarVerifier.VerifierEntry temp = entry;
-                    entry = null;
-                    verifier.verifySignatures(temp, zipEntry);
+                if (count == 0) {
+                    entry.verify();
                 }
+                return r;
+            } else {
+                return -1;
             }
-            return r;
         }
 
+        // BEGIN android-added
+        @Override
+        public int available() throws IOException {
+            if (count > 0) {
+                return super.available();
+            } else {
+                return 0;
+            }
+        }
+        // END android-added
+
         @Override
         public long skip(long nbytes) throws IOException {
             long cnt = 0, rem = 0;
@@ -146,12 +148,11 @@
 
     /**
      * Create a new {@code JarFile} using the contents of the specified file.
-     * 
+     *
      * @param file
      *            the JAR file as {@link File}.
      * @throws IOException
      *             If the file cannot be read.
-     * @since Android 1.0
      */
     public JarFile(File file) throws IOException {
         this(file, true);
@@ -159,14 +160,13 @@
 
     /**
      * Create a new {@code JarFile} using the contents of the specified file.
-     * 
+     *
      * @param file
      *            the JAR file as {@link File}.
      * @param verify
      *            if this JAR file is signed whether it must be verified.
      * @throws IOException
      *             If the file cannot be read.
-     * @since Android 1.0
      */
     public JarFile(File file, boolean verify) throws IOException {
         super(file);
@@ -178,7 +178,7 @@
 
     /**
      * Create a new {@code JarFile} using the contents of file.
-     * 
+     *
      * @param file
      *            the JAR file as {@link File}.
      * @param verify
@@ -188,7 +188,6 @@
      *            {@link ZipFile#OPEN_DELETE OPEN_DELETE}.
      * @throws IOException
      *             If the file cannot be read.
-     * @since Android 1.0
      */
     public JarFile(File file, boolean verify, int mode) throws IOException {
         super(file, mode);
@@ -201,12 +200,11 @@
     /**
      * Create a new {@code JarFile} from the contents of the file specified by
      * filename.
-     * 
+     *
      * @param filename
      *            the file name referring to the JAR file.
      * @throws IOException
      *             if file name cannot be opened for reading.
-     * @since Android 1.0
      */
     public JarFile(String filename) throws IOException {
         this(filename, true);
@@ -216,14 +214,13 @@
     /**
      * Create a new {@code JarFile} from the contents of the file specified by
      * {@code filename}.
-     * 
+     *
      * @param filename
      *            the file name referring to the JAR file.
      * @param verify
      *            if this JAR filed is signed whether it must be verified.
      * @throws IOException
      *             If file cannot be opened or read.
-     * @since Android 1.0
      */
     public JarFile(String filename, boolean verify) throws IOException {
         super(filename);
@@ -236,11 +233,10 @@
     /**
      * Return an enumeration containing the {@code JarEntrys} contained in this
      * {@code JarFile}.
-     * 
+     *
      * @return the {@code Enumeration} containing the JAR entries.
      * @throws IllegalStateException
      *             if this {@code JarFile} is closed.
-     * @since Android 1.0
      */
     @Override
     public Enumeration<JarEntry> entries() {
@@ -260,7 +256,7 @@
 
             public JarEntry nextElement() {
                 JarEntry je = new JarEntry(ze.nextElement());
-                je.parentJar = jf;                
+                je.parentJar = jf;
                 return je;
             }
         }
@@ -270,11 +266,10 @@
     /**
      * Return the {@code JarEntry} specified by its name or {@code null} if no
      * such entry exists.
-     * 
+     *
      * @param name
      *            the name of the entry in the JAR file.
      * @return the JAR entry defined by the name.
-     * @since Android 1.0
      */
     public JarEntry getJarEntry(String name) {
         return (JarEntry) getEntry(name);
@@ -302,14 +297,13 @@
     /**
      * Returns the {@code Manifest} object associated with this {@code JarFile}
      * or {@code null} if no MANIFEST entry exists.
-     * 
+     *
      * @return the MANIFEST.
      * @throws IOException
      *             if an error occurs reading the MANIFEST file.
      * @throws IllegalStateException
      *             if the jar file is closed.
      * @see Manifest
-     * @since Android 1.0
      */
     public Manifest getManifest() throws IOException {
         // BEGIN android-added
@@ -358,10 +352,14 @@
                     if (verifier == null) {
                         break;
                     }
-                } else if (verifier != null && entryName.length() > dirLength
-                        && (Util.ASCIIIgnoreCaseRegionMatches(entryName, entryName.length() - 3, ".SF", 0 ,3) //$NON-NLS-1$
-                           || Util.ASCIIIgnoreCaseRegionMatches(entryName, entryName.length() - 4, ".DSA", 0 ,4) //$NON-NLS-1$
-                           || Util.ASCIIIgnoreCaseRegionMatches(entryName, entryName.length() - 4, ".RSA", 0 ,4))){ //$NON-NLS-1$
+                } else if (verifier != null
+                        && entryName.length() > dirLength
+                        && (Util.ASCIIIgnoreCaseRegionMatches(entryName,
+                                entryName.length() - 3, ".SF", 0, 3) //$NON-NLS-1$
+                                || Util.ASCIIIgnoreCaseRegionMatches(entryName,
+                                        entryName.length() - 4, ".DSA", 0, 4) //$NON-NLS-1$
+                        || Util.ASCIIIgnoreCaseRegionMatches(entryName,
+                                entryName.length() - 4, ".RSA", 0, 4))) { //$NON-NLS-1$
                     signed = true;
                     InputStream is = super.getInputStream(entry);
                     // BEGIN android-modified
@@ -379,13 +377,12 @@
     /**
      * Return an {@code InputStream} for reading the decompressed contents of
      * ZIP entry.
-     * 
+     *
      * @param ze
      *            the ZIP entry to be read.
      * @return the input stream to read from.
      * @throws IOException
      *             if an error occurred while creating the input stream.
-     * @since Android 1.0
      */
     @Override
     public InputStream getInputStream(ZipEntry ze) throws IOException {
@@ -395,8 +392,7 @@
         if (verifier != null) {
             verifier.setManifest(getManifest());
             if (manifest != null) {
-                verifier.mainAttributesChunk = manifest
-                        .getMainAttributesChunk();
+                verifier.mainAttributesEnd = manifest.getMainAttributesEnd();
             }
             if (verifier.readCertificates()) {
                 verifier.removeMetaEntries();
@@ -412,19 +408,24 @@
         if (in == null) {
             return null;
         }
-        return new JarFileInputStream(in, ze, ze.getSize() >= 0 ? verifier
-                : null);
+        if (verifier == null || ze.getSize() == -1) {
+            return in;
+        }
+        JarVerifier.VerifierEntry entry = verifier.initEntry(ze.getName());
+        if (entry == null) {
+            return in;
+        }
+        return new JarFileInputStream(in, ze, entry);
     }
 
     /**
      * Return the {@code JarEntry} specified by name or {@code null} if no such
      * entry exists.
-     * 
+     *
      * @param name
      *            the name of the entry in the JAR file.
      * @return the ZIP entry extracted.
-     * @since Android 1.0
-     */         
+     */
     @Override
     public ZipEntry getEntry(String name) {
         ZipEntry ze = super.getEntry(name);
@@ -432,16 +433,16 @@
             return ze;
         }
         JarEntry je = new JarEntry(ze);
-        je.parentJar = this;        
+        je.parentJar = this;
         return je;
     }
 
     // BEGIN android-modified
     private ZipEntry[] getMetaEntriesImpl(byte[] buf) {
         int n = 0;
-        
+
         List<ZipEntry> list = new ArrayList<ZipEntry>();
-        
+
         Enumeration<? extends ZipEntry> allEntries = entries();
         while (allEntries.hasMoreElements()) {
             ZipEntry ze = allEntries.nextElement();
@@ -463,10 +464,9 @@
     // BEGIN android-added
     /**
      * Closes this {@code JarFile}.
-     * 
+     *
      * @throws IOException
      *             if an error occurs.
-     * @since Android 1.0
      */
     @Override
     public void close() throws IOException {
diff --git a/libcore/archive/src/main/java/java/util/jar/JarInputStream.java b/libcore/archive/src/main/java/java/util/jar/JarInputStream.java
index ef353ab..c803183 100644
--- a/libcore/archive/src/main/java/java/util/jar/JarInputStream.java
+++ b/libcore/archive/src/main/java/java/util/jar/JarInputStream.java
@@ -24,14 +24,13 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 
-import org.apache.harmony.archive.util.Util;
+import org.apache.harmony.luni.util.Util;
 
 /**
  * The input stream from which the JAR file to be read may be fetched. It is
  * used like the {@code ZipInputStream}.
- * 
+ *
  * @see ZipInputStream
- * @since Android 1.0
  */
 public class JarInputStream extends ZipInputStream {
 
@@ -51,7 +50,7 @@
 
     /**
      * Constructs a new {@code JarInputStream} from an input stream.
-     * 
+     *
      * @param stream
      *            the input stream containing the JAR file.
      * @param verify
@@ -59,7 +58,6 @@
      * @throws IOException
      *             If an error occurs reading entries from the input stream.
      * @see ZipInputStream#ZipInputStream(InputStream)
-     * @since Android 1.0
      */
     public JarInputStream(InputStream stream, boolean verify)
             throws IOException {
@@ -84,8 +82,8 @@
             if (verify) {
                 verifier.setManifest(manifest);
                 if (manifest != null) {
-                    verifier.mainAttributesChunk = manifest
-                            .getMainAttributesChunk();
+                    verifier.mainAttributesEnd = manifest
+                            .getMainAttributesEnd();
                 }
             }
 
@@ -103,13 +101,12 @@
 
     /**
      * Constructs a new {@code JarInputStream} from an input stream.
-     * 
+     *
      * @param stream
      *            the input stream containing the JAR file.
      * @throws IOException
      *             If an error occurs reading entries from the input stream.
      * @see ZipInputStream#ZipInputStream(InputStream)
-     * @since Android 1.0
      */
     public JarInputStream(InputStream stream) throws IOException {
         this(stream, true);
@@ -120,7 +117,6 @@
      * JarInputStream} or {@code null} if no manifest entry exists.
      * 
      * @return the MANIFEST specifying the contents of the JAR file.
-     * @since Android 1.0
      */
     public Manifest getManifest() {
         return manifest;
@@ -133,7 +129,6 @@
      * @return the next JAR entry.
      * @throws IOException
      *             if an error occurs while reading the entry.
-     * @since Android 1.0
      */
     public JarEntry getNextJarEntry() throws IOException {
         return (JarEntry) getNextEntry();
@@ -142,7 +137,7 @@
     /**
      * Reads up to {@code length} of decompressed data and stores it in
      * {@code buffer} starting at {@code offset}.
-     * 
+     *
      * @param buffer
      *            Buffer to store into
      * @param offset
@@ -152,7 +147,6 @@
      * @return Number of uncompressed bytes read
      * @throws IOException
      *             if an IOException occurs.
-     * @since Android 1.0
      */
     @Override
     public int read(byte[] buffer, int offset, int length) throws IOException {
@@ -175,9 +169,7 @@
                             throw e;
                         }
                     } else {
-                        verifier.verifySignatures(
-                                (JarVerifier.VerifierEntry) verStream,
-                                jarEntry);
+                        ((JarVerifier.VerifierEntry) verStream).verify();
                     }
                 }
             } else {
@@ -194,7 +186,6 @@
      * @return the next extracted ZIP entry.
      * @throws IOException
      *             if an error occurs while reading the entry.
-     * @since Android 1.0
      */
     @Override
     public ZipEntry getNextEntry() throws IOException {
diff --git a/libcore/archive/src/main/java/java/util/jar/JarOutputStream.java b/libcore/archive/src/main/java/java/util/jar/JarOutputStream.java
index 640f4ef..e901d87 100644
--- a/libcore/archive/src/main/java/java/util/jar/JarOutputStream.java
+++ b/libcore/archive/src/main/java/java/util/jar/JarOutputStream.java
@@ -25,8 +25,6 @@
 /**
  * The {@code JarOutputStream} is used to write data in the {@code JarFile}
  * format to an arbitrary output stream
- * 
- * @since Android 1.0
  */
 public class JarOutputStream extends ZipOutputStream {
 
@@ -79,7 +77,6 @@
      * @throws IOException
      *             if an error occurs writing to the entry.
      * @see ZipEntry
-     * @since Android 1.0
      */
     @Override
     public void putNextEntry(ZipEntry ze) throws IOException {
diff --git a/libcore/archive/src/main/java/java/util/jar/JarVerifier.java b/libcore/archive/src/main/java/java/util/jar/JarVerifier.java
index b9173f2..6c1ee93 100644
--- a/libcore/archive/src/main/java/java/util/jar/JarVerifier.java
+++ b/libcore/archive/src/main/java/java/util/jar/JarVerifier.java
@@ -31,13 +31,12 @@
 import java.util.Map;
 import java.util.StringTokenizer;
 import java.util.Vector;
-import java.util.zip.ZipEntry;
 
 import org.apache.harmony.archive.internal.nls.Messages;
 import org.apache.harmony.luni.util.Base64;
 import org.apache.harmony.security.utils.JarUtils;
 
-import org.apache.harmony.archive.util.Util;
+import org.apache.harmony.luni.util.Util;
 
 // BEGIN android-added
 import org.apache.harmony.xnet.provider.jsse.OpenSSLMessageDigestJDK;
@@ -65,65 +64,81 @@
 
     private HashMap<String, byte[]> metaEntries = new HashMap<String, byte[]>(5);
 
-    private final Hashtable<String, HashMap<String, Attributes>> signatures =
-        new Hashtable<String, HashMap<String, Attributes>>(5);
+    private final Hashtable<String, HashMap<String, Attributes>> signatures = new Hashtable<String, HashMap<String, Attributes>>(
+            5);
 
-    private final Hashtable<String, Certificate[]> certificates =
-        new Hashtable<String, Certificate[]>(5);
+    private final Hashtable<String, Certificate[]> certificates = new Hashtable<String, Certificate[]>(
+            5);
 
-    private final Hashtable<String, Certificate[]> verifiedEntries =
-        new Hashtable<String, Certificate[]>();
+    private final Hashtable<String, Certificate[]> verifiedEntries = new Hashtable<String, Certificate[]>();
 
-    byte[] mainAttributesChunk;
+    int mainAttributesEnd;
 
-    // BEGIN android-added
-    private static long measureCount = 0;
-    
-    private static long averageTime = 0;
-    // END android-added
-    
     /**
-     * TODO Type description
+     * Stores and a hash and a message digest and verifies that massage digest
+     * matches the hash.
      */
-    static class VerifierEntry extends OutputStream {
+    class VerifierEntry extends OutputStream {
 
-        MessageDigest digest;
+        private String name;
 
-        byte[] hash;
+        private MessageDigest digest;
 
-        Certificate[] certificates;
+        private byte[] hash;
 
-        VerifierEntry(MessageDigest digest, byte[] hash,
+        private Certificate[] certificates;
+
+        VerifierEntry(String name, MessageDigest digest, byte[] hash,
                 Certificate[] certificates) {
+            this.name = name;
             this.digest = digest;
             this.hash = hash;
             this.certificates = certificates;
         }
 
-        /*
-         * (non-Javadoc)
-         * 
-         * @see java.io.OutputStream#write(int)
+        /**
+         * Updates a digest with one byte.
          */
         @Override
         public void write(int value) {
             digest.update((byte) value);
         }
 
-        /*
-         * (non-Javadoc)
-         * 
-         * @see java.io.OutputStream#write(byte[], int, int)
+        /**
+         * Updates a digest with byte array.
          */
         @Override
         public void write(byte[] buf, int off, int nbytes) {
             digest.update(buf, off, nbytes);
         }
+
+        /**
+         * Verifies that the digests stored in the manifest match the decrypted
+         * digests from the .SF file. This indicates the validity of the
+         * signing, not the integrity of the file, as it's digest must be
+         * calculated and verified when its contents are read.
+         *
+         * @throws SecurityException
+         *             if the digest value stored in the manifest does <i>not</i>
+         *             agree with the decrypted digest as recovered from the
+         *             <code>.SF</code> file.
+         * @see #initEntry(String)
+         */
+        void verify() {
+            byte[] d = digest.digest();
+            if (!MessageDigest.isEqual(d, Base64.decode(hash))) {
+                throw new SecurityException(Messages.getString(
+                        "archive.32", new Object[] { //$NON-NLS-1$
+                        JarFile.MANIFEST_NAME, name, jarName }));
+            }
+            verifiedEntries.put(name, certificates);
+        }
+
     }
 
     /**
      * Constructs and returns a new instance of {@code JarVerifier}.
-     * 
+     *
      * @param name
      *            the name of the JAR file being verified.
      */
@@ -136,13 +151,12 @@
      * stream. This method constructs and returns a new {@link VerifierEntry}
      * which contains the certificates used to sign the entry and its hash value
      * as specified in the JAR MANIFEST format.
-     * 
+     *
      * @param name
      *            the name of an entry in a JAR file which is <b>not</b> in the
      *            {@code META-INF} directory.
      * @return a new instance of {@link VerifierEntry} which can be used by
      *         callers as an {@link OutputStream}.
-     * @since Android 1.0
      */
     VerifierEntry initEntry(String name) {
         // If no manifest is present by the time an entry is found,
@@ -159,8 +173,8 @@
         }
 
         Vector<Certificate> certs = new Vector<Certificate>();
-        Iterator<Map.Entry<String, HashMap<String, Attributes>>> it =
-            signatures.entrySet().iterator();
+        Iterator<Map.Entry<String, HashMap<String, Attributes>>> it = signatures
+                .entrySet().iterator();
         while (it.hasNext()) {
             Map.Entry<String, HashMap<String, Attributes>> entry = it.next();
             HashMap<String, Attributes> hm = entry.getValue();
@@ -197,18 +211,18 @@
             }
             byte[] hashBytes;
             try {
-                hashBytes = hash.getBytes("ISO8859_1"); //$NON-NLS-1$
+                hashBytes = hash.getBytes("ISO-8859-1"); //$NON-NLS-1$
             } catch (UnsupportedEncodingException e) {
                 throw new RuntimeException(e.toString());
             }
 
             try {
                 // BEGIN android-changed
-                return new VerifierEntry(OpenSSLMessageDigestJDK.getInstance(algorithm),
+                return new VerifierEntry(name, OpenSSLMessageDigestJDK.getInstance(algorithm),
                         hashBytes, certificatesArray);
                 // END android-changed
             } catch (NoSuchAlgorithmException e) {
-                // Ignored
+                // ignored
             }
         }
         return null;
@@ -219,7 +233,7 @@
      * entry in the {@code META-INF} directory including the manifest
      * file itself. Files associated with the signing of a JAR would also be
      * added to this collection.
-     * 
+     *
      * @param name
      *            the name of the file located in the {@code META-INF}
      *            directory.
@@ -234,7 +248,7 @@
     /**
      * If the associated JAR file is signed, check on the validity of all of the
      * known signatures.
-     * 
+     *
      * @return {@code true} if the associated JAR is signed and an internal
      *         check verifies the validity of the signature(s). {@code false} if
      *         the associated JAR file has no entries at all in its {@code
@@ -243,12 +257,10 @@
      *         <p>
      *         Will also return {@code true} if the JAR file is <i>not</i>
      *         signed.
-     *         </p>
      * @throws SecurityException
      *             if the JAR file is signed and it is determined that a
      *             signature block file contains an invalid signature for the
      *             corresponding signature file.
-     * @since Android 1.0
      */
     synchronized boolean readCertificates() {
         if (metaEntries == null) {
@@ -281,6 +293,12 @@
             return;
         }
 
+        byte[] manifest = metaEntries.get(JarFile.MANIFEST_NAME);
+        // Manifest entry is required for any verifications.
+        if (manifest == null) {
+            return;
+        }
+
         byte[] sBlockBytes = metaEntries.get(certFile);
         try {
             Certificate[] signerCertChain = JarUtils.verifySignature(
@@ -288,7 +306,7 @@
                     new ByteArrayInputStream(sBlockBytes));
             /*
              * Recursive call in loading security provider related class which
-             * is in a signed JAR. 
+             * is in a signed JAR.
              */
             if (null == metaEntries) {
                 return;
@@ -299,74 +317,70 @@
         } catch (IOException e) {
             return;
         } catch (GeneralSecurityException e) {
-            /* [MSG "archive.30", "{0} failed verification of {1}"] */
-            throw new SecurityException(
-                    Messages.getString("archive.30", jarName, signatureFile)); //$NON-NLS-1$
+            /* [MSG "archive.31", "{0} failed verification of {1}"] */
+            throw new SecurityException(Messages.getString(
+                    "archive.31", jarName, signatureFile)); //$NON-NLS-1$
         }
 
         // Verify manifest hash in .sf file
         Attributes attributes = new Attributes();
-        HashMap<String, Attributes> hm = new HashMap<String, Attributes>();
+        HashMap<String, Attributes> entries = new HashMap<String, Attributes>();
         try {
-            new InitManifest(new ByteArrayInputStream(sfBytes), attributes, hm,
-                    null, "Signature-Version"); //$NON-NLS-1$
+            InitManifest im = new InitManifest(sfBytes, attributes, Attributes.Name.SIGNATURE_VERSION);
+            im.initEntries(entries, null);
         } catch (IOException e) {
             return;
         }
 
         boolean createdBySigntool = false;
-        String createdByValue = attributes.getValue("Created-By"); //$NON-NLS-1$
-        if (createdByValue != null) {
-            createdBySigntool = createdByValue.indexOf("signtool") != -1; //$NON-NLS-1$
+        String createdBy = attributes.getValue("Created-By"); //$NON-NLS-1$
+        if (createdBy != null) {
+            createdBySigntool = createdBy.indexOf("signtool") != -1; //$NON-NLS-1$
         }
 
         // Use .SF to verify the mainAttributes of the manifest
         // If there is no -Digest-Manifest-Main-Attributes entry in .SF
         // file, such as those created before java 1.5, then we ignore
         // such verification.
-        // FIXME: The meaning of createdBySigntool
-        if (mainAttributesChunk != null && !createdBySigntool) {
+        if (mainAttributesEnd > 0 && !createdBySigntool) {
             String digestAttribute = "-Digest-Manifest-Main-Attributes"; //$NON-NLS-1$
-            if (!verify(attributes, digestAttribute, mainAttributesChunk,
-                    false, true)) {
-                /* [MSG "archive.30", "{0} failed verification of {1}"] */
-                throw new SecurityException(
-                        Messages.getString("archive.30", jarName, signatureFile)); //$NON-NLS-1$
+            if (!verify(attributes, digestAttribute, manifest, 0,
+                    mainAttributesEnd, false, true)) {
+                /* [MSG "archive.31", "{0} failed verification of {1}"] */
+                throw new SecurityException(Messages.getString(
+                        "archive.31", jarName, signatureFile)); //$NON-NLS-1$
             }
         }
 
-        byte[] manifest = metaEntries.get(JarFile.MANIFEST_NAME);
-        if (manifest == null) {
-            return;
-        }
-        // Use .SF to verify the whole manifest
+        // Use .SF to verify the whole manifest.
         String digestAttribute = createdBySigntool ? "-Digest" //$NON-NLS-1$
                 : "-Digest-Manifest"; //$NON-NLS-1$
-        if (!verify(attributes, digestAttribute, manifest, false, false)) {
-            Iterator<Map.Entry<String, Attributes>> it = hm.entrySet()
+        if (!verify(attributes, digestAttribute, manifest, 0, manifest.length,
+                false, false)) {
+            Iterator<Map.Entry<String, Attributes>> it = entries.entrySet()
                     .iterator();
             while (it.hasNext()) {
                 Map.Entry<String, Attributes> entry = it.next();
-                byte[] chunk = man.getChunk(entry.getKey());
+                Manifest.Chunk chunk = man.getChunk(entry.getKey());
                 if (chunk == null) {
                     return;
                 }
-                if (!verify(entry.getValue(), "-Digest", chunk, //$NON-NLS-1$
-                        createdBySigntool, false)) {
-                    /* [MSG "archive.31", "{0} has invalid digest for {1} in {2}"] */
-                    throw new SecurityException(
-                        Messages.getString("archive.31", //$NON-NLS-1$
-                            new Object[] { signatureFile, entry.getKey(), jarName }));
+                if (!verify(entry.getValue(), "-Digest", manifest, //$NON-NLS-1$
+                        chunk.start, chunk.end, createdBySigntool, false)) {
+                    throw new SecurityException(Messages.getString(
+                            "archive.32", //$NON-NLS-1$
+                            new Object[] { signatureFile, entry.getKey(),
+                                    jarName }));
                 }
             }
         }
         metaEntries.put(signatureFile, null);
-        signatures.put(signatureFile, hm);
+        signatures.put(signatureFile, entries);
     }
 
     /**
      * Associate this verifier with the specified {@link Manifest} object.
-     * 
+     *
      * @param mf
      *            a {@code java.util.jar.Manifest} object.
      */
@@ -375,36 +389,9 @@
     }
 
     /**
-     * Verifies that the digests stored in the manifest match the decrypted
-     * digests from the .SF file. This indicates the validity of the signing,
-     * not the integrity of the file, as it's digest must be calculated and
-     * verified when its contents are read.
-     * 
-     * @param entry
-     *            the {@link VerifierEntry} associated with the specified
-     *            {@code zipEntry}.
-     * @param zipEntry
-     *            an entry in the JAR file
-     * @throws SecurityException
-     *             if the digest value stored in the manifest does <i>not</i>
-     *             agree with the decrypted digest as recovered from the
-     *             {@code .SF} file.
-     * @see #initEntry(String)
-     */
-    void verifySignatures(VerifierEntry entry, ZipEntry zipEntry) {
-        byte[] digest = entry.digest.digest();
-        if (!MessageDigest.isEqual(digest, Base64.decode(entry.hash))) {
-            /* [MSG "archive.31", "{0} has invalid digest for {1} in {2}"] */
-            throw new SecurityException(Messages.getString("archive.31", new Object[] { //$NON-NLS-1$
-                    JarFile.MANIFEST_NAME, zipEntry.getName(), jarName }));
-        }
-        verifiedEntries.put(zipEntry.getName(), entry.certificates);
-    }
-
-    /**
-     * Returns a {@code boolean} indication of whether or not the
-     * associated JAR file is signed.
-     * 
+     * Returns a <code>boolean</code> indication of whether or not the
+     * associated jar file is signed.
+     *
      * @return {@code true} if the JAR is signed, {@code false}
      *         otherwise.
      */
@@ -413,7 +400,7 @@
     }
 
     private boolean verify(Attributes attributes, String entry, byte[] data,
-            boolean ignoreSecondEndline, boolean ignorable) {
+            int start, int end, boolean ignoreSecondEndline, boolean ignorable) {
         String algorithms = attributes.getValue("Digest-Algorithms"); //$NON-NLS-1$
         if (algorithms == null) {
             algorithms = "SHA SHA1"; //$NON-NLS-1$
@@ -434,16 +421,16 @@
             } catch (NoSuchAlgorithmException e) {
                 continue;
             }
-            if (ignoreSecondEndline && data[data.length - 1] == '\n'
-                    && data[data.length - 2] == '\n') {
-                md.update(data, 0, data.length - 1);
+            if (ignoreSecondEndline && data[end - 1] == '\n'
+                    && data[end - 2] == '\n') {
+                md.update(data, start, end - 1 - start);
             } else {
-                md.update(data, 0, data.length);
+                md.update(data, start, end - start);
             }
             byte[] b = md.digest();
             byte[] hashBytes;
             try {
-                hashBytes = hash.getBytes("ISO8859_1"); //$NON-NLS-1$
+                hashBytes = hash.getBytes("ISO-8859-1"); //$NON-NLS-1$
             } catch (UnsupportedEncodingException e) {
                 throw new RuntimeException(e.toString());
             }
@@ -456,7 +443,7 @@
      * Returns all of the {@link java.security.cert.Certificate} instances that
      * were used to verify the signature on the JAR entry called
      * {@code name}.
-     * 
+     *
      * @param name
      *            the name of a JAR entry.
      * @return an array of {@link java.security.cert.Certificate}.
@@ -472,7 +459,7 @@
     /**
      * Remove all entries from the internal collection of data held about each
      * JAR entry in the {@code META-INF} directory.
-     * 
+     *
      * @see #addMetaEntry(String, byte[])
      */
     void removeMetaEntries() {
@@ -483,7 +470,7 @@
      * Returns a {@code Vector} of all of the
      * {@link java.security.cert.Certificate}s that are associated with the
      * signing of the named signature file.
-     * 
+     *
      * @param signatureFileName
      *            the name of a signature file.
      * @param certificates
diff --git a/libcore/archive/src/main/java/java/util/jar/Manifest.java b/libcore/archive/src/main/java/java/util/jar/Manifest.java
index 3b0d89a..b28f3fb 100644
--- a/libcore/archive/src/main/java/java/util/jar/Manifest.java
+++ b/libcore/archive/src/main/java/java/util/jar/Manifest.java
@@ -17,47 +17,65 @@
 
 package java.util.jar;
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
-import java.nio.charset.Charset;
-import java.security.AccessController;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CoderResult;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 
-import org.apache.harmony.luni.util.PriviAction;
+import org.apache.harmony.archive.internal.nls.Messages;
+import org.apache.harmony.luni.util.InputStreamExposer;
+import org.apache.harmony.luni.util.ThreadLocalCache;
 
 /**
  * The {@code Manifest} class is used to obtain attribute information for a
  * {@code JarFile} and its entries.
- * 
- * @since Android 1.0
  */
 public class Manifest implements Cloneable {
-    private static final int LINE_LENGTH_LIMIT = 70;
+    static final int LINE_LENGTH_LIMIT = 72;
 
     private static final byte[] LINE_SEPARATOR = new byte[] { '\r', '\n' };
 
-    private static final Attributes.Name NAME_ATTRIBUTE = new Attributes.Name("Name"); //$NON-NLS-1$
+    private static final byte[] VALUE_SEPARATOR = new byte[] { ':', ' ' };
+
+    private static final Attributes.Name NAME_ATTRIBUTE = new Attributes.Name(
+            "Name"); //$NON-NLS-1$
 
     private Attributes mainAttributes = new Attributes();
 
-    private HashMap<String, Attributes> entryAttributes = new HashMap<String, Attributes>();
+    private HashMap<String, Attributes> entries = new HashMap<String, Attributes>();
 
-    private HashMap<String, byte[]> chunks;
+    static class Chunk {
+        int start;
+        int end;
+
+        Chunk(int start, int end) {
+            this.start = start;
+            this.end = end;
+        }
+    }
+
+    private HashMap<String, Chunk> chunks;
 
     /**
-     * The data chunk of main attributes in the manifest is needed in
+     * Manifest bytes are used for delayed entry parsing.
+     */
+    private InitManifest im;
+
+    /**
+     * The end of the main attributes section in the manifest is needed in
      * verification.
      */
-    private byte[] mainAttributesChunk;
+    private int mainEnd;
 
     /**
      * Creates a new {@code Manifest} instance.
-     * 
-     * @since Android 1.0
      */
     public Manifest() {
         super();
@@ -66,12 +84,11 @@
     /**
      * Creates a new {@code Manifest} instance using the attributes obtained
      * from the input stream.
-     * 
+     *
      * @param is
      *            {@code InputStream} to parse for attributes.
      * @throws IOException
      *             if an IO error occurs while creating this {@code Manifest}
-     * @since Android 1.0
      */
     public Manifest(InputStream is) throws IOException {
         super();
@@ -81,20 +98,20 @@
     /**
      * Creates a new {@code Manifest} instance. The new instance will have the
      * same attributes as those found in the parameter {@code Manifest}.
-     * 
+     *
      * @param man
      *            {@code Manifest} instance to obtain attributes from.
-     * @since Android 1.0
      */
     @SuppressWarnings("unchecked")
     public Manifest(Manifest man) {
         mainAttributes = (Attributes) man.mainAttributes.clone();
-        entryAttributes = (HashMap<String, Attributes>) man.entryAttributes.clone();
+        entries = (HashMap<String, Attributes>) ((HashMap<String, Attributes>) man
+                .getEntries()).clone();
     }
 
     Manifest(InputStream is, boolean readChunks) throws IOException {
         if (readChunks) {
-            chunks = new HashMap<String, byte[]>();
+            chunks = new HashMap<String, Chunk>();
         }
         read(is);
     }
@@ -102,23 +119,21 @@
     /**
      * Resets the both the main attributes as well as the entry attributes
      * associated with this {@code Manifest}.
-     * 
-     * @since Android 1.0
      */
     public void clear() {
-        entryAttributes.clear();
+        im = null;
+        entries.clear();
         mainAttributes.clear();
     }
 
     /**
      * Returns the {@code Attributes} associated with the parameter entry
      * {@code name}.
-     * 
+     *
      * @param name
      *            the name of the entry to obtain {@code Attributes} from.
      * @return the Attributes for the entry or {@code null} if the entry does
      *         not exist.
-     * @since Android 1.0
      */
     public Attributes getAttributes(String name) {
         return getEntries().get(name);
@@ -127,20 +142,31 @@
     /**
      * Returns a map containing the {@code Attributes} for each entry in the
      * {@code Manifest}.
-     * 
+     *
      * @return the map of entry attributes.
-     * @since Android 1.0
      */
     public Map<String, Attributes> getEntries() {
-        return entryAttributes;
+        initEntries();
+        return entries;
+    }
+
+    private void initEntries() {
+        if (im == null) {
+            return;
+        }
+        // try {
+        // im.initEntries(entries, chunks);
+        // } catch (IOException ioe) {
+        // throw new RuntimeException(ioe);
+        // }
+        // im = null;
     }
 
     /**
      * Returns the main {@code Attributes} of the {@code JarFile}.
-     * 
+     *
      * @return main {@code Attributes} associated with the source {@code
      *         JarFile}.
-     * @since Android 1.0
      */
     public Attributes getMainAttributes() {
         return mainAttributes;
@@ -149,9 +175,8 @@
     /**
      * Creates a copy of this {@code Manifest}. The returned {@code Manifest}
      * will equal the {@code Manifest} from which it was cloned.
-     * 
+     *
      * @return a copy of this instance.
-     * @since Android 1.0
      */
     @Override
     public Object clone() {
@@ -161,12 +186,11 @@
     /**
      * Writes out the attribute information of the receiver to the specified
      * {@code OutputStream}.
-     * 
+     *
      * @param os
      *            The {@code OutputStream} to write to.
      * @throws IOException
      *             If an error occurs writing the {@code Manifest}.
-     * @since Android 1.0
      */
     public void write(OutputStream os) throws IOException {
         write(this, os);
@@ -175,39 +199,98 @@
     /**
      * Constructs a new {@code Manifest} instance obtaining attribute
      * information from the specified input stream.
-     * 
+     *
      * @param is
      *            The {@code InputStream} to read from.
      * @throws IOException
      *             If an error occurs reading the {@code Manifest}.
-     * @since Android 1.0
      */
     public void read(InputStream is) throws IOException {
-        InitManifest initManifest = new InitManifest(is, mainAttributes, entryAttributes,
-                chunks, null);
-        mainAttributesChunk = initManifest.getMainAttributesChunk();
+        byte[] buf;
+        // Try to read get a reference to the bytes directly
+        try {
+            buf = InputStreamExposer.expose(is);
+        } catch (UnsupportedOperationException uoe) {
+            buf = readFully(is);
+        }
+
+        if (buf.length == 0) {
+            return;
+        }
+
+        // a workaround for HARMONY-5662
+        // replace EOF and NUL with another new line
+        // which does not trigger an error
+        byte b = buf[buf.length - 1];
+        if (0 == b || 26 == b) {
+            buf[buf.length - 1] = '\n';
+        }
+
+        // Attributes.Name.MANIFEST_VERSION is not used for
+        // the second parameter for RI compatibility
+        im = new InitManifest(buf, mainAttributes, null);
+        mainEnd = im.getPos();
+        // FIXME
+        im.initEntries(entries, chunks);
+        im = null;
+    }
+
+    /*
+     * 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.
+     */
+    private byte[] readFully(InputStream is) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        byte[] buffer = new byte[8192];
+
+        while (true) {
+            int count = is.read(buffer);
+            if (count == -1) {
+                // TODO: Do we need to copy this, or can we live with junk at the end?
+                return baos.toByteArray();
+            }
+            baos.write(buffer, 0, count);
+
+            if (!containsLine(buffer, count)) {
+                throw new IOException(Messages.getString("archive.2E")); //$NON-NLS-1$
+            }
+        }
+    }
+
+    /*
+     * 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;
+            }
+        }
+        return false;
     }
 
     /**
      * Returns the hash code for this instance.
-     * 
+     *
      * @return this {@code Manifest}'s hashCode.
-     * @since Android 1.0
      */
     @Override
     public int hashCode() {
-        return mainAttributes.hashCode() ^ entryAttributes.hashCode();
+        return mainAttributes.hashCode() ^ getEntries().hashCode();
     }
 
     /**
      * Determines if the receiver is equal to the parameter object. Two {@code
      * Manifest}s are equal if they have identical main attributes as well as
      * identical entry attributes.
-     * 
+     *
      * @param o
      *            the object to compare against.
      * @return {@code true} if the manifests are equal, {@code false} otherwise
-     * @since Android 1.0
      */
     @Override
     public boolean equals(Object o) {
@@ -220,10 +303,10 @@
         if (!mainAttributes.equals(((Manifest) o).mainAttributes)) {
             return false;
         }
-        return entryAttributes.equals(((Manifest) o).entryAttributes);
+        return getEntries().equals(((Manifest) o).getEntries());
     }
 
-    byte[] getChunk(String name) {
+    Chunk getChunk(String name) {
         return chunks.get(name);
     }
 
@@ -231,114 +314,84 @@
         chunks = null;
     }
 
-    byte[] getMainAttributesChunk() {
-        return mainAttributesChunk;
+    int getMainAttributesEnd() {
+        return mainEnd;
     }
 
     /**
      * Writes out the attribute information of the specified manifest to the
      * specified {@code OutputStream}
-     * 
+     *
      * @param manifest
      *            the manifest to write out.
      * @param out
      *            The {@code OutputStream} to write to.
      * @throws IOException
      *             If an error occurs writing the {@code Manifest}.
-     * @since Android 1.0
      */
     static void write(Manifest manifest, OutputStream out) throws IOException {
-        Charset charset = null;
-        String encoding = AccessController.doPrivileged(new PriviAction<String>(
-                "manifest.write.encoding")); //$NON-NLS-1$
-        if (encoding != null) {
-            if (encoding.length() == 0) {
-                encoding = "UTF8"; //$NON-NLS-1$
-            }
-            charset = Charset.forName(encoding);
-        }
-        String version = manifest.mainAttributes.getValue(Attributes.Name.MANIFEST_VERSION);
+        CharsetEncoder encoder = ThreadLocalCache.utf8Encoder.get();
+        ByteBuffer buffer = ThreadLocalCache.byteBuffer.get();
+
+        String version = manifest.mainAttributes
+                .getValue(Attributes.Name.MANIFEST_VERSION);
         if (version != null) {
-            writeEntry(out, charset, Attributes.Name.MANIFEST_VERSION, version);
+            writeEntry(out, Attributes.Name.MANIFEST_VERSION, version, encoder,
+                    buffer);
             Iterator<?> entries = manifest.mainAttributes.keySet().iterator();
             while (entries.hasNext()) {
                 Attributes.Name name = (Attributes.Name) entries.next();
                 if (!name.equals(Attributes.Name.MANIFEST_VERSION)) {
-                    writeEntry(out, charset, name, manifest.mainAttributes.getValue(name));
+                    writeEntry(out, name, manifest.mainAttributes
+                            .getValue(name), encoder, buffer);
                 }
             }
         }
         out.write(LINE_SEPARATOR);
-        Iterator<String> i = manifest.entryAttributes.keySet().iterator();
+        Iterator<String> i = manifest.getEntries().keySet().iterator();
         while (i.hasNext()) {
             String key = i.next();
-            writeEntry(out, charset, NAME_ATTRIBUTE, key);
-            Attributes attrib = manifest.entryAttributes.get(key);
+            writeEntry(out, NAME_ATTRIBUTE, key, encoder, buffer);
+            Attributes attrib = manifest.entries.get(key);
             Iterator<?> entries = attrib.keySet().iterator();
             while (entries.hasNext()) {
                 Attributes.Name name = (Attributes.Name) entries.next();
-                writeEntry(out, charset, name, attrib.getValue(name));
+                writeEntry(out, name, attrib.getValue(name), encoder, buffer);
             }
             out.write(LINE_SEPARATOR);
         }
     }
 
-    private static void writeEntry(OutputStream os, Charset charset, Attributes.Name name,
-            String value) throws IOException {
-        int offset = 0;
-        int limit = LINE_LENGTH_LIMIT;
-        byte[] out = (name.toString() + ": ").getBytes("ISO8859_1"); //$NON-NLS-1$ //$NON-NLS-2$
-        if (out.length > limit) {
-            while (out.length - offset >= limit) {
-                int len = out.length - offset;
-                if (len > limit) {
-                    len = limit;
-                }
-                if (offset > 0) {
-                    os.write(' ');
-                }
-                os.write(out, offset, len);
-                os.write(LINE_SEPARATOR);
-                offset += len;
-                limit = LINE_LENGTH_LIMIT - 1;
-            }
+    private static void writeEntry(OutputStream os, Attributes.Name name,
+            String value, CharsetEncoder encoder, ByteBuffer bBuf)
+            throws IOException {
+        byte[] out = name.getBytes();
+        if (out.length > LINE_LENGTH_LIMIT) {
+            throw new IOException(Messages.getString(
+                    "archive.33", name, Integer.valueOf(LINE_LENGTH_LIMIT))); //$NON-NLS-1$
         }
-        int size = out.length - offset;
-        final byte[] outBuf = new byte[LINE_LENGTH_LIMIT];
-        System.arraycopy(out, offset, outBuf, 0, size);
-        for (int i = 0; i < value.length(); i++) {
-            char[] oneChar = new char[1];
-            oneChar[0] = value.charAt(i);
-            byte[] buf;
-            if (oneChar[0] < 128 || charset == null) {
-                byte[] oneByte = new byte[1];
-                oneByte[0] = (byte) oneChar[0];
-                buf = oneByte;
-            } else {
-                buf = charset.encode(CharBuffer.wrap(oneChar, 0, 1)).array();
+
+        os.write(out);
+        os.write(VALUE_SEPARATOR);
+
+        encoder.reset();
+        bBuf.clear().limit(LINE_LENGTH_LIMIT - out.length - 2);
+
+        CharBuffer cBuf = CharBuffer.wrap(value);
+        CoderResult r;
+
+        while (true) {
+            r = encoder.encode(cBuf, bBuf, true);
+            if (CoderResult.UNDERFLOW == r) {
+                r = encoder.flush(bBuf);
             }
-            if (size + buf.length > limit) {
-                if (limit != LINE_LENGTH_LIMIT) {
-                    os.write(' ');
-                }
-                os.write(outBuf, 0, size);
-                os.write(LINE_SEPARATOR);
-                limit = LINE_LENGTH_LIMIT - 1;
-                size = 0;
-            }
-            if (buf.length == 1) {
-                outBuf[size] = buf[0];
-            } else {
-                System.arraycopy(buf, 0, outBuf, size, buf.length);
-            }
-            size += buf.length;
-        }
-        if (size > 0) {
-            if (limit != LINE_LENGTH_LIMIT) {
-                os.write(' ');
-            }
-            os.write(outBuf, 0, size);
+            os.write(bBuf.array(), bBuf.arrayOffset(), bBuf.position());
             os.write(LINE_SEPARATOR);
+            if (CoderResult.UNDERFLOW == r) {
+                break;
+            }
+            os.write(' ');
+            bBuf.clear().limit(LINE_LENGTH_LIMIT - 1);
         }
     }
 }
diff --git a/libcore/archive/src/main/java/java/util/jar/Pack200.java b/libcore/archive/src/main/java/java/util/jar/Pack200.java
index e0689e0..8145fa1 100644
--- a/libcore/archive/src/main/java/java/util/jar/Pack200.java
+++ b/libcore/archive/src/main/java/java/util/jar/Pack200.java
@@ -27,8 +27,6 @@
 
 /**
  * Class factory for {@link Pack200.Packer} and {@link Pack200.Unpacker}.
- * 
- * @since Android 1.0
  */
 public abstract class Pack200 {
 
@@ -39,8 +37,8 @@
     /**
      * Prevent this class from being instantiated.
      */
-    private Pack200(){
-        //do nothing
+    private Pack200() {
+        // do nothing
     }
 
     /**
@@ -50,10 +48,8 @@
      * {@code 'java.util.jar.Pack200.Packer'}. If this system property is
      * defined an instance of the specified class is returned, otherwise the
      * system's default implementation is returned.
-     * </p>
      * 
      * @return an instance of {@code Packer}
-     * @since Android 1.0
      */
     public static Pack200.Packer newPacker() {
         return (Packer) AccessController
@@ -82,10 +78,8 @@
      * property {@code 'java.util.jar.Pack200.Unpacker'}. If this system
      * property is defined an instance of the specified class is returned,
      * otherwise the system's default implementation is returned.
-     * </p>
      * 
      * @return a instance of {@code Unpacker}.
-     * @since Android 1.0
      */
     public static Pack200.Unpacker newUnpacker() {
         return (Unpacker) AccessController
@@ -107,150 +101,109 @@
     /**
      * The interface defining the API for converting a JAR file to an output
      * stream in the Pack200 format.
-     * 
-     * @since Android 1.0
      */
     public static interface Packer {
 
         /**
          * the format of a class attribute name.
-         * 
-         * @since Android 1.0
          */
         static final String CLASS_ATTRIBUTE_PFX = "pack.class.attribute."; //$NON-NLS-1$
 
         /**
          * the format of a code attribute name.
-         * 
-         * @since Android 1.0
          */
         static final String CODE_ATTRIBUTE_PFX = "pack.code.attribute."; //$NON-NLS-1$
 
         /**
          * the deflation hint to set in the output archive.
-         * 
-         * @since Android 1.0
          */
         static final String DEFLATE_HINT = "pack.deflate.hint";//$NON-NLS-1$
 
         /**
          * the indicated amount of effort to use in compressing the archive.
-         * 
-         * @since Android 1.0
          */
         static final String EFFORT = "pack.effort";//$NON-NLS-1$
 
         /**
          * a String representation for {@code error}.
-         * 
-         * @since Android 1.0
          */
         static final String ERROR = "error";//$NON-NLS-1$
 
         /**
          * a String representation of {@code false}.
-         * 
-         * @since Android 1.0
          */
         static final String FALSE = "false";//$NON-NLS-1$
 
         /**
          * the format of a field attribute name.
-         * 
-         * @since Android 1.0
          */
         static final String FIELD_ATTRIBUTE_PFX = "pack.field.attribute.";//$NON-NLS-1$
 
         /**
          * a String representation for {@code keep}.
-         * 
-         * @since Android 1.0
          */
         static final String KEEP = "keep";//$NON-NLS-1$
 
         /**
          * decide if all elements shall transmit in their original order.
-         * 
-         * @since Android 1.0
          */
         static final String KEEP_FILE_ORDER = "pack.keep.file.order";//$NON-NLS-1$
 
         /**
          * a String representation for {@code latest}.
-         * 
-         * @since Android 1.0
          */
         static final String LATEST = "latest";//$NON-NLS-1$
 
         /**
          * the format of a method attribute name.
-         * 
-         * @since Android 1.0
          */
         static final String METHOD_ATTRIBUTE_PFX = "pack.method.attribute.";//$NON-NLS-1$
 
         /**
          * if it shall attempt to determine the latest modification time if this
          * is set to {@code LATEST}.
-         * 
-         * @since Android 1.0
          */
         static final String MODIFICATION_TIME = "pack.modification.time";//$NON-NLS-1$
 
         /**
          * a String representation of {@code pass}.
-         * 
-         * @since Android 1.0
          */
         static final String PASS = "pass";//$NON-NLS-1$
 
         /**
          * the file that will not be compressed.
-         * 
-         * @since Android 1.0
          */
         static final String PASS_FILE_PFX = "pack.pass.file.";//$NON-NLS-1$
 
         /**
          * packer progress as a percentage.
-         * 
-         * @since Android 1.0
          */
         static final String PROGRESS = "pack.progress";//$NON-NLS-1$
 
         /**
          * The number of bytes of each archive segment.
-         * 
-         * @since Android 1.0
          */
         static final String SEGMENT_LIMIT = "pack.segment.limit";//$NON-NLS-1$
 
         /**
          * a String representation of {@code strip}.
-         * 
-         * @since Android 1.0
          */
         static final String STRIP = "strip";//$NON-NLS-1$
 
         /**
          * a String representation of {@code true}.
-         * 
-         * @since Android 1.0
          */
         static final String TRUE = "true";//$NON-NLS-1$
 
         /**
          * the action to take if an unknown attribute is encountered.
-         * 
-         * @since Android 1.0
          */
         static final String UNKNOWN_ATTRIBUTE = "pack.unknown.attribute";//$NON-NLS-1$
 
         /**
          * Returns a sorted map of the properties of this packer.
-         * 
+         *
          * @return the properties of the packer.
-         * @since Android 1.0
          */
         SortedMap<String, String> properties();
 
@@ -263,7 +216,6 @@
          *            stream of compressed data.
          * @throws IOException
          *             if I/O exception occurs.
-         * @since Android 1.0
          */
         void pack(JarFile in, OutputStream out) throws IOException;
 
@@ -277,7 +229,6 @@
          *            stream of compressed data.
          * @throws IOException
          *             if I/O exception occurs.
-         * @since Android 1.0
          */
         void pack(JarInputStream in, OutputStream out) throws IOException;
 
@@ -301,52 +252,39 @@
     /**
      * The interface defining the API for converting a packed stream in the
      * Pack200 format to a JAR file.
-     * 
-     * @since Android 1.0
      */
     public static interface Unpacker {
 
         /**
          * The String indicating if the unpacker should ignore all transmitted
          * values,can be replaced by either {@code true} or {@code false}.
-         * 
-         * @since Android 1.0
          */
         static final String DEFLATE_HINT = "unpack.deflate.hint";//$NON-NLS-1$
 
         /**
          * a String representation of {@code false}.
-         * 
-         * @since Android 1.0
          */
         static final String FALSE = "false";//$NON-NLS-1$
 
         /**
          * a String representation of {@code keep}.
-         * 
-         * @since Android 1.0
          */
         static final String KEEP = "keep";//$NON-NLS-1$
 
         /**
          * the progress as a {@code percentage}.
-         * 
-         * @since Android 1.0
          */
         static final String PROGRESS = "unpack.progress";//$NON-NLS-1$
 
         /**
          * a String representation of {@code true}.
-         * 
-         * @since Android 1.0
          */
         static final String TRUE = "true";//$NON-NLS-1$
 
         /**
          * Returns a sorted map of the properties of this unpacker.
-         * 
+         *
          * @return the properties of unpacker.
-         * @since Android 1.0
          */
         SortedMap<String, String> properties();
 
@@ -359,7 +297,6 @@
          *            JAR output stream of uncompressed data.
          * @throws IOException
          *             if I/O exception occurs.
-         * @since Android 1.0
          */
         void unpack(InputStream in, JarOutputStream out) throws IOException;
 
@@ -373,7 +310,6 @@
          *            JAR output stream of uncompressed data.
          * @throws IOException
          *             if I/O exception occurs.
-         * @since Android 1.0
          */
         void unpack(File in, JarOutputStream out) throws IOException;
 
diff --git a/libcore/archive/src/main/java/java/util/zip/Adler32.java b/libcore/archive/src/main/java/java/util/zip/Adler32.java
index 8eaa18e..a5f77d7 100644
--- a/libcore/archive/src/main/java/java/util/zip/Adler32.java
+++ b/libcore/archive/src/main/java/java/util/zip/Adler32.java
@@ -17,14 +17,12 @@
 
 package java.util.zip;
 
-
 /**
  * The Adler-32 class is used to compute the {@code Adler32} checksum from a set
  * of data. Compared to the CRC-32 algorithm it trades reliabilty for speed.
  * Refer to RFC 1950 for the specification.
- * 
+ *
  * @see CRC32
- * @since Android 1.0
  */
 public class Adler32 implements java.util.zip.Checksum {
 
@@ -34,7 +32,6 @@
      * Returns the {@code Adler32} checksum for all input received.
      * 
      * @return The checksum for this instance.
-     * @since Android 1.0
      */
     public long getValue() {
         return adler;
@@ -42,8 +39,6 @@
 
     /**
      * Reset this instance to its initial checksum.
-     * 
-     * @since Android 1.0
      */
     public void reset() {
         adler = 1;
@@ -55,7 +50,6 @@
      * 
      * @param i
      *            the byte to update checksum with.
-     * @since Android 1.0
      */
     public void update(int i) {
         adler = updateByteImpl(i, adler);
@@ -66,7 +60,6 @@
      * 
      * @param buf
      *            bytes to update checksum with.
-     * @since Android 1.0
      */
     public void update(byte[] buf) {
         update(buf, 0, buf.length);
@@ -85,7 +78,6 @@
      * @throws ArrayIndexOutOfBoundsException
      *             if {@code offset > buf.length} or {@code nbytes} is negative
      *             or {@code offset + nbytes > buf.length}.
-     * @since Android 1.0
      */
     public void update(byte[] buf, int off, int nbytes) {
         // avoid int overflow, check null buf
diff --git a/libcore/archive/src/main/java/java/util/zip/CRC32.java b/libcore/archive/src/main/java/java/util/zip/CRC32.java
index 36dc376..59e8f81 100644
--- a/libcore/archive/src/main/java/java/util/zip/CRC32.java
+++ b/libcore/archive/src/main/java/java/util/zip/CRC32.java
@@ -17,12 +17,9 @@
 
 package java.util.zip;
 
-
 /**
  * The CRC32 class is used to compute a CRC32 checksum from data provided as
  * input value.
- * 
- * @since Android 1.0
  */
 public class CRC32 implements java.util.zip.Checksum {
 
@@ -34,7 +31,6 @@
      * Returns the CRC32 checksum for all input received.
      * 
      * @return The checksum for this instance.
-     * @since Android 1.0
      */
     public long getValue() {
         return crc;
@@ -42,8 +38,6 @@
 
     /**
      * Resets the CRC32 checksum to it initial state.
-     * 
-     * @since Android 1.0
      */
     public void reset() {
         tbytes = crc = 0;
@@ -52,10 +46,9 @@
 
     /**
      * Updates this checksum with the byte value provided as integer.
-     * 
+     *
      * @param val
      *            represents the byte to update the checksum.
-     * @since Android 1.0
      */
     public void update(int val) {
         crc = updateByteImpl((byte) val, crc);
@@ -66,7 +59,6 @@
      * 
      * @param buf
      *            the buffer holding the data to update the checksum with.
-     * @since Android 1.0
      */
     public void update(byte[] buf) {
         update(buf, 0, buf.length);
@@ -82,7 +74,6 @@
      *            the offset in {@code buf} to obtain data from.
      * @param nbytes
      *            the number of bytes to read from {@code buf}.
-     * @since Android 1.0
      */
     public void update(byte[] buf, int off, int nbytes) {
         // avoid int overflow, check null buf
diff --git a/libcore/archive/src/main/java/java/util/zip/CheckedInputStream.java b/libcore/archive/src/main/java/java/util/zip/CheckedInputStream.java
index 659125a..6513e66 100644
--- a/libcore/archive/src/main/java/java/util/zip/CheckedInputStream.java
+++ b/libcore/archive/src/main/java/java/util/zip/CheckedInputStream.java
@@ -17,7 +17,6 @@
 
 package java.util.zip;
 
-
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -26,8 +25,6 @@
  * same time as the data, on which the checksum is computed, is read from a
  * stream. The purpose of this checksum is to establish data integrity,
  * comparing the computed checksum against a published checksum value.
- * 
- * @since Android 1.0
  */
 public class CheckedInputStream extends java.io.FilterInputStream {
 
@@ -42,7 +39,6 @@
      *            the input stream to calculate checksum from.
      * @param csum
      *            an entity implementing the checksum algorithm.
-     * @since Android 1.0
      */
     public CheckedInputStream(InputStream is, Checksum csum) {
         super(is);
@@ -57,7 +53,6 @@
      *         otherwise.
      * @throws IOException
      *             if an {@code IOException} occurs.
-     * @since Android 1.0
      */
     @Override
     public int read() throws IOException {
@@ -84,7 +79,6 @@
      *         end of the filtered stream while reading the data.
      * @throws IOException
      *             if this stream is closed or some I/O error occurs.
-     * @since Android 1.0
      */
     @Override
     public int read(byte[] buf, int off, int nbytes) throws IOException {
@@ -99,7 +93,6 @@
      * Returns the checksum calculated on the stream read so far.
      * 
      * @return the updated checksum.
-     * @since Android 1.0
      */
     public Checksum getChecksum() {
         return check;
@@ -114,7 +107,6 @@
      * @throws IOException
      *             if this stream is closed or another I/O error occurs.
      * @return the number of bytes skipped.
-     * @since Android 1.0
      */
     @Override
     public long skip(long nbytes) throws IOException {
diff --git a/libcore/archive/src/main/java/java/util/zip/CheckedOutputStream.java b/libcore/archive/src/main/java/java/util/zip/CheckedOutputStream.java
index 9699492..08fe799 100644
--- a/libcore/archive/src/main/java/java/util/zip/CheckedOutputStream.java
+++ b/libcore/archive/src/main/java/java/util/zip/CheckedOutputStream.java
@@ -17,7 +17,6 @@
 
 package java.util.zip;
 
-
 import java.io.IOException;
 import java.io.OutputStream;
 
@@ -26,8 +25,6 @@
  * of all data written to a stream. The purpose of this checksum is to establish
  * data integrity, by publishing the checksum to other parties wanting to read
  * the non corrupted data.
- * 
- * @since Android 1.0
  */
 public class CheckedOutputStream extends java.io.FilterOutputStream {
 
@@ -42,7 +39,6 @@
      *            the output stream to calculate checksum for.
      * @param cs
      *            an entity implementing the checksum algorithm.
-     * @since Android 1.0
      */
     public CheckedOutputStream(OutputStream os, Checksum cs) {
         super(os);
@@ -53,7 +49,6 @@
      * Returns the checksum calculated on the stream read so far.
      * 
      * @return the updated checksum.
-     * @since Android 1.0
      */
     public Checksum getChecksum() {
         return check;
@@ -67,7 +62,6 @@
      *            the data value to written to the output stream.
      * @throws IOException
      *             if an IO error has occurred.
-     * @since Android 1.0
      */
     @Override
     public void write(int val) throws IOException {
@@ -88,7 +82,6 @@
      *            number of bytes to write to the output stream.
      * @throws IOException
      *             if an IO error has occurred.
-     * @since Android 1.0
      */
     @Override
     public void write(byte[] buf, int off, int nbytes) throws IOException {
diff --git a/libcore/archive/src/main/java/java/util/zip/Checksum.java b/libcore/archive/src/main/java/java/util/zip/Checksum.java
index 0405c08..901ff7a 100644
--- a/libcore/archive/src/main/java/java/util/zip/Checksum.java
+++ b/libcore/archive/src/main/java/java/util/zip/Checksum.java
@@ -20,8 +20,6 @@
 /**
  * Holds information about a checksum which was computed with the methods
  * implementing a checksum algorithm.
- * 
- * @since Android 1.0
  */
 public interface Checksum {
 
@@ -29,37 +27,32 @@
      * Returns the current calculated checksum value.
      * 
      * @return the checksum.
-     * @since Android 1.0
      */
     public long getValue();
 
     /**
      * Resets the checksum value applied before beginning calculations on a new
      * stream of data.
-     * 
-     * @since Android 1.0
      */
     public void reset();
 
     /**
-     * Updates the checksum value with the given byte.
-     * 
-     * @param val
-     *            the byte to update the checksum with.
-     * @since Android 1.0
-     */
-    public void update(int val);
-
-    /**
      * Updates the checksum with the given bytes.
-     * 
+     *
      * @param buf
      *            the byte array from which to read the bytes.
      * @param off
      *            the initial position in {@code buf} to read the bytes from.
      * @param nbytes
      *            the number of bytes to read from {@code buf}.
-     * @since Android 1.0
      */
     public void update(byte[] buf, int off, int nbytes);
+
+    /**
+     * Updates the checksum value with the given byte.
+     * 
+     * @param val
+     *            the byte to update the checksum with.
+     */
+    public void update(int val);
 }
diff --git a/libcore/archive/src/main/java/java/util/zip/DataFormatException.java b/libcore/archive/src/main/java/java/util/zip/DataFormatException.java
index 9ffe2ab..1e9c5a2 100644
--- a/libcore/archive/src/main/java/java/util/zip/DataFormatException.java
+++ b/libcore/archive/src/main/java/java/util/zip/DataFormatException.java
@@ -20,8 +20,6 @@
 /**
  * {@code DataFormatException} is used to indicate an error in the format of a
  * particular data stream which is to be uncompressed.
- * 
- * @since Android 1.0
  */
 public class DataFormatException extends Exception {
 
@@ -29,8 +27,6 @@
 
     /**
      * Constructs a new {@code DataFormatException} instance.
-     * 
-     * @since Android 1.0
      */
     public DataFormatException() {
         super();
@@ -39,10 +35,9 @@
     /**
      * Constructs a new {@code DataFormatException} instance with the specified
      * message.
-     * 
+     *
      * @param detailMessage
      *            the detail message for the exception.
-     * @since Android 1.0
      */
     public DataFormatException(String detailMessage) {
         super(detailMessage);
diff --git a/libcore/archive/src/main/java/java/util/zip/Deflater.java b/libcore/archive/src/main/java/java/util/zip/Deflater.java
index f91f1ca..38771a8 100644
--- a/libcore/archive/src/main/java/java/util/zip/Deflater.java
+++ b/libcore/archive/src/main/java/java/util/zip/Deflater.java
@@ -17,6 +17,10 @@
 
 package java.util.zip;
 
+// BEGIN android-changed
+// import org.apache.harmony.luni.platform.OSResourcesMonitor;
+// END android-changed
+
 /**
  * This class compresses data using the <i>DEFLATE</i> algorithm (see <a
  * href="http://www.gzip.org/algorithm.txt">specification</a>).
@@ -24,83 +28,65 @@
  * Basically this class is part of the API to the stream based ZLIB compression
  * library and is used as such by {@code DeflaterOutputStream} and its
  * descendants.
- * </p>
  * <p>
  * The typical usage of a {@code Deflater} instance outside this package
  * consists of a specific call to one of its constructors before being passed to
  * an instance of {@code DeflaterOutputStream}.
- * </p>
- * 
+ *
  * @see DeflaterOutputStream
  * @see Inflater
- * @since Android 1.0
  */
 public class Deflater {
 
     /**
      * Upper bound for the compression level range.
-     * 
-     * @since Android 1.0
      */
     public static final int BEST_COMPRESSION = 9;
 
     /**
      * Lower bound for compression level range.
-     * 
-     * @since Android 1.0
      */
     public static final int BEST_SPEED = 1;
-    
+
     /**
      * Usage of the default compression level.
-     * 
-     * @since Android 1.0
      */
     public static final int DEFAULT_COMPRESSION = -1;
-    
+
     /**
      * Default value for compression strategy.
-     * 
-     * @since Android 1.0
      */
     public static final int DEFAULT_STRATEGY = 0;
-    
+
     /**
      * Default value for compression method.
-     * 
-     * @since Android 1.0
      */
     public static final int DEFLATED = 8;
-    
+
     /**
      * Possible value for compression strategy.
-     * 
-     * @since Android 1.0
      */
     public static final int FILTERED = 1;
-    
+
     /**
      * Possible value for compression strategy.
-     * 
-     * @since Android 1.0
      */
     public static final int HUFFMAN_ONLY = 2;
-    
+
     /**
      * Possible value for compression level.
-     * 
-     * @since Android 1.0
      */
     public static final int NO_COMPRESSION = 0;
 
     private static final int Z_NO_FLUSH = 0;
 
     private static final int Z_FINISH = 4;
-    
+
     // Fill in the JNI id caches
     private static native void oneTimeInitialization();
-    
-    // A stub buffer used when deflate() called while inputBuffer has not been set.
+
+    // A stub buffer used when deflate() called while inputBuffer has not been
+    // set.
     private static final byte[] STUB_INPUT_BUFFER = new byte[0];
 
     static {
@@ -120,21 +106,19 @@
     private byte[] inputBuffer;
 
     private int inRead;
-    
+
     private int inLength;
-    
+
     /**
      * Constructs a new {@code Deflater} instance with default compression
      * level. The strategy can be specified with {@link #setStrategy}, only. A
      * header is added to the output by default; use constructor {@code
      * Deflater(level, boolean)} if you need to omit the header.
-     * 
-     * @since Android 1.0
      */
     public Deflater() {
         this(DEFAULT_COMPRESSION, false);
     }
-    
+
     /**
      * Constructs a new {@code Deflater} instance with a specific compression
      * level. The strategy can be specified with {@code setStrategy}, only. A
@@ -143,7 +127,6 @@
      * 
      * @param level
      *            the compression level in the range between 0 and 9.
-     * @since Android 1.0
      */
     public Deflater(int level) {
         this(level, false);
@@ -159,7 +142,6 @@
      *            the compression level in the range between 0 and 9.
      * @param noHeader
      *            {@code true} indicates that no ZLIB header should be written.
-     * @since Android 1.0
      */
     public Deflater(int level, boolean noHeader) {
         super();
@@ -167,7 +149,8 @@
             throw new IllegalArgumentException();
         }
         compressLevel = level;
-        streamHandle = createStream(compressLevel, strategy, noHeader);
+        streamHandle = createStreamWithMemoryEnsurance(compressLevel, strategy,
+                noHeader);
     }
 
     /**
@@ -178,7 +161,6 @@
      *            buffer to write compressed data to.
      * @return number of bytes of compressed data written to {@code buf}.
      * @see #deflate(byte[], int, int)
-     * @since Android 1.0
      */
     public int deflate(byte[] buf) {
         return deflate(buf, 0, buf.length);
@@ -195,7 +177,6 @@
      * @param nbytes
      *            maximum number of bytes of compressed data to be written.
      * @return the number of bytes of compressed data written to {@code buf}.
-     * @since Android 1.0
      */
     public synchronized int deflate(byte[] buf, int off, int nbytes) {
         if (streamHandle == -1) {
@@ -224,8 +205,6 @@
      * finalize()}, it can be called explicitly in order to free native
      * resources before the next GC cycle. After {@code end()} was called other
      * methods will typically throw an {@code IllegalStateException}.
-     * 
-     * @since Android 1.0
      */
     public synchronized void end() {
         if (streamHandle != -1) {
@@ -245,7 +224,6 @@
      * to it.
      * 
      * @see #finished
-     * @since Android 1.0
      */
     public synchronized void finish() {
         flushParm = Z_FINISH;
@@ -256,7 +234,6 @@
      * compressed.
      * 
      * @return true if all data has been compressed, false otherwise.
-     * @since Android 1.0
      */
     public synchronized boolean finished() {
         return finished;
@@ -271,7 +248,6 @@
      *         used.
      * @see #setDictionary(byte[])
      * @see #setDictionary(byte[], int, int)
-     * @since Android 1.0
      */
     public synchronized int getAdler() {
         if (streamHandle == -1) {
@@ -287,14 +263,13 @@
      * Returns the total number of bytes of input consumed by the {@code Deflater}.
      * 
      * @return number of bytes of input read.
-     * @since Android 1.0
      */
     public synchronized int getTotalIn() {
         if (streamHandle == -1) {
             throw new IllegalStateException();
         }
 
-        return (int)getTotalInImpl(streamHandle);
+        return (int) getTotalInImpl(streamHandle);
     }
 
     private synchronized native long getTotalInImpl(long handle);
@@ -303,14 +278,13 @@
      * Returns the total number of compressed bytes output by this {@code Deflater}.
      * 
      * @return number of compressed bytes output.
-     * @since Android 1.0
      */
     public synchronized int getTotalOut() {
         if (streamHandle == -1) {
             throw new IllegalStateException();
         }
 
-        return (int)getTotalOutImpl(streamHandle);
+        return (int) getTotalOutImpl(streamHandle);
     }
 
     private synchronized native long getTotalOutImpl(long handle);
@@ -327,7 +301,6 @@
      * @see #finished()
      * @see #setInput(byte[])
      * @see #setInput(byte[], int, int)
-     * @since Android 1.0
      */
     public synchronized boolean needsInput() {
         if (inputBuffer == null) {
@@ -343,7 +316,6 @@
      * {@code true} if the {@code Deflater} is to be reused.
      * 
      * @see #finished
-     * @since Android 1.0
      */
     public synchronized void reset() {
         if (streamHandle == -1) {
@@ -367,7 +339,6 @@
      * @param buf
      *            the buffer containing the dictionary data bytes.
      * @see Deflater#Deflater(int, boolean)
-     * @since Android 1.0
      */
     public void setDictionary(byte[] buf) {
         setDictionary(buf, 0, buf.length);
@@ -378,7 +349,7 @@
      * setDictionary() can only be called if this {@code Deflater} supports the writing
      * of ZLIB headers. This is the default behaviour but can be overridden
      * using {@code Deflater(int, boolean)}.
-     * 
+     *
      * @param buf
      *            the buffer containing the dictionary data bytes.
      * @param off
@@ -386,7 +357,6 @@
      * @param nbytes
      *            the length of the data.
      * @see Deflater#Deflater(int, boolean)
-     * @since Android 1.0
      */
     public synchronized void setDictionary(byte[] buf, int off, int nbytes) {
         if (streamHandle == -1) {
@@ -410,7 +380,6 @@
      * 
      * @param buf
      *            the buffer.
-     * @since Android 1.0
      */
     public void setInput(byte[] buf) {
         setInput(buf, 0, buf.length);
@@ -427,7 +396,6 @@
      *            the offset of the data.
      * @param nbytes
      *            the length of the data.
-     * @since Android 1.0
      */
     public synchronized void setInput(byte[] buf, int off, int nbytes) {
         if (streamHandle == -1) {
@@ -463,7 +431,6 @@
      *            compression level to use
      * @exception IllegalArgumentException
      *                If the compression level is invalid.
-     * @since Android 1.0
      */
     public synchronized void setLevel(int level) {
         if (level < DEFAULT_COMPRESSION || level > BEST_COMPRESSION) {
@@ -485,7 +452,6 @@
      * @exception IllegalArgumentException
      *                If the strategy specified is not one of FILTERED,
      *                HUFFMAN_ONLY or DEFAULT_STRATEGY.
-     * @since Android 1.0
      */
     public synchronized void setStrategy(int strategy) {
         if (strategy < DEFAULT_STRATEGY || strategy > HUFFMAN_ONLY) {
@@ -496,14 +462,14 @@
         }
         this.strategy = strategy;
     }
-    
+
     /**
      * Returns a long int of total number of bytes read by the {@code Deflater}. This
      * method performs the same as {@code getTotalIn} except it returns a long value
      * instead of an integer
      * 
+     * @see #getTotalIn()
      * @return total number of bytes read by {@code Deflater}.
-     * @since Android 1.0
      */
     public synchronized long getBytesRead() {
         // Throw NPE here
@@ -518,8 +484,8 @@
      * method performs the same as {@code getTotalOut} except it returns a long
      * value instead of an integer
      * 
+     * @see #getTotalOut()
      * @return bytes exactly write by {@code Deflater}
-     * @since Android 1.0
      */
     public synchronized long getBytesWritten() {
         // Throw NPE here
@@ -529,5 +495,13 @@
         return getTotalOutImpl(streamHandle);
     }
 
+    private long createStreamWithMemoryEnsurance(int level, int strategy1,
+            boolean noHeader1) {
+        // BEGIN android-changed
+        // OSResourcesMonitor.ensurePhysicalMemoryCapacity();
+        // END android-changed
+        return createStream(level, strategy1, noHeader1);
+    }
+
     private native long createStream(int level, int strategy1, boolean noHeader1);
 }
diff --git a/libcore/archive/src/main/java/java/util/zip/DeflaterOutputStream.java b/libcore/archive/src/main/java/java/util/zip/DeflaterOutputStream.java
index 773e4c4..03769fb 100644
--- a/libcore/archive/src/main/java/java/util/zip/DeflaterOutputStream.java
+++ b/libcore/archive/src/main/java/java/util/zip/DeflaterOutputStream.java
@@ -17,7 +17,6 @@
 
 package java.util.zip;
 
-
 import java.io.FilterOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
@@ -28,24 +27,19 @@
  * This class provides an implementation of {@code FilterOutputStream} that
  * compresses data using the <i>DEFLATE</i> algorithm. Basically it wraps the
  * {@code Deflater} class and takes care of the buffering.
- * 
+ *
  * @see Deflater
- * @since Android 1.0
  */
 public class DeflaterOutputStream extends FilterOutputStream {
     static final int BUF_SIZE = 512;
 
     /**
      * The buffer for the data to be written to.
-     * 
-     * @since Android 1.0
      */
     protected byte[] buf;
 
     /**
      * The deflater used.
-     * 
-     * @since Android 1.0
      */
     protected Deflater def;
 
@@ -61,7 +55,6 @@
      * @param def
      *            is the specific {@code Deflater} that is used to compress
      *            data.
-     * @since Android 1.0
      */
     public DeflaterOutputStream(OutputStream os, Deflater def) {
         this(os, def, BUF_SIZE);
@@ -76,7 +69,6 @@
      * 
      * @param os
      *            is the OutputStream where to write the compressed data to.
-     * @since Android 1.0
      */
     public DeflaterOutputStream(OutputStream os) {
         this(os, new Deflater());
@@ -94,7 +86,6 @@
      *            data.
      * @param bsize
      *            is the size to be used for the internal buffer.
-     * @since Android 1.0
      */
     public DeflaterOutputStream(OutputStream os, Deflater def, int bsize) {
         super(os);
@@ -114,7 +105,6 @@
      * 
      * @throws IOException
      *             If an error occurs during deflation.
-     * @since Android 1.0
      */
     protected void deflate() throws IOException {
         int x = 0;
@@ -132,7 +122,6 @@
      * @throws IOException
      *             If an error occurs while closing the data compression
      *             process.
-     * @since Android 1.0
      */
     @Override
     public void close() throws IOException {
@@ -149,7 +138,6 @@
      * 
      * @throws IOException
      *             If an error occurs.
-     * @since Android 1.0
      */
     public void finish() throws IOException {
         if (done) {
@@ -186,7 +174,6 @@
      *            the number of bytes of data to read from the buffer.
      * @throws IOException
      *             If an error occurs during writing.
-     * @since Android 1.0
      */
     @Override
     public void write(byte[] buffer, int off, int nbytes) throws IOException {
diff --git a/libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java b/libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java
index fc70d62..bb84f5b 100644
--- a/libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java
+++ b/libcore/archive/src/main/java/java/util/zip/GZIPInputStream.java
@@ -17,7 +17,6 @@
 
 package java.util.zip;
 
-
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
@@ -28,49 +27,40 @@
  * 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.
- * 
- * @since Android 1.0
  */
-public class GZIPInputStream extends java.util.zip.InflaterInputStream {
+public class GZIPInputStream extends InflaterInputStream {
+
+    private static final int FCOMMENT = 16;
+
+    private static final int FEXTRA = 4;
+
+    private static final int FHCRC = 2;
+
+    private static final int FNAME = 8;
+
+    /**
+     * The magic header for the GZIP format.
+     */
+    public final static int GZIP_MAGIC = 0x8b1f;
 
     /**
      * The checksum algorithm used when handling uncompressed data.
-     * 
-     * @since Android 1.0
      */
     protected CRC32 crc = new CRC32();
 
     /**
      * Indicates the end of the input stream.
-     * 
-     * @since Android 1.0
      */
     protected boolean eos = false;
 
     /**
-     * The magic header for the GZIP format.
-     * 
-     * @since Android 1.0
-     */
-    public final static int GZIP_MAGIC = 0x8b1f;
-
-    private static final int FHCRC = 2;
-
-    private static final int FEXTRA = 4;
-
-    private static final int FNAME = 8;
-
-    private static final int FCOMMENT = 16;
-
-    /**
      * Construct a {@code GZIPInputStream} to read from GZIP data from the
      * underlying stream.
-     * 
+     *
      * @param is
      *            the {@code InputStream} to read data from.
      * @throws IOException
      *             if an {@code IOException} occurs.
-     * @since Android 1.0
      */
     public GZIPInputStream(InputStream is) throws IOException {
         this(is, BUF_SIZE);
@@ -86,7 +76,6 @@
      *            the internal read buffer size.
      * @throws IOException
      *             if an {@code IOException} occurs.
-     * @since Android 1.0
      */
     public GZIPInputStream(InputStream is, int size) throws IOException {
         super(is, new Inflater(true), size);
@@ -134,6 +123,15 @@
         }
     }
 
+    /**
+     * Closes this stream and any underlying streams.
+     */
+    @Override
+    public void close() throws IOException {
+        eos = true;
+        super.close();
+    }
+
     private long getLong(byte[] buffer, int off) {
         long l = 0;
         l |= (buffer[off] & 0xFF);
@@ -147,12 +145,23 @@
         return (buffer[off] & 0xFF) | ((buffer[off + 1] & 0xFF) << 8);
     }
 
+    /**
+     * Reads and decompresses GZIP data from the underlying stream into the
+     * given buffer.
+     *
+     * @param buffer
+     *            Buffer to receive data
+     * @param off
+     *            Offset in buffer to store data
+     * @param nbytes
+     *            Number of bytes to read
+     */
     @Override
     public int read(byte[] buffer, int off, int nbytes) throws IOException {
         if (closed) {
             throw new IOException(Messages.getString("archive.1E")); //$NON-NLS-1$
         }
-        if(eof){
+        if (eof) {
             return -1;
         }
         // avoid int overflow, check null buffer
@@ -164,17 +173,15 @@
             } else if (!eos) {
                 eos = true;
                 // Get non-compressed bytes read by fill
-                // BEGIN android-changed
-                // copied from newer version of harmony
                 int size = inf.getRemaining();
                 final int trailerSize = 8; // crc (4 bytes) + total out (4
-                                            // bytes)
+                // bytes)
                 byte[] b = new byte[trailerSize];
                 int copySize = (size > trailerSize) ? trailerSize : size;
 
                 System.arraycopy(buf, len - size, b, 0, copySize);
-                readFully(b, copySize, trailerSize - copySize);                
-                // END android-changed
+                readFully(b, copySize, trailerSize - copySize);
+
                 if (getLong(b, 0) != crc.getValue()) {
                     throw new IOException(Messages.getString("archive.20")); //$NON-NLS-1$
                 }
@@ -186,12 +193,6 @@
         }
         throw new ArrayIndexOutOfBoundsException();
     }
-    
-    @Override
-    public void close() throws IOException {
-        eos = true;
-        super.close();
-    }
 
     private void readFully(byte[] buffer, int offset, int length)
             throws IOException {
diff --git a/libcore/archive/src/main/java/java/util/zip/GZIPOutputStream.java b/libcore/archive/src/main/java/java/util/zip/GZIPOutputStream.java
index fa41e19..f146da1 100644
--- a/libcore/archive/src/main/java/java/util/zip/GZIPOutputStream.java
+++ b/libcore/archive/src/main/java/java/util/zip/GZIPOutputStream.java
@@ -17,22 +17,17 @@
 
 package java.util.zip;
 
-
 import java.io.IOException;
 import java.io.OutputStream;
 
 /**
  * The {@code GZIPOutputStream} class is used to write data to a stream in the
  * GZIP storage format.
- * 
- * @since Android 1.0
  */
 public class GZIPOutputStream extends DeflaterOutputStream {
 
     /**
      * The checksum algorithm used when treating uncompressed data.
-     * 
-     * @since Android 1.0
      */
     protected CRC32 crc = new CRC32();
 
@@ -44,7 +39,6 @@
      *            the {@code OutputStream} to write data to.
      * @throws IOException
      *             if an {@code IOException} occurs.
-     * @since Android 1.0
      */
     public GZIPOutputStream(OutputStream os) throws IOException {
         this(os, BUF_SIZE);
@@ -61,7 +55,6 @@
      *            the internal buffer size.
      * @throws IOException
      *             if an {@code IOException} occurs.
-     * @since Android 1.0
      */
     public GZIPOutputStream(OutputStream os, int size) throws IOException {
         super(os, new Deflater(Deflater.DEFAULT_COMPRESSION, true), size);
@@ -76,10 +69,9 @@
     /**
      * Indicates to the stream that all data has been written out, and any GZIP
      * terminal data can now be written.
-     * 
+     *
      * @throws IOException
      *             if an {@code IOException} occurs.
-     * @since Android 1.0
      */
     @Override
     public void finish() throws IOException {
@@ -88,24 +80,29 @@
         writeLong(crc.tbytes);
     }
 
+    /**
+     * Write up to nbytes of data from the given buffer, starting at offset off,
+     * to the underlying stream in GZIP format.
+     */
     @Override
     public void write(byte[] buffer, int off, int nbytes) throws IOException {
         super.write(buffer, off, nbytes);
         crc.update(buffer, off, nbytes);
     }
 
-    private int writeShort(int i) throws IOException {
-        out.write(i & 0xFF);
-        out.write((i >> 8) & 0xFF);
+    private long writeLong(long i) throws IOException {
+        // Write out the long value as an unsigned int
+        int unsigned = (int) i;
+        out.write(unsigned & 0xFF);
+        out.write((unsigned >> 8) & 0xFF);
+        out.write((unsigned >> 16) & 0xFF);
+        out.write((unsigned >> 24) & 0xFF);
         return i;
     }
 
-    private long writeLong(long i) throws IOException {
-        // Write out the long value as an unsigned int
-        out.write((int) (i & 0xFF));
-        out.write((int) (i >> 8) & 0xFF);
-        out.write((int) (i >> 16) & 0xFF);
-        out.write((int) (i >> 24) & 0xFF);
+    private int writeShort(int i) throws IOException {
+        out.write(i & 0xFF);
+        out.write((i >> 8) & 0xFF);
         return i;
     }
 }
diff --git a/libcore/archive/src/main/java/java/util/zip/Inflater.java b/libcore/archive/src/main/java/java/util/zip/Inflater.java
index 9b93e54..cb1ce68 100644
--- a/libcore/archive/src/main/java/java/util/zip/Inflater.java
+++ b/libcore/archive/src/main/java/java/util/zip/Inflater.java
@@ -30,45 +30,65 @@
  * Basically this class is part of the API to the stream based ZLIB compression
  * library and is used as such by {@code InflaterInputStream} and its
  * descendants.
- * </p>
  * <p>
  * The typical usage of a {@code Inflater} outside this package consists of a
  * specific call to one of its constructors before being passed to an instance
  * of {@code InflaterInputStream}.
- * </p>
- * 
+ *
  * @see InflaterInputStream
  * @see Deflater
- * @since Android 1.0
  */
 public class Inflater {
 
-    private boolean finished; // Set by the inflateImpl native
-
-    private boolean needsDictionary; // Set by the inflateImpl native
-
-    private long streamHandle = -1;
-
-    int inRead;
-    
-    int inLength;
-
-    // Fill in the JNI id caches
-    private static native void oneTimeInitialization();
+    private static final byte MAGIC_NUMBER = 120;
 
     static {
         oneTimeInitialization();
     }
-    
-    private static final byte MAGIC_NUMBER = 120;
+
+    // Fill in the JNI id caches
+    private static native void oneTimeInitialization();
+
+    private boolean finished; // Set by the inflateImpl native
+
     private boolean gotFirstByte = false;
+
+    int inLength;
+
+    int inRead;
+
+    private boolean needsDictionary; // Set by the inflateImpl native
+
     private boolean pass_magic_number_check = true;
-    
+
+    private long streamHandle = -1;
+
+    /**
+     * This constructor creates an inflater that expects a header from the input
+     * stream. Use {@code Inflater(boolean)} if the input comes without a ZLIB
+     * header.
+     */
+    public Inflater() {
+        this(false);
+    }
+
+    /**
+     * This constructor allows to create an inflater that expects no header from
+     * the input stream.
+     *
+     * @param noHeader
+     *            {@code true} indicates that no ZLIB header comes with the
+     *            input.
+     */
+    public Inflater(boolean noHeader) {
+        streamHandle = createStream(noHeader);
+    }
+
+    private native long createStream(boolean noHeader1);
+
     /**
      * Release any resources associated with this {@code Inflater}. Any unused
      * input/output is discarded. This is also called by the finalize method.
-     * 
-     * @since Android 1.0
      */
     public synchronized void end() {
         if (streamHandle != -1) {
@@ -91,10 +111,9 @@
      * stream. If deflated bytes remain and {@code needsInput()} returns {@code
      * true} this method will return {@code false}. This method should be
      * called after all deflated input is supplied to the {@code Inflater}.
-     * 
+     *
      * @return {@code true} if all input has been inflated, {@code false}
      *         otherwise.
-     * @since Android 1.0
      */
     public synchronized boolean finished() {
         return finished;
@@ -103,10 +122,9 @@
     /**
      * Returns the <i>Adler32</i> checksum of either all bytes inflated, or the
      * checksum of the preset dictionary if one has been supplied.
-     * 
+     *
      * @return The <i>Adler32</i> checksum associated with this
      *         {@code Inflater}.
-     * @since Android 1.0 .
      */
     public synchronized int getAdler() {
         if (streamHandle == -1) {
@@ -118,11 +136,40 @@
     private native synchronized int getAdlerImpl(long handle);
 
     /**
+     * Returns the total number of bytes read by the {@code Inflater}. This
+     * method performs the same as {@code getTotalIn()} except that it returns a
+     * {@code long} value instead of an integer.
+     *
+     * @return the total number of bytes read.
+     */
+    public synchronized long getBytesRead() {
+        // Throw NPE here
+        if (streamHandle == -1) {
+            throw new NullPointerException();
+        }
+        return getTotalInImpl(streamHandle);
+    }
+
+    /**
+     * Returns a the total number of bytes read by the {@code Inflater}. This
+     * method performs the same as {@code getTotalOut} except it returns a
+     * {@code long} value instead of an integer.
+     *
+     * @return the total bytes written to the output buffer.
+     */
+    public synchronized long getBytesWritten() {
+        // Throw NPE here
+        if (streamHandle == -1) {
+            throw new NullPointerException();
+        }
+        return getTotalOutImpl(streamHandle);
+    }
+
+    /**
      * Returns the number of bytes of current input remaining to be read by the
      * inflater.
-     * 
+     *
      * @return the number of bytes of unread input.
-     * @since Android 1.0
      */
     public synchronized int getRemaining() {
         return inLength - inRead;
@@ -131,9 +178,8 @@
     /**
      * Returns total number of bytes of input read by the {@code Inflater}. The
      * result value is limited by {@code Integer.MAX_VALUE}.
-     * 
+     *
      * @return the total number of bytes read.
-     * @since Android 1.0
      */
     public synchronized int getTotalIn() {
         if (streamHandle == -1) {
@@ -149,9 +195,8 @@
     /**
      * Returns total number of bytes written to the output buffer by the {@code
      * Inflater}. The result value is limited by {@code Integer.MAX_VALUE}.
-     * 
+     *
      * @return the total bytes of output data written.
-     * @since Android 1.0
      */
     public synchronized int getTotalOut() {
         if (streamHandle == -1) {
@@ -166,14 +211,13 @@
 
     /**
      * Inflates bytes from current input and stores them in {@code buf}.
-     * 
+     *
      * @param buf
      *            the buffer where decompressed data bytes are written.
      * @return the number of bytes inflated.
      * @throws DataFormatException
      *             if the underlying stream is corrupted or was not compressed
      *             using a {@code Deflater}.
-     * @since Android 1.0
      */
     public int inflate(byte[] buf) throws DataFormatException {
         return inflate(buf, 0, buf.length);
@@ -182,7 +226,7 @@
     /**
      * Inflates up to n bytes from the current input and stores them in {@code
      * buf} starting at {@code off}.
-     * 
+     *
      * @param buf
      *            the buffer to write inflated bytes to.
      * @param off
@@ -205,7 +249,7 @@
             if (streamHandle == -1) {
                 throw new IllegalStateException();
             }
-            
+
             if (!pass_magic_number_check) {
                 throw new DataFormatException();
             }
@@ -213,7 +257,7 @@
             if (needsInput()) {
                 return 0;
             }
-            
+
             boolean neededDict = needsDictionary;
             needsDictionary = false;
             int result = inflateImpl(buf, off, nbytes, streamHandle);
@@ -229,41 +273,15 @@
             int nbytes, long handle);
 
     /**
-     * This constructor creates an inflater that expects a header from the input
-     * stream. Use {@code Inflater(boolean)} if the input comes without a ZLIB
-     * header.
-     * 
-     * @since Android 1.0
-     * @since Android 1.0
-     */
-    public Inflater() {
-        this(false);
-    }
-
-    /**
-     * This constructor allows to create an inflater that expects no header from
-     * the input stream.
-     * 
-     * @param noHeader
-     *            {@code true} indicates that no ZLIB header comes with the
-     *            input.
-     * @since Android 1.0
-     */
-    public Inflater(boolean noHeader) {
-        streamHandle = createStream(noHeader);
-    }
-
-    /**
      * Indicates whether the input bytes were compressed with a preset
      * dictionary. This method should be called prior to {@code inflate()} to
      * determine whether a dictionary is required. If so {@code setDictionary()}
      * should be called with the appropriate dictionary prior to calling {@code
      * inflate()}.
-     * 
+     *
      * @return {@code true} if a preset dictionary is required for inflation.
      * @see #setDictionary(byte[])
      * @see #setDictionary(byte[], int, int)
-     * @since Android 1.0
      */
     public synchronized boolean needsDictionary() {
         return needsDictionary;
@@ -271,11 +289,10 @@
 
     /**
      * Indicates that input has to be passed to the inflater.
-     * 
+     *
      * @return {@code true} if {@code setInput} has to be called before
      *         inflation can proceed.
      * @see #setInput(byte[])
-     * @since Android 1.0
      */
     public synchronized boolean needsInput() {
         return inRead == inLength;
@@ -284,8 +301,6 @@
     /**
      * Resets the {@code Inflater}. Should be called prior to inflating a new
      * set of data.
-     * 
-     * @since Android 1.0
      */
     public synchronized void reset() {
         if (streamHandle == -1) {
@@ -303,11 +318,10 @@
      * Sets the preset dictionary to be used for inflation to {@code buf}.
      * {@code needsDictionary()} can be called to determine whether the current
      * input was deflated using a preset dictionary.
-     * 
+     *
      * @param buf
      *            The buffer containing the dictionary bytes.
      * @see #needsDictionary
-     * @since Android 1.0
      */
     public synchronized void setDictionary(byte[] buf) {
         setDictionary(buf, 0, buf.length);
@@ -316,7 +330,11 @@
     /**
      * Like {@code setDictionary(byte[])}, allowing to define a specific region
      * inside {@code buf} to be used as a dictionary.
-     * 
+     * <p>
+     * The dictionary should be set if the {@link #inflate(byte[])} returned
+     * zero bytes inflated and {@link #needsDictionary()} returns
+     * <code>true</code>.
+     *
      * @param buf
      *            the buffer containing the dictionary data bytes.
      * @param off
@@ -324,7 +342,6 @@
      * @param nbytes
      *            the length of the data.
      * @see #needsDictionary
-     * @since Android 1.0
      */
     public synchronized void setDictionary(byte[] buf, int off, int nbytes) {
         if (streamHandle == -1) {
@@ -345,11 +362,10 @@
     /**
      * Sets the current input to to be decrompressed. This method should only be
      * called if {@code needsInput()} returns {@code true}.
-     * 
+     *
      * @param buf
      *            the input buffer.
      * @see #needsInput
-     * @since Android 1.0
      */
     public synchronized void setInput(byte[] buf) {
         setInput(buf, 0, buf.length);
@@ -360,7 +376,7 @@
      * {@code off} and ending at {@code nbytes - 1} where data is written after
      * decompression. This method should only be called if {@code needsInput()}
      * returns {@code true}.
-     * 
+     *
      * @param buf
      *            the input buffer.
      * @param off
@@ -368,7 +384,6 @@
      * @param nbytes
      *            the number of bytes to read.
      * @see #needsInput
-     * @since Android 1.0
      */
     public synchronized void setInput(byte[] buf, int off, int nbytes) {
         if (streamHandle == -1) {
@@ -394,10 +409,9 @@
         //        And at a first glance it doesn't look like the first byte has
         //        to be 120.
         // END android-note
-        if(!gotFirstByte && nbytes>0)
-        {
-           pass_magic_number_check = (buf[off] == MAGIC_NUMBER || nbytes > 1);           
-           gotFirstByte = true;
+        if (!gotFirstByte && nbytes > 0) {
+            pass_magic_number_check = (buf[off] == MAGIC_NUMBER || nbytes > 1);
+            gotFirstByte = true;
         }
     }
 
@@ -407,14 +421,13 @@
      * off} and ending at {@code nbytes - 1}. This method should only be called
      * if {@code needsInput()} returns {@code true}.
      * 
-     * @param file
+     * @param fd
      *            the input file.
      * @param off
      *            the offset to read from in buffer.
      * @param nbytes
      *            the number of bytes to read.
      * @see #needsInput
-     * @since Android 1.0
      */
     synchronized int setFileInput(FileDescriptor fd, long off, int nbytes) {
         if (streamHandle == -1) {
@@ -426,38 +439,6 @@
     }
     // END android-added
 
-    /**
-     * Returns the total number of bytes read by the {@code Inflater}. This
-     * method performs the same as {@code getTotalIn()} except that it returns a
-     * {@code long} value instead of an integer.
-     * 
-     * @return the total number of bytes read.
-     * @since Android 1.0
-     */
-    public synchronized long getBytesRead() {
-        // Throw NPE here
-        if (streamHandle == -1) {
-            throw new NullPointerException();
-        }
-        return getTotalInImpl(streamHandle);
-    }
-
-    /**
-     * Returns a the total number of bytes read by the {@code Inflater}. This
-     * method performs the same as {@code getTotalOut} except it returns a
-     * {@code long} value instead of an integer.
-     * 
-     * @return the total bytes written to the output buffer.
-     * @since Android 1.0
-     */
-    public synchronized long getBytesWritten() {
-        // Throw NPE here
-        if (streamHandle == -1) {
-            throw new NullPointerException();
-        }
-        return getTotalOutImpl(streamHandle);
-    }
-
     private native synchronized void setInputImpl(byte[] buf, int off,
             int nbytes, long handle);
 
@@ -465,6 +446,4 @@
     private native synchronized int setFileInputImpl(FileDescriptor fd, long off,
             int nbytes, long handle);
     // END android-added
-
-    private native long createStream(boolean noHeader1);
 }
diff --git a/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java b/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java
index 5d3bda0..1fd3602 100644
--- a/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java
+++ b/libcore/archive/src/main/java/java/util/zip/InflaterInputStream.java
@@ -17,7 +17,6 @@
 
 package java.util.zip;
 
-
 import java.io.EOFException;
 import java.io.FilterInputStream;
 import java.io.IOException;
@@ -31,31 +30,24 @@
  * (see <a href="http://www.gzip.org/algorithm.txt">specification</a>).
  * Basically it wraps the {@code Inflater} class and takes care of the
  * buffering.
- * 
+ *
  * @see Inflater
  * @see DeflaterOutputStream
- * @since Android 1.0
  */
 public class InflaterInputStream extends FilterInputStream {
 
     /**
      * The inflater used for this stream.
-     * 
-     * @since Android 1.0
      */
     protected Inflater inf;
 
     /**
      * The input buffer used for decompression.
-     * 
-     * @since Android 1.0
      */
     protected byte[] buf;
 
     /**
      * The length of the buffer.
-     * 
-     * @since Android 1.0
      */
     protected int len;
 
@@ -74,10 +66,9 @@
      * InputStream} from which the compressed data is to be read from. Default
      * settings for the {@code Inflater} and internal buffer are be used. In
      * particular the Inflater expects a ZLIB header from the input stream.
-     * 
+     *
      * @param is
      *            the {@code InputStream} to read data from.
-     * @since Android 1.0
      */
     public InflaterInputStream(InputStream is) {
         this(is, new Inflater(), BUF_SIZE);
@@ -86,12 +77,11 @@
     /**
      * This constructor lets you pass a specifically initialized Inflater,
      * for example one that expects no ZLIB header.
-     * 
+     *
      * @param is
      *            the {@code InputStream} to read data from.
      * @param inf
      *            the specific {@code Inflater} for uncompressing data.
-     * @since Android 1.0 
      */
     public InflaterInputStream(InputStream is, Inflater inf) {
         this(is, inf, BUF_SIZE);
@@ -100,14 +90,13 @@
     /**
      * This constructor lets you specify both the {@code Inflater} as well as
      * the internal buffer size to be used.
-     * 
+     *
      * @param is
      *            the {@code InputStream} to read data from.
      * @param inf
      *            the specific {@code Inflater} for uncompressing data.
      * @param bsize
      *            the size to be used for the internal buffer.
-     * @since Android 1.0
      */
     public InflaterInputStream(InputStream is, Inflater inf, int bsize) {
         super(is);
@@ -129,11 +118,10 @@
 
     /**
      * Reads a single byte of decompressed data.
-     * 
+     *
      * @return the byte read.
      * @throws IOException
      *             if an error occurs reading the byte.
-     * @since Android 1.0
      */
     @Override
     public int read() throws IOException {
@@ -147,7 +135,7 @@
     /**
      * Reads up to {@code nbytes} of decompressed data and stores it in
      * {@code buffer} starting at {@code off}.
-     * 
+     *
      * @param buffer
      *            the buffer to write data to.
      * @param off
@@ -157,7 +145,6 @@
      * @return Number of uncompressed bytes read
      * @throws IOException
      *             if an IOException occurs.
-     * @since Android 1.0
      */
     @Override
     public int read(byte[] buffer, int off, int nbytes) throws IOException {
@@ -197,7 +184,7 @@
                     if (len == -1) {
                         throw new EOFException();
                     }
-                    throw (IOException)(new IOException().initCause(e));
+                    throw (IOException) (new IOException().initCause(e));
                 }
                 if (result > 0) {
                     return result;
@@ -208,20 +195,18 @@
                     return -1;
                 } else if (len == -1) {
                     throw new EOFException();
-                // If result == 0, fill() and try again
+                    // If result == 0, fill() and try again
                 }
             } while (true);
         }
         throw new ArrayIndexOutOfBoundsException();
     }
 
-    
     /**
      * Fills the input buffer with data to be decompressed.
-     * 
+     *
      * @throws IOException
      *             if an {@code IOException} occurs.
-     * @since Android 1.0
      */
     protected void fill() throws IOException {
         if (closed) {
@@ -246,17 +231,21 @@
 
     /**
      * Skips up to n bytes of uncompressed data.
-     * 
+     *
      * @param nbytes
      *            the number of bytes to skip.
      * @return the number of uncompressed bytes skipped.
      * @throws IOException
      *             if an error occurs skipping.
-     * @since Android 1.0
      */
     @Override
     public long skip(long nbytes) throws IOException {
         if (nbytes >= 0) {
+            // BEGIN android-changed
+            if (buf == null) {
+                buf = new byte[BUF_SIZE];
+            }
+            // END android-changed
             long count = 0, rem = 0;
             while (count < nbytes) {
                 int x = read(buf, 0,
@@ -275,11 +264,10 @@
 
     /**
      * Returns whether data can be read from this stream.
-     * 
+     *
      * @return 0 if this stream has been closed, 1 otherwise.
      * @throws IOException
      *             If an error occurs.
-     * @since Android 1.0
      */
     @Override
     public int available() throws IOException {
@@ -295,10 +283,9 @@
 
     /**
      * Closes the input stream.
-     * 
+     *
      * @throws IOException
      *             If an error occurs closing the input stream.
-     * @since Android 1.0
      */
     @Override
     public void close() throws IOException {
@@ -309,17 +296,15 @@
             super.close();
         }
     }
-    
+
     /**
-     * This implementation overrides the super type implementation to do nothing
-     * at all.
-     * 
+     * Marks the current position in the stream. This implementation overrides
+     * the super type implementation to do nothing at all.
+     *
      * @param readlimit
      *            of no use.
-     * @since Android 1.0
      */
     @Override
-    @SuppressWarnings("unused")
     public void mark(int readlimit) {
         // do nothing
     }
@@ -328,22 +313,20 @@
      * Reset the position of the stream to the last marked position. This
      * implementation overrides the supertype implementation and always throws
      * an {@link IOException IOException} when called.
-     * 
+     *
      * @throws IOException
      *             if the method is called
-     * @since Android 1.0
      */
     @Override
-    public void reset() throws IOException{
+    public void reset() throws IOException {
         throw new IOException();
     }
-    
+
     /**
      * Returns whether the receiver implements {@code mark} semantics. This type
      * does not support {@code mark()}, so always responds {@code false}.
-     * 
+     *
      * @return false, always
-     * @since Android 1.0
      */
     @Override
     public boolean markSupported() {
diff --git a/libcore/archive/src/main/java/java/util/zip/ZipConstants.java b/libcore/archive/src/main/java/java/util/zip/ZipConstants.java
index d804b0e..d00adc9 100644
--- a/libcore/archive/src/main/java/java/util/zip/ZipConstants.java
+++ b/libcore/archive/src/main/java/java/util/zip/ZipConstants.java
@@ -17,7 +17,6 @@
 
 package java.util.zip;
 
-
 interface ZipConstants {
 
     public static final long LOCSIG = 0x4034b50, EXTSIG = 0x8074b50,
diff --git a/libcore/archive/src/main/java/java/util/zip/ZipEntry.java b/libcore/archive/src/main/java/java/util/zip/ZipEntry.java
index 2cc7a9c..9774b6a 100644
--- a/libcore/archive/src/main/java/java/util/zip/ZipEntry.java
+++ b/libcore/archive/src/main/java/java/util/zip/ZipEntry.java
@@ -36,10 +36,9 @@
  * itself. For example when reading a <i>ZIP-file</i> you will first retrieve
  * all its entries in a collection and then read the data for a specific entry
  * through an input stream.
- * 
+ *
  * @see ZipFile
  * @see ZipOutputStream
- * @since Android 1.0
  */
 public class ZipEntry implements ZipConstants, Cloneable {
     String name, comment;
@@ -94,26 +93,21 @@
 
     /**
      * Zip entry state: Deflated.
-     * 
-     * @since Android 1.0
      */
     public static final int DEFLATED = 8;
 
     /**
      * Zip entry state: Stored.
-     * 
-     * @since Android 1.0
      */
     public static final int STORED = 0;
 
     /**
      * Constructs a new {@code ZipEntry} with the specified name.
-     * 
+     *
      * @param name
      *            the name of the ZIP entry.
      * @throws IllegalArgumentException
      *             if the name length is outside the range (> 0xFFFF).
-     * @since Android 1.0
      */
     public ZipEntry(String name) {
         if (name == null) {
@@ -147,11 +141,10 @@
 
     /**
      * Gets the comment for this {@code ZipEntry}.
-     * 
+     *
      * @return the comment for this {@code ZipEntry}, or {@code null} if there
      *         is no comment. If we're reading an archive with
      *         {@code ZipInputStream} the comment is not available.
-     * @since Android 1.0
      */
     public String getComment() {
         return comment;
@@ -159,10 +152,9 @@
 
     /**
      * Gets the compressed size of this {@code ZipEntry}.
-     * 
+     *
      * @return the compressed size, or -1 if the compressed size has not been
      *         set.
-     * @since Android 1.0
      */
     public long getCompressedSize() {
         return compressedSize;
@@ -170,9 +162,8 @@
 
     /**
      * Gets the checksum for this {@code ZipEntry}.
-     * 
+     *
      * @return the checksum, or -1 if the checksum has not been set.
-     * @since Android 1.0
      */
     public long getCrc() {
         return crc;
@@ -180,10 +171,9 @@
 
     /**
      * Gets the extra information for this {@code ZipEntry}.
-     * 
+     *
      * @return a byte array containing the extra information, or {@code null} if
      *         there is none.
-     * @since Android 1.0
      */
     public byte[] getExtra() {
         return extra;
@@ -191,10 +181,9 @@
 
     /**
      * Gets the compression method for this {@code ZipEntry}.
-     * 
+     *
      * @return the compression method, either {@code DEFLATED}, {@code STORED}
      *         or -1 if the compression method has not been set.
-     * @since Android 1.0
      */
     public int getMethod() {
         return compressionMethod;
@@ -202,9 +191,8 @@
 
     /**
      * Gets the name of this {@code ZipEntry}.
-     * 
+     *
      * @return the entry name.
-     * @since Android 1.0
      */
     public String getName() {
         return name;
@@ -212,10 +200,9 @@
 
     /**
      * Gets the uncompressed size of this {@code ZipEntry}.
-     * 
+     *
      * @return the uncompressed size, or {@code -1} if the size has not been
      *         set.
-     * @since Android 1.0
      */
     public long getSize() {
         return size;
@@ -223,10 +210,9 @@
 
     /**
      * Gets the last modification time of this {@code ZipEntry}.
-     * 
+     *
      * @return the last modification time as the number of milliseconds since
      *         Jan. 1, 1970.
-     * @since Android 1.0
      */
     public long getTime() {
         if (time != -1) {
@@ -242,10 +228,9 @@
 
     /**
      * Determine whether or not this {@code ZipEntry} is a directory.
-     * 
+     *
      * @return {@code true} when this {@code ZipEntry} is a directory, {@code
      *         false} otherwise.
-     * @since Android 1.0
      */
     public boolean isDirectory() {
         return name.charAt(name.length() - 1) == '/';
@@ -253,10 +238,9 @@
 
     /**
      * Sets the comment for this {@code ZipEntry}.
-     * 
+     *
      * @param string
      *            the comment for this entry.
-     * @since Android 1.0
      */
     public void setComment(String string) {
         if (string == null || string.length() <= 0xFFFF) {
@@ -268,10 +252,9 @@
 
     /**
      * Sets the compressed size for this {@code ZipEntry}.
-     * 
+     *
      * @param value
      *            the compressed size (in bytes).
-     * @since Android 1.0
      */
     public void setCompressedSize(long value) {
         compressedSize = value;
@@ -279,12 +262,11 @@
 
     /**
      * Sets the checksum for this {@code ZipEntry}.
-     * 
+     *
      * @param value
      *            the checksum for this entry.
      * @throws IllegalArgumentException
      *             if {@code value} is < 0 or > 0xFFFFFFFFL.
-     * @since Android 1.0
      */
     public void setCrc(long value) {
         if (value >= 0 && value <= 0xFFFFFFFFL) {
@@ -296,12 +278,11 @@
 
     /**
      * Sets the extra information for this {@code ZipEntry}.
-     * 
+     *
      * @param data
      *            a byte array containing the extra information.
      * @throws IllegalArgumentException
      *             when the length of data is greater than 0xFFFF bytes.
-     * @since Android 1.0
      */
     public void setExtra(byte[] data) {
         if (data == null || data.length <= 0xFFFF) {
@@ -313,13 +294,12 @@
 
     /**
      * Sets the compression method for this {@code ZipEntry}.
-     * 
+     *
      * @param value
      *            the compression method, either {@code DEFLATED} or {@code
      *            STORED}.
      * @throws IllegalArgumentException
      *             when value is not {@code DEFLATED} or {@code STORED}.
-     * @since Android 1.0
      */
     public void setMethod(int value) {
         if (value != STORED && value != DEFLATED) {
@@ -330,12 +310,11 @@
 
     /**
      * Sets the uncompressed size of this {@code ZipEntry}.
-     * 
+     *
      * @param value
      *            the uncompressed size for this entry.
      * @throws IllegalArgumentException
      *             if {@code value} < 0 or {@code value} > 0xFFFFFFFFL.
-     * @since Android 1.0
      */
     public void setSize(long value) {
         if (value >= 0 && value <= 0xFFFFFFFFL) {
@@ -347,11 +326,10 @@
 
     /**
      * Sets the modification time of this {@code ZipEntry}.
-     * 
+     *
      * @param value
      *            the modification time as the number of milliseconds since Jan.
      *            1, 1970.
-     * @since Android 1.0
      */
     public void setTime(long value) {
         GregorianCalendar cal = new GregorianCalendar();
@@ -372,9 +350,8 @@
 
     /**
      * Returns the string representation of this {@code ZipEntry}.
-     * 
+     *
      * @return the string representation of this {@code ZipEntry}.
-     * @since Android 1.0
      */
     @Override
     public String toString() {
@@ -401,10 +378,9 @@
     /**
      * Constructs a new {@code ZipEntry} using the values obtained from {@code
      * ze}.
-     * 
+     *
      * @param ze
      *            the {@code ZipEntry} from which to obtain values.
-     * @since Android 1.0
      */
     public ZipEntry(ZipEntry ze) {
         name = ze.name;
@@ -434,9 +410,8 @@
 
     /**
      * Returns a shallow copy of this entry.
-     * 
+     *
      * @return a copy of this entry.
-     * @since Android 1.0
      */
     @Override
     public Object clone() {
@@ -445,9 +420,8 @@
 
     /**
      * Returns the hash code for this {@code ZipEntry}.
-     * 
+     *
      * @return the hash code of the entry.
-     * @since Android 1.0
      */
     @Override
     public int hashCode() {
@@ -471,7 +445,7 @@
          * readIntLE, so we're going to read the entire header at once
          * and then parse the results out without using any function calls.
          * Uglier, but should be much faster.
-         * 
+         *
          * Note that some lines look a bit different, because the corresponding
          * fields or locals are long and so we need to do & 0xffffffffl to avoid
          * problems induced by sign extension.
@@ -549,7 +523,7 @@
         int count;
         int len = b.length;
         int off = 0;
-    
+
         while (len > 0) {
             count = in.read(b, off, len);
             if (count <= 0)
diff --git a/libcore/archive/src/main/java/java/util/zip/ZipException.java b/libcore/archive/src/main/java/java/util/zip/ZipException.java
index 590117b..6dab26f 100644
--- a/libcore/archive/src/main/java/java/util/zip/ZipException.java
+++ b/libcore/archive/src/main/java/java/util/zip/ZipException.java
@@ -17,7 +17,6 @@
 
 package java.util.zip;
 
-
 import java.io.IOException;
 
 /**
@@ -26,7 +25,6 @@
  * 
  * @see ZipFile
  * @see ZipInputStream
- * @since Android 1.0
  */
 public class ZipException extends IOException {
 
@@ -34,8 +32,6 @@
 
     /**
      * Constructs a new {@code ZipException} instance.
-     * 
-     * @since Android 1.0
      */
     public ZipException() {
         super();
@@ -44,10 +40,9 @@
     /**
      * Constructs a new {@code ZipException} instance with the specified
      * message.
-     * 
+     *
      * @param detailMessage
      *            the detail message for the exception.
-     * @since Android 1.0
      */
     public ZipException(String detailMessage) {
         super(detailMessage);
diff --git a/libcore/archive/src/main/java/java/util/zip/ZipFile.java b/libcore/archive/src/main/java/java/util/zip/ZipFile.java
index f1415d9..653b2c9 100644
--- a/libcore/archive/src/main/java/java/util/zip/ZipFile.java
+++ b/libcore/archive/src/main/java/java/util/zip/ZipFile.java
@@ -37,17 +37,13 @@
  * While {@code ZipInputStream} provides stream based read access to a
  * <i>ZIP-archive</i>, this class implements more efficient (file based) access
  * and makes use of the <i>central directory</i> within a <i>ZIP-archive</i>.
- * </p>
  * <p>
  * Use {@code ZipOutputStream} if you want to create an archive.
- * </p>
  * <p>
  * A temporary ZIP file can be marked for automatic deletion upon closing it.
- * </p>
- * 
+ *
  * @see ZipEntry
  * @see ZipOutputStream
- * @since Android 1.0
  */
 public class ZipFile implements ZipConstants {
 
@@ -57,28 +53,23 @@
 
     /**
      * Open zip file for read.
-     * 
-     * @since Android 1.0
      */
     public static final int OPEN_READ = 1;
 
     /**
      * Delete zip file when closed.
-     * 
-     * @since Android 1.0
      */
     public static final int OPEN_DELETE = 4;
 
     /**
      * Constructs a new {@code ZipFile} with the specified file.
-     * 
+     *
      * @param file
      *            the file to read from.
      * @throws ZipException
      *             if a ZIP error occurs.
      * @throws IOException
      *             if an {@code IOException} occurs.
-     * @since Android 1.0
      */
     public ZipFile(File file) throws ZipException, IOException {
         this(file, OPEN_READ);
@@ -88,22 +79,31 @@
      * Opens a file as <i>ZIP-archive</i>. "mode" must be {@code OPEN_READ} or
      * {@code OPEN_DELETE} . The latter sets the "delete on exit" flag through a
      * file.
-     * 
+     *
      * @param file
      *            the ZIP file to read.
      * @param mode
      *            the mode of the file open operation.
      * @throws IOException
      *             if an {@code IOException} occurs.
-     * @since Android 1.0
      */
     public ZipFile(File file, int mode) throws IOException {
-        if (mode == (OPEN_READ | OPEN_DELETE))
-            fileToDeleteOnClose = file; // file.deleteOnExit();
-        else if (mode != OPEN_READ)
-            throw new IllegalArgumentException("invalid mode");
-
         fileName = file.getPath();
+        if (mode == OPEN_READ || mode == (OPEN_READ | OPEN_DELETE)) {
+            SecurityManager security = System.getSecurityManager();
+            if (security != null) {
+                security.checkRead(fileName);
+            }
+            if ((mode & OPEN_DELETE) != 0) {
+                if (security != null) {
+                    security.checkDelete(fileName);
+                }
+                fileToDeleteOnClose = file; // file.deleteOnExit();
+            }
+        } else {
+            throw new IllegalArgumentException();
+        }
+
         mRaf = new RandomAccessFile(fileName, "r");
 
         mEntryList = new ArrayList<ZipEntry>();
@@ -124,12 +124,11 @@
 
     /**
      * Opens a ZIP archived file.
-     * 
+     *
      * @param name
      *            the name of the ZIP file.
      * @throws IOException
      *             if an IOException occurs.
-     * @since Android 1.0
      */
     public ZipFile(String name) throws IOException {
         this(new File(name), OPEN_READ);
@@ -142,10 +141,9 @@
 
     /**
      * Closes this ZIP file.
-     * 
+     *
      * @throws IOException
      *             if an IOException occurs.
-     * @since Android 1.0
      */
     public void close() throws IOException {
         RandomAccessFile raf = mRaf;
@@ -171,9 +169,8 @@
     /**
      * Returns an enumeration of the entries. The entries are listed in the
      * order in which they appear in the ZIP archive.
-     * 
+     *
      * @return the enumeration of the entries.
-     * @since Android 1.0
      */
     public Enumeration<? extends ZipEntry> entries() {
         return new Enumeration<ZipEntry>() {
@@ -195,12 +192,11 @@
 
     /**
      * Gets the ZIP entry with the specified name from this {@code ZipFile}.
-     * 
+     *
      * @param entryName
      *            the name of the entry in the ZIP file.
      * @return a {@code ZipEntry} or {@code null} if the entry name does not
      *         exist in the ZIP file.
-     * @since Android 1.0
      */
     public ZipEntry getEntry(String entryName) {
         if (entryName != null) {
@@ -213,13 +209,12 @@
 
     /**
      * Returns an input stream on the data of the specified {@code ZipEntry}.
-     * 
+     *
      * @param entry
      *            the ZipEntry.
      * @return an input stream of the data contained in the {@code ZipEntry}.
      * @throws IOException
      *             if an {@code IOException} occurs.
-     * @since Android 1.0
      */
     public InputStream getInputStream(ZipEntry entry) throws IOException {
         /*
@@ -259,9 +254,8 @@
 
     /**
      * Gets the file name of this {@code ZipFile}.
-     * 
+     *
      * @return the file name of this {@code ZipFile}.
-     * @since Android 1.0
      */
     public String getName() {
         return fileName;
@@ -269,9 +263,8 @@
 
     /**
      * Returns the number of {@code ZipEntries} in this {@code ZipFile}.
-     * 
+     *
      * @return the number of entries in this file.
-     * @since Android 1.0
      */
     public int size() {
         return mEntryList.size();
diff --git a/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java b/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java
index 262fa3f..1a35b1c 100644
--- a/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java
+++ b/libcore/archive/src/main/java/java/util/zip/ZipInputStream.java
@@ -17,7 +17,6 @@
 
 package java.util.zip;
 
-
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
@@ -36,18 +35,14 @@
  * 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>
  * <p>
  * While {@code InflaterInputStream} can read a compressed <i>ZIP-archive</i>
  * entry, this extension can read uncompressed entries as well.
- * </p>
  * <p>
  * Use {@code ZipFile} if you can access the archive as a file directly.
- * </p>
- * 
+ *
  * @see ZipEntry
  * @see ZipFile
- * @since Android 1.0
  */
 public class ZipInputStream extends InflaterInputStream implements ZipConstants {
     static final int DEFLATED = 8;
@@ -58,7 +53,7 @@
 
     static final int ZIPLocalHeaderVersionNeeded = 20;
 
-    // BEGI android-removed
+    // BEGIN android-removed
     // private boolean zipClosed = false;
     // END android-removed
 
@@ -82,10 +77,9 @@
 
     /**
      * Constructs a new {@code ZipInputStream} from the specified input stream.
-     * 
+     *
      * @param stream
      *            the input stream to representing a ZIP archive.
-     * @since Android 1.0
      */
     public ZipInputStream(InputStream stream) {
         super(new PushbackInputStream(stream, BUF_SIZE), new Inflater(true));
@@ -96,10 +90,9 @@
 
     /**
      * Closes this {@code ZipInputStream}.
-     * 
+     *
      * @throws IOException
      *             if an {@code IOException} occurs.
-     * @since Android 1.0
      */
     @Override
     public void close() throws IOException {
@@ -113,10 +106,9 @@
 
     /**
      * Closes the current ZIP entry and positions to read the next entry.
-     * 
+     *
      * @throws IOException
      *             if an {@code IOException} occurs.
-     * @since Android 1.0
      */
     public void closeEntry() throws IOException {
         // BEGIN android-changed
@@ -173,13 +165,12 @@
 
     /**
      * Reads the next entry from this {@code ZipInputStream}.
-     * 
+     *
      * @return the next {@code ZipEntry} contained in the input stream.
      * @throws IOException
      *             if the stream is not positioned at the beginning of an entry
      *             or if an other {@code IOException} occurs.
      * @see ZipEntry
-     * @since Android 1.0
      */
     public ZipEntry getNextEntry() throws IOException {
         if (currentEntry != null) {
@@ -274,6 +265,18 @@
 
     /* Read 4 bytes from the buffer and store it as an int */
 
+    /**
+     * Reads up to the specified number of uncompressed bytes into the buffer
+     * starting at the offset.
+     *
+     * @param buffer
+     *            a byte array
+     * @param start
+     *            the starting offset into the buffer
+     * @param length
+     *            the number of bytes to read
+     * @return the number of bytes read
+     */
     @Override
     public int read(byte[] buffer, int start, int length) throws IOException {
         // BEGIN android-changed
@@ -340,13 +343,12 @@
 
     /**
      * Skips up to the specified number of bytes in the current ZIP entry.
-     * 
+     *
      * @param value
      *            the number of bytes to skip.
      * @return the number of bytes skipped.
      * @throws IOException
      *             if an {@code IOException} occurs.
-     * @since Android 1.0
      */
     @Override
     public long skip(long value) throws IOException {
@@ -364,15 +366,14 @@
             return skipped;
         }
         throw new IllegalArgumentException();
-}
+    }
 
     /**
      * Returns 0 if the {@code EOF} has been reached, otherwise returns 1.
-     * 
+     *
      * @return 0 after {@code EOF} of current entry, 1 otherwise.
      * @throws IOException
      *             if an IOException occurs.
-     * @since Android 1.0
      */
     @Override
     public int available() throws IOException {
@@ -389,11 +390,10 @@
 
     /**
      * creates a {@link ZipEntry } with the given name.
-     * 
+     *
      * @param name
      *            the name of the entry.
      * @return the created {@code ZipEntry}.
-     * @since Android 1.0
      */
     protected ZipEntry createZipEntry(String name) {
         return new ZipEntry(name);
diff --git a/libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java b/libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java
index 4ddf643..58e781f 100644
--- a/libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java
+++ b/libcore/archive/src/main/java/java/util/zip/ZipOutputStream.java
@@ -17,7 +17,6 @@
 
 package java.util.zip;
 
-
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
@@ -32,33 +31,26 @@
  * {@code ZipOutputStream} is used to write {@code ZipEntries} to the underlying
  * stream. Output from {@code ZipOutputStream} conforms to the {@code ZipFile}
  * file format.
- * </p>
  * <p>
  * While {@code DeflaterOutputStream} can write a compressed <i>ZIP-archive</i>
  * entry, this extension can write uncompressed entries as well. In this case
  * special rules apply, for this purpose refer to the <a
  * href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">file format
  * specification</a>.
- * </p>
- * 
+ *
  * @see ZipEntry
  * @see ZipFile
- * @since Android 1.0
  */
 public class ZipOutputStream extends DeflaterOutputStream implements
         ZipConstants {
 
     /**
      * Indicates deflated entries.
-     * 
-     * @since Android 1.0
      */
     public static final int DEFLATED = 8;
 
     /**
      * Indicates uncompressed entries.
-     * 
-     * @since Android 1.0
      */
     public static final int STORED = 0;
 
@@ -90,7 +82,6 @@
      * 
      * @param p1
      *            the {@code OutputStream} to write the data to.
-     * @since Android 1.0
      */
     public ZipOutputStream(OutputStream p1) {
         super(p1, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
@@ -102,7 +93,6 @@
      * 
      * @throws IOException
      *             If an error occurs closing the stream.
-     * @since Android 1.0
      */
     @Override
     public void close() throws IOException {
@@ -119,7 +109,6 @@
      * 
      * @throws IOException
      *             If an error occurs closing the entry.
-     * @since Android 1.0
      */
     public void closeEntry() throws IOException {
         if (cDir == null) {
@@ -205,7 +194,6 @@
      * 
      * @throws IOException
      *             if an error occurs while terminating the stream.
-     * @since Android 1.0
      */
     @Override
     public void finish() throws IOException {
@@ -253,7 +241,6 @@
      * @throws IOException
      *             If an error occurs storing the entry.
      * @see #write
-     * @since Android 1.0
      */
     public void putNextEntry(ZipEntry ze) throws java.io.IOException {
         if (currentEntry != null) {
@@ -286,7 +273,8 @@
         nameLength = utf8Count(ze.name);
         if (nameLength > 0xffff) {
             /* [MSG "archive.2A", "Name too long: {0}"] */
-            throw new IllegalArgumentException(Messages.getString("archive.2A", ze.name)); //$NON-NLS-1$
+            throw new IllegalArgumentException(Messages.getString(
+                    "archive.2A", ze.name)); //$NON-NLS-1$
         }
 
         def.setLevel(compressLevel);
@@ -338,7 +326,6 @@
      * 
      * @param comment
      *            the comment associated with the file.
-     * @since Android 1.0
      */
     public void setComment(String comment) {
         if (comment.length() > 0xFFFF) {
@@ -355,7 +342,6 @@
      * @param level
      *            the compression level (ranging from -1 to 8).
      * @see Deflater
-     * @since Android 1.0
      */
     public void setLevel(int level) {
         if (level < Deflater.DEFAULT_COMPRESSION
@@ -372,7 +358,6 @@
      * 
      * @param method
      *            the compression method to use.
-     * @since Android 1.0
      */
     public void setMethod(int method) {
         if (method != STORED && method != DEFLATED) {
@@ -398,11 +383,17 @@
 
     }
 
+    /**
+     * Writes data for the current entry to the underlying stream.
+     * 
+     * @exception IOException
+     *                If an error occurs writing to the stream
+     */
     @Override
     public void write(byte[] buffer, int off, int nbytes)
             throws java.io.IOException {
         // avoid int overflow, check null buf
-        if ((off > buffer.length) || (nbytes < 0) || (off < 0)
+        if ((off < 0 || (nbytes < 0) || off > buffer.length)
                 || (buffer.length - off < nbytes)) {
             throw new IndexOutOfBoundsException();
         }
diff --git a/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java b/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java
index 764f34d..3ba50fa 100644
--- a/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java
+++ b/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/Messages.java
@@ -27,7 +27,6 @@
 
 package org.apache.harmony.archive.internal.nls;
 
-
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.Locale;
@@ -50,7 +49,7 @@
  * is looked up, or resource bundle support is not available, the key itself
  * will be returned as the associated message. This means that the <em>KEY</em>
  * should a reasonable human-readable (english) string.
- * 
+ *
  */
 public class Messages {
 
@@ -61,7 +60,7 @@
 
     /**
      * Retrieves a message which has no arguments.
-     * 
+     *
      * @param msg
      *            String the key to look up.
      * @return String the message for that key in the system message bundle.
@@ -74,7 +73,7 @@
 
     /**
      * Retrieves a message which takes 1 argument.
-     * 
+     *
      * @param msg
      *            String the key to look up.
      * @param arg
@@ -87,7 +86,7 @@
 
     /**
      * Retrieves a message which takes 1 integer argument.
-     * 
+     *
      * @param msg
      *            String the key to look up.
      * @param arg
@@ -100,7 +99,7 @@
 
     /**
      * Retrieves a message which takes 1 character argument.
-     * 
+     *
      * @param msg
      *            String the key to look up.
      * @param arg
@@ -113,7 +112,7 @@
 
     /**
      * Retrieves a message which takes 2 arguments.
-     * 
+     *
      * @param msg
      *            String the key to look up.
      * @param arg1
@@ -128,7 +127,7 @@
 
     /**
      * Retrieves a message which takes several arguments.
-     * 
+     *
      * @param msg
      *            String the key to look up.
      * @param args
diff --git a/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties b/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties
index 1ae5c5e..e909af0 100644
--- a/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties
+++ b/libcore/archive/src/main/java/org/apache/harmony/archive/internal/nls/messages.properties
@@ -60,8 +60,11 @@
 archive.2A=Name too long: {0}
 archive.2B=String is too long
 archive.2C=No active entry
-archive.2D=Missing version string\: {0}
-archive.2E=Line too long
-archive.2F=Invalid attribute {0}
-archive.30={0} failed verification of {1}
-archive.31={0} has invalid digest for {1} in {2}
+archive.2D=Missing version attribute\: {0}
+archive.2E=Manifest is too long
+archive.2F=NUL character in a manifest
+archive.30=Invalid attribute {0}
+archive.31={0} failed verification of {1}
+archive.32={0} has invalid digest for {1} in {2}
+archive.33=A length of the encoded header name "{1}" exceeded maximum length {2}
+archive.34=A jar verifier does not support more than one entry with the same name
diff --git a/libcore/archive/src/main/java/org/apache/harmony/archive/util/Util.java b/libcore/archive/src/main/java/org/apache/harmony/archive/util/Util.java
index bed3e91..b15108a 100644
--- a/libcore/archive/src/main/java/org/apache/harmony/archive/util/Util.java
+++ b/libcore/archive/src/main/java/org/apache/harmony/archive/util/Util.java
@@ -30,39 +30,49 @@
                 return false;
             }
 
-            s1 = s1.substring(start1, start1 + length);
-            s2 = s2.substring(start2, start2 + length);
-
-            return toASCIILowerCase(s1).equals(toASCIILowerCase(s2));
+            char c1, c2;
+            for (int i = 0; i < length; i++) {
+                if ((c1 = s1.charAt(start1++)) != (c2 = s2.charAt(start2++))
+                        && toASCIIUpperCase(c1) != toASCIIUpperCase(c2)) {
+                    return false;
+                }
+            }
+            return true;
         }
         throw new NullPointerException();
     }
 
-    public static String toASCIILowerCase(String s) {
-        int len = s.length();
-        StringBuilder buffer = new StringBuilder(len);
-        for (int i = 0; i < len; i++) {
-            char c = s.charAt(i);
-            if ('A' <= c && c <= 'Z') {
-                buffer.append((char) (c + ('a' - 'A')));
-            } else {
-                buffer.append(c);
+    public static final boolean equalsIgnoreCase(byte[] buf1, byte[] buf2) {
+        if (buf1 == buf2) {
+            return true;
+        }
+
+        if (buf1 == null || buf2 == null || buf1.length != buf2.length) {
+            return false;
+        }
+
+        byte b1, b2;
+
+        for (int i = 0; i < buf1.length; i++) {
+            if ((b1 = buf1[i]) != (b2 = buf2[i])
+                    && toASCIIUpperCase(b1) != toASCIIUpperCase(b2)) {
+                return false;
             }
         }
-        return buffer.toString();
+        return true;
     }
 
-    public static String toASCIIUpperCase(String s) {
-        int len = s.length();
-        StringBuilder buffer = new StringBuilder(len);
-        for (int i = 0; i < len; i++) {
-            char c = s.charAt(i);
-            if ('a' <= c && c <= 'z') {
-                buffer.append((char) (c - ('a' - 'A')));
-            } else {
-                buffer.append(c);
-            }
+    static final char toASCIIUpperCase(char c) {
+        if ('a' <= c && c <= 'z') {
+            return (char) (c - ('a' - 'A'));
         }
-        return buffer.toString();
+        return c;
+    }
+
+    static final byte toASCIIUpperCase(byte b) {
+        if ('a' <= b && b <= 'z') {
+            return (byte) (b - ('a' - 'A'));
+        }
+        return b;
     }
 }
diff --git a/libcore/archive/src/main/native/java_util_zip_Adler32.c b/libcore/archive/src/main/native/java_util_zip_Adler32.c
index a7a182a..1b02a11 100644
--- a/libcore/archive/src/main/native/java_util_zip_Adler32.c
+++ b/libcore/archive/src/main/native/java_util_zip_Adler32.c
@@ -15,23 +15,25 @@
  * limitations under the License.
  */
 
+#include "jni.h"
 #include "hy2sie.h"
-
 #include "zlib.h"
-
+#include "sieb.h"
 
 JNIEXPORT jlong JNICALL
 Java_java_util_zip_Adler32_updateImpl (JNIEnv * env, jobject recv,
                                        jbyteArray buf, int off, int len,
                                        jlong crc)
 {
-  PORT_ACCESS_FROM_ENV (env);
-
   jbyte *b;
   jboolean isCopy;
   jlong result;
 
   b = (*env)->GetPrimitiveArrayCritical (env, buf, &isCopy);
+  if (b == NULL) {
+    throwNewOutOfMemoryError(env, "");
+    return 0;
+  }
   result = (jlong) adler32 ((uLong) crc, (Bytef *) (b + off), (uInt) len);
   (*env)->ReleasePrimitiveArrayCritical (env, buf, b, JNI_ABORT);
 
@@ -42,9 +44,8 @@
 Java_java_util_zip_Adler32_updateByteImpl (JNIEnv * env, jobject recv,
                                            jint val, jlong crc)
 {
-  PORT_ACCESS_FROM_ENV (env);
-
-  return adler32 ((uLong) crc, (Bytef *) (&val), 1);
+  Bytef bytefVal = val;
+  return adler32 ((uLong) crc, (Bytef *) (&bytefVal), 1);
 }
 
 
diff --git a/libcore/archive/src/main/native/java_util_zip_CRC32.c b/libcore/archive/src/main/native/java_util_zip_CRC32.c
index 0688868..cee25e5 100644
--- a/libcore/archive/src/main/native/java_util_zip_CRC32.c
+++ b/libcore/archive/src/main/native/java_util_zip_CRC32.c
@@ -16,6 +16,7 @@
  */
 
 #include "hy2sie.h"
+#include "sieb.h"
 
 #include "zlib.h"
 
@@ -28,8 +29,10 @@
   jlong result;
 
   b = ((*env)->GetPrimitiveArrayCritical (env, buf, 0));
-  if (b == NULL)
+  if (b == NULL) {
+    throwNewOutOfMemoryError(env, "");
     return -1;
+  }
   result = crc32 ((uLong) crc, (Bytef *) (b + off), (uInt) len);
   ((*env)->ReleasePrimitiveArrayCritical (env, buf, b, JNI_ABORT));
   return result;
diff --git a/libcore/archive/src/main/native/java_util_zip_Deflater.c b/libcore/archive/src/main/native/java_util_zip_Deflater.c
index c8bd199..2e0e268 100644
--- a/libcore/archive/src/main/native/java_util_zip_Deflater.c
+++ b/libcore/archive/src/main/native/java_util_zip_Deflater.c
@@ -18,11 +18,13 @@
 #include "hy2sie.h"
 
 #include "zlib.h"
-#include "zipsup.h"
+#include "zip.h"
+#include "jni.h"
 
-
+#ifndef HY_ZIP_API
 void zfree PROTOTYPE ((void *opaque, void *address));
 void *zalloc PROTOTYPE ((void *opaque, U_32 items, U_32 size));
+#endif
 
 
 static struct {
@@ -52,10 +54,10 @@
   if (err != Z_OK)
     {
       jclmem_free_memory (env, dBytes);
-      throwNewIllegalArgumentException (env, "");
+      THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException);
       return;
     }
-  stream->dict = dBytes;
+  stream->dict = (U_8*) dBytes;
 }
 
 JNIEXPORT jlong JNICALL
@@ -94,9 +96,8 @@
 Java_java_util_zip_Deflater_createStream (JNIEnv * env, jobject recv,
 					  jint level, jint strategy,
 					  jboolean noHeader)
-{  
+{
   PORT_ACCESS_FROM_ENV (env);
-
   JCLZipStream *jstream;
   z_stream *stream;
   int err = 0;
@@ -109,7 +110,12 @@
                   // results in 2 x 128K being allocated per Deflater, which is
                   // not acceptable.
   // END android-changed
-  
+#ifdef HY_ZIP_API
+  VMI_ACCESS_FROM_ENV (env);
+  VMIZipFunctionTable *zipFuncs;
+  zipFuncs = (*VMI)->GetZipFunctions(VMI);
+#endif
+
   /*Allocate mem for wrapped struct */
   jstream = jclmem_allocate_memory (env, sizeof (JCLZipStream));
   if (jstream == NULL)
@@ -141,11 +147,10 @@
 		      mlevel,	/*Memory allocation for internal compression state. 9 uses the most. */
 		      // END android-changed
 		      strategy);
-  if (err != Z_OK)
-    {
-      throwNewIllegalArgumentException (env, "");
-      return -1;
-    }
+  if (err != Z_OK) {
+    THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException);
+    return -1;
+  }
 
   return (jlong) ((IDATA) jstream);
 }
@@ -170,8 +175,10 @@
       return;
     }
   in = ((*env)->GetPrimitiveArrayCritical (env, buf, 0));
-  if (in == NULL)
+  if (in == NULL) {
+    throwNewOutOfMemoryError(env, "");
     return;
+  }
   memcpy (stream->inaddr, (in + off), len);
   ((*env)->ReleasePrimitiveArrayCritical (env, buf, in, JNI_ABORT));
   stream->stream->next_in = (Bytef *) stream->inaddr;
@@ -185,8 +192,6 @@
 					 jbyteArray buf, int off, int len,
 					 jlong handle, int flushParm)
 {
-  PORT_ACCESS_FROM_ENV (env);
-
   jbyte *out;
   JCLZipStream *stream;
   jint err = 0;
@@ -203,29 +208,34 @@
   sin = stream->stream->total_in;
   sout = stream->stream->total_out;
   out = ((*env)->GetPrimitiveArrayCritical (env, buf, 0));
-  if (out == NULL)
+  if (out == NULL) {
+    throwNewOutOfMemoryError(env, "");
     return -1;
+  }
   stream->stream->next_out = (Bytef *) out + off;
   err = deflate (stream->stream, flushParm);
   ((*env)->ReleasePrimitiveArrayCritical (env, buf, out, 0));
-  if (err != Z_OK)
-    {
-      if (err == Z_STREAM_END)
-	{
-	  ((*env)->
-	   SetBooleanField (env, recv,
-			    gCachedFields.finished,
-			    JNI_TRUE));
-	  return stream->stream->total_out - sout;
-	}
+  if (err != Z_OK) {
+    if (err == Z_MEM_ERROR) {
+      throwNewOutOfMemoryError(env, "");
+      return 0;
     }
+    if (err == Z_STREAM_END)
+      {
+        ((*env)->
+         SetBooleanField (env, recv,
+                          gCachedFields.finished,
+                          JNI_TRUE));
+        return stream->stream->total_out - sout;
+      }
+  }
   if (flushParm != Z_FINISH)
     {
       /* Need to update the number of input bytes read. */
       ((*env)->
        SetIntField (env, recv,
-		    gCachedFields.inRead,
-		    (jint) stream->stream->total_in - sin + inBytes));
+                    gCachedFields.inRead,
+                    (jint) stream->stream->total_in - sin + inBytes));
     }
   return stream->stream->total_out - sout;
 }
@@ -262,8 +272,6 @@
 					   int level, int strategy,
 					   jlong handle)
 {
-  PORT_ACCESS_FROM_ENV (env);
-
   JCLZipStream *stream;
   jbyte b = 0;
   int err = 0;
@@ -276,8 +284,9 @@
   stream = (JCLZipStream *) ((IDATA) handle);
   stream->stream->next_out = (Bytef *) & b;
   err = deflateParams (stream->stream, level, strategy);
-  if (err != Z_OK)
-    throwNewIllegalStateException (env, "");
+  if (err != Z_OK) {
+    THROW_ZIP_EXCEPTION(env, err, IllegalStateException);
+  }
 }
 
 JNIEXPORT void JNICALL
diff --git a/libcore/archive/src/main/native/java_util_zip_Inflater.c b/libcore/archive/src/main/native/java_util_zip_Inflater.c
index d3a7d7c..4b30d4e 100644
--- a/libcore/archive/src/main/native/java_util_zip_Inflater.c
+++ b/libcore/archive/src/main/native/java_util_zip_Inflater.c
@@ -16,6 +16,7 @@
  */
 
 #include "hy2sie.h"
+#include "zip.h"
 
 #include "zlib.h"
 #include <memory.h>
@@ -24,6 +25,7 @@
 
 #include <fcntl.h>
 
+void throwNewDataFormatException (JNIEnv * env, const char *message);
 
 void zfree PROTOTYPE ((void *opaque, void *address));
 void *zalloc PROTOTYPE ((void *opaque, U_32 items, U_32 size));
@@ -36,27 +38,6 @@
 } gCachedFields;
 
 
-// Contents from Harmony's inflater.h was put here:
-//
-typedef struct JCLZipStream
-{
-  U_8 *inaddr;
-  int inCap;
-  U_8 *dict;
-  z_stream *stream;
-} JCLZipStream;
-
-
-
-/**
-  * Throw java.util.zip.DataFormatException
-  */
-void
-throwNewDataFormatException (JNIEnv * env, const char *message)
-{
-  jniThrowException(env, "java/util/zip/DataFormatException", message);
-}
-
 
 /* Create a new stream . This stream cannot be used until it has been properly initialized. */
 JNIEXPORT jlong JNICALL
@@ -69,6 +50,11 @@
   z_stream *stream;
   int err = 0;
   int wbits = 15;               /*Use MAX for fastest */
+#ifdef HY_ZIP_API
+  VMI_ACCESS_FROM_ENV (env);
+  VMIZipFunctionTable *zipFuncs;
+  zipFuncs = (*VMI)->GetZipFunctions(VMI);
+#endif
 
   /*Allocate mem for wrapped struct */
   jstream = jclmem_allocate_memory (env, sizeof (JCLZipStream));
@@ -104,7 +90,7 @@
     {
       jclmem_free_memory (env, stream);
       jclmem_free_memory (env, jstream);
-      throwNewIllegalArgumentException (env, "");
+      THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException);
       return -1;
     }
 
@@ -134,8 +120,10 @@
   stream->stream->next_in = (Bytef *) baseAddr;
   stream->stream->avail_in = len;
   in = ((*env)->GetPrimitiveArrayCritical (env, buf, 0));
-  if (in == NULL)
+  if (in == NULL) {
+    throwNewOutOfMemoryError(env, "");
     return;
+  }
   memcpy (baseAddr, (in + off), len);
   ((*env)->ReleasePrimitiveArrayCritical (env, buf, in, JNI_ABORT));
   return;
@@ -176,8 +164,6 @@
                                          jbyteArray buf, int off, int len,
                                          jlong handle)
 {
-  PORT_ACCESS_FROM_ENV (env);
-
   jbyte *out;
   JCLZipStream *stream = (JCLZipStream *) ((IDATA) handle);
   jint err = 0;
@@ -192,9 +178,10 @@
   sin = stream->stream->total_in;
   sout = stream->stream->total_out;
   out = ((*env)->GetPrimitiveArrayCritical (env, buf, 0));
-
-  if (out == NULL)
+  if (out == NULL) {
+    throwNewOutOfMemoryError(env, "");
     return -1;
+  }
   stream->stream->next_out = (Bytef *) out + off;
   err = inflate (stream->stream, Z_SYNC_FLUSH);
   ((*env)->ReleasePrimitiveArrayCritical (env, buf, out, 0));
@@ -217,7 +204,7 @@
         }
       else
         {
-          throwNewDataFormatException (env, "");
+          THROW_ZIP_EXCEPTION(env, err, DataFormatException);
           return -1;
         }
     }
@@ -280,7 +267,7 @@
   if (err != Z_OK)
     {
       jclmem_free_memory (env, dBytes);
-      throwNewIllegalArgumentException (env, "");
+      THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException);
       return;
     }
   stream->dict = dBytes;
@@ -297,11 +284,19 @@
   err = inflateReset (stream->stream);
   if (err != Z_OK)
     {
-      throwNewIllegalArgumentException (env, "");
+      THROW_ZIP_EXCEPTION(env, err, IllegalArgumentException);
       return;
     }
 }
 
+/**
+  * Throw java.util.zip.DataFormatException
+  */
+void
+throwNewDataFormatException (JNIEnv * env, const char *message)
+{
+  jniThrowException(env, "java/util/zip/DataFormatException", message);
+}
 
 JNIEXPORT jlong JNICALL
 Java_java_util_zip_Inflater_getTotalOutImpl (JNIEnv * env, jobject recv,
@@ -311,6 +306,7 @@
 
   stream = (JCLZipStream *) ((IDATA) handle);
   return stream->stream->total_out;
+
 }
 
 JNIEXPORT jlong JNICALL
diff --git a/libcore/archive/src/main/native/sieb.c b/libcore/archive/src/main/native/sieb.c
index ab9430b..6881cf6 100644
--- a/libcore/archive/src/main/native/sieb.c
+++ b/libcore/archive/src/main/native/sieb.c
@@ -10,20 +10,6 @@
     jniThrowException(env, "java/lang/OutOfMemoryError", message);
 }
 
-// Throw java.lang.IllegalStateException
-void throwNewIllegalStateException (JNIEnv * env, const char *message)
-{
-  jniThrowException(env, "java/lang/IllegalStateException", message);
-}
-
-// Throw java.lang.IllegalArgumentException
-void throwNewIllegalArgumentException (JNIEnv * env, const char *message)
-{
-  jniThrowException(env, "java/lang/IllegalArgumentException", message);
-}
-
-
-
 void * sieb_malloc (JNIEnv * env, size_t byteCnt) {
     void * adr = malloc(byteCnt);
     if (adr == 0) {
diff --git a/libcore/archive/src/main/native/sieb.h b/libcore/archive/src/main/native/sieb.h
index 536c806..541ad90 100644
--- a/libcore/archive/src/main/native/sieb.h
+++ b/libcore/archive/src/main/native/sieb.h
@@ -7,9 +7,8 @@
 
 
 
-void throwNewOutOfMemoryError (JNIEnv * env, const char *message);
-void throwNewIllegalArgumentException (JNIEnv * env, const char *message);
-void throwNewIllegalStateException (JNIEnv * env, const char *message);
+void throwNewOutOfMemoryError (JNIEnv * env,
+                               const char *message);
 
 
 void * sieb_malloc (JNIEnv * env, size_t byteCnt);
diff --git a/libcore/archive/src/main/native/sub.mk b/libcore/archive/src/main/native/sub.mk
index 047c319..694c185 100644
--- a/libcore/archive/src/main/native/sub.mk
+++ b/libcore/archive/src/main/native/sub.mk
@@ -7,7 +7,8 @@
 	java_util_zip_CRC32.c \
 	java_util_zip_Deflater.c \
 	java_util_zip_Inflater.c \
-  zipalloc.c \
+	zip.c \
+	zipalloc.c \
 	sieb.c
 
 LOCAL_C_INCLUDES += \
diff --git a/libcore/archive/src/main/native/zip.c b/libcore/archive/src/main/native/zip.c
new file mode 100644
index 0000000..3d15d2a
--- /dev/null
+++ b/libcore/archive/src/main/native/zip.c
@@ -0,0 +1,37 @@
+/* 
+ * 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.
+ */
+
+#include "zip.h"
+#include "jni.h"
+
+/**
+  * Throw java.lang.IllegalStateException
+  */
+void
+throwNewIllegalStateException (JNIEnv * env, const char *message)
+{
+  jniThrowException(env, "java/lang/IllegalStateException", message);
+}
+
+/**
+  * Throw java.lang.IllegalArgumentException
+  */
+void
+throwNewIllegalArgumentException (JNIEnv * env, const char *message)
+{
+  jniThrowException(env, "java/lang/IllegalArgumentException", message);
+}
diff --git a/libcore/archive/src/main/native/zip.h b/libcore/archive/src/main/native/zip.h
new file mode 100644
index 0000000..1452073
--- /dev/null
+++ b/libcore/archive/src/main/native/zip.h
@@ -0,0 +1,70 @@
+/* 
+ * 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.
+ */
+
+#if !defined(zip_h)
+#define zip_h
+
+#ifndef HY_ZIP_API
+#include "zipsup.h"
+#else /* HY_ZIP_API */
+#include "vmizip.h"
+#endif /* HY_ZIP_API */
+
+#include "hymutex.h"
+
+typedef struct JCLZipFile
+{
+  struct JCLZipFile *last;
+  struct JCLZipFile *next;
+#ifndef HY_ZIP_API
+  HyZipFile hyZipFile;
+#else
+  VMIZipFile hyZipFile;
+#endif
+} JCLZipFile;
+
+/* Fake JCLZipFile entry. last, next must be in the same position as JCLZipFile */
+typedef struct JCLZipFileLink
+{
+  JCLZipFile *last;
+  JCLZipFile *next;
+  MUTEX mutex;
+} JCLZipFileLink;
+
+// Contents from Harmony's inflater.h was put here:
+//
+typedef struct JCLZipStream
+{
+  U_8 *inaddr;
+  int inCap;
+  U_8 *dict;
+  z_stream *stream;
+} JCLZipStream;
+
+#define THROW_ZIP_EXCEPTION(env, err, type)            \
+  if (err == Z_MEM_ERROR) {                            \
+    throwNewOutOfMemoryError(env, "");                 \
+  } else {                                             \
+    throwNew##type(env, (const char*) zError(err));    \
+  }
+
+void throwNewIllegalStateException PROTOTYPE((JNIEnv* env,
+                                              const char* message));
+void throwNewIllegalArgumentException PROTOTYPE((JNIEnv* env,
+                                                 const char* message));
+
+#endif /* zip_h */
diff --git a/libcore/archive/src/main/native/zipsup.c b/libcore/archive/src/main/native/zipsup.c
index 1bbe51f..22ea7e9 100644
--- a/libcore/archive/src/main/native/zipsup.c
+++ b/libcore/archive/src/main/native/zipsup.c
@@ -1438,7 +1438,7 @@
                   return ZIP_ERR_FILE_CORRUPT;  /* should never happen! */
                 }
               result = zip_establishCache (portLib, zipFile);
-              if (result)
+              if (!result)
                 {
                   /* (silently start operating without a cache if we couldn't make a new one) */
                 }
diff --git a/libcore/archive/src/main/native/zipsup.h b/libcore/archive/src/main/native/zipsup.h
index adc086a..67a2eda 100644
--- a/libcore/archive/src/main/native/zipsup.h
+++ b/libcore/archive/src/main/native/zipsup.h
@@ -34,23 +34,17 @@
 #include "zlib.h"
 
 
-// Contents from Harmony's inflater.h was put here:
-//
-typedef struct JCLZipStream
-{
-  U_8 *inaddr;
-  U_8 *dict;
-  z_stream *stream;
-} JCLZipStream;
-
-
 typedef struct HyZipCachePool HyZipCachePool;
 
 HyZipCachePool *
 zipsup_GetZipCachePool(HyPortLibrary * portLib);
 
 
+#if defined(HY_LOCAL_ZLIB)
+#define HY_ZIP_DLL_NAME "z"
+#else
 #define HY_ZIP_DLL_NAME "hyzlib"
+#endif
 
 #define ZIP_INTERNAL_MAX  80
 #define ZIP_CM_Reduced1  2
@@ -156,18 +150,6 @@
 
 
 
-// Contents from Harmony's zip.h were put in java_util_zip_ZipFile.c
-// and here:
-typedef struct JCLZipFile
-{
-  struct JCLZipFile *last;
-  struct JCLZipFile *next;
-  HyZipFile hyZipFile;
-} JCLZipFile;
-
-
-
-
 #include "hymutex.h"
 extern MUTEX zip_GlobalMutex;
 
diff --git a/libcore/archive/src/test/java-internal/org/apache/harmony/archive/util/UtilTest.java b/libcore/archive/src/test/java-internal/org/apache/harmony/archive/util/UtilTest.java
new file mode 100644
index 0000000..9c28dc2
--- /dev/null
+++ b/libcore/archive/src/test/java-internal/org/apache/harmony/archive/util/UtilTest.java
@@ -0,0 +1,87 @@
+/*
+ *  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.archive.util;
+
+import junit.framework.TestCase;
+
+public class UtilTest extends TestCase {
+    private static final String ASCII_ALPHABET_LC = "abcdefghijklmnopqrstuvwxyz";
+    private static final String ASCII_ALPHABET_UC = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+    private static final byte[] ASCII_ALPHABET_LC_BYTES;
+    private static final byte[] ASCII_ALPHABET_UC_BYTES;
+
+    static {
+        ASCII_ALPHABET_LC_BYTES = new byte[ASCII_ALPHABET_LC.length()];
+        for (int i = 0; i < ASCII_ALPHABET_LC_BYTES.length; i++) {
+            final char c = ASCII_ALPHABET_LC.charAt(i);
+            final byte b = (byte) c;
+            assert ((char) b) == c;
+            ASCII_ALPHABET_LC_BYTES[i] = b;
+        }
+
+        ASCII_ALPHABET_UC_BYTES = new byte[ASCII_ALPHABET_UC.length()];
+        for (int i = 0; i < ASCII_ALPHABET_UC_BYTES.length; i++) {
+            final char c = ASCII_ALPHABET_UC.charAt(i);
+            final byte b = (byte) c;
+            assert ((char) b) == c;
+            ASCII_ALPHABET_UC_BYTES[i] = b;
+        }
+    }
+
+    public void testASCIIIgnoreCaseRegionMatches() {
+        final String s1 = ASCII_ALPHABET_LC;
+        final String s2 = ASCII_ALPHABET_UC;
+        for (int i = 0; i < s1.length(); i++) {
+            assertTrue(Util.ASCIIIgnoreCaseRegionMatches(s1, i, s2, i, s1
+                    .length()
+                    - i));
+        }
+    }
+
+    public void testToASCIIUpperCaseByte() {
+        for (int i = 0; i < ASCII_ALPHABET_LC_BYTES.length; i++) {
+            assertEquals(ASCII_ALPHABET_UC_BYTES[i], Util
+                    .toASCIIUpperCase(ASCII_ALPHABET_LC_BYTES[i]));
+        }
+        for (int i = 0; i < ASCII_ALPHABET_UC_BYTES.length; i++) {
+            assertEquals(ASCII_ALPHABET_UC_BYTES[i], Util
+                    .toASCIIUpperCase(ASCII_ALPHABET_UC_BYTES[i]));
+        }
+    }
+
+    public void testToASCIIUpperCaseChar() {
+        for (int i = 0; i < ASCII_ALPHABET_LC.length(); i++) {
+            assertEquals(ASCII_ALPHABET_UC.charAt(i), Util
+                    .toASCIIUpperCase(ASCII_ALPHABET_LC.charAt(i)));
+        }
+        for (int i = 0; i < ASCII_ALPHABET_UC.length(); i++) {
+            assertEquals(ASCII_ALPHABET_UC.charAt(i), Util
+                    .toASCIIUpperCase(ASCII_ALPHABET_UC.charAt(i)));
+        }
+    }
+
+    public void testEqualsIgnoreCaseByteArrayByteArray() {
+        assertTrue(Util.equalsIgnoreCase(ASCII_ALPHABET_LC_BYTES,
+                ASCII_ALPHABET_LC_BYTES));
+        assertTrue(Util.equalsIgnoreCase(ASCII_ALPHABET_LC_BYTES,
+                ASCII_ALPHABET_UC_BYTES));
+        assertTrue(Util.equalsIgnoreCase(ASCII_ALPHABET_UC_BYTES,
+                ASCII_ALPHABET_UC_BYTES));
+    }
+
+}
diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java
index 0a8b037..0b3d2cf 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/AttributesTest.java
@@ -421,6 +421,27 @@
         assertNull(attribute.get(name));
     }
 
+    /**
+     * @tests java.util.jar.Attributes.hashCode()
+     */
+    @TestTargetNew(
+            level = TestLevel.PARTIAL_COMPLETE,
+            notes = "",
+            method = "hashCode",
+            args = {}
+    )
+    public void test_hashCode_consistent_with_map() {
+        MockAttributes mockAttr = new MockAttributes();
+        mockAttr.putValue("1", "one");
+        assertEquals(mockAttr.getMap().hashCode(), mockAttr.hashCode());
+    }
+
+    private static class MockAttributes extends Attributes {
+        public Map<Object, Object> getMap() {
+            return map;
+        }
+    }
+
     @TestTargetNew(
         level = TestLevel.COMPLETE,
         notes = "",
@@ -470,7 +491,7 @@
     }
 
     @TestTargetNew(
-        level = TestLevel.COMPLETE,
+        level = TestLevel.PARTIAL_COMPLETE,
         notes = "",
         method = "hashCode",
         args = {}
diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java
index 40eff3b..90144be 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarEntryTest.java
@@ -72,6 +72,29 @@
     }
 
     /**
+     * @throws IOException
+     * @tests java.util.jar.JarEntry#JarEntry(java.util.jar.JarEntry)
+     */
+    @TestTargetNew(
+            level = TestLevel.PARTIAL_COMPLETE,
+            notes = "",
+            method = "JarEntry",
+            args = {java.util.jar.JarEntry.class}
+    )
+    public void test_ConstructorLjava_util_jar_JarEntry_on_null() throws IOException {
+        JarEntry newJarEntry = new JarEntry(jarFile.getJarEntry(entryName));
+        assertNotNull(newJarEntry);
+
+        jarEntry = null;
+        try {
+            newJarEntry = new JarEntry(jarEntry);
+            fail("Should throw NullPointerException");
+        } catch (NullPointerException e) {
+            // Expected
+        }
+    }
+
+    /**
      * @tests java.util.jar.JarEntry#JarEntry(java.util.zip.ZipEntry)
      */
     @TestTargetNew(
@@ -163,10 +186,21 @@
         JarEntry jarEntry2 = jarFile.getJarEntry("Test.class");
         InputStream in = jarFile.getInputStream(jarEntry1);
         byte[] buffer = new byte[1024];
+        // BEGIN android-changed
+        // the certificates are non-null too early and in.available() fails
+        // while (in.available() > 0) {
+        //     assertNull("getCertificates() should be null until the entry is read",
+        //             jarEntry1.getCertificates());
+        //     assertNull(jarEntry2.getCertificates());
+        //     in.read(buffer);
+        // }
         while (in.read(buffer) >= 0);
         in.close();
+        // END android-changed
+        assertEquals("the file is fully read", -1, in.read());
         assertNotNull(jarEntry1.getCertificates());
         assertNotNull(jarEntry2.getCertificates());
+        in.close();
     }
 
     /**
@@ -187,8 +221,14 @@
         InputStream in = jarFile.getInputStream(jarEntry);
         byte[] buffer = new byte[1024];
         while (in.available() > 0) {
+            // BEGIN android-changed
+            // the code signers are non-null too early
+            // assertNull("getCodeSigners() should be null until the entry is read",
+            //         jarEntry.getCodeSigners());
+            // END android-changed
             in.read(buffer);
         }
+        assertEquals("the file is fully read", -1, in.read());
         CodeSigner[] codeSigners = jarEntry.getCodeSigners();
         assertEquals(2, codeSigners.length);
         List<?> certs_bob = codeSigners[0].getSignerCertPath()
@@ -240,7 +280,7 @@
     }
 
     @TestTargetNew(
-        level = TestLevel.COMPLETE,
+        level = TestLevel.PARTIAL_COMPLETE,
         notes = "",
         method = "JarEntry",
         args = {java.util.jar.JarEntry.class}
diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java
index 720f78d..96321a4 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarFileTest.java
@@ -14,13 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.harmony.archive.tests.java.util.jar;
 
 
 import dalvik.annotation.AndroidOnly;
 import dalvik.annotation.TestTargetClass;
-import dalvik.annotation.TestTargets;
 import dalvik.annotation.TestLevel;
 import dalvik.annotation.TestTargetNew;
 
@@ -73,13 +71,17 @@
     private final String jarName3 = "hyts_manifest1.jar";
 
     private final String jarName4 = "hyts_signed.jar";
-    
+
     private final String jarName5 = "hyts_signed_inc.jar";
 
+    private final String integrateJar = "Integrate.jar";
+
     private final String entryName = "foo/bar/A.class";
 
     private final String entryName3 = "coucou/FileAccess.class";
 
+    private final String integrateJarEntry = "Test.class";
+
     private File resources;
 
     // custom security manager
@@ -102,7 +104,7 @@
      * @tests java.util.jar.JarFile#JarFile(java.io.File)
      */
     @TestTargetNew(
-        level = TestLevel.COMPLETE,
+        level = TestLevel.PARTIAL_COMPLETE,
         notes = "",
         method = "JarFile",
         args = {java.io.File.class}
@@ -300,6 +302,27 @@
     }
 
     /**
+     * Constructs JarFile object.
+     *
+     * @tests java.util.jar.JarFile#JarFile(java.io.File)
+     * @tests java.util.jar.JarFile#JarFile(java.lang.String)
+     */
+    @TestTargetNew(
+            level = TestLevel.PARTIAL_COMPLETE,
+            notes = "",
+            method = "JarFile",
+            args = {java.io.File.class}
+    )
+    public void testConstructor_file() throws IOException {
+        File f = new File(resources, jarName);
+        Support_Resources.copyFile(resources, null, jarName);
+        assertTrue(new JarFile(f).getEntry(entryName).getName().equals(
+                entryName));
+        assertTrue(new JarFile(f.getPath()).getEntry(entryName).getName()
+                .equals(entryName));
+    }
+
+    /**
      * @tests java.util.jar.JarFile#entries()
      */
     @TestTargetNew(
@@ -316,11 +339,11 @@
         Support_Resources.copyFile(resources, null, jarName);
         JarFile jarFile = new JarFile(new File(resources, jarName));
         Enumeration<JarEntry> e = jarFile.entries();
-        int i = 0;
-        while (e.hasMoreElements()) {
-            i++;
+        int i;
+        for (i = 0; e.hasMoreElements(); i++) {
             e.nextElement();
         }
+        assertEquals(jarFile.size(), i);
         jarFile.close();
         assertEquals(6, i);
     }
@@ -336,24 +359,20 @@
         JarFile jarFile = new JarFile(new File(resources, jarName));
         Enumeration<JarEntry> enumeration = jarFile.entries();
         jarFile.close();
-        boolean pass = false;
         try {
             enumeration.hasMoreElements();
+            fail("hasMoreElements() did not detect a closed jar file");
         } catch (IllegalStateException e) {
-            pass = true;
         }
-        assertTrue("hasMoreElements did not detect closed jar file", pass);
         Support_Resources.copyFile(resources, null, jarName);
         jarFile = new JarFile(new File(resources, jarName));
         enumeration = jarFile.entries();
         jarFile.close();
-        pass = false;
         try {
             enumeration.nextElement();
+            fail("nextElement() did not detect closed jar file");
         } catch (IllegalStateException e) {
-            pass = true;
         }
-        assertTrue("nextElement did not detect closed jar file", pass);
     }
 
     /**
@@ -361,7 +380,7 @@
      * @tests java.util.jar.JarFile#getJarEntry(java.lang.String)
      */
     @TestTargetNew(
-        level = TestLevel.COMPLETE,
+        level = TestLevel.PARTIAL_COMPLETE,
         notes = "",
         method = "getEntry",
         args = {java.lang.String.class}
@@ -442,6 +461,92 @@
         }
     }
 
+
+    /**
+     * @tests java.util.jar.JarFile#getJarEntry(java.lang.String)
+     */
+    @TestTargetNew(
+            level = TestLevel.PARTIAL_COMPLETE,
+            notes = "",
+            method = "getEntry",
+            args = {java.lang.String.class}
+    )
+    public void testGetJarEntry() throws Exception {
+        Support_Resources.copyFile(resources, null, jarName);
+        JarFile jarFile = new JarFile(new File(resources, jarName));
+        assertEquals("Error in returned entry", 311, jarFile.getEntry(
+                entryName).getSize());
+        jarFile.close();
+
+        // tests for signed jars
+        // test all signed jars in the /Testres/Internal/SignedJars directory
+        String jarDirUrl = Support_Resources
+                .getResourceURL("/../internalres/signedjars");
+        Vector<String> signedJars = new Vector<String>();
+        try {
+            InputStream is = new URL(jarDirUrl + "/jarlist.txt").openStream();
+            while (is.available() > 0) {
+                StringBuilder linebuff = new StringBuilder(80); // Typical line
+                // length
+                done: while (true) {
+                    int nextByte = is.read();
+                    switch (nextByte) {
+                        case -1:
+                            break done;
+                        case (byte) '\r':
+                            if (linebuff.length() == 0) {
+                                // ignore
+                            }
+                            break done;
+                        case (byte) '\n':
+                            if (linebuff.length() == 0) {
+                                // ignore
+                            }
+                            break done;
+                        default:
+                            linebuff.append((char) nextByte);
+                    }
+                }
+                if (linebuff.length() == 0) {
+                    break;
+                }
+                String line = linebuff.toString();
+                signedJars.add(line);
+            }
+            is.close();
+        } catch (IOException e) {
+            // no list of jars found
+        }
+
+        for (int i = 0; i < signedJars.size(); i++) {
+            String jarName = signedJars.get(i);
+            try {
+                File file = Support_Resources.getExternalLocalFile(jarDirUrl
+                        + "/" + jarName);
+                jarFile = new JarFile(file, true);
+                boolean foundCerts = false;
+                Enumeration<JarEntry> e = jarFile.entries();
+                while (e.hasMoreElements()) {
+                    JarEntry entry = e.nextElement();
+                    InputStream is = jarFile.getInputStream(entry);
+                    is.skip(100000);
+                    is.close();
+                    Certificate[] certs = entry.getCertificates();
+                    if (certs != null && certs.length > 0) {
+                        foundCerts = true;
+                        break;
+                    }
+                }
+                assertTrue(
+                        "No certificates found during signed jar test for jar \""
+                                + jarName + "\"", foundCerts);
+            } catch (IOException e) {
+                fail("Exception during signed jar test for jar \"" + jarName
+                        + "\": " + e.toString());
+            }
+        }
+    }
+
     /**
      * @tests java.util.jar.JarFile#getManifest()
      */
@@ -540,85 +645,6 @@
     }
 
     /**
-     * @throws IOException
-     * @tests java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry)
-     */
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        notes = "",
-        method = "getInputStream",
-        args = {java.util.zip.ZipEntry.class}
-    )
-    public void test_getInputStreamLjava_util_jar_JarEntry() throws IOException {
-        File localFile = null;
-        try {
-            Support_Resources.copyFile(resources, null, jarName);
-            localFile = new File(resources, jarName);
-        } catch (Exception e) {
-            fail("Failed to create local file: " + e);
-        }
-
-        byte[] b = new byte[1024];
-        try {
-            JarFile jf = new JarFile(localFile);
-            java.io.InputStream is = jf.getInputStream(jf.getEntry(entryName));
-            // BEGIN android-removed
-            // jf.close();
-            // END android-removed
-            assertTrue("Returned invalid stream", is.available() > 0);
-            int r = is.read(b, 0, 1024);
-            is.close();
-            StringBuffer sb = new StringBuffer(r);
-            for (int i = 0; i < r; i++) {
-                sb.append((char) (b[i] & 0xff));
-            }
-            String contents = sb.toString();
-            assertTrue("Incorrect stream read", contents.indexOf("bar") > 0);
-            // BEGIN android-added
-            jf.close();
-            // END android-added
-        } catch (Exception e) {
-            fail("Exception during test: " + e.toString());
-        }
-
-        try {
-            JarFile jf = new JarFile(localFile);
-            InputStream in = jf.getInputStream(new JarEntry("invalid"));
-            assertNull("Got stream for non-existent entry", in);
-        } catch (Exception e) {
-            fail("Exception during test 2: " + e);
-        }
-
-        try {
-            Support_Resources.copyFile(resources, null, jarName);
-            File signedFile = new File(resources, jarName);
-            JarFile jf = new JarFile(signedFile);
-            JarEntry jre = new JarEntry("foo/bar/A.class");
-            jf.getInputStream(jre);
-            // InputStream returned in any way, exception can be thrown in case
-            // of reading from this stream only.
-            // fail("Should throw ZipException");
-        } catch (ZipException ee) {
-            // expected
-        }
-
-        try {
-            Support_Resources.copyFile(resources, null, jarName);
-            File signedFile = new File(resources, jarName);
-            JarFile jf = new JarFile(signedFile);
-            JarEntry jre = new JarEntry("foo/bar/A.class");
-            jf.close();
-            jf.getInputStream(jre);
-            // InputStream returned in any way, exception can be thrown in case
-            // of reading from this stream only.
-            // The same for IOException
-            fail("Should throw IllegalStateException");
-        } catch (IllegalStateException ee) {
-            // expected
-        }
-    }
-
-    /**
      * @tests java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry)
      */
     @TestTargetNew(
@@ -660,7 +686,7 @@
         } catch (Exception e) {
             fail("Exception during test 4: " + e);
         }
-        
+
         try {
             JarFile jar = new JarFile(signedFile);
             JarEntry entry = new JarEntry(entryName3);
@@ -682,7 +708,7 @@
         } catch (Exception e) {
             fail("Failed to create local file 5: " + e);
         }
-        
+
         try {
             JarFile jar = new JarFile(signedFile);
             JarEntry entry = new JarEntry(entryName3);
@@ -732,7 +758,37 @@
         Enumeration<JarEntry> entries = jarFile.entries();
         while (entries.hasMoreElements()) {
             ZipEntry zipEntry = entries.nextElement();
-            jarFile.getInputStream(zipEntry);
+            jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE);
+        }
+    }
+
+    /**
+     * The jar is intact, but the entry object is modified.
+     */
+    @TestTargetNew(
+            level = TestLevel.PARTIAL_COMPLETE,
+            notes = "",
+            method = "getInputStream",
+            args = {ZipEntry.class}
+    )
+    public void testJarVerificationModifiedEntry() throws IOException {
+        Support_Resources.copyFile(resources, null, integrateJar);
+        File f = new File(resources, integrateJar);
+
+        JarFile jarFile = new JarFile(f);
+        ZipEntry zipEntry = jarFile.getJarEntry(integrateJarEntry);
+        zipEntry.setSize(zipEntry.getSize() + 1);
+        jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE);
+
+        jarFile = new JarFile(f);
+        zipEntry = jarFile.getJarEntry(integrateJarEntry);
+        zipEntry.setSize(zipEntry.getSize() - 1);
+        try {
+            //jarFile.getInputStream(zipEntry).skip(Long.MAX_VALUE);
+            jarFile.getInputStream(zipEntry).read(new byte[5000], 0, 5000);
+            fail("SecurityException expected");
+        } catch (SecurityException e) {
+            // desired
         }
     }
 
@@ -781,7 +837,6 @@
         Enumeration<JarEntry> entries = jarFile.entries();
         int count = 0;
         while (entries.hasMoreElements()) {
-
             ZipEntry zipEntry = entries.nextElement();
             jarFile.getInputStream(zipEntry);
             count++;
@@ -818,7 +873,7 @@
             while (in.available() > 0) {
                 in.read(buffer);
             }
-            fail("should throw Security Exception");
+            fail("SecurityException expected");
         } catch (SecurityException e) {
             // desired
         }
@@ -827,7 +882,7 @@
     /*
      * In the Modified.jar, the main attributes of META-INF/MANIFEST.MF is
      * tampered manually. Hence the RI 5.0 JarFile.getInputStream of any
-     * JarEntry will throw security exception, but the apache harmony will not.
+     * JarEntry will throw security exception.
      */
     @TestTargetNew(
         level = TestLevel.PARTIAL_COMPLETE,
@@ -846,7 +901,7 @@
             ZipEntry zipEntry = entries.nextElement();
             try {
                 jarFile.getInputStream(zipEntry);
-                fail("should throw Security Exception");
+                fail("SecurityException expected");
             } catch (SecurityException e) {
                 // desired
             }
@@ -927,4 +982,83 @@
 
         // Can not check IOException
     }
+
+    /**
+     * @throws IOException
+     * @tests java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry)
+     */
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "",
+            method = "getInputStream",
+            args = {java.util.zip.ZipEntry.class}
+    )
+    public void test_getInputStreamLjava_util_jar_JarEntry() throws IOException {
+        File localFile = null;
+        try {
+            Support_Resources.copyFile(resources, null, jarName);
+            localFile = new File(resources, jarName);
+        } catch (Exception e) {
+            fail("Failed to create local file: " + e);
+        }
+
+        byte[] b = new byte[1024];
+        try {
+            JarFile jf = new JarFile(localFile);
+            java.io.InputStream is = jf.getInputStream(jf.getEntry(entryName));
+            // BEGIN android-removed
+            // jf.close();
+            // END android-removed
+            assertTrue("Returned invalid stream", is.available() > 0);
+            int r = is.read(b, 0, 1024);
+            is.close();
+            StringBuffer sb = new StringBuffer(r);
+            for (int i = 0; i < r; i++) {
+                sb.append((char) (b[i] & 0xff));
+            }
+            String contents = sb.toString();
+            assertTrue("Incorrect stream read", contents.indexOf("bar") > 0);
+            // BEGIN android-added
+            jf.close();
+            // END android-added
+        } catch (Exception e) {
+            fail("Exception during test: " + e.toString());
+        }
+
+        try {
+            JarFile jf = new JarFile(localFile);
+            InputStream in = jf.getInputStream(new JarEntry("invalid"));
+            assertNull("Got stream for non-existent entry", in);
+        } catch (Exception e) {
+            fail("Exception during test 2: " + e);
+        }
+
+        try {
+            Support_Resources.copyFile(resources, null, jarName);
+            File signedFile = new File(resources, jarName);
+            JarFile jf = new JarFile(signedFile);
+            JarEntry jre = new JarEntry("foo/bar/A.class");
+            jf.getInputStream(jre);
+            // InputStream returned in any way, exception can be thrown in case
+            // of reading from this stream only.
+            // fail("Should throw ZipException");
+        } catch (ZipException ee) {
+            // expected
+        }
+
+        try {
+            Support_Resources.copyFile(resources, null, jarName);
+            File signedFile = new File(resources, jarName);
+            JarFile jf = new JarFile(signedFile);
+            JarEntry jre = new JarEntry("foo/bar/A.class");
+            jf.close();
+            jf.getInputStream(jre);
+            // InputStream returned in any way, exception can be thrown in case
+            // of reading from this stream only.
+            // The same for IOException
+            fail("Should throw IllegalStateException");
+        } catch (IllegalStateException ee) {
+            // expected
+        }
+    }
 }
diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java
index e652137..acdad71 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/JarOutputStreamTest.java
@@ -48,7 +48,7 @@
         method = "putNextEntry",
         args = {java.util.zip.ZipEntry.class}
     )
-    public void test_putNextEntryLjava_util_zip_ZipEntry() {
+    public void test_putNextEntryLjava_util_zip_ZipEntry() throws Exception {
         // testClass file`s actual extension is .class, since having .class
         // extension files in source dir causes
         // problems on eclipse, the extension is changed into .ser or it can be
@@ -76,35 +76,30 @@
             File outputJar = null;
             JarOutputStream jout = null;
 
-            try {
-                // open the output jarfile
-                outputJar = File.createTempFile("hyts_", ".jar");
-                jout = new JarOutputStream(new FileOutputStream(outputJar),
-                        newman);
-                jout.putNextEntry(new JarEntry(entryName));
-            } catch (Exception e) {
-                fail("Error creating JarOutputStream: " + e);
-            }
+            // open the output jarfile
+            outputJar = File.createTempFile("hyts_", ".jar");
+            jout = new JarOutputStream(new FileOutputStream(outputJar),
+                    newman);
+            jout.putNextEntry(new JarEntry(entryName));
+
             File resources = Support_Resources.createTempFolder();
-            try {
-                // read in the class file, and output it to the jar
-                Support_Resources.copyFile(resources, null, testClass);
-                URL jarURL = new URL((new File(resources, testClass)).toURL()
-                        .toString());
-                InputStream jis = jarURL.openStream();
 
-                byte[] bytes = new byte[1024];
-                int len;
-                while ((len = jis.read(bytes)) != -1) {
-                    jout.write(bytes, 0, len);
-                }
+            // read in the class file, and output it to the jar
+            Support_Resources.copyFile(resources, null, testClass);
+            URL jarURL = new URL((new File(resources, testClass)).toURL()
+                    .toString());
+            InputStream jis = jarURL.openStream();
 
-                jout.flush();
-                jout.close();
-                jis.close();
-            } catch (Exception e) {
-                fail("Error writing JAR file for testing: " + e);
+            byte[] bytes = new byte[1024];
+            int len;
+            while ((len = jis.read(bytes)) != -1) {
+                jout.write(bytes, 0, len);
             }
+
+            jout.flush();
+            jout.close();
+            jis.close();
+
             String res = null;
             // set up the VM parameters
             String[] args = new String[2];
diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java
index 57e4744..42b2543 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/jar/ManifestTest.java
@@ -14,12 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.harmony.archive.tests.java.util.jar;
 
-import dalvik.annotation.TestTargetClass;
-import dalvik.annotation.TestTargets;
+import dalvik.annotation.KnownFailure;
 import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
 import dalvik.annotation.TestTargetNew;
 
 import java.io.ByteArrayInputStream;
@@ -28,14 +27,15 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.MalformedURLException;
 import java.net.URL;
+import java.net.MalformedURLException;
 import java.util.Map;
 import java.util.jar.Attributes;
 import java.util.jar.JarFile;
 import java.util.jar.Manifest;
 
 import junit.framework.TestCase;
+
 import tests.support.resource.Support_Resources;
 
 @TestTargetClass(Manifest.class)
@@ -49,6 +49,10 @@
 
     private Manifest m2;
 
+    private final String ATT_ENTRY_NAME = "HasAttributes.txt";
+
+    private final String MANIFEST_NAME = "manifest/hyts_MANIFEST.MF";
+
     private File resources;
 
     @Override
@@ -68,6 +72,19 @@
         }
     }
 
+    private Manifest getManifest(String fileName) {
+        try {
+            Support_Resources.copyFile(resources, null, fileName);
+            JarFile jarFile = new JarFile(new File(resources, fileName));
+            Manifest m = jarFile.getManifest();
+            jarFile.close();
+            return m;
+        } catch (Exception e) {
+            fail("Exception during setup: " + e.toString());
+            return null;
+        }
+    }
+
     /**
      * @tests java.util.jar.Manifest#Manifest()
      */
@@ -87,245 +104,29 @@
     }
 
     /**
-     * @tests java.util.jar.Manifest#Manifest(java.io.InputStream)
+     * @tests java.util.jar.Manifest#Manifest(java.util.jar.Manifest)
      */
     @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        notes = "IOException checking missed.",
-        method = "Manifest",
-        args = {java.io.InputStream.class}
+            level = TestLevel.PARTIAL_COMPLETE,
+            notes = "",
+            method = "Manifest",
+            args = {java.util.jar.Manifest.class}
     )
-    public void test_ConstructorLjava_io_InputStream() {
-        // Test for method java.util.jar.Manifest(java.io.InputStream)
-        /*
-         * ByteArrayOutputStream baos = new ByteArrayOutputStream();
-         * m2.write(baos); InputSteam is = new ByteArrayInputStream
-         * (baos.toByteArray()); Manifest myManifest = new Manifest (is);
-         * assertTrue("Manifests should be equal", myManifest.equals(m2));
-         */
-
-        Manifest manifest = null;
-        InputStream is = null;
-        try {
-            is = new URL(Support_Resources.getURL("manifest/hyts_MANIFEST.MF"))
-                    .openStream();
-        } catch (MalformedURLException e1) {
-            fail("Failed to create InputStream object");
-        } catch (IOException e1) {
-            fail("Failed to create InputStream object");
-        }
-        try {
-            manifest = new Manifest(is);
-        } catch (MalformedURLException e) {
-            fail("Malformed URL");
-        } catch (IOException e) {
-            fail("IOException");
-        }
-        Attributes main = manifest.getMainAttributes();
-        assertEquals("Bundle-Name not correct", "ClientSupport", main
-                .getValue("Bundle-Name"));
-        assertEquals(
-                "Bundle-Description not correct",
-
-                "Provides SessionService, AuthenticationService. Extends RegistryService.",
-                main.getValue("Bundle-Description"));
-        assertEquals("Bundle-Activator not correct",
-                "com.ibm.ive.eccomm.client.support.ClientSupportActivator",
-                main.getValue("Bundle-Activator"));
-        assertEquals(
-                "Import-Package not correct",
-
-                "com.ibm.ive.eccomm.client.services.log,com.ibm.ive.eccomm.client.services.registry,com.ibm.ive.eccomm.service.registry; specification-version=1.0.0,com.ibm.ive.eccomm.service.session; specification-version=1.0.0,com.ibm.ive.eccomm.service.framework; specification-version=1.2.0,org.osgi.framework; specification-version=1.0.0,org.osgi.service.log; specification-version=1.0.0,com.ibm.ive.eccomm.flash; specification-version=1.2.0,com.ibm.ive.eccomm.client.xml,com.ibm.ive.eccomm.client.http.common,com.ibm.ive.eccomm.client.http.client",
-                main.getValue("Import-Package"));
-        assertEquals(
-                "Import-Service not correct",
-
-                "org.osgi.service.log.LogReaderServiceorg.osgi.service.log.LogService,com.ibm.ive.eccomm.service.registry.RegistryService",
-                main.getValue("Import-Service"));
-        assertEquals(
-                "Export-Package not correct",
-
-                "com.ibm.ive.eccomm.client.services.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.service.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.common; specification-version=1.0.0,com.ibm.ive.eccomm.client.services.registry.store; specification-version=1.0.0",
-                main.getValue("Export-Package"));
-        assertEquals(
-                "Export-Service not correct",
-
-                "com.ibm.ive.eccomm.service.authentication.AuthenticationService,com.ibm.ive.eccomm.service.session.SessionService",
-                main.getValue("Export-Service"));
-        assertEquals("Bundle-Vendor not correct", "IBM", main
-                .getValue("Bundle-Vendor"));
-        assertEquals("Bundle-Version not correct", "1.2.0", main
-                .getValue("Bundle-Version"));
-        try {
-            is.close();
-        } catch (IOException e1) {
-            fail("Failed to close InputStream object");
-        }
-        try {
-            manifest = new Manifest(is);
-            fail("IOException expected");
-        } catch (MalformedURLException e) {
-            fail("IOException expected");
-        } catch (IOException e) {
-            // expected
-        }
-    }
-
-    /**
-     * @tests java.util.jar.Manifest#clear()
-     */
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        notes = "",
-        method = "clear",
-        args = {}
-    )
-    public void test_clear() {
-        // Test for method void java.util.jar.Manifest.clear()
-        m2.clear();
-        assertTrue("Should have no entries", m2.getEntries().isEmpty());
-        assertTrue("Should have no main attributes", m2.getMainAttributes()
-                .isEmpty());
-    }
-
-    /**
-     * @tests java.util.jar.Manifest#getAttributes(java.lang.String)
-     */
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        notes = "",
-        method = "getAttributes",
-        args = {java.lang.String.class}
-    )
-    public void test_getAttributesLjava_lang_String() {
-        // Test for method java.util.jar.Attributes
-        // java.util.jar.Manifest.getAttributes(java.lang.String)
-        assertNull("Should not exist", m2.getAttributes("Doesn't Exist"));
-        assertEquals("Should exist", "OK", m2
-                .getAttributes("HasAttributes.txt").get(
-                        new Attributes.Name("MyAttribute")));
-    }
-
-    /**
-     * @tests java.util.jar.Manifest#getEntries()
-     */
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        notes = "",
-        method = "getEntries",
-        args = {}
-    )
-    public void test_getEntries() {
-        // Test for method java.util.Map java.util.jar.Manifest.getEntries()
-        Map<String, Attributes> myMap = m2.getEntries();
-        assertNull("Shouldn't exist", myMap.get("Doesn't exist"));
-        assertEquals("Should exist", "OK", myMap.get("HasAttributes.txt").get(
-                new Attributes.Name("MyAttribute")));
-
-    }
-
-    /**
-     * @tests java.util.jar.Manifest#getMainAttributes()
-     */
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        notes = "",
-        method = "getMainAttributes",
-        args = {}
-    )
-    public void test_getMainAttributes() {
-        // Test for method java.util.jar.Attributes
-        // java.util.jar.Manifest.getMainAttributes()
-        Attributes a = m.getMainAttributes();
-        assertEquals("Manifest_Version should return 1.0", "1.0", a
-                .get(Attributes.Name.MANIFEST_VERSION));
-    }
-
-    /**
-     * @tests {@link java.util.jar.Manifest#read(java.io.InputStream)
-     */
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        notes = "",
-        method = "read",
-        args = {java.io.InputStream.class}
-    )
-    public void test_readLjava_io_InputStream() {
-        // Regression for HARMONY-89
-        InputStream is = new InputStreamImpl();
-        try {
-            new Manifest().read(is);
-            fail("Assert 0: Should have thrown IOException");
-        } catch (IOException e) {
-            // expected
-        }
-
-        Manifest manifest = new Manifest();
-        try {
-            manifest.read(new URL(Support_Resources
-                    .getURL("manifest/hyts_MANIFEST.MF")).openStream());
-        } catch (MalformedURLException e) {
-            fail("Can nor read manifest");
-        } catch (IOException e) {
-            fail("Can nor read manifest");
-        }
-        Attributes main = manifest.getMainAttributes();
-        assertEquals("Bundle-Name not correct", "ClientSupport", main
-                .getValue("Bundle-Name"));
-        assertEquals(
-                "Bundle-Description not correct",
-
-                "Provides SessionService, AuthenticationService. Extends RegistryService.",
-                main.getValue("Bundle-Description"));
-        assertEquals("Bundle-Activator not correct",
-                "com.ibm.ive.eccomm.client.support.ClientSupportActivator",
-                main.getValue("Bundle-Activator"));
-        assertEquals(
-                "Import-Package not correct",
-
-                "com.ibm.ive.eccomm.client.services.log,com.ibm.ive.eccomm.client.services.registry,com.ibm.ive.eccomm.service.registry; specification-version=1.0.0,com.ibm.ive.eccomm.service.session; specification-version=1.0.0,com.ibm.ive.eccomm.service.framework; specification-version=1.2.0,org.osgi.framework; specification-version=1.0.0,org.osgi.service.log; specification-version=1.0.0,com.ibm.ive.eccomm.flash; specification-version=1.2.0,com.ibm.ive.eccomm.client.xml,com.ibm.ive.eccomm.client.http.common,com.ibm.ive.eccomm.client.http.client",
-                main.getValue("Import-Package"));
-        assertEquals(
-                "Import-Service not correct",
-
-                "org.osgi.service.log.LogReaderServiceorg.osgi.service.log.LogService,com.ibm.ive.eccomm.service.registry.RegistryService",
-                main.getValue("Import-Service"));
-        assertEquals(
-                "Export-Package not correct",
-
-                "com.ibm.ive.eccomm.client.services.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.service.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.common; specification-version=1.0.0,com.ibm.ive.eccomm.client.services.registry.store; specification-version=1.0.0",
-                main.getValue("Export-Package"));
-        assertEquals(
-                "Export-Service not correct",
-
-                "com.ibm.ive.eccomm.service.authentication.AuthenticationService,com.ibm.ive.eccomm.service.session.SessionService",
-                main.getValue("Export-Service"));
-        assertEquals("Bundle-Vendor not correct", "IBM", main
-                .getValue("Bundle-Vendor"));
-        assertEquals("Bundle-Version not correct", "1.2.0", main
-                .getValue("Bundle-Version"));
-    }
-
-    // helper class
-    class InputStreamImpl extends InputStream {
-        public InputStreamImpl() {
-            super();
-        }
-
-        @Override
-        public int read() {
-            return 0;
-        }
+    public void testCopyingConstructor() throws IOException {
+        Manifest firstManifest = new Manifest(new URL(Support_Resources
+                .getURL(MANIFEST_NAME)).openStream());
+        Manifest secondManifest = new Manifest(firstManifest);
+        assertEquals(firstManifest, secondManifest);
     }
 
     /**
      * @tests java.util.jar.Manifest#Manifest(Manifest)
      */
     @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        notes = "",
-        method = "Manifest",
-        args = {java.util.jar.Manifest.class}
+            level = TestLevel.PARTIAL_COMPLETE,
+            notes = "",
+            method = "Manifest",
+            args = {java.util.jar.Manifest.class}
     )
     public void test_ConstructorLjava_util_jar_Manifest() {
         // Test for method java.util.jar.Manifest()
@@ -338,13 +139,101 @@
         assertEquals(emptyClone, emptyManifest.clone());
     }
 
+    private void assertAttribute(Attributes attr, String name, String value) {
+        assertEquals("Incorrect " + name, value, attr.getValue(name));
+    }
+
+    private void checkManifest(Manifest manifest) {
+        Attributes main = manifest.getMainAttributes();
+        assertAttribute(main, "Bundle-Name", "ClientSupport");
+        assertAttribute(main, "Bundle-Description",
+                "Provides SessionService, AuthenticationService. Extends RegistryService.");
+        assertAttribute(main, "Bundle-Activator",
+                "com.ibm.ive.eccomm.client.support.ClientSupportActivator");
+        assertAttribute(
+                main,
+                "Import-Package",
+                "com.ibm.ive.eccomm.client.services.log,com.ibm.ive.eccomm.client.services.registry,com.ibm.ive.eccomm.service.registry; specification-version=1.0.0,com.ibm.ive.eccomm.service.session; specification-version=1.0.0,com.ibm.ive.eccomm.service.framework; specification-version=1.2.0,org.osgi.framework; specification-version=1.0.0,org.osgi.service.log; specification-version=1.0.0,com.ibm.ive.eccomm.flash; specification-version=1.2.0,com.ibm.ive.eccomm.client.xml,com.ibm.ive.eccomm.client.http.common,com.ibm.ive.eccomm.client.http.client");
+        assertAttribute(
+                main,
+                "Import-Service",
+                "org.osgi.service.log.LogReaderServiceorg.osgi.service.log.LogService,com.ibm.ive.eccomm.service.registry.RegistryService");
+        assertAttribute(
+                main,
+                "Export-Package",
+                "com.ibm.ive.eccomm.client.services.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.service.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.common; specification-version=1.0.0,com.ibm.ive.eccomm.client.services.registry.store; specification-version=1.0.0");
+        assertAttribute(
+                main,
+                "Export-Service",
+                "com.ibm.ive.eccomm.service.authentication.AuthenticationService,com.ibm.ive.eccomm.service.session.SessionService");
+        assertAttribute(main, "Bundle-Vendor", "IBM");
+        assertAttribute(main, "Bundle-Version", "1.2.0");
+    }
+
+    /**
+     * @tests java.util.jar.Manifest#Manifest(java.io.InputStream)
+     */
     @TestTargetNew(
         level = TestLevel.COMPLETE,
-        notes = "",
-        method = "clone",
-        args = {}
+        notes = "IOException checking missed.",
+        method = "Manifest",
+        args = {java.io.InputStream.class}
     )
-    public void test_clone() {
+    public void test_ConstructorLjava_io_InputStream() throws IOException {
+        Manifest m = getManifest(attJarName);
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        m.write(baos);
+        InputStream is = new ByteArrayInputStream(baos.toByteArray());
+        Manifest mCopy = new Manifest(is);
+        assertEquals(m, mCopy);
+
+        Manifest manifest = new Manifest(new URL(Support_Resources
+                .getURL(MANIFEST_NAME)).openStream());
+        checkManifest(manifest);
+
+        // regression test for HARMONY-5424
+        String manifestContent = "Manifest-Version: 1.0\nCreated-By: Apache\nPackage: \nBuild-Jdk: 1.4.1_01\n\n"
+                + "Name: \nSpecification-Title: foo\nSpecification-Version: 1.0\nSpecification-Vendor: \n"
+                + "Implementation-Title: \nImplementation-Version: 1.0\nImplementation-Vendor: \n\n";
+        ByteArrayInputStream bis = new ByteArrayInputStream(manifestContent
+                .getBytes("ISO-8859-1"));
+
+
+        Manifest mf = new Manifest(bis);
+        assertEquals("Should be 4 main attributes", 4, mf.getMainAttributes()
+                .size());
+
+        Map<String, Attributes> entries = mf.getEntries();
+        assertEquals("Should be one named entry", 1, entries.size());
+
+        Attributes namedEntryAttributes = (Attributes) (entries.get(""));
+        assertEquals("Should be 6 named entry attributes", 6,
+                namedEntryAttributes.size());
+    }
+
+    /**
+     * @tests java.util.jar.Manifest#clear()
+     */
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "",
+            method = "clear",
+            args = {}
+    )
+    public void test_clear() {
+        m2.clear();
+        assertTrue("Should have no entries", m2.getEntries().isEmpty());
+        assertTrue("Should have no main attributes", m2.getMainAttributes()
+                .isEmpty());
+    }
+
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "",
+            method = "clone",
+            args = {}
+    )
+    public void test_clone() throws IOException {
         Manifest emptyManifest = new Manifest();
         Manifest emptyClone = (Manifest) emptyManifest.clone();
         assertTrue("Should have no entries", emptyClone.getEntries().isEmpty());
@@ -354,88 +243,25 @@
         assertEquals(emptyManifest.clone().getClass().getName(),
                 "java.util.jar.Manifest");
 
-        Manifest manifest = null;
-        try {
-            manifest = new Manifest(new URL(Support_Resources
-                    .getURL("manifest/hyts_MANIFEST.MF")).openStream());
-        } catch (MalformedURLException e) {
-            fail("Malformed URL");
-        } catch (IOException e) {
-            fail("IOException");
-        }
+        Manifest manifest = new Manifest(new URL(Support_Resources
+                .getURL("manifest/hyts_MANIFEST.MF")).openStream());
         Manifest manifestClone = (Manifest) manifest.clone();
-        Attributes main = manifestClone.getMainAttributes();
-        assertEquals("Bundle-Name not correct", "ClientSupport", main
-                .getValue("Bundle-Name"));
-        assertEquals(
-                "Bundle-Description not correct",
-
-                "Provides SessionService, AuthenticationService. Extends RegistryService.",
-                main.getValue("Bundle-Description"));
-        assertEquals("Bundle-Activator not correct",
-                "com.ibm.ive.eccomm.client.support.ClientSupportActivator",
-                main.getValue("Bundle-Activator"));
-        assertEquals(
-                "Import-Package not correct",
-
-                "com.ibm.ive.eccomm.client.services.log,com.ibm.ive.eccomm.client.services.registry,com.ibm.ive.eccomm.service.registry; specification-version=1.0.0,com.ibm.ive.eccomm.service.session; specification-version=1.0.0,com.ibm.ive.eccomm.service.framework; specification-version=1.2.0,org.osgi.framework; specification-version=1.0.0,org.osgi.service.log; specification-version=1.0.0,com.ibm.ive.eccomm.flash; specification-version=1.2.0,com.ibm.ive.eccomm.client.xml,com.ibm.ive.eccomm.client.http.common,com.ibm.ive.eccomm.client.http.client",
-                main.getValue("Import-Package"));
-        assertEquals(
-                "Import-Service not correct",
-
-                "org.osgi.service.log.LogReaderServiceorg.osgi.service.log.LogService,com.ibm.ive.eccomm.service.registry.RegistryService",
-                main.getValue("Import-Service"));
-        assertEquals(
-                "Export-Package not correct",
-
-                "com.ibm.ive.eccomm.client.services.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.service.authentication; specification-version=1.0.0,com.ibm.ive.eccomm.common; specification-version=1.0.0,com.ibm.ive.eccomm.client.services.registry.store; specification-version=1.0.0",
-                main.getValue("Export-Package"));
-        assertEquals(
-                "Export-Service not correct",
-
-                "com.ibm.ive.eccomm.service.authentication.AuthenticationService,com.ibm.ive.eccomm.service.session.SessionService",
-                main.getValue("Export-Service"));
-        assertEquals("Bundle-Vendor not correct", "IBM", main
-                .getValue("Bundle-Vendor"));
-        assertEquals("Bundle-Version not correct", "1.2.0", main
-                .getValue("Bundle-Version"));
+        manifestClone.getMainAttributes();
+        checkManifest(manifestClone);
     }
 
     @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        notes = "",
-        method = "equals",
-        args = {java.lang.Object.class}
+            level = TestLevel.COMPLETE,
+            notes = "",
+            method = "equals",
+            args = {java.lang.Object.class}
     )
-    public void test_equals() {
-        Manifest manifest1 = null;
-        Manifest manifest2 = null;
+    public void test_equals() throws IOException {
+        Manifest manifest1 = new Manifest(new URL(Support_Resources.getURL(
+                "manifest/hyts_MANIFEST.MF")).openStream());
+        Manifest manifest2 = new Manifest(new URL(Support_Resources.getURL(
+                "manifest/hyts_MANIFEST.MF")).openStream());
         Manifest manifest3 = new Manifest();
-        InputStream is = null;
-        try {
-            is = new URL(Support_Resources.getURL("manifest/hyts_MANIFEST.MF"))
-                    .openStream();
-        } catch (MalformedURLException e1) {
-            fail("Failed to create InputStream object");
-        } catch (IOException e1) {
-            fail("Failed to create InputStream object");
-        }
-        try {
-            manifest1 = new Manifest(is);
-        } catch (MalformedURLException e) {
-            fail("Malformed URL");
-        } catch (IOException e) {
-            fail("IOException");
-        }
-
-        try {
-            manifest2 = new Manifest(new URL(Support_Resources
-                    .getURL("manifest/hyts_MANIFEST.MF")).openStream());
-        } catch (MalformedURLException e) {
-            fail("Malformed URL");
-        } catch (IOException e) {
-            fail("IOException");
-        }
 
         assertTrue(manifest1.equals(manifest1));
         assertTrue(manifest1.equals(manifest2));
@@ -444,27 +270,69 @@
     }
 
     @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        notes = "",
-        method = "hashCode",
-        args = {}
+            level = TestLevel.COMPLETE,
+            notes = "",
+            method = "hashCode",
+            args = {}
     )
-    public void test_hashCode() {
-        Manifest manifest1 = null;
+    public void test_hashCode() throws IOException {
+        Manifest manifest1 = new Manifest(new URL(Support_Resources
+                .getURL("manifest/hyts_MANIFEST.MF")).openStream());
         Manifest manifest2 = new Manifest();
-        InputStream is = null;
-        try {
-            manifest1 = new Manifest(new URL(Support_Resources
-                    .getURL("manifest/hyts_MANIFEST.MF")).openStream());
-        } catch (MalformedURLException e) {
-            fail("Malformed URL");
-        } catch (IOException e) {
-            fail("IOException");
-        }
         assertEquals(manifest1.hashCode(), manifest1.hashCode());
         assertNotSame(manifest1.hashCode(), manifest2.hashCode());
     }
 
+	/**
+	 * @tests java.util.jar.Manifest#getAttributes(java.lang.String)
+	 */
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "",
+            method = "getAttributes",
+            args = {String.class}
+    )
+	public void test_getAttributesLjava_lang_String() {
+		assertNull("Should not exist",
+				m2.getAttributes("Doesn't Exist"));
+		assertEquals("Should exist", "OK", m2.getAttributes("HasAttributes.txt").get(
+				new Attributes.Name("MyAttribute")));
+	}
+
+	/**
+	 * @tests java.util.jar.Manifest#getEntries()
+	 */
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "",
+            method = "getEntries",
+            args = {}
+    )
+	public void test_getEntries() {
+		Map<String, Attributes> myMap = m2.getEntries();
+		assertNull("Shouldn't exist", myMap.get("Doesn't exist"));
+		assertEquals("Should exist",
+				"OK", myMap.get("HasAttributes.txt").get(
+						new Attributes.Name("MyAttribute")));
+	}
+
+	/**
+	 * @tests java.util.jar.Manifest#getMainAttributes()
+	 */
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "",
+            method = "getMainAttributes",
+            args = {}
+    )
+	public void test_getMainAttributes() {
+		// Test for method java.util.jar.Attributes
+		// java.util.jar.Manifest.getMainAttributes()
+		Attributes a = m.getMainAttributes();
+		assertEquals("Manifest_Version should return 1.0", "1.0", a.get(
+				Attributes.Name.MANIFEST_VERSION));
+	}
+
     @TestTargetNew(
         level = TestLevel.COMPLETE,
         notes = "",
@@ -510,4 +378,219 @@
 
         assertTrue(manifest1.equals(manifest2));
     }
+
+    /**
+     * Ensures compatibility with manifests produced by gcc.
+     *
+     * @see <a
+     *      href="http://issues.apache.org/jira/browse/HARMONY-5662">HARMONY-5662</a>
+     */
+    @TestTargetNew(
+            level = TestLevel.PARTIAL_COMPLETE,
+            notes = "",
+            method = "Manifest",
+            args = {InputStream.class}
+    )
+    public void testNul() throws IOException {
+        String manifestContent =
+                "Manifest-Version: 1.0\nCreated-By: nasty gcc tool\n\n\0";
+
+        byte[] bytes = manifestContent.getBytes("ISO-8859-1");
+        new Manifest(new ByteArrayInputStream(bytes)); // the last NUL is ok
+
+        bytes[bytes.length - 1] = 26;
+        new Manifest(new ByteArrayInputStream(bytes)); // the last EOF is ok
+
+        bytes[bytes.length - 1] = 'A'; // the last line ignored
+        new Manifest(new ByteArrayInputStream(bytes));
+
+        bytes[2] = 0; // NUL char in Manifest
+        try {
+            new Manifest(new ByteArrayInputStream(bytes));
+            fail("IOException expected");
+        } catch (IOException e) {
+            // desired
+        }
+    }
+
+    @TestTargetNew(
+            level = TestLevel.PARTIAL_COMPLETE,
+            method = "Manifest",
+            args = {InputStream.class}
+    )
+    @KnownFailure("CharsetDecoder fails with an IllegalStateException")
+    public void testDecoding() throws IOException {
+        Manifest m = getManifest(attJarName);
+        final byte[] bVendor = new byte[] { (byte) 0xd0, (byte) 0x9C,
+                (byte) 0xd0, (byte) 0xb8, (byte) 0xd0, (byte) 0xbb,
+                (byte) 0xd0, (byte) 0xb0, (byte) 0xd1, (byte) 0x8f, ' ',
+                (byte) 0xd0, (byte) 0xb4, (byte) 0xd0, (byte) 0xbe,
+                (byte) 0xd1, (byte) 0x87, (byte) 0xd1, (byte) 0x83,
+                (byte) 0xd0, (byte) 0xbd, (byte) 0xd1, (byte) 0x8C,
+                (byte) 0xd0, (byte) 0xba, (byte) 0xd0, (byte) 0xb0, ' ',
+                (byte) 0xd0, (byte) 0x9C, (byte) 0xd0, (byte) 0xb0,
+                (byte) 0xd1, (byte) 0x88, (byte) 0xd0, (byte) 0xb0 };
+
+        final byte[] bSpec = new byte[] { (byte) 0xe1, (byte) 0x88,
+                (byte) 0xb0, (byte) 0xe1, (byte) 0x88, (byte) 0x8b,
+                (byte) 0xe1, (byte) 0x88, (byte) 0x9d, ' ', (byte) 0xe1,
+                (byte) 0x9a, (byte) 0xa0, (byte) 0xe1, (byte) 0x9a,
+                (byte) 0xb1, (byte) 0xe1, (byte) 0x9b, (byte) 0x81,
+                (byte) 0xe1, (byte) 0x9a, (byte) 0xa6, ' ', (byte) 0xd8,
+                (byte) 0xb3, (byte) 0xd9, (byte) 0x84, (byte) 0xd8,
+                (byte) 0xa7, (byte) 0xd9, (byte) 0x85, ' ', (byte) 0xd8,
+                (byte) 0xb9, (byte) 0xd8, (byte) 0xb3, (byte) 0xd9,
+                (byte) 0x84, (byte) 0xd8, (byte) 0xa7, (byte) 0xd9,
+                (byte) 0x85, (byte) 0xd8, (byte) 0xa9, ' ', (byte) 0xdc,
+                (byte) 0xab, (byte) 0xdc, (byte) 0xa0, (byte) 0xdc,
+                (byte) 0xa1, (byte) 0xdc, (byte) 0x90, ' ', (byte) 0xe0,
+                (byte) 0xa6, (byte) 0xb6, (byte) 0xe0, (byte) 0xa6,
+                (byte) 0xbe, (byte) 0xe0, (byte) 0xa6, (byte) 0xa8,
+                (byte) 0xe0, (byte) 0xa7, (byte) 0x8d, (byte) 0xe0,
+                (byte) 0xa6, (byte) 0xa4, (byte) 0xe0, (byte) 0xa6,
+                (byte) 0xbf, ' ', (byte) 0xd0, (byte) 0xa0, (byte) 0xd0,
+                (byte) 0xb5, (byte) 0xd0, (byte) 0xba, (byte) 0xd1,
+                (byte) 0x8a, (byte) 0xd0, (byte) 0xb5, (byte) 0xd0,
+                (byte) 0xbb, ' ', (byte) 0xd0, (byte) 0x9c, (byte) 0xd0,
+                (byte) 0xb8, (byte) 0xd1, (byte) 0x80, ' ', (byte) 0xe0,
+                (byte) 0xa6, (byte) 0xb6, (byte) 0xe0, (byte) 0xa6,
+                (byte) 0xbe, (byte) 0xe0, (byte) 0xa6, (byte) 0xa8,
+                (byte) 0xe0, (byte) 0xa7, (byte) 0x8d, (byte) 0xe0,
+                (byte) 0xa6, (byte) 0xa4, (byte) 0xe0, (byte) 0xa6,
+                (byte) 0xbf, ' ', (byte) 0xe0, (byte) 0xbd, (byte) 0x9e,
+                (byte) 0xe0, (byte) 0xbd, (byte) 0xb2, (byte) 0xe0,
+                (byte) 0xbc, (byte) 0x8b, (byte) 0xe0, (byte) 0xbd,
+                (byte) 0x96, (byte) 0xe0, (byte) 0xbd, (byte) 0x91,
+                (byte) 0xe0, (byte) 0xbd, (byte) 0xba, ' ', (byte) 0xd0,
+                (byte) 0x9c, (byte) 0xd0, (byte) 0xb0, (byte) 0xd1,
+                (byte) 0x88, (byte) 0xd0, (byte) 0xb0, (byte) 0xd1,
+                (byte) 0x80, ' ', (byte) 0xe1, (byte) 0x8f, (byte) 0x99,
+                (byte) 0xe1, (byte) 0x8e, (byte) 0xaf, (byte) 0xe1,
+                (byte) 0x8f, (byte) 0xb1, ' ', (byte) 0xcf, (byte) 0xa8,
+                (byte) 0xce, (byte) 0xb9, (byte) 0xcf, (byte) 0x81,
+                (byte) 0xce, (byte) 0xb7, (byte) 0xce, (byte) 0xbd,
+                (byte) 0xce, (byte) 0xb7, ' ', (byte) 0xde, (byte) 0x90,
+                (byte) 0xde, (byte) 0xaa, (byte) 0xde, (byte) 0x85,
+                (byte) 0xde, (byte) 0xa6, ' ', (byte) 0xe0, (byte) 0xbd,
+                (byte) 0x82, (byte) 0xe0, (byte) 0xbd, (byte) 0x9e,
+                (byte) 0xe0, (byte) 0xbd, (byte) 0xb2, (byte) 0xe0,
+                (byte) 0xbc, (byte) 0x8b, (byte) 0xe0, (byte) 0xbd,
+                (byte) 0x96, (byte) 0xe0, (byte) 0xbd, (byte) 0x91,
+                (byte) 0xe0, (byte) 0xbd, (byte) 0xba, ' ', (byte) 0xce,
+                (byte) 0x95, (byte) 0xce, (byte) 0xb9, (byte) 0xcf,
+                (byte) 0x81, (byte) 0xce, (byte) 0xae, (byte) 0xce,
+                (byte) 0xbd, (byte) 0xce, (byte) 0xb7, ' ', (byte) 0xd8,
+                (byte) 0xb5, (byte) 0xd9, (byte) 0x84, (byte) 0xd8,
+                (byte) 0xad, ' ', (byte) 0xe0, (byte) 0xaa, (byte) 0xb6,
+                (byte) 0xe0, (byte) 0xaa, (byte) 0xbe, (byte) 0xe0,
+                (byte) 0xaa, (byte) 0x82, (byte) 0xe0, (byte) 0xaa,
+                (byte) 0xa4, (byte) 0xe0, (byte) 0xaa, (byte) 0xbf, ' ',
+                (byte) 0xe5, (byte) 0xb9, (byte) 0xb3, (byte) 0xe5,
+                (byte) 0x92, (byte) 0x8c, ' ', (byte) 0xd7, (byte) 0xa9,
+                (byte) 0xd7, (byte) 0x9c, (byte) 0xd7, (byte) 0x95,
+                (byte) 0xd7, (byte) 0x9d, ' ', (byte) 0xd7, (byte) 0xa4,
+                (byte) 0xd7, (byte) 0xa8, (byte) 0xd7, (byte) 0x99,
+                (byte) 0xd7, (byte) 0x93, (byte) 0xd7, (byte) 0x9f, ' ',
+                (byte) 0xe5, (byte) 0x92, (byte) 0x8c, (byte) 0xe5,
+                (byte) 0xb9, (byte) 0xb3, ' ', (byte) 0xe5, (byte) 0x92,
+                (byte) 0x8c, (byte) 0xe5, (byte) 0xb9, (byte) 0xb3, ' ',
+                (byte) 0xd8, (byte) 0xaa, (byte) 0xd9, (byte) 0x89,
+                (byte) 0xd9, (byte) 0x86, (byte) 0xda, (byte) 0x86,
+                (byte) 0xd9, (byte) 0x84, (byte) 0xd9, (byte) 0x89,
+                (byte) 0xd9, (byte) 0x82, ' ', (byte) 0xe0, (byte) 0xae,
+                (byte) 0x85, (byte) 0xe0, (byte) 0xae, (byte) 0xae,
+                (byte) 0xe0, (byte) 0xaf, (byte) 0x88, (byte) 0xe0,
+                (byte) 0xae, (byte) 0xa4, (byte) 0xe0, (byte) 0xae,
+                (byte) 0xbf, ' ', (byte) 0xe0, (byte) 0xb0, (byte) 0xb6,
+                (byte) 0xe0, (byte) 0xb0, (byte) 0xbe, (byte) 0xe0,
+                (byte) 0xb0, (byte) 0x82, (byte) 0xe0, (byte) 0xb0,
+                (byte) 0xa4, (byte) 0xe0, (byte) 0xb0, (byte) 0xbf, ' ',
+                (byte) 0xe0, (byte) 0xb8, (byte) 0xaa, (byte) 0xe0,
+                (byte) 0xb8, (byte) 0xb1, (byte) 0xe0, (byte) 0xb8,
+                (byte) 0x99, (byte) 0xe0, (byte) 0xb8, (byte) 0x95,
+                (byte) 0xe0, (byte) 0xb8, (byte) 0xb4, (byte) 0xe0,
+                (byte) 0xb8, (byte) 0xa0, (byte) 0xe0, (byte) 0xb8,
+                (byte) 0xb2, (byte) 0xe0, (byte) 0xb8, (byte) 0x9e, ' ',
+                (byte) 0xe1, (byte) 0x88, (byte) 0xb0, (byte) 0xe1,
+                (byte) 0x88, (byte) 0x8b, (byte) 0xe1, (byte) 0x88,
+                (byte) 0x9d, ' ', (byte) 0xe0, (byte) 0xb7, (byte) 0x83,
+                (byte) 0xe0, (byte) 0xb7, (byte) 0x8f, (byte) 0xe0,
+                (byte) 0xb6, (byte) 0xb8, (byte) 0xe0, (byte) 0xb6,
+                (byte) 0xba, ' ', (byte) 0xe0, (byte) 0xa4, (byte) 0xb6,
+                (byte) 0xe0, (byte) 0xa4, (byte) 0xbe, (byte) 0xe0,
+                (byte) 0xa4, (byte) 0xa8, (byte) 0xe0, (byte) 0xa5,
+                (byte) 0x8d, (byte) 0xe0, (byte) 0xa4, (byte) 0xa4,
+                (byte) 0xe0, (byte) 0xa4, (byte) 0xbf, (byte) 0xe0,
+                (byte) 0xa4, (byte) 0x83, ' ', (byte) 0xe1, (byte) 0x83,
+                (byte) 0x9b, (byte) 0xe1, (byte) 0x83, (byte) 0xa8,
+                (byte) 0xe1, (byte) 0x83, (byte) 0x95, (byte) 0xe1,
+                (byte) 0x83, (byte) 0x98, (byte) 0xe1, (byte) 0x83,
+                (byte) 0x93, (byte) 0xe1, (byte) 0x83, (byte) 0x9d,
+                (byte) 0xe1, (byte) 0x83, (byte) 0x91, (byte) 0xe1,
+                (byte) 0x83, (byte) 0x90 };
+        // TODO Cannot make the following word work, encoder changes needed
+        // (byte) 0xed, (byte) 0xa0, (byte) 0x80,
+        // (byte) 0xed, (byte) 0xbc, (byte) 0xb2, (byte) 0xed,
+        // (byte) 0xa0, (byte) 0x80, (byte) 0xed, (byte) 0xbc,
+        // (byte) 0xb0, (byte) 0xed, (byte) 0xa0, (byte) 0x80,
+        // (byte) 0xed, (byte) 0xbd, (byte) 0x85, (byte) 0xed,
+        // (byte) 0xa0, (byte) 0x80, (byte) 0xed, (byte) 0xbc,
+        // (byte) 0xb0, (byte) 0xed, (byte) 0xa0, (byte) 0x80,
+        // (byte) 0xed, (byte) 0xbc, (byte) 0xb9, (byte) 0xed,
+        // (byte) 0xa0, (byte) 0x80, (byte) 0xed, (byte) 0xbc,
+        // (byte) 0xb8, (byte) 0xed, (byte) 0xa0, (byte) 0x80,
+        // (byte) 0xed, (byte) 0xbc, (byte) 0xb9, ' '
+
+        final String vendor = new String(bVendor, "UTF-8");
+        final String spec = new String(bSpec, "UTF-8");
+        m.getMainAttributes()
+                .put(Attributes.Name.IMPLEMENTATION_VENDOR, vendor);
+        m.getAttributes(ATT_ENTRY_NAME).put(
+                Attributes.Name.IMPLEMENTATION_VENDOR, vendor);
+        m.getEntries().get(ATT_ENTRY_NAME).put(
+                Attributes.Name.SPECIFICATION_TITLE, spec);
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        m.write(baos);
+        m = new Manifest(new ByteArrayInputStream(baos.toByteArray()));
+
+        assertEquals(vendor, m.getMainAttributes().get(
+                Attributes.Name.IMPLEMENTATION_VENDOR));
+        assertEquals(vendor, m.getEntries().get(ATT_ENTRY_NAME).get(
+                Attributes.Name.IMPLEMENTATION_VENDOR));
+        assertEquals(spec, m.getAttributes(ATT_ENTRY_NAME).get(
+                Attributes.Name.SPECIFICATION_TITLE));
+    }
+
+    /**
+     * @tests {@link java.util.jar.Manifest#read(java.io.InputStream)
+     */
+    @TestTargetNew(
+            level = TestLevel.PARTIAL,
+            notes = "",
+            method = "read",
+            args = {InputStream.class}
+    )
+    public void testRead() {
+        // Regression for HARMONY-89
+        InputStream is = new InputStreamImpl();
+        try {
+            new Manifest().read(is);
+            fail("IOException expected");
+        } catch (IOException e) {
+            // desired
+        }
+    }
+
+    // helper class
+    private class InputStreamImpl extends InputStream {
+        public InputStreamImpl() {
+            super();
+        }
+
+        @Override
+        public int read() {
+            return 0;
+        }
+    }
 }
diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java
index 75060bd..1e8ddb4 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPInputStreamTest.java
@@ -25,6 +25,8 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.URL;
@@ -310,7 +312,43 @@
         }
     }
 
-    @Override
+    /**
+     * Regression test for HARMONY-3703.
+     * @tests java.util.zip.GZIPInputStream#read()
+     */
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "",
+            method = "read",
+            args = {byte[].class}
+    )
+    public void test_read() throws IOException {
+        GZIPInputStream gis = null;
+        int result = 0;
+        byte[] buffer = new byte[] {1,2,3,4,5,6,7,8,9,10};
+        File f = new File(resources.getAbsolutePath() + "test.gz");
+        FileOutputStream out = new FileOutputStream(f);
+        GZIPOutputStream gout = new GZIPOutputStream(out);
+
+        // write 100 bytes to the stream
+        for(int i = 0; i < 10; i++) {
+            gout.write(buffer);
+        }
+        gout.finish();
+        out.write(1);
+        out.close();
+
+        gis = new GZIPInputStream(new FileInputStream(f));
+        buffer = new byte[100];
+        gis.read(buffer);
+        result = gis.read();
+        gis.close();
+        f.delete();
+
+        assertEquals("Incorrect value returned at the end of the file", -1, result);
+    }
+
+	@Override
     protected void setUp() {
         resources = Support_Resources.createTempFolder();
     }
diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java
index 9b23b56..b71ce63 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/GZIPOutputStreamTest.java
@@ -201,8 +201,6 @@
             int r = 0;
             try {
                 outGZIP.write(byteArray, 0, 11);
-            } catch (ArrayIndexOutOfBoundsException e) {
-                r = 1;
             } catch (IndexOutOfBoundsException ee) {
                 r = 1;
             }
diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java
index 8b89180..6039c5b 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/InflaterTest.java
@@ -409,6 +409,22 @@
     }
 
     /**
+     * @tests java.util.zip.Inflater#Inflater()
+     */
+    @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "",
+            method = "Inflater",
+            args = {}
+    )
+    public void test_Constructor() {
+        // test method of java.util.zip.inflater.Inflater()
+        Inflater inflate = new Inflater();
+        assertNotNull("failed to create the instance of inflater",
+                        inflate);
+    }
+
+    /**
      * @tests java.util.zip.Inflater#inflate(byte[], int, int)
      */
     @TestTargetNew(
@@ -504,27 +520,6 @@
     }
 
     /**
-     * @tests java.util.zip.Inflater#Inflater()
-     */
-    @TestTargetNew(
-        level = TestLevel.COMPLETE,
-        notes = "",
-        method = "Inflater",
-        args = {}
-    )
-    public void test_Constructor() {
-        // test method of java.util.zip.inflater.Inflater()
-        try {
-            Inflater inflate = new Inflater();
-            assertNotNull("failed to create the instance of inflater", inflate);
-
-        } catch (Exception e) {
-
-            assertTrue("Inflate () constructor threw an exception", true);
-        }
-    }
-
-    /**
      * @tests java.util.zip.Inflater#Inflater(boolean)
      */
     @TestTargetNew(
diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java
index 5530a2e..c9e7bb8 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipFileTest.java
@@ -67,7 +67,7 @@
         public void checkPermission(Permission perm) {
             // only check if it's a FilePermission because Locale checks
             // for a PropertyPermission with action"read" to get system props.
-            if (perm instanceof FilePermission 
+            if (perm instanceof FilePermission
                     && perm.getActions().equals(forbidenPermissionAction)) {
                 throw new SecurityException();
             }
@@ -145,7 +145,7 @@
     public void test_ConstructorLjava_lang_String() throws IOException {
         String oldUserDir = System.getProperty("user.dir");
         System.setProperty("user.dir", System.getProperty("java.io.tmpdir"));
-        
+
         zfile.close(); // about to reopen the same temp file
         ZipFile zip = new ZipFile(tempFileName);
         zip.close();
@@ -260,7 +260,7 @@
         method = "entries",
         args = {}
     )
-    public void test_entries() {
+    public void test_entries() throws Exception {
         // Test for method java.util.Enumeration java.util.zip.ZipFile.entries()
         Enumeration<? extends ZipEntry> enumer = zfile.entries();
         int c = 0;
@@ -270,20 +270,16 @@
         }
         assertTrue("Incorrect number of entries returned: " + c, c == 6);
 
+        Enumeration<? extends ZipEntry> enumeration = zfile.entries();
+        zfile.close();
+        zfile = null;
+        boolean pass = false;
         try {
-            Enumeration<? extends ZipEntry> enumeration = zfile.entries();
-            zfile.close();
-            zfile = null;
-            boolean pass = false;
-            try {
-                enumeration.hasMoreElements();
-            } catch (IllegalStateException e) {
-                pass = true;
-            }
-            assertTrue("did not detect closed jar file", pass);
-        } catch (Exception e) {
-            fail("Exception during entries test: " + e.toString());
+            enumeration.hasMoreElements();
+        } catch (IllegalStateException e) {
+            pass = true;
         }
+        assertTrue("did not detect closed jar file", pass);
     }
 
     /**
@@ -454,6 +450,99 @@
     }
 
     /**
+     * @tests java.io.InputStream#reset()
+     */
+    @TestTargetNew(
+            level = TestLevel.PARTIAL_COMPLETE,
+            method = "getInputStream",
+            args = {java.util.zip.ZipEntry.class}
+    )
+    @KnownFailure("ZipEntry.getInputStream().reset() fails with an IOException")
+    public void test_reset() throws IOException {
+        // read an uncompressed entry
+        ZipEntry zentry = zfile.getEntry("File1.txt");
+        InputStream is = zfile.getInputStream(zentry);
+        byte[] rbuf1 = new byte[6];
+        byte[] rbuf2 = new byte[6];
+        int r1, r2;
+        r1 = is.read(rbuf1);
+        assertEquals(rbuf1.length, r1);
+        r2 = is.read(rbuf2);
+        assertEquals(rbuf2.length, r2);
+
+        is.reset();
+        r2 = is.read(rbuf2);
+        assertEquals(rbuf2.length, r2);
+        is.close();
+
+        // read a compressed entry
+        byte[] rbuf3 = new byte[4185];
+        ZipEntry zentry2 = zfile.getEntry("File3.txt");
+        is = zfile.getInputStream(zentry2);
+        r1 = is.read(rbuf3);
+        assertEquals(4183, r1);
+        is.reset();
+
+        r1 = is.read(rbuf3);
+        assertEquals(4183, r1);
+        is.close();
+
+        is = zfile.getInputStream(zentry2);
+        r1 = is.read(rbuf3, 0, 3000);
+        assertEquals(3000, r1);
+        is.reset();
+        r1 = is.read(rbuf3, 0, 3000);
+        assertEquals(3000, r1);
+        is.close();
+    }
+
+    /**
+     * @tests java.io.InputStream#reset()
+     */
+    @TestTargetNew(
+            level = TestLevel.PARTIAL_COMPLETE,
+            method = "getInputStream",
+            args = {java.util.zip.ZipEntry.class}
+    )
+    @KnownFailure("ZipEntry.getInputStream().reset() fails with an IOException")
+    public void test_reset_subtest0() throws IOException {
+        // read an uncompressed entry
+        ZipEntry zentry = zfile.getEntry("File1.txt");
+        InputStream is = zfile.getInputStream(zentry);
+        byte[] rbuf1 = new byte[12];
+        byte[] rbuf2 = new byte[12];
+        int r = is.read(rbuf1, 0, 4);
+        assertEquals(4, r);
+        is.mark(0);
+        r = is.read(rbuf1);
+        assertEquals(8, r);
+        assertEquals(-1, is.read());
+
+        is.reset();
+        r = is.read(rbuf2);
+        assertEquals(8, r);
+        assertEquals(-1, is.read());
+        is.close();
+
+        // read a compressed entry
+        byte[] rbuf3 = new byte[4185];
+        ZipEntry zentry2 = zfile.getEntry("File3.txt");
+        is = zfile.getInputStream(zentry2);
+        r = is.read(rbuf3, 0, 3000);
+        assertEquals(3000, r);
+        is.mark(0);
+        r = is.read(rbuf3);
+        assertEquals(1183, r);
+        assertEquals(-1, is.read());
+
+        is.reset();
+        r = is.read(rbuf3);
+        assertEquals(1183, r);
+        assertEquals(-1, is.read());
+        is.close();
+    }
+
+	/**
      * Sets up the fixture, for example, open a network connection. This method
      * is called before a test is executed.
      */
diff --git a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java
index 9a5f63a..8ca551d 100644
--- a/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java
+++ b/libcore/archive/src/test/java/org/apache/harmony/archive/tests/java/util/zip/ZipOutputStreamTest.java
@@ -170,11 +170,8 @@
     public void test_setCommentLjava_lang_String() {
         // There is no way to get the comment back, so no way to determine if
         // the comment is set correct
-        try {
-            zos.setComment("test setComment");
-        } catch (Exception e) {
-            fail("Trying to set comment failed");
-        }
+        zos.setComment("test setComment");
+
         try {
             zos.setComment(new String(new byte[0xFFFF + 1]));
             fail("Comment over 0xFFFF in length should throw exception");
@@ -301,6 +298,17 @@
         } catch (IndexOutOfBoundsException e) {
             // expected
         }
+        
+        // Regression for HARMONY-4405
+        try {
+            zip.write(null, 0, -2);
+            fail("Should throw IndexOutOfBoundsException");
+        } catch (IndexOutOfBoundsException e) {
+            // expected
+        }
+
+        // Close stream because ZIP is invalid
+        stream.close();
     }
 
     /**
@@ -337,6 +345,8 @@
         } catch (IOException e2) {
             // expected
         }
+        
+        zip1.close();
     }
 
     @Override
diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/util/InputStreamExposer.java b/libcore/luni/src/main/java/org/apache/harmony/luni/util/InputStreamExposer.java
new file mode 100644
index 0000000..d7c2ab8
--- /dev/null
+++ b/libcore/luni/src/main/java/org/apache/harmony/luni/util/InputStreamExposer.java
@@ -0,0 +1,117 @@
+/*
+ *  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;
+
+/**
+ * The class contains static {@link java.io.InputStream} utilities.
+ */
+public class InputStreamExposer {
+
+    /**
+     * 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];
+    }
+
+    /**
+     * 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 {
+        // BEGIN android-changed
+        // 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();
+    }
+}
diff --git a/libcore/luni/src/main/java/org/apache/harmony/luni/util/ThreadLocalCache.java b/libcore/luni/src/main/java/org/apache/harmony/luni/util/ThreadLocalCache.java
new file mode 100644
index 0000000..cdfe0c8
--- /dev/null
+++ b/libcore/luni/src/main/java/org/apache/harmony/luni/util/ThreadLocalCache.java
@@ -0,0 +1,103 @@
+/*
+ *  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.lang.ref.SoftReference;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharsetEncoder;
+
+/**
+ * The class extends the functionality of {@link java.lang.ThreadLocal} with
+ * possibility of discarding the thread local storage content when a heap is
+ * exhausted.
+ */
+public class ThreadLocalCache<T> {
+
+    private SoftReference<ThreadLocal<T>> storage = new SoftReference<ThreadLocal<T>>(
+            null);
+
+    private ThreadLocal<T> getThreadLocal() {
+        ThreadLocal<T> tls = storage.get();
+        if (tls == null) {
+            tls = new ThreadLocal<T>() {
+                public T initialValue() {
+                    return ThreadLocalCache.this.initialValue();
+                }
+            };
+            storage = new SoftReference<ThreadLocal<T>>(tls);
+        }
+        return tls;
+    }
+
+    /**
+     * Returns the initial value for the cache for the current thread.
+     */
+    protected T initialValue() {
+        return null;
+    }
+
+    /**
+     * Returns the thread local value of this object.
+     */
+    public T get() {
+        return getThreadLocal().get();
+    }
+
+    /**
+     * Sets the value of this variable for the current thread. Might be useful
+     * for expanding the thread local cache.
+     */
+    public void set(T value) {
+        getThreadLocal().set(value);
+    }
+
+    /**
+     * Discards the cache for all threads.
+     */
+    public void remove() {
+        storage.clear();
+    }
+
+    public static ThreadLocalCache<CharsetDecoder> utf8Decoder = new ThreadLocalCache<CharsetDecoder>() {
+        protected CharsetDecoder initialValue() {
+            return Charset.forName("UTF-8").newDecoder();
+        }
+    };
+
+    public static ThreadLocalCache<CharsetEncoder> utf8Encoder = new ThreadLocalCache<CharsetEncoder>() {
+        protected CharsetEncoder initialValue() {
+            return Charset.forName("UTF-8").newEncoder();
+        }
+    };
+
+    public static ThreadLocalCache<java.nio.ByteBuffer> byteBuffer = new ThreadLocalCache<java.nio.ByteBuffer>() {
+        protected java.nio.ByteBuffer initialValue() {
+            return java.nio.ByteBuffer.allocate(72); // >=
+            // Manifest.LINE_LENGTH_LIMIT
+        }
+    };
+
+    public static ThreadLocalCache<CharBuffer> charBuffer = new ThreadLocalCache<CharBuffer>() {
+        protected CharBuffer initialValue() {
+            return CharBuffer.allocate(72); // no specific requirement
+        }
+    };
+
+}