Add sources for API 35

Downloaded from https://dl.google.com/android/repository/source-35_r01.zip
using SdkManager in Studio

Test: None
Change-Id: I83f78aa820b66edfdc9f8594d17bc7b6cacccec1
diff --git a/android-35/java/util/zip/Adler32.java b/android-35/java/util/zip/Adler32.java
new file mode 100644
index 0000000..04e57d4
--- /dev/null
+++ b/android-35/java/util/zip/Adler32.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.lang.ref.Reference;
+import java.nio.ByteBuffer;
+import sun.nio.ch.DirectBuffer;
+
+import jdk.internal.vm.annotation.IntrinsicCandidate;
+
+/**
+ * A class that can be used to compute the Adler-32 checksum of a data
+ * stream. An Adler-32 checksum is almost as reliable as a CRC-32 but
+ * can be computed much faster.
+ *
+ * <p> Passing a {@code null} argument to a method in this class will cause
+ * a {@link NullPointerException} to be thrown.</p>
+ *
+ * @author      David Connelly
+ * @since 1.1
+ */
+public class Adler32 implements Checksum {
+
+    private int adler = 1;
+
+    /**
+     * Creates a new Adler32 object.
+     */
+    public Adler32() {
+    }
+
+    /**
+     * Updates the checksum with the specified byte (the low eight
+     * bits of the argument b).
+     */
+    @Override
+    public void update(int b) {
+        adler = update(adler, b);
+    }
+
+    /**
+     * Updates the checksum with the specified array of bytes.
+     *
+     * @throws ArrayIndexOutOfBoundsException
+     *         if {@code off} is negative, or {@code len} is negative, or
+     *         {@code off+len} is negative or greater than the length of
+     *         the array {@code b}.
+     */
+    @Override
+    public void update(byte[] b, int off, int len) {
+        if (b == null) {
+            throw new NullPointerException();
+        }
+        if (off < 0 || len < 0 || off > b.length - len) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        adler = updateBytes(adler, b, off, len);
+    }
+
+    // Android-changed: method kept during jdk17u update for compatibility.
+    /**
+     * Updates the checksum with the specified array of bytes.
+     *
+     * @param b the byte array to update the checksum with
+     */
+    @Override
+    public void update(byte[] b) {
+        adler = updateBytes(adler, b, 0, b.length);
+    }
+
+    /**
+     * Updates the checksum with the bytes from the specified buffer.
+     *
+     * The checksum is updated with the remaining bytes in the buffer, starting
+     * at the buffer's position. Upon return, the buffer's position will be
+     * updated to its limit; its limit will not have been changed.
+     *
+     * @since 1.8
+     */
+    @Override
+    public void update(ByteBuffer buffer) {
+        int pos = buffer.position();
+        int limit = buffer.limit();
+        assert (pos <= limit);
+        int rem = limit - pos;
+        if (rem <= 0)
+            return;
+        if (buffer.isDirect()) {
+            try {
+                adler = updateByteBuffer(adler, ((DirectBuffer)buffer).address(), pos, rem);
+            } finally {
+                Reference.reachabilityFence(buffer);
+            }
+        } else if (buffer.hasArray()) {
+            adler = updateBytes(adler, buffer.array(), pos + buffer.arrayOffset(), rem);
+        } else {
+            byte[] b = new byte[Math.min(buffer.remaining(), 4096)];
+            while (buffer.hasRemaining()) {
+                int length = Math.min(buffer.remaining(), b.length);
+                buffer.get(b, 0, length);
+                update(b, 0, length);
+            }
+        }
+        buffer.position(limit);
+    }
+
+    /**
+     * Resets the checksum to initial value.
+     */
+    @Override
+    public void reset() {
+        adler = 1;
+    }
+
+    /**
+     * Returns the checksum value.
+     */
+    @Override
+    public long getValue() {
+        return (long)adler & 0xffffffffL;
+    }
+
+    private static native int update(int adler, int b);
+
+    @IntrinsicCandidate
+    private static native int updateBytes(int adler, byte[] b, int off,
+                                          int len);
+    @IntrinsicCandidate
+    private static native int updateByteBuffer(int adler, long addr,
+                                               int off, int len);
+}
diff --git a/android-35/java/util/zip/CRC32.java b/android-35/java/util/zip/CRC32.java
new file mode 100644
index 0000000..49c9c95
--- /dev/null
+++ b/android-35/java/util/zip/CRC32.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.lang.ref.Reference;
+import java.nio.ByteBuffer;
+import java.util.Objects;
+
+import sun.nio.ch.DirectBuffer;
+import jdk.internal.vm.annotation.IntrinsicCandidate;
+
+/**
+ * A class that can be used to compute the CRC-32 of a data stream.
+ *
+ * <p> Passing a {@code null} argument to a method in this class will cause
+ * a {@link NullPointerException} to be thrown.</p>
+ *
+ * @author      David Connelly
+ * @since 1.1
+ */
+public class CRC32 implements Checksum {
+    private int crc;
+
+    /**
+     * Creates a new CRC32 object.
+     */
+    public CRC32() {
+    }
+
+
+    /**
+     * Updates the CRC-32 checksum with the specified byte (the low
+     * eight bits of the argument b).
+     */
+    @Override
+    public void update(int b) {
+        crc = update(crc, b);
+    }
+
+    /**
+     * Updates the CRC-32 checksum with the specified array of bytes.
+     *
+     * @throws ArrayIndexOutOfBoundsException
+     *         if {@code off} is negative, or {@code len} is negative, or
+     *         {@code off+len} is negative or greater than the length of
+     *         the array {@code b}.
+     */
+    @Override
+    public void update(byte[] b, int off, int len) {
+        if (b == null) {
+            throw new NullPointerException();
+        }
+        if (off < 0 || len < 0 || off > b.length - len) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        crc = updateBytes(crc, b, off, len);
+    }
+
+    // Android-changed: method kept during jdk17u update for compatibility.
+    /**
+     * Updates the CRC-32 checksum with the specified array of bytes.
+     *
+     * @param b the array of bytes to update the checksum with
+     */
+    @Override
+    public void update(byte[] b) {
+        crc = updateBytes(crc, b, 0, b.length);
+    }
+
+    /**
+     * Updates the CRC-32 checksum with the bytes from the specified buffer.
+     *
+     * The checksum is updated with the remaining bytes in the buffer, starting
+     * at the buffer's position. Upon return, the buffer's position will be
+     * updated to its limit; its limit will not have been changed.
+     *
+     * @since 1.8
+     */
+    @Override
+    public void update(ByteBuffer buffer) {
+        int pos = buffer.position();
+        int limit = buffer.limit();
+        assert (pos <= limit);
+        int rem = limit - pos;
+        if (rem <= 0)
+            return;
+        if (buffer.isDirect()) {
+            try {
+                crc = updateByteBuffer(crc, ((DirectBuffer)buffer).address(), pos, rem);
+            } finally {
+                Reference.reachabilityFence(buffer);
+            }
+        } else if (buffer.hasArray()) {
+            crc = updateBytes(crc, buffer.array(), pos + buffer.arrayOffset(), rem);
+        } else {
+            byte[] b = new byte[Math.min(buffer.remaining(), 4096)];
+            while (buffer.hasRemaining()) {
+                int length = Math.min(buffer.remaining(), b.length);
+                buffer.get(b, 0, length);
+                update(b, 0, length);
+            }
+        }
+        buffer.position(limit);
+    }
+
+    /**
+     * Resets CRC-32 to initial value.
+     */
+    @Override
+    public void reset() {
+        crc = 0;
+    }
+
+    /**
+     * Returns CRC-32 value.
+     */
+    @Override
+    public long getValue() {
+        return (long)crc & 0xffffffffL;
+    }
+
+    @IntrinsicCandidate
+    private static native int update(int crc, int b);
+
+    private static int updateBytes(int crc, byte[] b, int off, int len) {
+        updateBytesCheck(b, off, len);
+        return updateBytes0(crc, b, off, len);
+    }
+
+    @IntrinsicCandidate
+    private static native int updateBytes0(int crc, byte[] b, int off, int len);
+
+    private static void updateBytesCheck(byte[] b, int off, int len) {
+        if (len <= 0) {
+            return;  // not an error because updateBytesImpl won't execute if len <= 0
+        }
+
+        Objects.requireNonNull(b);
+
+        if (off < 0 || off >= b.length) {
+            throw new ArrayIndexOutOfBoundsException(off);
+        }
+
+        int endIndex = off + len - 1;
+        if (endIndex < 0 || endIndex >= b.length) {
+            throw new ArrayIndexOutOfBoundsException(endIndex);
+        }
+    }
+
+    private static int updateByteBuffer(int alder, long addr,
+                                        int off, int len) {
+        updateByteBufferCheck(addr);
+        return updateByteBuffer0(alder, addr, off, len);
+    }
+
+    @IntrinsicCandidate
+    private static native int updateByteBuffer0(int alder, long addr,
+                                                int off, int len);
+
+    private static void updateByteBufferCheck(long addr) {
+        // Performs only a null check because bounds checks
+        // are not easy to do on raw addresses.
+        if (addr == 0L) {
+            throw new NullPointerException();
+        }
+    }
+}
diff --git a/android-35/java/util/zip/CRC32C.java b/android-35/java/util/zip/CRC32C.java
new file mode 100644
index 0000000..fa138d4
--- /dev/null
+++ b/android-35/java/util/zip/CRC32C.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.zip;
+
+import java.lang.ref.Reference;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import jdk.internal.misc.Unsafe;
+import jdk.internal.vm.annotation.IntrinsicCandidate;
+import sun.nio.ch.DirectBuffer;
+
+/**
+ * A class that can be used to compute the CRC-32C of a data stream.
+ *
+ * <p>
+ * CRC-32C is defined in <a href="http://www.ietf.org/rfc/rfc3720.txt">RFC
+ * 3720</a>: Internet Small Computer Systems Interface (iSCSI).
+ * </p>
+ *
+ * <p>
+ * Passing a {@code null} argument to a method in this class will cause a
+ * {@link NullPointerException} to be thrown.
+ * </p>
+ *
+ * @since 9
+ */
+public final class CRC32C implements Checksum {
+
+    /*
+     * This CRC-32C implementation uses the 'slicing-by-8' algorithm described
+     * in the paper "A Systematic Approach to Building High Performance
+     * Software-Based CRC Generators" by Michael E. Kounavis and Frank L. Berry,
+     * Intel Research and Development
+     */
+
+    /**
+     * CRC-32C Polynomial
+     */
+    private static final int CRC32C_POLY = 0x1EDC6F41;
+    private static final int REVERSED_CRC32C_POLY = Integer.reverse(CRC32C_POLY);
+
+    private static final Unsafe UNSAFE = Unsafe.getUnsafe();
+
+    // Lookup tables
+    // Lookup table for single byte calculations
+    private static final int[] byteTable;
+    // Lookup tables for bulk operations in 'slicing-by-8' algorithm
+    private static final int[][] byteTables = new int[8][256];
+    private static final int[] byteTable0 = byteTables[0];
+    private static final int[] byteTable1 = byteTables[1];
+    private static final int[] byteTable2 = byteTables[2];
+    private static final int[] byteTable3 = byteTables[3];
+    private static final int[] byteTable4 = byteTables[4];
+    private static final int[] byteTable5 = byteTables[5];
+    private static final int[] byteTable6 = byteTables[6];
+    private static final int[] byteTable7 = byteTables[7];
+
+    static {
+        // Generate lookup tables
+        // High-order polynomial term stored in LSB of r.
+        for (int index = 0; index < byteTables[0].length; index++) {
+           int r = index;
+            for (int i = 0; i < Byte.SIZE; i++) {
+                if ((r & 1) != 0) {
+                    r = (r >>> 1) ^ REVERSED_CRC32C_POLY;
+                } else {
+                    r >>>= 1;
+                }
+            }
+            byteTables[0][index] = r;
+        }
+
+        for (int index = 0; index < byteTables[0].length; index++) {
+            int r = byteTables[0][index];
+
+            for (int k = 1; k < byteTables.length; k++) {
+                r = byteTables[0][r & 0xFF] ^ (r >>> 8);
+                byteTables[k][index] = r;
+            }
+        }
+
+        if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) {
+            byteTable = byteTables[0];
+        } else { // ByteOrder.BIG_ENDIAN
+            byteTable = new int[byteTable0.length];
+            System.arraycopy(byteTable0, 0, byteTable, 0, byteTable0.length);
+            for (int[] table : byteTables) {
+                for (int index = 0; index < table.length; index++) {
+                    table[index] = Integer.reverseBytes(table[index]);
+                }
+            }
+        }
+    }
+
+    /**
+     * Calculated CRC-32C value
+     */
+    private int crc = 0xFFFFFFFF;
+
+    /**
+     * Creates a new CRC32C object.
+     */
+    public CRC32C() {
+    }
+
+    /**
+     * Updates the CRC-32C checksum with the specified byte (the low eight bits
+     * of the argument b).
+     */
+    @Override
+    public void update(int b) {
+        crc = (crc >>> 8) ^ byteTable[(crc ^ (b & 0xFF)) & 0xFF];
+    }
+
+    /**
+     * Updates the CRC-32C checksum with the specified array of bytes.
+     *
+     * @throws ArrayIndexOutOfBoundsException
+     *         if {@code off} is negative, or {@code len} is negative, or
+     *         {@code off+len} is negative or greater than the length of
+     *         the array {@code b}.
+     */
+    @Override
+    public void update(byte[] b, int off, int len) {
+        if (b == null) {
+            throw new NullPointerException();
+        }
+        if (off < 0 || len < 0 || off > b.length - len) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        crc = updateBytes(crc, b, off, (off + len));
+    }
+
+    /**
+     * Updates the CRC-32C checksum with the bytes from the specified buffer.
+     *
+     * The checksum is updated with the remaining bytes in the buffer, starting
+     * at the buffer's position. Upon return, the buffer's position will be
+     * updated to its limit; its limit will not have been changed.
+     */
+    @Override
+    public void update(ByteBuffer buffer) {
+        int pos = buffer.position();
+        int limit = buffer.limit();
+        assert (pos <= limit);
+        int rem = limit - pos;
+        if (rem <= 0) {
+            return;
+        }
+
+        if (buffer.isDirect()) {
+            try {
+                crc = updateDirectByteBuffer(crc, ((DirectBuffer) buffer).address(),
+                        pos, limit);
+            } finally {
+                Reference.reachabilityFence(buffer);
+            }
+        } else if (buffer.hasArray()) {
+            crc = updateBytes(crc, buffer.array(), pos + buffer.arrayOffset(),
+                              limit + buffer.arrayOffset());
+        } else {
+            byte[] b = new byte[Math.min(buffer.remaining(), 4096)];
+            while (buffer.hasRemaining()) {
+                int length = Math.min(buffer.remaining(), b.length);
+                buffer.get(b, 0, length);
+                update(b, 0, length);
+            }
+        }
+        buffer.position(limit);
+    }
+
+    /**
+     * Resets CRC-32C to initial value.
+     */
+    @Override
+    public void reset() {
+        crc = 0xFFFFFFFF;
+    }
+
+    /**
+     * Returns CRC-32C value.
+     */
+    @Override
+    public long getValue() {
+        return (~crc) & 0xFFFFFFFFL;
+    }
+
+    /**
+     * Updates the CRC-32C checksum with the specified array of bytes.
+     */
+    @IntrinsicCandidate
+    private static int updateBytes(int crc, byte[] b, int off, int end) {
+
+        // Do only byte reads for arrays so short they can't be aligned
+        // or if bytes are stored with a larger witdh than one byte.,%
+        if (end - off >= 8 && Unsafe.ARRAY_BYTE_INDEX_SCALE == 1) {
+
+            // align on 8 bytes
+            int alignLength
+                    = (8 - ((Unsafe.ARRAY_BYTE_BASE_OFFSET + off) & 0x7)) & 0x7;
+            for (int alignEnd = off + alignLength; off < alignEnd; off++) {
+                crc = (crc >>> 8) ^ byteTable[(crc ^ b[off]) & 0xFF];
+            }
+
+            if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) {
+                crc = Integer.reverseBytes(crc);
+            }
+
+            // slicing-by-8
+            for (; off < (end - Long.BYTES); off += Long.BYTES) {
+                int firstHalf;
+                int secondHalf;
+                if (Unsafe.ADDRESS_SIZE == 4) {
+                    // On 32 bit platforms read two ints instead of a single 64bit long
+                    firstHalf = UNSAFE.getInt(b, (long)Unsafe.ARRAY_BYTE_BASE_OFFSET + off);
+                    secondHalf = UNSAFE.getInt(b, (long)Unsafe.ARRAY_BYTE_BASE_OFFSET + off
+                                               + Integer.BYTES);
+                } else {
+                    long value = UNSAFE.getLong(b, (long)Unsafe.ARRAY_BYTE_BASE_OFFSET + off);
+                    if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) {
+                        firstHalf = (int) value;
+                        secondHalf = (int) (value >>> 32);
+                    } else { // ByteOrder.BIG_ENDIAN
+                        firstHalf = (int) (value >>> 32);
+                        secondHalf = (int) value;
+                    }
+                }
+                crc ^= firstHalf;
+                if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) {
+                    crc = byteTable7[crc & 0xFF]
+                            ^ byteTable6[(crc >>> 8) & 0xFF]
+                            ^ byteTable5[(crc >>> 16) & 0xFF]
+                            ^ byteTable4[crc >>> 24]
+                            ^ byteTable3[secondHalf & 0xFF]
+                            ^ byteTable2[(secondHalf >>> 8) & 0xFF]
+                            ^ byteTable1[(secondHalf >>> 16) & 0xFF]
+                            ^ byteTable0[secondHalf >>> 24];
+                } else { // ByteOrder.BIG_ENDIAN
+                    crc = byteTable0[secondHalf & 0xFF]
+                            ^ byteTable1[(secondHalf >>> 8) & 0xFF]
+                            ^ byteTable2[(secondHalf >>> 16) & 0xFF]
+                            ^ byteTable3[secondHalf >>> 24]
+                            ^ byteTable4[crc & 0xFF]
+                            ^ byteTable5[(crc >>> 8) & 0xFF]
+                            ^ byteTable6[(crc >>> 16) & 0xFF]
+                            ^ byteTable7[crc >>> 24];
+                }
+            }
+
+            if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) {
+                crc = Integer.reverseBytes(crc);
+            }
+        }
+
+        // Tail
+        for (; off < end; off++) {
+            crc = (crc >>> 8) ^ byteTable[(crc ^ b[off]) & 0xFF];
+        }
+
+        return crc;
+    }
+
+    /**
+     * Updates the CRC-32C checksum reading from the specified address.
+     */
+    @IntrinsicCandidate
+    private static int updateDirectByteBuffer(int crc, long address,
+                                              int off, int end) {
+
+        // Do only byte reads for arrays so short they can't be aligned
+        if (end - off >= 8) {
+
+            // align on 8 bytes
+            int alignLength = (8 - (int) ((address + off) & 0x7)) & 0x7;
+            for (int alignEnd = off + alignLength; off < alignEnd; off++) {
+                crc = (crc >>> 8)
+                        ^ byteTable[(crc ^ UNSAFE.getByte(address + off)) & 0xFF];
+            }
+
+            if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) {
+                crc = Integer.reverseBytes(crc);
+            }
+
+            // slicing-by-8
+            for (; off <= (end - Long.BYTES); off += Long.BYTES) {
+                // Always reading two ints as reading a long followed by
+                // shifting and casting was slower.
+                int firstHalf = UNSAFE.getInt(address + off);
+                int secondHalf = UNSAFE.getInt(address + off + Integer.BYTES);
+                crc ^= firstHalf;
+                if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) {
+                    crc = byteTable7[crc & 0xFF]
+                            ^ byteTable6[(crc >>> 8) & 0xFF]
+                            ^ byteTable5[(crc >>> 16) & 0xFF]
+                            ^ byteTable4[crc >>> 24]
+                            ^ byteTable3[secondHalf & 0xFF]
+                            ^ byteTable2[(secondHalf >>> 8) & 0xFF]
+                            ^ byteTable1[(secondHalf >>> 16) & 0xFF]
+                            ^ byteTable0[secondHalf >>> 24];
+                } else { // ByteOrder.BIG_ENDIAN
+                    crc = byteTable0[secondHalf & 0xFF]
+                            ^ byteTable1[(secondHalf >>> 8) & 0xFF]
+                            ^ byteTable2[(secondHalf >>> 16) & 0xFF]
+                            ^ byteTable3[secondHalf >>> 24]
+                            ^ byteTable4[crc & 0xFF]
+                            ^ byteTable5[(crc >>> 8) & 0xFF]
+                            ^ byteTable6[(crc >>> 16) & 0xFF]
+                            ^ byteTable7[crc >>> 24];
+                }
+            }
+
+            if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) {
+                crc = Integer.reverseBytes(crc);
+            }
+        }
+
+        // Tail
+        for (; off < end; off++) {
+            crc = (crc >>> 8)
+                    ^ byteTable[(crc ^ UNSAFE.getByte(address + off)) & 0xFF];
+        }
+
+        return crc;
+    }
+}
diff --git a/android-35/java/util/zip/CheckedInputStream.java b/android-35/java/util/zip/CheckedInputStream.java
new file mode 100644
index 0000000..a97c589
--- /dev/null
+++ b/android-35/java/util/zip/CheckedInputStream.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * An input stream that also maintains a checksum of the data being read.
+ * The checksum can then be used to verify the integrity of the input data.
+ *
+ * @see         Checksum
+ * @author      David Connelly
+ * @since 1.1
+ */
+public class CheckedInputStream extends FilterInputStream {
+    private Checksum cksum;
+
+    /**
+     * Creates an input stream using the specified Checksum.
+     * @param in the input stream
+     * @param cksum the Checksum
+     */
+    public CheckedInputStream(InputStream in, Checksum cksum) {
+        super(in);
+        this.cksum = cksum;
+    }
+
+    /**
+     * Reads a byte. Will block if no input is available.
+     * @return the byte read, or -1 if the end of the stream is reached.
+     * @throws    IOException if an I/O error has occurred
+     */
+    public int read() throws IOException {
+        int b = in.read();
+        if (b != -1) {
+            cksum.update(b);
+        }
+        return b;
+    }
+
+    /**
+     * Reads into an array of bytes. If {@code len} is not zero, the method
+     * blocks until some input is available; otherwise, no
+     * bytes are read and {@code 0} is returned.
+     * @param buf the buffer into which the data is read
+     * @param off the start offset in the destination array {@code b}
+     * @param len the maximum number of bytes read
+     * @return    the actual number of bytes read, or -1 if the end
+     *            of the stream is reached.
+     * @throws     NullPointerException If {@code buf} is {@code null}.
+     * @throws     IndexOutOfBoundsException If {@code off} is negative,
+     * {@code len} is negative, or {@code len} is greater than
+     * {@code buf.length - off}
+     * @throws    IOException if an I/O error has occurred
+     */
+    public int read(byte[] buf, int off, int len) throws IOException {
+        len = in.read(buf, off, len);
+        if (len != -1) {
+            cksum.update(buf, off, len);
+        }
+        return len;
+    }
+
+    /**
+     * Skips specified number of bytes of input.
+     * @param n the number of bytes to skip
+     * @return the actual number of bytes skipped
+     * @throws    IOException if an I/O error has occurred
+     */
+    public long skip(long n) throws IOException {
+        byte[] buf = new byte[512];
+        long total = 0;
+        while (total < n) {
+            long len = n - total;
+            len = read(buf, 0, len < buf.length ? (int)len : buf.length);
+            if (len == -1) {
+                return total;
+            }
+            total += len;
+        }
+        return total;
+    }
+
+    /**
+     * Returns the Checksum for this input stream.
+     * @return the Checksum value
+     */
+    public Checksum getChecksum() {
+        return cksum;
+    }
+}
diff --git a/android-35/java/util/zip/CheckedOutputStream.java b/android-35/java/util/zip/CheckedOutputStream.java
new file mode 100644
index 0000000..10a0b26
--- /dev/null
+++ b/android-35/java/util/zip/CheckedOutputStream.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.FilterOutputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * An output stream that also maintains a checksum of the data being
+ * written. The checksum can then be used to verify the integrity of
+ * the output data.
+ *
+ * @see         Checksum
+ * @author      David Connelly
+ * @since 1.1
+ */
+public class CheckedOutputStream extends FilterOutputStream {
+    private Checksum cksum;
+
+    /**
+     * Creates an output stream with the specified Checksum.
+     * @param out the output stream
+     * @param cksum the checksum
+     */
+    public CheckedOutputStream(OutputStream out, Checksum cksum) {
+        super(out);
+        this.cksum = cksum;
+    }
+
+    /**
+     * Writes a byte. Will block until the byte is actually written.
+     * @param b the byte to be written
+     * @throws    IOException if an I/O error has occurred
+     */
+    public void write(int b) throws IOException {
+        out.write(b);
+        cksum.update(b);
+    }
+
+    /**
+     * Writes an array of bytes. Will block until the bytes are
+     * actually written.
+     * @param b the data to be written
+     * @param off the start offset of the data
+     * @param len the number of bytes to be written
+     * @throws    IOException if an I/O error has occurred
+     */
+    public void write(byte[] b, int off, int len) throws IOException {
+        out.write(b, off, len);
+        cksum.update(b, off, len);
+    }
+
+    /**
+     * Returns the Checksum for this output stream.
+     * @return the Checksum
+     */
+    public Checksum getChecksum() {
+        return cksum;
+    }
+}
diff --git a/android-35/java/util/zip/Checksum.java b/android-35/java/util/zip/Checksum.java
new file mode 100644
index 0000000..db9490c
--- /dev/null
+++ b/android-35/java/util/zip/Checksum.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.zip;
+
+import java.nio.ByteBuffer;
+
+/**
+ * An interface representing a data checksum.
+ *
+ * @author David Connelly
+ * @since 1.1
+ */
+public interface Checksum {
+
+    /**
+     * Updates the current checksum with the specified byte.
+     *
+     * @param b the byte to update the checksum with
+     */
+    public void update(int b);
+
+    /**
+     * Updates the current checksum with the specified array of bytes.
+     *
+     * @implSpec This default implementation is equal to calling
+     * {@code update(b, 0, b.length)}.
+     *
+     * @param b the array of bytes to update the checksum with
+     *
+     * @throws NullPointerException
+     *         if {@code b} is {@code null}
+     *
+     * @since 9
+     */
+    default public void update(byte[] b) {
+        update(b, 0, b.length);
+    }
+
+    /**
+     * Updates the current checksum with the specified array of bytes.
+     *
+     * @param b the byte array to update the checksum with
+     * @param off the start offset of the data
+     * @param len the number of bytes to use for the update
+     */
+    public void update(byte[] b, int off, int len);
+
+    /**
+     * Updates the current checksum with the bytes from the specified buffer.
+     *
+     * The checksum is updated with the remaining bytes in the buffer, starting
+     * at the buffer's position. Upon return, the buffer's position will be
+     * updated to its limit; its limit will not have been changed.
+     *
+     * @apiNote For best performance with DirectByteBuffer and other ByteBuffer
+     * implementations without a backing array implementers of this interface
+     * should override this method.
+     *
+     * @implSpec The default implementation has the following behavior.<br>
+     * For ByteBuffers backed by an accessible byte array.
+     * <pre>{@code
+     * update(buffer.array(),
+     *        buffer.position() + buffer.arrayOffset(),
+     *        buffer.remaining());
+     * }</pre>
+     * For ByteBuffers not backed by an accessible byte array.
+     * <pre>{@code
+     * byte[] b = new byte[Math.min(buffer.remaining(), 4096)];
+     * while (buffer.hasRemaining()) {
+     *     int length = Math.min(buffer.remaining(), b.length);
+     *     buffer.get(b, 0, length);
+     *     update(b, 0, length);
+     * }
+     * }</pre>
+     *
+     * @param buffer the ByteBuffer to update the checksum with
+     *
+     * @throws NullPointerException
+     *         if {@code buffer} is {@code null}
+     *
+     * @since 9
+     */
+    default public void update(ByteBuffer buffer) {
+        int pos = buffer.position();
+        int limit = buffer.limit();
+        assert (pos <= limit);
+        int rem = limit - pos;
+        if (rem <= 0) {
+            return;
+        }
+        if (buffer.hasArray()) {
+            update(buffer.array(), pos + buffer.arrayOffset(), rem);
+        } else {
+            byte[] b = new byte[Math.min(buffer.remaining(), 4096)];
+            while (buffer.hasRemaining()) {
+                int length = Math.min(buffer.remaining(), b.length);
+                buffer.get(b, 0, length);
+                update(b, 0, length);
+            }
+        }
+        buffer.position(limit);
+    }
+
+    /**
+     * Returns the current checksum value.
+     *
+     * @return the current checksum value
+     */
+    public long getValue();
+
+    /**
+     * Resets the checksum to its initial value.
+     */
+    public void reset();
+}
diff --git a/android-35/java/util/zip/DataFormatException.java b/android-35/java/util/zip/DataFormatException.java
new file mode 100644
index 0000000..cfb6eb6
--- /dev/null
+++ b/android-35/java/util/zip/DataFormatException.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+/**
+ * Signals that a data format error has occurred.
+ *
+ * @author      David Connelly
+ * @since 1.1
+ */
+public class DataFormatException extends Exception {
+    @java.io.Serial
+    private static final long serialVersionUID = 2219632870893641452L;
+
+    /**
+     * Constructs a DataFormatException with no detail message.
+     */
+    public DataFormatException() {
+        super();
+    }
+
+    /**
+     * Constructs a DataFormatException with the specified detail message.
+     * A detail message is a String that describes this particular exception.
+     * @param s the String containing a detail message
+     */
+    public DataFormatException(String s) {
+        super(s);
+    }
+}
diff --git a/android-35/java/util/zip/Deflater.java b/android-35/java/util/zip/Deflater.java
new file mode 100644
index 0000000..9910016
--- /dev/null
+++ b/android-35/java/util/zip/Deflater.java
@@ -0,0 +1,982 @@
+/*
+ * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import dalvik.annotation.optimization.ReachabilitySensitive;
+import dalvik.system.CloseGuard;
+import java.lang.ref.Cleaner.Cleanable;
+import java.lang.ref.Reference;
+import java.nio.ByteBuffer;
+import java.nio.ReadOnlyBufferException;
+import java.util.Objects;
+
+import jdk.internal.ref.CleanerFactory;
+import sun.nio.ch.DirectBuffer;
+
+/**
+ * This class provides support for general purpose compression using the
+ * popular ZLIB compression library. The ZLIB compression library was
+ * initially developed as part of the PNG graphics standard and is not
+ * protected by patents. It is fully described in the specifications at
+ * the <a href="package-summary.html#package-description">java.util.zip
+ * package description</a>.
+ * <p>
+ * This class deflates sequences of bytes into ZLIB compressed data format.
+ * The input byte sequence is provided in either byte array or byte buffer,
+ * via one of the {@code setInput()} methods. The output byte sequence is
+ * written to the output byte array or byte buffer passed to the
+ * {@code deflate()} methods.
+ * <p>
+ * The following code fragment demonstrates a trivial compression
+ * and decompression of a string using {@code Deflater} and
+ * {@code Inflater}.
+ *
+ * <blockquote><pre>
+ * try {
+ *     // Encode a String into bytes
+ *     String inputString = "blahblahblah";
+ *     byte[] input = inputString.getBytes("UTF-8");
+ *
+ *     // Compress the bytes
+ *     byte[] output = new byte[100];
+ *     Deflater compresser = new Deflater();
+ *     compresser.setInput(input);
+ *     compresser.finish();
+ *     int compressedDataLength = compresser.deflate(output);
+ *     compresser.end();
+ *
+ *     // Decompress the bytes
+ *     Inflater decompresser = new Inflater();
+ *     decompresser.setInput(output, 0, compressedDataLength);
+ *     byte[] result = new byte[100];
+ *     int resultLength = decompresser.inflate(result);
+ *     decompresser.end();
+ *
+ *     // Decode the bytes into a String
+ *     String outputString = new String(result, 0, resultLength, "UTF-8");
+ * } catch (java.io.UnsupportedEncodingException ex) {
+ *     // handle
+ * } catch (java.util.zip.DataFormatException ex) {
+ *     // handle
+ * }
+ * </pre></blockquote>
+ *
+ * @apiNote
+ * To release resources used by this {@code Deflater}, the {@link #end()} method
+ * should be called explicitly. Subclasses are responsible for the cleanup of resources
+ * acquired by the subclass. Subclasses that override {@link #finalize()} in order
+ * to perform cleanup should be modified to use alternative cleanup mechanisms such
+ * as {@link java.lang.ref.Cleaner} and remove the overriding {@code finalize} method.
+ *
+ * @see         Inflater
+ * @author      David Connelly
+ * @since 1.1
+ */
+
+public class Deflater {
+
+    private final DeflaterZStreamRef zsRef;
+    private ByteBuffer input = ZipUtils.defaultBuf;
+    private byte[] inputArray;
+    private int inputPos, inputLim;
+    private int level, strategy;
+    private boolean setParams;
+    private boolean finish, finished;
+    private long bytesRead;
+    private long bytesWritten;
+
+    // Android-added: CloseGuard support.
+    @ReachabilitySensitive
+    private final CloseGuard guard = CloseGuard.get();
+
+    /**
+     * Compression method for the deflate algorithm (the only one currently
+     * supported).
+     */
+    public static final int DEFLATED = 8;
+
+    /**
+     * Compression level for no compression.
+     */
+    public static final int NO_COMPRESSION = 0;
+
+    /**
+     * Compression level for fastest compression.
+     */
+    public static final int BEST_SPEED = 1;
+
+    /**
+     * Compression level for best compression.
+     */
+    public static final int BEST_COMPRESSION = 9;
+
+    /**
+     * Default compression level.
+     */
+    public static final int DEFAULT_COMPRESSION = -1;
+
+    /**
+     * Compression strategy best used for data consisting mostly of small
+     * values with a somewhat random distribution. Forces more Huffman coding
+     * and less string matching.
+     */
+    public static final int FILTERED = 1;
+
+    /**
+     * Compression strategy for Huffman coding only.
+     */
+    public static final int HUFFMAN_ONLY = 2;
+
+    /**
+     * Default compression strategy.
+     */
+    public static final int DEFAULT_STRATEGY = 0;
+
+    /**
+     * Compression flush mode used to achieve best compression result.
+     *
+     * @see Deflater#deflate(byte[], int, int, int)
+     * @since 1.7
+     */
+    public static final int NO_FLUSH = 0;
+
+    /**
+     * Compression flush mode used to flush out all pending output; may
+     * degrade compression for some compression algorithms.
+     *
+     * @see Deflater#deflate(byte[], int, int, int)
+     * @since 1.7
+     */
+    public static final int SYNC_FLUSH = 2;
+
+    /**
+     * Compression flush mode used to flush out all pending output and
+     * reset the deflater. Using this mode too often can seriously degrade
+     * compression.
+     *
+     * @see Deflater#deflate(byte[], int, int, int)
+     * @since 1.7
+     */
+    public static final int FULL_FLUSH = 3;
+
+    /**
+     * Flush mode to use at the end of output.  Can only be provided by the
+     * user by way of {@link #finish()}.
+     */
+    private static final int FINISH = 4;
+
+    // Android-removed: initIDs handled in register method.
+    /*
+    static {
+        ZipUtils.loadLibrary();
+    }
+    */
+
+    /**
+     * Creates a new compressor using the specified compression level.
+     * If 'nowrap' is true then the ZLIB header and checksum fields will
+     * not be used in order to support the compression format used in
+     * both GZIP and PKZIP.
+     * @param level the compression level (0-9)
+     * @param nowrap if true then use GZIP compatible compression
+     */
+    public Deflater(int level, boolean nowrap) {
+        this.level = level;
+        this.strategy = DEFAULT_STRATEGY;
+        this.zsRef = new DeflaterZStreamRef(this,
+                init(level, DEFAULT_STRATEGY, nowrap));
+        // Android-added: CloseGuard support.
+        guard.open("end");
+    }
+
+    /**
+     * Creates a new compressor using the specified compression level.
+     * Compressed data will be generated in ZLIB format.
+     * @param level the compression level (0-9)
+     */
+    public Deflater(int level) {
+        this(level, false);
+    }
+
+    /**
+     * Creates a new compressor with the default compression level.
+     * Compressed data will be generated in ZLIB format.
+     */
+    public Deflater() {
+        this(DEFAULT_COMPRESSION, false);
+    }
+
+    /**
+     * Sets input data for compression.
+     * <p>
+     * One of the {@code setInput()} methods should be called whenever
+     * {@code needsInput()} returns true indicating that more input data
+     * is required.
+     * @param input the input data bytes
+     * @param off the start offset of the data
+     * @param len the length of the data
+     * @see Deflater#needsInput
+     */
+    public void setInput(byte[] input, int off, int len) {
+        if (off < 0 || len < 0 || off > input.length - len) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        synchronized (zsRef) {
+            this.input = null;
+            this.inputArray = input;
+            this.inputPos = off;
+            this.inputLim = off + len;
+        }
+    }
+
+    /**
+     * Sets input data for compression.
+     * <p>
+     * One of the {@code setInput()} methods should be called whenever
+     * {@code needsInput()} returns true indicating that more input data
+     * is required.
+     * @param input the input data bytes
+     * @see Deflater#needsInput
+     */
+    public void setInput(byte[] input) {
+        setInput(input, 0, input.length);
+    }
+
+    /**
+     * Sets input data for compression.
+     * <p>
+     * One of the {@code setInput()} methods should be called whenever
+     * {@code needsInput()} returns true indicating that more input data
+     * is required.
+     * <p>
+     * The given buffer's position will be advanced as deflate
+     * operations are performed, up to the buffer's limit.
+     * The input buffer may be modified (refilled) between deflate
+     * operations; doing so is equivalent to creating a new buffer
+     * and setting it with this method.
+     * <p>
+     * Modifying the input buffer's contents, position, or limit
+     * concurrently with an deflate operation will result in
+     * undefined behavior, which may include incorrect operation
+     * results or operation failure.
+     *
+     * @param input the input data bytes
+     * @see Deflater#needsInput
+     * @since 11
+     */
+    public void setInput(ByteBuffer input) {
+        Objects.requireNonNull(input);
+        synchronized (zsRef) {
+            this.input = input;
+            this.inputArray = null;
+        }
+    }
+
+    /**
+     * Sets preset dictionary for compression. A preset dictionary is used
+     * when the history buffer can be predetermined. When the data is later
+     * uncompressed with Inflater.inflate(), Inflater.getAdler() can be called
+     * in order to get the Adler-32 value of the dictionary required for
+     * decompression.
+     * @param dictionary the dictionary data bytes
+     * @param off the start offset of the data
+     * @param len the length of the data
+     * @see Inflater#inflate
+     * @see Inflater#getAdler
+     */
+    public void setDictionary(byte[] dictionary, int off, int len) {
+        if (off < 0 || len < 0 || off > dictionary.length - len) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        synchronized (zsRef) {
+            ensureOpen();
+            setDictionary(zsRef.address(), dictionary, off, len);
+        }
+    }
+
+    /**
+     * Sets preset dictionary for compression. A preset dictionary is used
+     * when the history buffer can be predetermined. When the data is later
+     * uncompressed with Inflater.inflate(), Inflater.getAdler() can be called
+     * in order to get the Adler-32 value of the dictionary required for
+     * decompression.
+     * @param dictionary the dictionary data bytes
+     * @see Inflater#inflate
+     * @see Inflater#getAdler
+     */
+    public void setDictionary(byte[] dictionary) {
+        setDictionary(dictionary, 0, dictionary.length);
+    }
+
+    /**
+     * Sets preset dictionary for compression. A preset dictionary is used
+     * when the history buffer can be predetermined. When the data is later
+     * uncompressed with Inflater.inflate(), Inflater.getAdler() can be called
+     * in order to get the Adler-32 value of the dictionary required for
+     * decompression.
+     * <p>
+     * The bytes in given byte buffer will be fully consumed by this method.  On
+     * return, its position will equal its limit.
+     *
+     * @param dictionary the dictionary data bytes
+     * @see Inflater#inflate
+     * @see Inflater#getAdler
+     */
+    public void setDictionary(ByteBuffer dictionary) {
+        synchronized (zsRef) {
+            int position = dictionary.position();
+            int remaining = Math.max(dictionary.limit() - position, 0);
+            ensureOpen();
+            if (dictionary.isDirect()) {
+                long address = ((DirectBuffer) dictionary).address();
+                try {
+                    setDictionaryBuffer(zsRef.address(), address + position, remaining);
+                } finally {
+                    Reference.reachabilityFence(dictionary);
+                }
+            } else {
+                byte[] array = ZipUtils.getBufferArray(dictionary);
+                int offset = ZipUtils.getBufferOffset(dictionary);
+                setDictionary(zsRef.address(), array, offset + position, remaining);
+            }
+            dictionary.position(position + remaining);
+        }
+    }
+
+    /**
+     * Sets the compression strategy to the specified value.
+     *
+     * <p> If the compression strategy is changed, the next invocation
+     * of {@code deflate} will compress the input available so far with
+     * the old strategy (and may be flushed); the new strategy will take
+     * effect only after that invocation.
+     *
+     * @param strategy the new compression strategy
+     * @throws    IllegalArgumentException if the compression strategy is
+     *                                     invalid
+     */
+    public void setStrategy(int strategy) {
+        switch (strategy) {
+          case DEFAULT_STRATEGY:
+          case FILTERED:
+          case HUFFMAN_ONLY:
+            break;
+          default:
+            throw new IllegalArgumentException();
+        }
+        synchronized (zsRef) {
+            if (this.strategy != strategy) {
+                this.strategy = strategy;
+                setParams = true;
+            }
+        }
+    }
+
+    /**
+     * Sets the compression level to the specified value.
+     *
+     * <p> If the compression level is changed, the next invocation
+     * of {@code deflate} will compress the input available so far
+     * with the old level (and may be flushed); the new level will
+     * take effect only after that invocation.
+     *
+     * @param level the new compression level (0-9)
+     * @throws    IllegalArgumentException if the compression level is invalid
+     */
+    public void setLevel(int level) {
+        if ((level < 0 || level > 9) && level != DEFAULT_COMPRESSION) {
+            throw new IllegalArgumentException("invalid compression level");
+        }
+        synchronized (zsRef) {
+            if (this.level != level) {
+                this.level = level;
+                setParams = true;
+            }
+        }
+    }
+
+    /**
+     * Returns true if no data remains in the input buffer. This can
+     * be used to determine if one of the {@code setInput()} methods should be
+     * called in order to provide more input.
+     *
+     * @return true if the input data buffer is empty and setInput()
+     * should be called in order to provide more input
+     */
+    public boolean needsInput() {
+        synchronized (zsRef) {
+            ByteBuffer input = this.input;
+            return input == null ? inputLim == inputPos : ! input.hasRemaining();
+        }
+    }
+
+    /**
+     * When called, indicates that compression should end with the current
+     * contents of the input buffer.
+     */
+    public void finish() {
+        synchronized (zsRef) {
+            finish = true;
+        }
+    }
+
+    /**
+     * Returns true if the end of the compressed data output stream has
+     * been reached.
+     * @return true if the end of the compressed data output stream has
+     * been reached
+     */
+    public boolean finished() {
+        synchronized (zsRef) {
+            return finished;
+        }
+    }
+
+    /**
+     * Compresses the input data and fills specified buffer with compressed
+     * data. Returns actual number of bytes of compressed data. A return value
+     * of 0 indicates that {@link #needsInput() needsInput} should be called
+     * in order to determine if more input data is required.
+     *
+     * <p>This method uses {@link #NO_FLUSH} as its compression flush mode.
+     * An invocation of this method of the form {@code deflater.deflate(b, off, len)}
+     * yields the same result as the invocation of
+     * {@code deflater.deflate(b, off, len, Deflater.NO_FLUSH)}.
+     *
+     * @param output the buffer for the compressed data
+     * @param off the start offset of the data
+     * @param len the maximum number of bytes of compressed data
+     * @return the actual number of bytes of compressed data written to the
+     *         output buffer
+     */
+    public int deflate(byte[] output, int off, int len) {
+        return deflate(output, off, len, NO_FLUSH);
+    }
+
+    /**
+     * Compresses the input data and fills specified buffer with compressed
+     * data. Returns actual number of bytes of compressed data. A return value
+     * of 0 indicates that {@link #needsInput() needsInput} should be called
+     * in order to determine if more input data is required.
+     *
+     * <p>This method uses {@link #NO_FLUSH} as its compression flush mode.
+     * An invocation of this method of the form {@code deflater.deflate(b)}
+     * yields the same result as the invocation of
+     * {@code deflater.deflate(b, 0, b.length, Deflater.NO_FLUSH)}.
+     *
+     * @param output the buffer for the compressed data
+     * @return the actual number of bytes of compressed data written to the
+     *         output buffer
+     */
+    public int deflate(byte[] output) {
+        return deflate(output, 0, output.length, NO_FLUSH);
+    }
+
+    /**
+     * Compresses the input data and fills specified buffer with compressed
+     * data. Returns actual number of bytes of compressed data. A return value
+     * of 0 indicates that {@link #needsInput() needsInput} should be called
+     * in order to determine if more input data is required.
+     *
+     * <p>This method uses {@link #NO_FLUSH} as its compression flush mode.
+     * An invocation of this method of the form {@code deflater.deflate(output)}
+     * yields the same result as the invocation of
+     * {@code deflater.deflate(output, Deflater.NO_FLUSH)}.
+     *
+     * @param output the buffer for the compressed data
+     * @return the actual number of bytes of compressed data written to the
+     *         output buffer
+     * @since 11
+     */
+    public int deflate(ByteBuffer output) {
+        return deflate(output, NO_FLUSH);
+    }
+
+    /**
+     * Compresses the input data and fills the specified buffer with compressed
+     * data. Returns actual number of bytes of data compressed.
+     *
+     * <p>Compression flush mode is one of the following three modes:
+     *
+     * <ul>
+     * <li>{@link #NO_FLUSH}: allows the deflater to decide how much data
+     * to accumulate, before producing output, in order to achieve the best
+     * compression (should be used in normal use scenario). A return value
+     * of 0 in this flush mode indicates that {@link #needsInput()} should
+     * be called in order to determine if more input data is required.
+     *
+     * <li>{@link #SYNC_FLUSH}: all pending output in the deflater is flushed,
+     * to the specified output buffer, so that an inflater that works on
+     * compressed data can get all input data available so far (In particular
+     * the {@link #needsInput()} returns {@code true} after this invocation
+     * if enough output space is provided). Flushing with {@link #SYNC_FLUSH}
+     * may degrade compression for some compression algorithms and so it
+     * should be used only when necessary.
+     *
+     * <li>{@link #FULL_FLUSH}: all pending output is flushed out as with
+     * {@link #SYNC_FLUSH}. The compression state is reset so that the inflater
+     * that works on the compressed output data can restart from this point
+     * if previous compressed data has been damaged or if random access is
+     * desired. Using {@link #FULL_FLUSH} too often can seriously degrade
+     * compression.
+     * </ul>
+     *
+     * <p>In the case of {@link #FULL_FLUSH} or {@link #SYNC_FLUSH}, if
+     * the return value is {@code len}, the space available in output
+     * buffer {@code b}, this method should be invoked again with the same
+     * {@code flush} parameter and more output space. Make sure that
+     * {@code len} is greater than 6 to avoid flush marker (5 bytes) being
+     * repeatedly output to the output buffer every time this method is
+     * invoked.
+     *
+     * <p>If the {@link #setInput(ByteBuffer)} method was called to provide a buffer
+     * for input, the input buffer's position will be advanced by the number of bytes
+     * consumed by this operation.
+     *
+     * @param output the buffer for the compressed data
+     * @param off the start offset of the data
+     * @param len the maximum number of bytes of compressed data
+     * @param flush the compression flush mode
+     * @return the actual number of bytes of compressed data written to
+     *         the output buffer
+     *
+     * @throws IllegalArgumentException if the flush mode is invalid
+     * @since 1.7
+     */
+    public int deflate(byte[] output, int off, int len, int flush) {
+        if (off < 0 || len < 0 || off > output.length - len) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        if (flush != NO_FLUSH && flush != SYNC_FLUSH && flush != FULL_FLUSH) {
+            throw new IllegalArgumentException();
+        }
+        synchronized (zsRef) {
+            ensureOpen();
+
+            ByteBuffer input = this.input;
+            if (finish) {
+                // disregard given flush mode in this case
+                flush = FINISH;
+            }
+            int params;
+            if (setParams) {
+                // bit 0: true to set params
+                // bit 1-2: strategy (0, 1, or 2)
+                // bit 3-31: level (0..9 or -1)
+                params = 1 | strategy << 1 | level << 3;
+            } else {
+                params = 0;
+            }
+            int inputPos;
+            long result;
+            if (input == null) {
+                inputPos = this.inputPos;
+                result = deflateBytesBytes(zsRef.address(),
+                    inputArray, inputPos, inputLim - inputPos,
+                    output, off, len,
+                    flush, params);
+            } else {
+                inputPos = input.position();
+                int inputRem = Math.max(input.limit() - inputPos, 0);
+                if (input.isDirect()) {
+                    try {
+                        long inputAddress = ((DirectBuffer) input).address();
+                        result = deflateBufferBytes(zsRef.address(),
+                            inputAddress + inputPos, inputRem,
+                            output, off, len,
+                            flush, params);
+                    } finally {
+                        Reference.reachabilityFence(input);
+                    }
+                } else {
+                    byte[] inputArray = ZipUtils.getBufferArray(input);
+                    int inputOffset = ZipUtils.getBufferOffset(input);
+                    result = deflateBytesBytes(zsRef.address(),
+                        inputArray, inputOffset + inputPos, inputRem,
+                        output, off, len,
+                        flush, params);
+                }
+            }
+            int read = (int) (result & 0x7fff_ffffL);
+            int written = (int) (result >>> 31 & 0x7fff_ffffL);
+            if ((result >>> 62 & 1) != 0) {
+                finished = true;
+            }
+            if (params != 0 && (result >>> 63 & 1) == 0) {
+                setParams = false;
+            }
+            if (input != null) {
+                input.position(inputPos + read);
+            } else {
+                this.inputPos = inputPos + read;
+            }
+            bytesWritten += written;
+            bytesRead += read;
+            return written;
+        }
+    }
+
+    /**
+     * Compresses the input data and fills the specified buffer with compressed
+     * data. Returns actual number of bytes of data compressed.
+     *
+     * <p>Compression flush mode is one of the following three modes:
+     *
+     * <ul>
+     * <li>{@link #NO_FLUSH}: allows the deflater to decide how much data
+     * to accumulate, before producing output, in order to achieve the best
+     * compression (should be used in normal use scenario). A return value
+     * of 0 in this flush mode indicates that {@link #needsInput()} should
+     * be called in order to determine if more input data is required.
+     *
+     * <li>{@link #SYNC_FLUSH}: all pending output in the deflater is flushed,
+     * to the specified output buffer, so that an inflater that works on
+     * compressed data can get all input data available so far (In particular
+     * the {@link #needsInput()} returns {@code true} after this invocation
+     * if enough output space is provided). Flushing with {@link #SYNC_FLUSH}
+     * may degrade compression for some compression algorithms and so it
+     * should be used only when necessary.
+     *
+     * <li>{@link #FULL_FLUSH}: all pending output is flushed out as with
+     * {@link #SYNC_FLUSH}. The compression state is reset so that the inflater
+     * that works on the compressed output data can restart from this point
+     * if previous compressed data has been damaged or if random access is
+     * desired. Using {@link #FULL_FLUSH} too often can seriously degrade
+     * compression.
+     * </ul>
+     *
+     * <p>In the case of {@link #FULL_FLUSH} or {@link #SYNC_FLUSH}, if
+     * the return value is equal to the {@linkplain ByteBuffer#remaining() remaining space}
+     * of the buffer, this method should be invoked again with the same
+     * {@code flush} parameter and more output space. Make sure that
+     * the buffer has at least 6 bytes of remaining space to avoid the
+     * flush marker (5 bytes) being repeatedly output to the output buffer
+     * every time this method is invoked.
+     *
+     * <p>On success, the position of the given {@code output} byte buffer will be
+     * advanced by as many bytes as were produced by the operation, which is equal
+     * to the number returned by this method.
+     *
+     * <p>If the {@link #setInput(ByteBuffer)} method was called to provide a buffer
+     * for input, the input buffer's position will be advanced by the number of bytes
+     * consumed by this operation.
+     *
+     * @param output the buffer for the compressed data
+     * @param flush the compression flush mode
+     * @return the actual number of bytes of compressed data written to
+     *         the output buffer
+     *
+     * @throws IllegalArgumentException if the flush mode is invalid
+     * @since 11
+     */
+    public int deflate(ByteBuffer output, int flush) {
+        if (output.isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
+        if (flush != NO_FLUSH && flush != SYNC_FLUSH && flush != FULL_FLUSH) {
+            throw new IllegalArgumentException();
+        }
+        synchronized (zsRef) {
+            ensureOpen();
+
+            ByteBuffer input = this.input;
+            if (finish) {
+                // disregard given flush mode in this case
+                flush = FINISH;
+            }
+            int params;
+            if (setParams) {
+                // bit 0: true to set params
+                // bit 1-2: strategy (0, 1, or 2)
+                // bit 3-31: level (0..9 or -1)
+                params = 1 | strategy << 1 | level << 3;
+            } else {
+                params = 0;
+            }
+            int outputPos = output.position();
+            int outputRem = Math.max(output.limit() - outputPos, 0);
+            int inputPos;
+            long result;
+            if (input == null) {
+                inputPos = this.inputPos;
+                if (output.isDirect()) {
+                    long outputAddress = ((DirectBuffer) output).address();
+                    try {
+                        result = deflateBytesBuffer(zsRef.address(),
+                            inputArray, inputPos, inputLim - inputPos,
+                            outputAddress + outputPos, outputRem,
+                            flush, params);
+                    } finally {
+                        Reference.reachabilityFence(output);
+                    }
+                } else {
+                    byte[] outputArray = ZipUtils.getBufferArray(output);
+                    int outputOffset = ZipUtils.getBufferOffset(output);
+                    result = deflateBytesBytes(zsRef.address(),
+                        inputArray, inputPos, inputLim - inputPos,
+                        outputArray, outputOffset + outputPos, outputRem,
+                        flush, params);
+                }
+            } else {
+                inputPos = input.position();
+                int inputRem = Math.max(input.limit() - inputPos, 0);
+                if (input.isDirect()) {
+                    long inputAddress = ((DirectBuffer) input).address();
+                    try {
+                        if (output.isDirect()) {
+                            long outputAddress = outputPos + ((DirectBuffer) output).address();
+                            try {
+                                result = deflateBufferBuffer(zsRef.address(),
+                                    inputAddress + inputPos, inputRem,
+                                    outputAddress, outputRem,
+                                    flush, params);
+                            } finally {
+                                Reference.reachabilityFence(output);
+                            }
+                        } else {
+                            byte[] outputArray = ZipUtils.getBufferArray(output);
+                            int outputOffset = ZipUtils.getBufferOffset(output);
+                            result = deflateBufferBytes(zsRef.address(),
+                                inputAddress + inputPos, inputRem,
+                                outputArray, outputOffset + outputPos, outputRem,
+                                flush, params);
+                        }
+                    } finally {
+                        Reference.reachabilityFence(input);
+                    }
+                } else {
+                    byte[] inputArray = ZipUtils.getBufferArray(input);
+                    int inputOffset = ZipUtils.getBufferOffset(input);
+                    if (output.isDirect()) {
+                        long outputAddress = ((DirectBuffer) output).address();
+                        try {
+                            result = deflateBytesBuffer(zsRef.address(),
+                                inputArray, inputOffset + inputPos, inputRem,
+                                outputAddress + outputPos, outputRem,
+                                flush, params);
+                        } finally {
+                            Reference.reachabilityFence(output);
+                        }
+                    } else {
+                        byte[] outputArray = ZipUtils.getBufferArray(output);
+                        int outputOffset = ZipUtils.getBufferOffset(output);
+                        result = deflateBytesBytes(zsRef.address(),
+                            inputArray, inputOffset + inputPos, inputRem,
+                            outputArray, outputOffset + outputPos, outputRem,
+                            flush, params);
+                    }
+                }
+            }
+            int read = (int) (result & 0x7fff_ffffL);
+            int written = (int) (result >>> 31 & 0x7fff_ffffL);
+            if ((result >>> 62 & 1) != 0) {
+                finished = true;
+            }
+            if (params != 0 && (result >>> 63 & 1) == 0) {
+                setParams = false;
+            }
+            if (input != null) {
+                input.position(inputPos + read);
+            } else {
+                this.inputPos = inputPos + read;
+            }
+            output.position(outputPos + written);
+            bytesWritten += written;
+            bytesRead += read;
+            return written;
+        }
+    }
+
+    /**
+     * Returns the ADLER-32 value of the uncompressed data.
+     * @return the ADLER-32 value of the uncompressed data
+     */
+    public int getAdler() {
+        synchronized (zsRef) {
+            ensureOpen();
+            return getAdler(zsRef.address());
+        }
+    }
+
+    /**
+     * Returns the total number of uncompressed bytes input so far.
+     *
+     * <p>Since the number of bytes may be greater than
+     * Integer.MAX_VALUE, the {@link #getBytesRead()} method is now
+     * the preferred means of obtaining this information.</p>
+     *
+     * @return the total number of uncompressed bytes input so far
+     */
+    public int getTotalIn() {
+        return (int) getBytesRead();
+    }
+
+    /**
+     * Returns the total number of uncompressed bytes input so far.
+     *
+     * @return the total (non-negative) number of uncompressed bytes input so far
+     * @since 1.5
+     */
+    public long getBytesRead() {
+        synchronized (zsRef) {
+            ensureOpen();
+            return bytesRead;
+        }
+    }
+
+    /**
+     * Returns the total number of compressed bytes output so far.
+     *
+     * <p>Since the number of bytes may be greater than
+     * Integer.MAX_VALUE, the {@link #getBytesWritten()} method is now
+     * the preferred means of obtaining this information.</p>
+     *
+     * @return the total number of compressed bytes output so far
+     */
+    public int getTotalOut() {
+        return (int) getBytesWritten();
+    }
+
+    /**
+     * Returns the total number of compressed bytes output so far.
+     *
+     * @return the total (non-negative) number of compressed bytes output so far
+     * @since 1.5
+     */
+    public long getBytesWritten() {
+        synchronized (zsRef) {
+            ensureOpen();
+            return bytesWritten;
+        }
+    }
+
+    /**
+     * Resets deflater so that a new set of input data can be processed.
+     * Keeps current compression level and strategy settings.
+     */
+    public void reset() {
+        synchronized (zsRef) {
+            ensureOpen();
+            reset(zsRef.address());
+            finish = false;
+            finished = false;
+            input = ZipUtils.defaultBuf;
+            inputArray = null;
+            bytesRead = bytesWritten = 0;
+        }
+    }
+
+    /**
+     * Closes the compressor and discards any unprocessed input.
+     *
+     * This method should be called when the compressor is no longer
+     * being used. Once this method is called, the behavior of the
+     * Deflater object is undefined.
+     */
+    public void end() {
+        synchronized (zsRef) {
+            zsRef.clean();
+            // Android-added: CloseGuard support.
+            if (guard != null) {
+                guard.close();
+            }
+            input = ZipUtils.defaultBuf;
+        }
+    }
+
+    private void ensureOpen() {
+        assert Thread.holdsLock(zsRef);
+        if (zsRef.address() == 0)
+            throw new NullPointerException("Deflater has been closed");
+    }
+
+    /**
+     * Returns the value of 'finish' flag.
+     * 'finish' will be set to true if def.finish() method is called.
+     */
+    boolean shouldFinish() {
+        synchronized (zsRef) {
+            return finish;
+        }
+    }
+
+    private static native long init(int level, int strategy, boolean nowrap);
+    private static native void setDictionary(long addr, byte[] b, int off,
+                                             int len);
+    private static native void setDictionaryBuffer(long addr, long bufAddress, int len);
+    private native long deflateBytesBytes(long addr,
+        byte[] inputArray, int inputOff, int inputLen,
+        byte[] outputArray, int outputOff, int outputLen,
+        int flush, int params);
+    private native long deflateBytesBuffer(long addr,
+        byte[] inputArray, int inputOff, int inputLen,
+        long outputAddress, int outputLen,
+        int flush, int params);
+    private native long deflateBufferBytes(long addr,
+        long inputAddress, int inputLen,
+        byte[] outputArray, int outputOff, int outputLen,
+        int flush, int params);
+    private native long deflateBufferBuffer(long addr,
+        long inputAddress, int inputLen,
+        long outputAddress, int outputLen,
+        int flush, int params);
+    private static native int getAdler(long addr);
+    private static native void reset(long addr);
+    private static native void end(long addr);
+
+    /**
+     * A reference to the native zlib's z_stream structure. It also
+     * serves as the "cleaner" to clean up the native resource when
+     * the Deflater is ended, closed or cleaned.
+     */
+    static class DeflaterZStreamRef implements Runnable {
+
+        private long address;
+        private final Cleanable cleanable;
+
+        private DeflaterZStreamRef(Deflater owner, long addr) {
+            this.cleanable = (owner != null) ? CleanerFactory.cleaner().register(owner, this) : null;
+            this.address = addr;
+        }
+
+        long address() {
+            return address;
+        }
+
+        void clean() {
+            cleanable.clean();
+        }
+
+        public synchronized void run() {
+            long addr = address;
+            address = 0;
+            if (addr != 0) {
+                end(addr);
+            }
+        }
+
+    }
+}
diff --git a/android-35/java/util/zip/DeflaterInputStream.java b/android-35/java/util/zip/DeflaterInputStream.java
new file mode 100644
index 0000000..1408c61
--- /dev/null
+++ b/android-35/java/util/zip/DeflaterInputStream.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 2006, 2021, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * Implements an input stream filter for compressing data in the "deflate"
+ * compression format.
+ *
+ * @since       1.6
+ * @author      David R Tribble ([email protected])
+ *
+ * @see DeflaterOutputStream
+ * @see InflaterOutputStream
+ * @see InflaterInputStream
+ */
+
+public class DeflaterInputStream extends FilterInputStream {
+    /** Compressor for this stream. */
+    protected final Deflater def;
+
+    /** Input buffer for reading compressed data. */
+    protected final byte[] buf;
+
+    /** Temporary read buffer. */
+    private byte[] rbuf = new byte[1];
+
+    /** Default compressor is used. */
+    private boolean usesDefaultDeflater = false;
+
+    /** End of the underlying input stream has been reached. */
+    private boolean reachEOF = false;
+
+    /**
+     * Check to make sure that this stream has not been closed.
+     */
+    private void ensureOpen() throws IOException {
+        if (in == null) {
+            throw new IOException("Stream closed");
+        }
+    }
+
+    /**
+     * Creates a new input stream with a default compressor and buffer
+     * size.
+     *
+     * @param in input stream to read the uncompressed data to
+     * @throws NullPointerException if {@code in} is null
+     */
+    public DeflaterInputStream(InputStream in) {
+        this(in, in != null ? new Deflater() : null);
+        usesDefaultDeflater = true;
+    }
+
+    /**
+     * Creates a new input stream with the specified compressor and a
+     * default buffer size.
+     *
+     * @param in input stream to read the uncompressed data to
+     * @param defl compressor ("deflater") for this stream
+     * @throws NullPointerException if {@code in} or {@code defl} is null
+     */
+    public DeflaterInputStream(InputStream in, Deflater defl) {
+        this(in, defl, 512);
+    }
+
+    /**
+     * Creates a new input stream with the specified compressor and buffer
+     * size.
+     *
+     * @param in input stream to read the uncompressed data to
+     * @param defl compressor ("deflater") for this stream
+     * @param bufLen compression buffer size
+     * @throws IllegalArgumentException if {@code bufLen <= 0}
+     * @throws NullPointerException if {@code in} or {@code defl} is null
+     */
+    public DeflaterInputStream(InputStream in, Deflater defl, int bufLen) {
+        super(in);
+
+        // Sanity checks
+        if (in == null)
+            throw new NullPointerException("Null input");
+        if (defl == null)
+            throw new NullPointerException("Null deflater");
+        if (bufLen < 1)
+            throw new IllegalArgumentException("Buffer size < 1");
+
+        // Initialize
+        def = defl;
+        buf = new byte[bufLen];
+    }
+
+    /**
+     * Closes this input stream and its underlying input stream, discarding
+     * any pending uncompressed data.
+     *
+     * @throws IOException if an I/O error occurs
+     */
+    public void close() throws IOException {
+        if (in != null) {
+            try {
+                // Clean up
+                if (usesDefaultDeflater) {
+                    def.end();
+                }
+
+                in.close();
+            } finally {
+                in = null;
+            }
+        }
+    }
+
+    /**
+     * Reads a single byte of compressed data from the input stream.
+     * This method will block until some input can be read and compressed.
+     *
+     * @return a single byte of compressed data, or -1 if the end of the
+     * uncompressed input stream is reached
+     * @throws IOException if an I/O error occurs or if this stream is
+     * already closed
+     */
+    public int read() throws IOException {
+        // Read a single byte of compressed data
+        int len = read(rbuf, 0, 1);
+        if (len <= 0)
+            return -1;
+        return (rbuf[0] & 0xFF);
+    }
+
+    /**
+     * Reads compressed data into a byte array.
+     * This method will block until some input can be read and compressed.
+     *
+     * @param b buffer into which the data is read
+     * @param off starting offset of the data within {@code b}
+     * @param len maximum number of compressed bytes to read into {@code b}
+     * @return the actual number of bytes read, or -1 if the end of the
+     * uncompressed input stream is reached
+     * @throws IndexOutOfBoundsException  if {@code len > b.length - off}
+     * @throws IOException if an I/O error occurs or if this input stream is
+     * already closed
+     */
+    public int read(byte[] b, int off, int len) throws IOException {
+        // Sanity checks
+        ensureOpen();
+        if (b == null) {
+            throw new NullPointerException("Null buffer for read");
+        } else if (off < 0 || len < 0 || len > b.length - off) {
+            throw new IndexOutOfBoundsException();
+        } else if (len == 0) {
+            return 0;
+        }
+
+        // Read and compress (deflate) input data bytes
+        int cnt = 0;
+        while (len > 0 && !def.finished()) {
+            int n;
+
+            // Read data from the input stream
+            if (def.needsInput()) {
+                n = in.read(buf, 0, buf.length);
+                if (n < 0) {
+                    // End of the input stream reached
+                    def.finish();
+                } else if (n > 0) {
+                    def.setInput(buf, 0, n);
+                }
+            }
+
+            // Compress the input data, filling the read buffer
+            n = def.deflate(b, off, len);
+            cnt += n;
+            off += n;
+            len -= n;
+        }
+        // BEGIN Android-changed: Return more accurate value from available().
+        // Set reachEOF eagerly when the Deflater has finished, and not just when the number of
+        // bytes is zero so that available is more accurate.
+        // See http://b/111589691
+        /*
+        if (cnt == 0 && def.finished()) {
+            reachEOF = true;
+            cnt = -1;
+        }
+        */
+        if (def.finished()) {
+            reachEOF = true;
+            if (cnt == 0) {
+                cnt = -1;
+            }
+        }
+        // END Android-changed: Return more accurate value from available().
+
+        return cnt;
+    }
+
+    /**
+     * Skips over and discards data from the input stream.
+     * This method may block until the specified number of bytes are read and
+     * skipped. <em>Note:</em> While {@code n} is given as a {@code long},
+     * the maximum number of bytes which can be skipped is
+     * {@code Integer.MAX_VALUE}.
+     *
+     * @param n number of bytes to be skipped
+     * @return the actual number of bytes skipped
+     * @throws IOException if an I/O error occurs or if this stream is
+     * already closed
+     */
+    public long skip(long n) throws IOException {
+        if (n < 0) {
+            throw new IllegalArgumentException("negative skip length");
+        }
+        ensureOpen();
+
+        // Skip bytes by repeatedly decompressing small blocks
+        if (rbuf.length < 512)
+            rbuf = new byte[512];
+
+        int total = (int)Math.min(n, Integer.MAX_VALUE);
+        long cnt = 0;
+        while (total > 0) {
+            // Read a small block of uncompressed bytes
+            int len = read(rbuf, 0, (total <= rbuf.length ? total : rbuf.length));
+
+            if (len < 0) {
+                break;
+            }
+            cnt += len;
+            total -= len;
+        }
+        return cnt;
+    }
+
+    /**
+     * Returns 0 after EOF has been reached, otherwise always return 1.
+     * <p>
+     * Programs should not count on this method to return the actual number
+     * of bytes that could be read without blocking
+     * @return zero after the end of the underlying input stream has been
+     * reached, otherwise always returns 1
+     * @throws IOException if an I/O error occurs or if this stream is
+     * already closed
+     */
+    public int available() throws IOException {
+        ensureOpen();
+        if (reachEOF) {
+            return 0;
+        }
+        return 1;
+    }
+
+    /**
+     * Always returns {@code false} because this input stream does not support
+     * the {@link #mark mark()} and {@link #reset reset()} methods.
+     *
+     * @return false, always
+     */
+    public boolean markSupported() {
+        return false;
+    }
+
+    /**
+     * <i>This operation is not supported</i>.
+     *
+     * @param limit maximum bytes that can be read before invalidating the position marker
+     */
+    public void mark(int limit) {
+        // Operation not supported
+    }
+
+    /**
+     * <i>This operation is not supported</i>.
+     *
+     * @throws IOException always thrown
+     */
+    public void reset() throws IOException {
+        throw new IOException("mark/reset not supported");
+    }
+}
diff --git a/android-35/java/util/zip/DeflaterOutputStream.java b/android-35/java/util/zip/DeflaterOutputStream.java
new file mode 100644
index 0000000..8f3379f
--- /dev/null
+++ b/android-35/java/util/zip/DeflaterOutputStream.java
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.FilterOutputStream;
+import java.io.OutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * This class implements an output stream filter for compressing data in
+ * the "deflate" compression format. It is also used as the basis for other
+ * types of compression filters, such as GZIPOutputStream.
+ *
+ * @see         Deflater
+ * @author      David Connelly
+ * @since 1.1
+ */
+public class DeflaterOutputStream extends FilterOutputStream {
+    /**
+     * Compressor for this stream.
+     */
+    protected Deflater def;
+
+    /**
+     * Output buffer for writing compressed data.
+     */
+    protected byte[] buf;
+
+    /**
+     * Indicates that the stream has been closed.
+     */
+    private boolean closed = false;
+
+    private final boolean syncFlush;
+
+    /**
+     * Creates a new output stream with the specified compressor,
+     * buffer size and flush mode.
+     *
+     * @param out the output stream
+     * @param def the compressor ("deflater")
+     * @param size the output buffer size
+     * @param syncFlush
+     *        if {@code true} the {@link #flush()} method of this
+     *        instance flushes the compressor with flush mode
+     *        {@link Deflater#SYNC_FLUSH} before flushing the output
+     *        stream, otherwise only flushes the output stream
+     *
+     * @throws IllegalArgumentException if {@code size <= 0}
+     *
+     * @since 1.7
+     */
+    public DeflaterOutputStream(OutputStream out,
+                                Deflater def,
+                                int size,
+                                boolean syncFlush) {
+        super(out);
+        if (out == null || def == null) {
+            throw new NullPointerException();
+        } else if (size <= 0) {
+            throw new IllegalArgumentException("buffer size <= 0");
+        }
+        this.def = def;
+        this.buf = new byte[size];
+        this.syncFlush = syncFlush;
+    }
+
+
+    /**
+     * Creates a new output stream with the specified compressor and
+     * buffer size.
+     *
+     * <p>The new output stream instance is created as if by invoking
+     * the 4-argument constructor DeflaterOutputStream(out, def, size, false).
+     *
+     * @param out the output stream
+     * @param def the compressor ("deflater")
+     * @param size the output buffer size
+     * @throws    IllegalArgumentException if {@code size <= 0}
+     */
+    public DeflaterOutputStream(OutputStream out, Deflater def, int size) {
+        this(out, def, size, false);
+    }
+
+    /**
+     * Creates a new output stream with the specified compressor, flush
+     * mode and a default buffer size.
+     *
+     * @param out the output stream
+     * @param def the compressor ("deflater")
+     * @param syncFlush
+     *        if {@code true} the {@link #flush()} method of this
+     *        instance flushes the compressor with flush mode
+     *        {@link Deflater#SYNC_FLUSH} before flushing the output
+     *        stream, otherwise only flushes the output stream
+     *
+     * @since 1.7
+     */
+    public DeflaterOutputStream(OutputStream out,
+                                Deflater def,
+                                boolean syncFlush) {
+        this(out, def, 512, syncFlush);
+    }
+
+
+    /**
+     * Creates a new output stream with the specified compressor and
+     * a default buffer size.
+     *
+     * <p>The new output stream instance is created as if by invoking
+     * the 3-argument constructor DeflaterOutputStream(out, def, false).
+     *
+     * @param out the output stream
+     * @param def the compressor ("deflater")
+     */
+    public DeflaterOutputStream(OutputStream out, Deflater def) {
+        this(out, def, 512, false);
+    }
+
+    boolean usesDefaultDeflater = false;
+
+
+    /**
+     * Creates a new output stream with a default compressor, a default
+     * buffer size and the specified flush mode.
+     *
+     * @param out the output stream
+     * @param syncFlush
+     *        if {@code true} the {@link #flush()} method of this
+     *        instance flushes the compressor with flush mode
+     *        {@link Deflater#SYNC_FLUSH} before flushing the output
+     *        stream, otherwise only flushes the output stream
+     *
+     * @since 1.7
+     */
+    public DeflaterOutputStream(OutputStream out, boolean syncFlush) {
+        this(out, out != null ? new Deflater() : null, 512, syncFlush);
+        usesDefaultDeflater = true;
+    }
+
+    /**
+     * Creates a new output stream with a default compressor and buffer size.
+     *
+     * <p>The new output stream instance is created as if by invoking
+     * the 2-argument constructor DeflaterOutputStream(out, false).
+     *
+     * @param out the output stream
+     */
+    public DeflaterOutputStream(OutputStream out) {
+        this(out, false);
+        usesDefaultDeflater = true;
+    }
+
+    /**
+     * Writes a byte to the compressed output stream. This method will
+     * block until the byte can be written.
+     * @param b the byte to be written
+     * @throws    IOException if an I/O error has occurred
+     */
+    public void write(int b) throws IOException {
+        byte[] buf = new byte[1];
+        buf[0] = (byte)(b & 0xff);
+        write(buf, 0, 1);
+    }
+
+    /**
+     * Writes an array of bytes to the compressed output stream. This
+     * method will block until all the bytes are written.
+     * @param b the data to be written
+     * @param off the start offset of the data
+     * @param len the length of the data
+     * @throws    IOException if an I/O error has occurred
+     */
+    public void write(byte[] b, int off, int len) throws IOException {
+        if (def.finished()) {
+            throw new IOException("write beyond end of stream");
+        }
+        if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
+            throw new IndexOutOfBoundsException();
+        } else if (len == 0) {
+            return;
+        }
+        if (!def.finished()) {
+            def.setInput(b, off, len);
+            while (!def.needsInput()) {
+                deflate();
+            }
+        }
+    }
+
+    /**
+     * Finishes writing compressed data to the output stream without closing
+     * the underlying stream. Use this method when applying multiple filters
+     * in succession to the same output stream.
+     * @throws    IOException if an I/O error has occurred
+     */
+    public void finish() throws IOException {
+        if (!def.finished()) {
+            try{
+                def.finish();
+                while (!def.finished()) {
+                    deflate();
+                }
+            } catch(IOException e) {
+                if (usesDefaultDeflater)
+                    def.end();
+                throw e;
+            }
+        }
+    }
+
+    /**
+     * Writes remaining compressed data to the output stream and closes the
+     * underlying stream.
+     * @throws    IOException if an I/O error has occurred
+     */
+    public void close() throws IOException {
+        if (!closed) {
+            try {
+                finish();
+            } finally {
+                if (usesDefaultDeflater)
+                    def.end();
+            }
+            out.close();
+            closed = true;
+        }
+    }
+
+    /**
+     * Writes next block of compressed data to the output stream.
+     * @throws IOException if an I/O error has occurred
+     */
+    protected void deflate() throws IOException {
+        // Android-changed: Output all available compressed data (b/4005091).
+        // See http://b/111496419 for more details.
+        // int len = def.deflate(buf, 0, buf.length);
+        // if (len > 0) {
+        //     out.write(buf, 0, len);
+        // }
+        int len = 0;
+        while ((len = def.deflate(buf, 0, buf.length)) > 0) {
+          out.write(buf, 0, len);
+        }
+    }
+
+    /**
+     * Flushes the compressed output stream.
+     *
+     * If {@link #DeflaterOutputStream(OutputStream, Deflater, int, boolean)
+     * syncFlush} is {@code true} when this compressed output stream is
+     * constructed, this method first flushes the underlying {@code compressor}
+     * with the flush mode {@link Deflater#SYNC_FLUSH} to force
+     * all pending data to be flushed out to the output stream and then
+     * flushes the output stream. Otherwise this method only flushes the
+     * output stream without flushing the {@code compressor}.
+     *
+     * @throws IOException if an I/O error has occurred
+     *
+     * @since 1.7
+     */
+    public void flush() throws IOException {
+        if (syncFlush && !def.finished()) {
+            int len = 0;
+            while ((len = def.deflate(buf, 0, buf.length, Deflater.SYNC_FLUSH)) > 0)
+            {
+                out.write(buf, 0, len);
+                if (len < buf.length)
+                    break;
+            }
+        }
+        out.flush();
+    }
+}
diff --git a/android-35/java/util/zip/GZIPInputStream.java b/android-35/java/util/zip/GZIPInputStream.java
new file mode 100644
index 0000000..871e9d3
--- /dev/null
+++ b/android-35/java/util/zip/GZIPInputStream.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.SequenceInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.EOFException;
+
+/**
+ * This class implements a stream filter for reading compressed data in
+ * the GZIP file format.
+ *
+ * @see         InflaterInputStream
+ * @author      David Connelly
+ * @since 1.1
+ *
+ */
+public class GZIPInputStream extends InflaterInputStream {
+    /**
+     * CRC-32 for uncompressed data.
+     */
+    protected CRC32 crc = new CRC32();
+
+    /**
+     * Indicates end of input stream.
+     */
+    protected boolean eos;
+
+    private boolean closed = false;
+
+    /**
+     * Check to make sure that this stream has not been closed
+     */
+    private void ensureOpen() throws IOException {
+        if (closed) {
+            throw new IOException("Stream closed");
+        }
+    }
+
+    /**
+     * Creates a new input stream with the specified buffer size.
+     *
+     * Android-note: Android limits the number of UnbufferedIO operations that can be performed, so
+     * consider using buffered inputs with this class. More information can be found in the
+     * <a href="https://developer.android.com/reference/android/os/StrictMode.ThreadPolicy.Builder#detectUnbufferedIo()">
+     * UnbufferedIO</a> and
+     * <a href="https://developer.android.com/reference/android/os/StrictMode"> StrictMode</a>
+     * documentation.
+     *
+     * @param in the input stream
+     * @param size the input buffer size
+     *
+     * @throws    ZipException if a GZIP format error has occurred or the
+     *                         compression method used is unsupported
+     * @throws    IOException if an I/O error has occurred
+     * @throws    IllegalArgumentException if {@code size <= 0}
+     */
+    public GZIPInputStream(InputStream in, int size) throws IOException {
+        super(in, in != null ? new Inflater(true) : null, size);
+        // Android-removed: Unconditionally close external inflaters (b/26462400)
+        // usesDefaultInflater = true;
+        // BEGIN Android-changed: Do not rely on finalization to inf.end().
+        // readHeader(in);
+        try {
+            readHeader(in);
+        } catch (Exception e) {
+            inf.end();
+            throw e;
+        }
+        // END Android-changed: Do not rely on finalization to inf.end().
+    }
+
+    /**
+     * Creates a new input stream with a default buffer size.
+     * @param in the input stream
+     *
+     * @throws    ZipException if a GZIP format error has occurred or the
+     *                         compression method used is unsupported
+     * @throws    IOException if an I/O error has occurred
+     */
+    public GZIPInputStream(InputStream in) throws IOException {
+        this(in, 512);
+    }
+
+    /**
+     * Reads uncompressed data into an array of bytes. If {@code len} is not
+     * zero, the method will block until some input can be decompressed; otherwise,
+     * no bytes are read and {@code 0} is returned.
+     * @param buf the buffer into which the data is read
+     * @param off the start offset in the destination array {@code b}
+     * @param len the maximum number of bytes read
+     * @return  the actual number of bytes read, or -1 if the end of the
+     *          compressed input stream is reached
+     *
+     * @throws     NullPointerException If {@code buf} is {@code null}.
+     * @throws     IndexOutOfBoundsException If {@code off} is negative,
+     * {@code len} is negative, or {@code len} is greater than
+     * {@code buf.length - off}
+     * @throws    ZipException if the compressed input data is corrupt.
+     * @throws    IOException if an I/O error has occurred.
+     *
+     */
+    public int read(byte[] buf, int off, int len) throws IOException {
+        ensureOpen();
+        if (eos) {
+            return -1;
+        }
+        int n = super.read(buf, off, len);
+        if (n == -1) {
+            if (readTrailer())
+                eos = true;
+            else
+                return this.read(buf, off, len);
+        } else {
+            crc.update(buf, off, n);
+        }
+        return n;
+    }
+
+    /**
+     * Closes this input stream and releases any system resources associated
+     * with the stream.
+     * @throws    IOException if an I/O error has occurred
+     */
+    public void close() throws IOException {
+        if (!closed) {
+            super.close();
+            eos = true;
+            closed = true;
+        }
+    }
+
+    /**
+     * GZIP header magic number.
+     */
+    public static final int GZIP_MAGIC = 0x8b1f;
+
+    /*
+     * File header flags.
+     */
+    private static final int FTEXT      = 1;    // Extra text
+    private static final int FHCRC      = 2;    // Header CRC
+    private static final int FEXTRA     = 4;    // Extra field
+    private static final int FNAME      = 8;    // File name
+    private static final int FCOMMENT   = 16;   // File comment
+
+    /*
+     * Reads GZIP member header and returns the total byte number
+     * of this member header.
+     */
+    private int readHeader(InputStream this_in) throws IOException {
+        CheckedInputStream in = new CheckedInputStream(this_in, crc);
+        crc.reset();
+        // Check header magic
+        if (readUShort(in) != GZIP_MAGIC) {
+            throw new ZipException("Not in GZIP format");
+        }
+        // Check compression method
+        if (readUByte(in) != 8) {
+            throw new ZipException("Unsupported compression method");
+        }
+        // Read flags
+        int flg = readUByte(in);
+        // Skip MTIME, XFL, and OS fields
+        skipBytes(in, 6);
+        int n = 2 + 2 + 6;
+        // Skip optional extra field
+        if ((flg & FEXTRA) == FEXTRA) {
+            int m = readUShort(in);
+            skipBytes(in, m);
+            n += m + 2;
+        }
+        // Skip optional file name
+        if ((flg & FNAME) == FNAME) {
+            do {
+                n++;
+            } while (readUByte(in) != 0);
+        }
+        // Skip optional file comment
+        if ((flg & FCOMMENT) == FCOMMENT) {
+            do {
+                n++;
+            } while (readUByte(in) != 0);
+        }
+        // Check optional header CRC
+        if ((flg & FHCRC) == FHCRC) {
+            int v = (int)crc.getValue() & 0xffff;
+            if (readUShort(in) != v) {
+                throw new ZipException("Corrupt GZIP header");
+            }
+            n += 2;
+        }
+        crc.reset();
+        return n;
+    }
+
+    /*
+     * Reads GZIP member trailer and returns true if the eos
+     * reached, false if there are more (concatenated gzip
+     * data set)
+     */
+    private boolean readTrailer() throws IOException {
+        InputStream in = this.in;
+        int n = inf.getRemaining();
+        if (n > 0) {
+            in = new SequenceInputStream(
+                        new ByteArrayInputStream(buf, len - n, n),
+                        new FilterInputStream(in) {
+                            public void close() throws IOException {}
+                        });
+        }
+        // Uses left-to-right evaluation order
+        if ((readUInt(in) != crc.getValue()) ||
+            // rfc1952; ISIZE is the input size modulo 2^32
+            (readUInt(in) != (inf.getBytesWritten() & 0xffffffffL)))
+            throw new ZipException("Corrupt GZIP trailer");
+
+        // If there are more bytes available in "in" or
+        // the leftover in the "inf" is > 26 bytes:
+        // this.trailer(8) + next.header.min(10) + next.trailer(8)
+        // try concatenated case
+        if (this.in.available() > 0 || n > 26) {
+            int m = 8;                  // this.trailer
+            try {
+                m += readHeader(in);    // next.header
+            } catch (IOException ze) {
+                return true;  // ignore any malformed, do nothing
+            }
+            inf.reset();
+            if (n > m)
+                inf.setInput(buf, len - n + m, n - m);
+            return false;
+        }
+        return true;
+    }
+
+    /*
+     * Reads unsigned integer in Intel byte order.
+     */
+    private long readUInt(InputStream in) throws IOException {
+        long s = readUShort(in);
+        return ((long)readUShort(in) << 16) | s;
+    }
+
+    /*
+     * Reads unsigned short in Intel byte order.
+     */
+    private int readUShort(InputStream in) throws IOException {
+        int b = readUByte(in);
+        return (readUByte(in) << 8) | b;
+    }
+
+    /*
+     * Reads unsigned byte.
+     */
+    private int readUByte(InputStream in) throws IOException {
+        int b = in.read();
+        if (b == -1) {
+            throw new EOFException();
+        }
+        if (b < -1 || b > 255) {
+            // Report on this.in, not argument in; see read{Header, Trailer}.
+            throw new IOException(this.in.getClass().getName()
+                + ".read() returned value out of range -1..255: " + b);
+        }
+        return b;
+    }
+
+    private byte[] tmpbuf = new byte[128];
+
+    /*
+     * Skips bytes of input data blocking until all bytes are skipped.
+     * Does not assume that the input stream is capable of seeking.
+     */
+    private void skipBytes(InputStream in, int n) throws IOException {
+        while (n > 0) {
+            int len = in.read(tmpbuf, 0, n < tmpbuf.length ? n : tmpbuf.length);
+            if (len == -1) {
+                throw new EOFException();
+            }
+            n -= len;
+        }
+    }
+}
diff --git a/android-35/java/util/zip/GZIPOutputStream.java b/android-35/java/util/zip/GZIPOutputStream.java
new file mode 100644
index 0000000..6752092
--- /dev/null
+++ b/android-35/java/util/zip/GZIPOutputStream.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * This class implements a stream filter for writing compressed data in
+ * the GZIP file format.
+ * @author      David Connelly
+ * @since 1.1
+ *
+ */
+public class GZIPOutputStream extends DeflaterOutputStream {
+    /**
+     * CRC-32 of uncompressed data.
+     */
+    protected CRC32 crc = new CRC32();
+
+    /*
+     * GZIP header magic number.
+     */
+    private static final int GZIP_MAGIC = 0x8b1f;
+
+    /*
+     * Trailer size in bytes.
+     *
+     */
+    private static final int TRAILER_SIZE = 8;
+
+    // Represents the default "unknown" value for OS header, per RFC-1952
+    private static final byte OS_UNKNOWN = (byte) 255;
+
+    /**
+     * Creates a new output stream with the specified buffer size.
+     *
+     * <p>The new output stream instance is created as if by invoking
+     * the 3-argument constructor GZIPOutputStream(out, size, false).
+     *
+     * Android-note: Android limits the number of UnbufferedIO operations that can be performed, so
+     * consider using buffered inputs with this class. More information can be found in the
+     * <a href="https://developer.android.com/reference/android/os/StrictMode.ThreadPolicy.Builder#detectUnbufferedIo()">
+     * UnbufferedIO</a> and
+     * <a href="https://developer.android.com/reference/android/os/StrictMode"> StrictMode</a>
+     * documentation.
+     *
+     * @param out the output stream
+     * @param size the output buffer size
+     * @throws    IOException If an I/O error has occurred.
+     * @throws    IllegalArgumentException if {@code size <= 0}
+     */
+    public GZIPOutputStream(OutputStream out, int size) throws IOException {
+        this(out, size, false);
+    }
+
+    /**
+     * Creates a new output stream with the specified buffer size and
+     * flush mode.
+     *
+     * @param out the output stream
+     * @param size the output buffer size
+     * @param syncFlush
+     *        if {@code true} invocation of the inherited
+     *        {@link DeflaterOutputStream#flush() flush()} method of
+     *        this instance flushes the compressor with flush mode
+     *        {@link Deflater#SYNC_FLUSH} before flushing the output
+     *        stream, otherwise only flushes the output stream
+     * @throws    IOException If an I/O error has occurred.
+     * @throws    IllegalArgumentException if {@code size <= 0}
+     *
+     * @since 1.7
+     */
+    public GZIPOutputStream(OutputStream out, int size, boolean syncFlush)
+        throws IOException
+    {
+        super(out, out != null ? new Deflater(Deflater.DEFAULT_COMPRESSION, true) : null,
+              size,
+              syncFlush);
+        usesDefaultDeflater = true;
+        writeHeader();
+        crc.reset();
+    }
+
+
+    /**
+     * Creates a new output stream with a default buffer size.
+     *
+     * <p>The new output stream instance is created as if by invoking
+     * the 2-argument constructor GZIPOutputStream(out, false).
+     *
+     * @param out the output stream
+     * @throws    IOException If an I/O error has occurred.
+     */
+    public GZIPOutputStream(OutputStream out) throws IOException {
+        this(out, 512, false);
+    }
+
+    /**
+     * Creates a new output stream with a default buffer size and
+     * the specified flush mode.
+     *
+     * @param out the output stream
+     * @param syncFlush
+     *        if {@code true} invocation of the inherited
+     *        {@link DeflaterOutputStream#flush() flush()} method of
+     *        this instance flushes the compressor with flush mode
+     *        {@link Deflater#SYNC_FLUSH} before flushing the output
+     *        stream, otherwise only flushes the output stream
+     *
+     * @throws    IOException If an I/O error has occurred.
+     *
+     * @since 1.7
+     */
+    public GZIPOutputStream(OutputStream out, boolean syncFlush)
+        throws IOException
+    {
+        this(out, 512, syncFlush);
+    }
+
+    /**
+     * Writes array of bytes to the compressed output stream. This method
+     * will block until all the bytes are written.
+     * @param buf the data to be written
+     * @param off the start offset of the data
+     * @param len the length of the data
+     * @throws    IOException If an I/O error has occurred.
+     */
+    public synchronized void write(byte[] buf, int off, int len)
+        throws IOException
+    {
+        super.write(buf, off, len);
+        crc.update(buf, off, len);
+    }
+
+    /**
+     * Finishes writing compressed data to the output stream without closing
+     * the underlying stream. Use this method when applying multiple filters
+     * in succession to the same output stream.
+     * @throws    IOException if an I/O error has occurred
+     */
+    public void finish() throws IOException {
+        if (!def.finished()) {
+            try {
+                def.finish();
+                while (!def.finished()) {
+                    int len = def.deflate(buf, 0, buf.length);
+                    if (def.finished() && len <= buf.length - TRAILER_SIZE) {
+                        // last deflater buffer. Fit trailer at the end
+                        writeTrailer(buf, len);
+                        len = len + TRAILER_SIZE;
+                        out.write(buf, 0, len);
+                        return;
+                    }
+                    if (len > 0)
+                        out.write(buf, 0, len);
+                }
+                // if we can't fit the trailer at the end of the last
+                // deflater buffer, we write it separately
+                byte[] trailer = new byte[TRAILER_SIZE];
+                writeTrailer(trailer, 0);
+                out.write(trailer);
+            } catch (IOException e) {
+                if (usesDefaultDeflater)
+                    def.end();
+                throw e;
+            }
+        }
+    }
+
+    /*
+     * Writes GZIP member header.
+     */
+    private void writeHeader() throws IOException {
+        out.write(new byte[] {
+                      (byte) GZIP_MAGIC,        // Magic number (short)
+                      (byte)(GZIP_MAGIC >> 8),  // Magic number (short)
+                      Deflater.DEFLATED,        // Compression method (CM)
+                      0,                        // Flags (FLG)
+                      0,                        // Modification time MTIME (int)
+                      0,                        // Modification time MTIME (int)
+                      0,                        // Modification time MTIME (int)
+                      0,                        // Modification time MTIME (int)
+                      0,                        // Extra flags (XFLG)
+                      OS_UNKNOWN                // Operating system (OS)
+                  });
+    }
+
+    /*
+     * Writes GZIP member trailer to a byte array, starting at a given
+     * offset.
+     */
+    private void writeTrailer(byte[] buf, int offset) throws IOException {
+        writeInt((int)crc.getValue(), buf, offset); // CRC-32 of uncompr. data
+        writeInt(def.getTotalIn(), buf, offset + 4); // Number of uncompr. bytes
+    }
+
+    /*
+     * Writes integer in Intel byte order to a byte array, starting at a
+     * given offset.
+     */
+    private void writeInt(int i, byte[] buf, int offset) throws IOException {
+        writeShort(i & 0xffff, buf, offset);
+        writeShort((i >> 16) & 0xffff, buf, offset + 2);
+    }
+
+    /*
+     * Writes short integer in Intel byte order to a byte array, starting
+     * at a given offset
+     */
+    private void writeShort(int s, byte[] buf, int offset) throws IOException {
+        buf[offset] = (byte)(s & 0xff);
+        buf[offset + 1] = (byte)((s >> 8) & 0xff);
+    }
+}
diff --git a/android-35/java/util/zip/Inflater.java b/android-35/java/util/zip/Inflater.java
new file mode 100644
index 0000000..6e6c83e
--- /dev/null
+++ b/android-35/java/util/zip/Inflater.java
@@ -0,0 +1,787 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import dalvik.annotation.optimization.ReachabilitySensitive;
+import dalvik.system.CloseGuard;
+import java.lang.ref.Cleaner.Cleanable;
+import java.lang.ref.Reference;
+import java.nio.ByteBuffer;
+import java.nio.ReadOnlyBufferException;
+import java.util.Objects;
+
+import jdk.internal.ref.CleanerFactory;
+import sun.nio.ch.DirectBuffer;
+
+/**
+ * This class provides support for general purpose decompression using the
+ * popular ZLIB compression library. The ZLIB compression library was
+ * initially developed as part of the PNG graphics standard and is not
+ * protected by patents. It is fully described in the specifications at
+ * the <a href="package-summary.html#package-description">java.util.zip
+ * package description</a>.
+ * <p>
+ * This class inflates sequences of ZLIB compressed bytes. The input byte
+ * sequence is provided in either byte array or byte buffer, via one of the
+ * {@code setInput()} methods. The output byte sequence is written to the
+ * output byte array or byte buffer passed to the {@code deflate()} methods.
+ * <p>
+ * The following code fragment demonstrates a trivial compression
+ * and decompression of a string using {@code Deflater} and
+ * {@code Inflater}.
+ *
+ * <blockquote><pre>
+ * try {
+ *     // Encode a String into bytes
+ *     String inputString = "blahblahblah\u20AC\u20AC";
+ *     byte[] input = inputString.getBytes("UTF-8");
+ *
+ *     // Compress the bytes
+ *     byte[] output = new byte[100];
+ *     Deflater compresser = new Deflater();
+ *     compresser.setInput(input);
+ *     compresser.finish();
+ *     int compressedDataLength = compresser.deflate(output);
+ *
+ *     // Decompress the bytes
+ *     Inflater decompresser = new Inflater();
+ *     decompresser.setInput(output, 0, compressedDataLength);
+ *     byte[] result = new byte[100];
+ *     int resultLength = decompresser.inflate(result);
+ *     decompresser.end();
+ *
+ *     // Decode the bytes into a String
+ *     String outputString = new String(result, 0, resultLength, "UTF-8");
+ * } catch (java.io.UnsupportedEncodingException ex) {
+ *     // handle
+ * } catch (java.util.zip.DataFormatException ex) {
+ *     // handle
+ * }
+ * </pre></blockquote>
+ *
+ * @apiNote
+ * To release resources used by this {@code Inflater}, the {@link #end()} method
+ * should be called explicitly. Subclasses are responsible for the cleanup of resources
+ * acquired by the subclass. Subclasses that override {@link #finalize()} in order
+ * to perform cleanup should be modified to use alternative cleanup mechanisms such
+ * as {@link java.lang.ref.Cleaner} and remove the overriding {@code finalize} method.
+ *
+ * @see         Deflater
+ * @author      David Connelly
+ * @since 1.1
+ *
+ */
+
+public class Inflater {
+
+    private final InflaterZStreamRef zsRef;
+    private ByteBuffer input = ZipUtils.defaultBuf;
+    private byte[] inputArray;
+    private int inputPos, inputLim;
+    private boolean finished;
+    private boolean needDict;
+    private long bytesRead;
+    private long bytesWritten;
+
+    // Android-added: CloseGuard support.
+    @ReachabilitySensitive
+    private final CloseGuard guard = CloseGuard.get();
+
+    /*
+     * These fields are used as an "out" parameter from JNI when a
+     * DataFormatException is thrown during the inflate operation.
+     */
+    private int inputConsumed;
+    private int outputConsumed;
+
+    // Android-removed: initIDs handled in register method.
+    /*
+    static {
+        ZipUtils.loadLibrary();
+        initIDs();
+    }
+    */
+
+    /**
+     * Creates a new decompressor. If the parameter 'nowrap' is true then
+     * the ZLIB header and checksum fields will not be used. This provides
+     * compatibility with the compression format used by both GZIP and PKZIP.
+     * <p>
+     * Note: When using the 'nowrap' option it is also necessary to provide
+     * an extra "dummy" byte as input. This is required by the ZLIB native
+     * library in order to support certain optimizations.
+     *
+     * @param nowrap if true then support GZIP compatible compression
+     */
+    public Inflater(boolean nowrap) {
+        this.zsRef = new InflaterZStreamRef(this, init(nowrap));
+        // Android-added: CloseGuard support.
+        guard.open("end");
+    }
+
+    /**
+     * Creates a new decompressor.
+     */
+    public Inflater() {
+        this(false);
+    }
+
+    /**
+     * Sets input data for decompression.
+     * <p>
+     * One of the {@code setInput()} methods should be called whenever
+     * {@code needsInput()} returns true indicating that more input data
+     * is required.
+     *
+     * @param input the input data bytes
+     * @param off the start offset of the input data
+     * @param len the length of the input data
+     * @see Inflater#needsInput
+     */
+    public void setInput(byte[] input, int off, int len) {
+        if (off < 0 || len < 0 || off > input.length - len) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        synchronized (zsRef) {
+            this.input = null;
+            this.inputArray = input;
+            this.inputPos = off;
+            this.inputLim = off + len;
+        }
+    }
+
+    /**
+     * Sets input data for decompression.
+     * <p>
+     * One of the {@code setInput()} methods should be called whenever
+     * {@code needsInput()} returns true indicating that more input data
+     * is required.
+     *
+     * @param input the input data bytes
+     * @see Inflater#needsInput
+     */
+    public void setInput(byte[] input) {
+        setInput(input, 0, input.length);
+    }
+
+    /**
+     * Sets input data for decompression.
+     * <p>
+     * One of the {@code setInput()} methods should be called whenever
+     * {@code needsInput()} returns true indicating that more input data
+     * is required.
+     * <p>
+     * The given buffer's position will be advanced as inflate
+     * operations are performed, up to the buffer's limit.
+     * The input buffer may be modified (refilled) between inflate
+     * operations; doing so is equivalent to creating a new buffer
+     * and setting it with this method.
+     * <p>
+     * Modifying the input buffer's contents, position, or limit
+     * concurrently with an inflate operation will result in
+     * undefined behavior, which may include incorrect operation
+     * results or operation failure.
+     *
+     * @param input the input data bytes
+     * @see Inflater#needsInput
+     * @since 11
+     */
+    public void setInput(ByteBuffer input) {
+        Objects.requireNonNull(input);
+        synchronized (zsRef) {
+            this.input = input;
+            this.inputArray = null;
+        }
+    }
+
+    /**
+     * Sets the preset dictionary to the given array of bytes. Should be
+     * called when inflate() returns 0 and needsDictionary() returns true
+     * indicating that a preset dictionary is required. The method getAdler()
+     * can be used to get the Adler-32 value of the dictionary needed.
+     * @param dictionary the dictionary data bytes
+     * @param off the start offset of the data
+     * @param len the length of the data
+     * @see Inflater#needsDictionary
+     * @see Inflater#getAdler
+     */
+    public void setDictionary(byte[] dictionary, int off, int len) {
+        if (off < 0 || len < 0 || off > dictionary.length - len) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        synchronized (zsRef) {
+            ensureOpen();
+            setDictionary(zsRef.address(), dictionary, off, len);
+            needDict = false;
+        }
+    }
+
+    /**
+     * Sets the preset dictionary to the given array of bytes. Should be
+     * called when inflate() returns 0 and needsDictionary() returns true
+     * indicating that a preset dictionary is required. The method getAdler()
+     * can be used to get the Adler-32 value of the dictionary needed.
+     * @param dictionary the dictionary data bytes
+     * @see Inflater#needsDictionary
+     * @see Inflater#getAdler
+     */
+    public void setDictionary(byte[] dictionary) {
+        setDictionary(dictionary, 0, dictionary.length);
+    }
+
+    /**
+     * Sets the preset dictionary to the bytes in the given buffer. Should be
+     * called when inflate() returns 0 and needsDictionary() returns true
+     * indicating that a preset dictionary is required. The method getAdler()
+     * can be used to get the Adler-32 value of the dictionary needed.
+     * <p>
+     * The bytes in given byte buffer will be fully consumed by this method.  On
+     * return, its position will equal its limit.
+     *
+     * @param dictionary the dictionary data bytes
+     * @see Inflater#needsDictionary
+     * @see Inflater#getAdler
+     * @since 11
+     */
+    public void setDictionary(ByteBuffer dictionary) {
+        synchronized (zsRef) {
+            int position = dictionary.position();
+            int remaining = Math.max(dictionary.limit() - position, 0);
+            ensureOpen();
+            if (dictionary.isDirect()) {
+                long address = ((DirectBuffer) dictionary).address();
+                try {
+                    setDictionaryBuffer(zsRef.address(), address + position, remaining);
+                } finally {
+                    Reference.reachabilityFence(dictionary);
+                }
+            } else {
+                byte[] array = ZipUtils.getBufferArray(dictionary);
+                int offset = ZipUtils.getBufferOffset(dictionary);
+                setDictionary(zsRef.address(), array, offset + position, remaining);
+            }
+            dictionary.position(position + remaining);
+            needDict = false;
+        }
+    }
+
+    /**
+     * Returns the total number of bytes remaining in the input buffer.
+     * This can be used to find out what bytes still remain in the input
+     * buffer after decompression has finished.
+     * @return the total number of bytes remaining in the input buffer
+     */
+    public int getRemaining() {
+        synchronized (zsRef) {
+            ByteBuffer input = this.input;
+            return input == null ? inputLim - inputPos : input.remaining();
+        }
+    }
+
+    /**
+     * Returns true if no data remains in the input buffer. This can
+     * be used to determine if one of the {@code setInput()} methods should be
+     * called in order to provide more input.
+     *
+     * @return true if no data remains in the input buffer
+     */
+    public boolean needsInput() {
+        synchronized (zsRef) {
+            ByteBuffer input = this.input;
+            return input == null ? inputLim == inputPos : ! input.hasRemaining();
+        }
+    }
+
+    /**
+     * Returns true if a preset dictionary is needed for decompression.
+     * @return true if a preset dictionary is needed for decompression
+     * @see Inflater#setDictionary
+     */
+    public boolean needsDictionary() {
+        synchronized (zsRef) {
+            return needDict;
+        }
+    }
+
+    /**
+     * Returns true if the end of the compressed data stream has been
+     * reached.
+     * @return true if the end of the compressed data stream has been
+     * reached
+     */
+    public boolean finished() {
+        synchronized (zsRef) {
+            return finished;
+        }
+    }
+
+    /**
+     * Uncompresses bytes into specified buffer. Returns actual number
+     * of bytes uncompressed. A return value of 0 indicates that
+     * needsInput() or needsDictionary() should be called in order to
+     * determine if more input data or a preset dictionary is required.
+     * In the latter case, getAdler() can be used to get the Adler-32
+     * value of the dictionary required.
+     * <p>
+     * If the {@link #setInput(ByteBuffer)} method was called to provide a buffer
+     * for input, the input buffer's position will be advanced by the number of bytes
+     * consumed by this operation, even in the event that a {@link DataFormatException}
+     * is thrown.
+     * <p>
+     * The {@linkplain #getRemaining() remaining byte count} will be reduced by
+     * the number of consumed input bytes.  If the {@link #setInput(ByteBuffer)}
+     * method was called to provide a buffer for input, the input buffer's position
+     * will be advanced the number of consumed bytes.
+     * <p>
+     * These byte totals, as well as
+     * the {@linkplain #getBytesRead() total bytes read}
+     * and the {@linkplain #getBytesWritten() total bytes written}
+     * values, will be updated even in the event that a {@link DataFormatException}
+     * is thrown to reflect the amount of data consumed and produced before the
+     * exception occurred.
+     *
+     * @param output the buffer for the uncompressed data
+     * @param off the start offset of the data
+     * @param len the maximum number of uncompressed bytes
+     * @return the actual number of uncompressed bytes
+     * @throws DataFormatException if the compressed data format is invalid
+     * @see Inflater#needsInput
+     * @see Inflater#needsDictionary
+     */
+    public int inflate(byte[] output, int off, int len)
+        throws DataFormatException
+    {
+        if (off < 0 || len < 0 || off > output.length - len) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+        synchronized (zsRef) {
+            ensureOpen();
+            ByteBuffer input = this.input;
+            long result;
+            int inputPos;
+            try {
+                if (input == null) {
+                    inputPos = this.inputPos;
+                    try {
+                        result = inflateBytesBytes(zsRef.address(),
+                            inputArray, inputPos, inputLim - inputPos,
+                            output, off, len);
+                    } catch (DataFormatException e) {
+                        this.inputPos = inputPos + inputConsumed;
+                        throw e;
+                    }
+                } else {
+                    inputPos = input.position();
+                    try {
+                        int inputRem = Math.max(input.limit() - inputPos, 0);
+                        if (input.isDirect()) {
+                            try {
+                                long inputAddress = ((DirectBuffer) input).address();
+                                result = inflateBufferBytes(zsRef.address(),
+                                    inputAddress + inputPos, inputRem,
+                                    output, off, len);
+                            } finally {
+                                Reference.reachabilityFence(input);
+                            }
+                        } else {
+                            byte[] inputArray = ZipUtils.getBufferArray(input);
+                            int inputOffset = ZipUtils.getBufferOffset(input);
+                            result = inflateBytesBytes(zsRef.address(),
+                                inputArray, inputOffset + inputPos, inputRem,
+                                output, off, len);
+                        }
+                    } catch (DataFormatException e) {
+                        input.position(inputPos + inputConsumed);
+                        throw e;
+                    }
+                }
+            } catch (DataFormatException e) {
+                bytesRead += inputConsumed;
+                inputConsumed = 0;
+                int written = outputConsumed;
+                bytesWritten += written;
+                outputConsumed = 0;
+                throw e;
+            }
+            int read = (int) (result & 0x7fff_ffffL);
+            int written = (int) (result >>> 31 & 0x7fff_ffffL);
+            if ((result >>> 62 & 1) != 0) {
+                finished = true;
+            }
+            if ((result >>> 63 & 1) != 0) {
+                needDict = true;
+            }
+            if (input != null) {
+                input.position(inputPos + read);
+            } else {
+                this.inputPos = inputPos + read;
+            }
+            bytesWritten += written;
+            bytesRead += read;
+            return written;
+        }
+    }
+
+    /**
+     * Uncompresses bytes into specified buffer. Returns actual number
+     * of bytes uncompressed. A return value of 0 indicates that
+     * needsInput() or needsDictionary() should be called in order to
+     * determine if more input data or a preset dictionary is required.
+     * In the latter case, getAdler() can be used to get the Adler-32
+     * value of the dictionary required.
+     * <p>
+     * The {@linkplain #getRemaining() remaining byte count} will be reduced by
+     * the number of consumed input bytes.  If the {@link #setInput(ByteBuffer)}
+     * method was called to provide a buffer for input, the input buffer's position
+     * will be advanced the number of consumed bytes.
+     * <p>
+     * These byte totals, as well as
+     * the {@linkplain #getBytesRead() total bytes read}
+     * and the {@linkplain #getBytesWritten() total bytes written}
+     * values, will be updated even in the event that a {@link DataFormatException}
+     * is thrown to reflect the amount of data consumed and produced before the
+     * exception occurred.
+     *
+     * @param output the buffer for the uncompressed data
+     * @return the actual number of uncompressed bytes
+     * @throws DataFormatException if the compressed data format is invalid
+     * @see Inflater#needsInput
+     * @see Inflater#needsDictionary
+     */
+    public int inflate(byte[] output) throws DataFormatException {
+        return inflate(output, 0, output.length);
+    }
+
+    /**
+     * Uncompresses bytes into specified buffer. Returns actual number
+     * of bytes uncompressed. A return value of 0 indicates that
+     * needsInput() or needsDictionary() should be called in order to
+     * determine if more input data or a preset dictionary is required.
+     * In the latter case, getAdler() can be used to get the Adler-32
+     * value of the dictionary required.
+     * <p>
+     * On success, the position of the given {@code output} byte buffer will be
+     * advanced by as many bytes as were produced by the operation, which is equal
+     * to the number returned by this method.  Note that the position of the
+     * {@code output} buffer will be advanced even in the event that a
+     * {@link DataFormatException} is thrown.
+     * <p>
+     * The {@linkplain #getRemaining() remaining byte count} will be reduced by
+     * the number of consumed input bytes.  If the {@link #setInput(ByteBuffer)}
+     * method was called to provide a buffer for input, the input buffer's position
+     * will be advanced the number of consumed bytes.
+     * <p>
+     * These byte totals, as well as
+     * the {@linkplain #getBytesRead() total bytes read}
+     * and the {@linkplain #getBytesWritten() total bytes written}
+     * values, will be updated even in the event that a {@link DataFormatException}
+     * is thrown to reflect the amount of data consumed and produced before the
+     * exception occurred.
+     *
+     * @param output the buffer for the uncompressed data
+     * @return the actual number of uncompressed bytes
+     * @throws DataFormatException if the compressed data format is invalid
+     * @throws ReadOnlyBufferException if the given output buffer is read-only
+     * @see Inflater#needsInput
+     * @see Inflater#needsDictionary
+     * @since 11
+     */
+    public int inflate(ByteBuffer output) throws DataFormatException {
+        if (output.isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
+        synchronized (zsRef) {
+            ensureOpen();
+            ByteBuffer input = this.input;
+            long result;
+            int inputPos;
+            int outputPos = output.position();
+            int outputRem = Math.max(output.limit() - outputPos, 0);
+            try {
+                if (input == null) {
+                    inputPos = this.inputPos;
+                    try {
+                        if (output.isDirect()) {
+                            long outputAddress = ((DirectBuffer) output).address();
+                            try {
+                                result = inflateBytesBuffer(zsRef.address(),
+                                    inputArray, inputPos, inputLim - inputPos,
+                                    outputAddress + outputPos, outputRem);
+                            } finally {
+                                Reference.reachabilityFence(output);
+                            }
+                        } else {
+                            byte[] outputArray = ZipUtils.getBufferArray(output);
+                            int outputOffset = ZipUtils.getBufferOffset(output);
+                            result = inflateBytesBytes(zsRef.address(),
+                                inputArray, inputPos, inputLim - inputPos,
+                                outputArray, outputOffset + outputPos, outputRem);
+                        }
+                    } catch (DataFormatException e) {
+                        this.inputPos = inputPos + inputConsumed;
+                        throw e;
+                    }
+                } else {
+                    inputPos = input.position();
+                    int inputRem = Math.max(input.limit() - inputPos, 0);
+                    try {
+                        if (input.isDirect()) {
+                            long inputAddress = ((DirectBuffer) input).address();
+                            try {
+                                if (output.isDirect()) {
+                                    long outputAddress = ((DirectBuffer) output).address();
+                                    try {
+                                        result = inflateBufferBuffer(zsRef.address(),
+                                            inputAddress + inputPos, inputRem,
+                                            outputAddress + outputPos, outputRem);
+                                    } finally {
+                                        Reference.reachabilityFence(output);
+                                    }
+                                } else {
+                                    byte[] outputArray = ZipUtils.getBufferArray(output);
+                                    int outputOffset = ZipUtils.getBufferOffset(output);
+                                    result = inflateBufferBytes(zsRef.address(),
+                                        inputAddress + inputPos, inputRem,
+                                        outputArray, outputOffset + outputPos, outputRem);
+                                }
+                            } finally {
+                                Reference.reachabilityFence(input);
+                            }
+                        } else {
+                            byte[] inputArray = ZipUtils.getBufferArray(input);
+                            int inputOffset = ZipUtils.getBufferOffset(input);
+                            if (output.isDirect()) {
+                                long outputAddress = ((DirectBuffer) output).address();
+                                try {
+                                    result = inflateBytesBuffer(zsRef.address(),
+                                        inputArray, inputOffset + inputPos, inputRem,
+                                        outputAddress + outputPos, outputRem);
+                                } finally {
+                                    Reference.reachabilityFence(output);
+                                }
+                            } else {
+                                byte[] outputArray = ZipUtils.getBufferArray(output);
+                                int outputOffset = ZipUtils.getBufferOffset(output);
+                                result = inflateBytesBytes(zsRef.address(),
+                                    inputArray, inputOffset + inputPos, inputRem,
+                                    outputArray, outputOffset + outputPos, outputRem);
+                            }
+                        }
+                    } catch (DataFormatException e) {
+                        input.position(inputPos + inputConsumed);
+                        throw e;
+                    }
+                }
+            } catch (DataFormatException e) {
+                bytesRead += inputConsumed;
+                inputConsumed = 0;
+                int written = outputConsumed;
+                output.position(outputPos + written);
+                bytesWritten += written;
+                outputConsumed = 0;
+                throw e;
+            }
+            int read = (int) (result & 0x7fff_ffffL);
+            int written = (int) (result >>> 31 & 0x7fff_ffffL);
+            if ((result >>> 62 & 1) != 0) {
+                finished = true;
+            }
+            if ((result >>> 63 & 1) != 0) {
+                needDict = true;
+            }
+            if (input != null) {
+                input.position(inputPos + read);
+            } else {
+                this.inputPos = inputPos + read;
+            }
+            // Note: this method call also serves to keep the byteBuffer ref alive
+            output.position(outputPos + written);
+            bytesWritten += written;
+            bytesRead += read;
+            return written;
+        }
+    }
+
+    /**
+     * Returns the ADLER-32 value of the uncompressed data.
+     * @return the ADLER-32 value of the uncompressed data
+     */
+    public int getAdler() {
+        synchronized (zsRef) {
+            ensureOpen();
+            return getAdler(zsRef.address());
+        }
+    }
+
+    /**
+     * Returns the total number of compressed bytes input so far.
+     *
+     * <p>Since the number of bytes may be greater than
+     * Integer.MAX_VALUE, the {@link #getBytesRead()} method is now
+     * the preferred means of obtaining this information.</p>
+     *
+     * @return the total number of compressed bytes input so far
+     */
+    public int getTotalIn() {
+        return (int) getBytesRead();
+    }
+
+    /**
+     * Returns the total number of compressed bytes input so far.
+     *
+     * @return the total (non-negative) number of compressed bytes input so far
+     * @since 1.5
+     */
+    public long getBytesRead() {
+        synchronized (zsRef) {
+            ensureOpen();
+            return bytesRead;
+        }
+    }
+
+    /**
+     * Returns the total number of uncompressed bytes output so far.
+     *
+     * <p>Since the number of bytes may be greater than
+     * Integer.MAX_VALUE, the {@link #getBytesWritten()} method is now
+     * the preferred means of obtaining this information.</p>
+     *
+     * @return the total number of uncompressed bytes output so far
+     */
+    public int getTotalOut() {
+        return (int) getBytesWritten();
+    }
+
+    /**
+     * Returns the total number of uncompressed bytes output so far.
+     *
+     * @return the total (non-negative) number of uncompressed bytes output so far
+     * @since 1.5
+     */
+    public long getBytesWritten() {
+        synchronized (zsRef) {
+            ensureOpen();
+            return bytesWritten;
+        }
+    }
+
+    /**
+     * Resets inflater so that a new set of input data can be processed.
+     */
+    public void reset() {
+        synchronized (zsRef) {
+            ensureOpen();
+            reset(zsRef.address());
+            // Android-added: CloseGuard support.
+            guard.close();
+            input = ZipUtils.defaultBuf;
+            inputArray = null;
+            finished = false;
+            needDict = false;
+            bytesRead = bytesWritten = 0;
+        }
+    }
+
+    /**
+     * Closes the decompressor and discards any unprocessed input.
+     *
+     * This method should be called when the decompressor is no longer
+     * being used. Once this method is called, the behavior of the
+     * Inflater object is undefined.
+     */
+    public void end() {
+        synchronized (zsRef) {
+            zsRef.clean();
+            // Android-added: CloseGuard support.
+            guard.close();
+            input = ZipUtils.defaultBuf;
+            inputArray = null;
+        }
+    }
+
+
+    private void ensureOpen () {
+        assert Thread.holdsLock(zsRef);
+        if (zsRef.address() == 0)
+            throw new NullPointerException("Inflater has been closed");
+    }
+
+    // Android-changed: initIDs handled in register method.
+    // private native static void initIDs();
+    private static native long init(boolean nowrap);
+    private static native void setDictionary(long addr, byte[] b, int off,
+                                             int len);
+    private static native void setDictionaryBuffer(long addr, long bufAddress, int len);
+    private native long inflateBytesBytes(long addr,
+        byte[] inputArray, int inputOff, int inputLen,
+        byte[] outputArray, int outputOff, int outputLen) throws DataFormatException;
+    private native long inflateBytesBuffer(long addr,
+        byte[] inputArray, int inputOff, int inputLen,
+        long outputAddress, int outputLen) throws DataFormatException;
+    private native long inflateBufferBytes(long addr,
+        long inputAddress, int inputLen,
+        byte[] outputArray, int outputOff, int outputLen) throws DataFormatException;
+    private native long inflateBufferBuffer(long addr,
+        long inputAddress, int inputLen,
+        long outputAddress, int outputLen) throws DataFormatException;
+    private static native int getAdler(long addr);
+    private static native void reset(long addr);
+    private static native void end(long addr);
+
+    /**
+     * A reference to the native zlib's z_stream structure. It also
+     * serves as the "cleaner" to clean up the native resource when
+     * the Inflater is ended, closed or cleaned.
+     */
+    static class InflaterZStreamRef implements Runnable {
+
+        private long address;
+        private final Cleanable cleanable;
+
+        private InflaterZStreamRef(Inflater owner, long addr) {
+            this.cleanable = (owner != null) ? CleanerFactory.cleaner().register(owner, this) : null;
+            this.address = addr;
+        }
+
+        long address() {
+            return address;
+        }
+
+        void clean() {
+            cleanable.clean();
+        }
+
+        public synchronized void run() {
+            long addr = address;
+            address = 0;
+            if (addr != 0) {
+                end(addr);
+            }
+        }
+
+    }
+}
diff --git a/android-35/java/util/zip/InflaterInputStream.java b/android-35/java/util/zip/InflaterInputStream.java
new file mode 100644
index 0000000..0eb85ad
--- /dev/null
+++ b/android-35/java/util/zip/InflaterInputStream.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.EOFException;
+
+/**
+ * This class implements a stream filter for uncompressing data in the
+ * "deflate" compression format. It is also used as the basis for other
+ * decompression filters, such as GZIPInputStream.
+ *
+ * @see         Inflater
+ * @author      David Connelly
+ * @since 1.1
+ */
+public class InflaterInputStream extends FilterInputStream {
+    /**
+     * Decompressor for this stream.
+     */
+    protected Inflater inf;
+
+    /**
+     * Input buffer for decompression.
+     */
+    protected byte[] buf;
+
+    /**
+     * Length of input buffer.
+     */
+    protected int len;
+
+    // Android-changed: Make closed accessible to subclasses.
+    // This was made protected because it needed to be accessed by
+    // StrictJarFile.ZipInflaterInputStream. Unfortunately, it was not marked as @hide and so it
+    // inadvertently became part of the public API. It will be marked as @removed to remove it from
+    // the public API in a future release of Android. See http://b/111592689 for more information.
+    // private boolean closed = false;
+    /**
+     * Indicates whether the {@link #close()} method has been called, internal use only.
+     *
+     * @deprecated This field will be removed from a future version of Android and should not be
+     * used. Subclasses that access this field need to be modified to keep track of their own
+     * closed state by overriding close().
+     */
+    @Deprecated
+    protected boolean closed = false;
+
+    // this flag is set to true after EOF has reached
+    private boolean reachEOF = false;
+
+    /**
+     * Check to make sure that this stream has not been closed
+     */
+    private void ensureOpen() throws IOException {
+        if (closed) {
+            throw new IOException("Stream closed");
+        }
+    }
+
+    // Android-added: constructor which explicitly sets whether Inflater is owned by this stream.
+    /**
+     * Creates a new input stream with the specified decompressor and
+     * buffer size.
+     * @param in the input stream
+     * @param inf the decompressor ("inflater")
+     * @param size the input buffer size
+     * @param ownsInflater whether this {@code InflaterInputStream} controls its inflater
+     *                     lifetime and should call {@link Inflater#end} when it is closed.
+     * @throws    IllegalArgumentException if {@code size <= 0}
+     * @hide
+     */
+    InflaterInputStream(InputStream in, Inflater inf, int size, boolean ownsInflater) {
+        super(in);
+        if (in == null || inf == null) {
+            throw new NullPointerException();
+        } else if (size <= 0) {
+            throw new IllegalArgumentException("buffer size <= 0");
+        }
+        this.inf = inf;
+        buf = new byte[size];
+        this.ownsInflater = ownsInflater;
+    }
+
+    /**
+     * Creates a new input stream with the specified decompressor and
+     * buffer size.
+     * @param in the input stream
+     * @param inf the decompressor ("inflater")
+     * @param size the input buffer size
+     * @throws    IllegalArgumentException if {@code size <= 0}
+     */
+    public InflaterInputStream(InputStream in, Inflater inf, int size) {
+        // Android-changed: refer initialization to constructor which specifies ownInflater.
+        /*
+        super(in);
+        if (in == null || inf == null) {
+            throw new NullPointerException();
+        } else if (size <= 0) {
+            throw new IllegalArgumentException("buffer size <= 0");
+        }
+        this.inf = inf;
+        buf = new byte[size];
+        */
+        this(in, inf, size, /* ownsInflater= */ true);
+    }
+
+    /**
+     * Creates a new input stream with the specified decompressor and a
+     * default buffer size.
+     * @param in the input stream
+     * @param inf the decompressor ("inflater")
+     */
+    public InflaterInputStream(InputStream in, Inflater inf) {
+        this(in, inf, 512);
+    }
+
+    // Android-changed: Unconditionally close external inflaters (b/26462400)
+    // See http://b/111630946 for more details.
+    // boolean usesDefaultInflater = false;
+
+    // Android-added: functionally this is identical to usesDefaultInflater, but re-using
+    // it will be confusing. Setting it to true keeps old Android behaviour.
+    // This is added just for ZipFileInflaterInputStream - moving to usesDefaultInflater
+    // is trickier.
+    private final boolean ownsInflater;
+
+    /**
+     * Creates a new input stream with a default decompressor and buffer size.
+     * @param in the input stream
+     */
+    public InflaterInputStream(InputStream in) {
+        this(in, in != null ? new Inflater() : null);
+        // Android-changed: Unconditionally close external inflaters (b/26462400)
+        // usesDefaultInflater = true;
+    }
+
+    private byte[] singleByteBuf = new byte[1];
+
+    /**
+     * Reads a byte of uncompressed data. This method will block until
+     * enough input is available for decompression.
+     * @return the byte read, or -1 if end of compressed input is reached
+     * @throws    IOException if an I/O error has occurred
+     */
+    public int read() throws IOException {
+        ensureOpen();
+        return read(singleByteBuf, 0, 1) == -1 ? -1 : Byte.toUnsignedInt(singleByteBuf[0]);
+    }
+
+    /**
+     * Reads uncompressed data into an array of bytes. If {@code len} is not
+     * zero, the method will block until some input can be decompressed; otherwise,
+     * no bytes are read and {@code 0} is returned.
+     * @param b the buffer into which the data is read
+     * @param off the start offset in the destination array {@code b}
+     * @param len the maximum number of bytes read
+     * @return the actual number of bytes read, or -1 if the end of the
+     *         compressed input is reached or a preset dictionary is needed
+     * @throws     NullPointerException If {@code b} is {@code null}.
+     * @throws     IndexOutOfBoundsException If {@code off} is negative,
+     * {@code len} is negative, or {@code len} is greater than
+     * {@code b.length - off}
+     * @throws    ZipException if a ZIP format error has occurred
+     * @throws    IOException if an I/O error has occurred
+     */
+    public int read(byte[] b, int off, int len) throws IOException {
+        ensureOpen();
+        if (b == null) {
+            throw new NullPointerException();
+        } else if (off < 0 || len < 0 || len > b.length - off) {
+            throw new IndexOutOfBoundsException();
+        } else if (len == 0) {
+            return 0;
+        }
+        try {
+            int n;
+            while ((n = inf.inflate(b, off, len)) == 0) {
+                if (inf.finished() || inf.needsDictionary()) {
+                    reachEOF = true;
+                    return -1;
+                }
+                if (inf.needsInput()) {
+                    fill();
+                }
+            }
+            return n;
+        } catch (DataFormatException e) {
+            String s = e.getMessage();
+            throw new ZipException(s != null ? s : "Invalid ZLIB data format");
+        }
+    }
+
+    /**
+     * Returns 0 after EOF has been reached, otherwise always return 1.
+     * <p>
+     * Programs should not count on this method to return the actual number
+     * of bytes that could be read without blocking.
+     *
+     * @return     1 before EOF and 0 after EOF.
+     * @throws     IOException  if an I/O error occurs.
+     *
+     */
+    public int available() throws IOException {
+        ensureOpen();
+        if (reachEOF) {
+            return 0;
+        } else if (inf.finished()) {
+            // the end of the compressed data stream has been reached
+            reachEOF = true;
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    private byte[] b = new byte[512];
+
+    /**
+     * Skips specified number of bytes of uncompressed data.
+     * @param n the number of bytes to skip
+     * @return the actual number of bytes skipped.
+     * @throws    IOException if an I/O error has occurred
+     * @throws    IllegalArgumentException if {@code n < 0}
+     */
+    public long skip(long n) throws IOException {
+        if (n < 0) {
+            throw new IllegalArgumentException("negative skip length");
+        }
+        ensureOpen();
+        int max = (int)Math.min(n, Integer.MAX_VALUE);
+        int total = 0;
+        while (total < max) {
+            int len = max - total;
+            if (len > b.length) {
+                len = b.length;
+            }
+            len = read(b, 0, len);
+            if (len == -1) {
+                reachEOF = true;
+                break;
+            }
+            total += len;
+        }
+        return total;
+    }
+
+    /**
+     * Closes this input stream and releases any system resources associated
+     * with the stream.
+     * @throws    IOException if an I/O error has occurred
+     */
+    public void close() throws IOException {
+        if (!closed) {
+            // BEGIN Android-changed: close inflater only if it is owned by this
+            // InflaterInputStream. (b/26462400)
+            /*
+            if (usesDefaultInflater)
+                inf.end();
+            */
+            if (ownsInflater) {
+                inf.end();
+            }
+            // END Android-changed: close inflater only if it is owned by this
+            // InflaterInputStream. (b/26462400)
+            in.close();
+            closed = true;
+        }
+    }
+
+    /**
+     * Fills input buffer with more data to decompress.
+     * @throws    IOException if an I/O error has occurred
+     */
+    protected void fill() throws IOException {
+        ensureOpen();
+        len = in.read(buf, 0, buf.length);
+        if (len == -1) {
+            throw new EOFException("Unexpected end of ZLIB input stream");
+        }
+        inf.setInput(buf, 0, len);
+    }
+
+    /**
+     * Tests if this input stream supports the {@code mark} and
+     * {@code reset} methods. The {@code markSupported}
+     * method of {@code InflaterInputStream} returns
+     * {@code false}.
+     *
+     * @return  a {@code boolean} indicating if this stream type supports
+     *          the {@code mark} and {@code reset} methods.
+     * @see     java.io.InputStream#mark(int)
+     * @see     java.io.InputStream#reset()
+     */
+    public boolean markSupported() {
+        return false;
+    }
+
+    /**
+     * Marks the current position in this input stream.
+     *
+     * <p> The {@code mark} method of {@code InflaterInputStream}
+     * does nothing.
+     *
+     * @param   readlimit   the maximum limit of bytes that can be read before
+     *                      the mark position becomes invalid.
+     * @see     java.io.InputStream#reset()
+     */
+    public synchronized void mark(int readlimit) {
+    }
+
+    /**
+     * Repositions this stream to the position at the time the
+     * {@code mark} method was last called on this input stream.
+     *
+     * <p> The method {@code reset} for class
+     * {@code InflaterInputStream} does nothing except throw an
+     * {@code IOException}.
+     *
+     * @throws     IOException  if this method is invoked.
+     * @see     java.io.InputStream#mark(int)
+     * @see     java.io.IOException
+     */
+    public synchronized void reset() throws IOException {
+        throw new IOException("mark/reset not supported");
+    }
+}
diff --git a/android-35/java/util/zip/InflaterOutputStream.java b/android-35/java/util/zip/InflaterOutputStream.java
new file mode 100644
index 0000000..aa30623
--- /dev/null
+++ b/android-35/java/util/zip/InflaterOutputStream.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2006, 2021, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Implements an output stream filter for uncompressing data stored in the
+ * "deflate" compression format.
+ *
+ * @since       1.6
+ * @author      David R Tribble ([email protected])
+ *
+ * @see InflaterInputStream
+ * @see DeflaterInputStream
+ * @see DeflaterOutputStream
+ */
+
+public class InflaterOutputStream extends FilterOutputStream {
+    /** Decompressor for this stream. */
+    protected final Inflater inf;
+
+    /** Output buffer for writing uncompressed data. */
+    protected final byte[] buf;
+
+    /** Temporary write buffer. */
+    private final byte[] wbuf = new byte[1];
+
+    /** Default decompressor is used. */
+    private boolean usesDefaultInflater = false;
+
+    /** true iff {@link #close()} has been called. */
+    private boolean closed = false;
+
+    /**
+     * Checks to make sure that this stream has not been closed.
+     */
+    private void ensureOpen() throws IOException {
+        if (closed) {
+            throw new IOException("Stream closed");
+        }
+    }
+
+    /**
+     * Creates a new output stream with a default decompressor and buffer
+     * size.
+     *
+     * @param out output stream to write the uncompressed data to
+     * @throws NullPointerException if {@code out} is null
+     */
+    public InflaterOutputStream(OutputStream out) {
+        this(out, out != null ? new Inflater() : null);
+        usesDefaultInflater = true;
+    }
+
+    /**
+     * Creates a new output stream with the specified decompressor and a
+     * default buffer size.
+     *
+     * @param out output stream to write the uncompressed data to
+     * @param infl decompressor ("inflater") for this stream
+     * @throws NullPointerException if {@code out} or {@code infl} is null
+     */
+    public InflaterOutputStream(OutputStream out, Inflater infl) {
+        this(out, infl, 512);
+    }
+
+    /**
+     * Creates a new output stream with the specified decompressor and
+     * buffer size.
+     *
+     * @param out output stream to write the uncompressed data to
+     * @param infl decompressor ("inflater") for this stream
+     * @param bufLen decompression buffer size
+     * @throws IllegalArgumentException if {@code bufLen <= 0}
+     * @throws NullPointerException if {@code out} or {@code infl} is null
+     */
+    public InflaterOutputStream(OutputStream out, Inflater infl, int bufLen) {
+        super(out);
+
+        // Sanity checks
+        if (out == null)
+            throw new NullPointerException("Null output");
+        if (infl == null)
+            throw new NullPointerException("Null inflater");
+        if (bufLen <= 0)
+            throw new IllegalArgumentException("Buffer size < 1");
+
+        // Initialize
+        inf = infl;
+        buf = new byte[bufLen];
+    }
+
+    /**
+     * Writes any remaining uncompressed data to the output stream and closes
+     * the underlying output stream.
+     *
+     * @throws IOException if an I/O error occurs
+     */
+    public void close() throws IOException {
+        if (!closed) {
+            // Complete the uncompressed output
+            try {
+                finish();
+            } finally {
+                out.close();
+                closed = true;
+            }
+        }
+    }
+
+    /**
+     * Flushes this output stream, forcing any pending buffered output bytes to be
+     * written.
+     *
+     * @throws IOException if an I/O error occurs or this stream is already
+     * closed
+     */
+    public void flush() throws IOException {
+        ensureOpen();
+
+        // Finish decompressing and writing pending output data
+        if (!inf.finished()) {
+            try {
+                while (!inf.finished()  &&  !inf.needsInput()) {
+                    int n;
+
+                    // Decompress pending output data
+                    n = inf.inflate(buf, 0, buf.length);
+                    if (n < 1) {
+                        break;
+                    }
+
+                    // Write the uncompressed output data block
+                    out.write(buf, 0, n);
+                }
+                super.flush();
+            } catch (DataFormatException ex) {
+                // Improperly formatted compressed (ZIP) data
+                String msg = ex.getMessage();
+                if (msg == null) {
+                    msg = "Invalid ZLIB data format";
+                }
+                throw new ZipException(msg);
+            }
+        }
+    }
+
+    /**
+     * Finishes writing uncompressed data to the output stream without closing
+     * the underlying stream.  Use this method when applying multiple filters in
+     * succession to the same output stream.
+     *
+     * @throws IOException if an I/O error occurs or this stream is already
+     * closed
+     */
+    public void finish() throws IOException {
+        ensureOpen();
+
+        // Finish decompressing and writing pending output data
+        flush();
+        if (usesDefaultInflater) {
+            inf.end();
+        }
+    }
+
+    /**
+     * Writes a byte to the uncompressed output stream.
+     *
+     * @param b a single byte of compressed data to decompress and write to
+     * the output stream
+     * @throws IOException if an I/O error occurs or this stream is already
+     * closed
+     * @throws ZipException if a compression (ZIP) format error occurs
+     */
+    public void write(int b) throws IOException {
+        // Write a single byte of data
+        wbuf[0] = (byte) b;
+        write(wbuf, 0, 1);
+    }
+
+    /**
+     * Writes an array of bytes to the uncompressed output stream.
+     *
+     * @param b buffer containing compressed data to decompress and write to
+     * the output stream
+     * @param off starting offset of the compressed data within {@code b}
+     * @param len number of bytes to decompress from {@code b}
+     * @throws IndexOutOfBoundsException if {@code off < 0}, or if
+     * {@code len < 0}, or if {@code len > b.length - off}
+     * @throws IOException if an I/O error occurs or this stream is already
+     * closed
+     * @throws NullPointerException if {@code b} is null
+     * @throws ZipException if a compression (ZIP) format error occurs
+     */
+    public void write(byte[] b, int off, int len) throws IOException {
+        // Sanity checks
+        ensureOpen();
+        if (b == null) {
+            throw new NullPointerException("Null buffer for read");
+        } else if (off < 0 || len < 0 || len > b.length - off) {
+            throw new IndexOutOfBoundsException();
+        } else if (len == 0) {
+            return;
+        }
+
+        // Write uncompressed data to the output stream
+        try {
+            for (;;) {
+                int n;
+
+                // Fill the decompressor buffer with output data
+                if (inf.needsInput()) {
+                    inf.setInput(b, off, len);
+                    // Only use input buffer once.
+                    len = 0;
+                }
+
+                // Decompress and write blocks of output data
+                do {
+                    n = inf.inflate(buf, 0, buf.length);
+                    if (n > 0) {
+                        out.write(buf, 0, n);
+                    }
+                } while (n > 0);
+
+                // Check for missing dictionary first
+                if (inf.needsDictionary()) {
+                    throw new ZipException("ZLIB dictionary missing");
+                }
+                // Check the decompressor
+                if (inf.finished() || (len == 0)/* no more input */) {
+                    break;
+                }
+            }
+        } catch (DataFormatException ex) {
+            // Improperly formatted compressed (ZIP) data
+            String msg = ex.getMessage();
+            if (msg == null) {
+                msg = "Invalid ZLIB data format";
+            }
+            throw new ZipException(msg);
+        }
+    }
+}
diff --git a/android-35/java/util/zip/ZipCoder.java b/android-35/java/util/zip/ZipCoder.java
new file mode 100644
index 0000000..450d7a6
--- /dev/null
+++ b/android-35/java/util/zip/ZipCoder.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2009, 2021, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CharsetEncoder;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+/**
+ * Utility class for zipfile name and comment decoding and encoding
+ */
+class ZipCoder {
+
+    // Android-removed:
+    // private static final jdk.internal.access.JavaLangAccess JLA =
+    //    jdk.internal.access.SharedSecrets.getJavaLangAccess();
+
+    // Encoding/decoding is stateless, so make it singleton.
+    // Android-changed: use StandardCharsets.
+    // static final UTF8ZipCoder UTF8 = new UTF8ZipCoder(UTF_8.INSTANCE);
+    static final UTF8ZipCoder UTF8 = new UTF8ZipCoder(StandardCharsets.UTF_8);
+
+    public static ZipCoder get(Charset charset) {
+        // Android-changed: use equals method, not reference comparison.
+        // if (charset == UTF_8.INSTANCE) {
+        if (StandardCharsets.UTF_8.equals(charset)) {
+            return UTF8;
+        }
+        return new ZipCoder(charset);
+    }
+
+    String toString(byte[] ba, int off, int length) {
+        try {
+            return decoder().decode(ByteBuffer.wrap(ba, off, length)).toString();
+        } catch (CharacterCodingException x) {
+            throw new IllegalArgumentException(x);
+        }
+    }
+
+    String toString(byte[] ba, int length) {
+        return toString(ba, 0, length);
+    }
+
+    String toString(byte[] ba) {
+        return toString(ba, 0, ba.length);
+    }
+
+    byte[] getBytes(String s) {
+        try {
+            ByteBuffer bb = encoder().encode(CharBuffer.wrap(s));
+            int pos = bb.position();
+            int limit = bb.limit();
+            if (bb.hasArray() && pos == 0 && limit == bb.capacity()) {
+                return bb.array();
+            }
+            byte[] bytes = new byte[bb.limit() - bb.position()];
+            bb.get(bytes);
+            return bytes;
+        } catch (CharacterCodingException x) {
+            throw new IllegalArgumentException(x);
+        }
+    }
+
+    static String toStringUTF8(byte[] ba, int len) {
+        return UTF8.toString(ba, 0, len);
+    }
+
+    boolean isUTF8() {
+        return false;
+    }
+
+    // Hash code functions for ZipFile entry names. We generate the hash as-if
+    // we first decoded the byte sequence to a String, then appended '/' if no
+    // trailing slash was found, then called String.hashCode(). This
+    // normalization ensures we can simplify and speed up lookups.
+    //
+    // Does encoding error checking and hashing in a single pass for efficiency.
+    // On an error, this function will throw CharacterCodingException while the
+    // UTF8ZipCoder override will throw IllegalArgumentException, so we declare
+    // throws Exception to keep things simple.
+    int checkedHash(byte[] a, int off, int len) throws Exception {
+        if (len == 0) {
+            return 0;
+        }
+
+        int h = 0;
+        // cb will be a newly allocated CharBuffer with pos == 0,
+        // arrayOffset == 0, backed by an array.
+        CharBuffer cb = decoder().decode(ByteBuffer.wrap(a, off, len));
+        int limit = cb.limit();
+        char[] decoded = cb.array();
+        for (int i = 0; i < limit; i++) {
+            h = 31 * h + decoded[i];
+        }
+        if (limit > 0 && decoded[limit - 1] != '/') {
+            h = 31 * h + '/';
+        }
+        return h;
+    }
+
+    // Hash function equivalent of checkedHash for String inputs
+    static int hash(String name) {
+        int hsh = name.hashCode();
+        int len = name.length();
+        if (len > 0 && name.charAt(len - 1) != '/') {
+            hsh = hsh * 31 + '/';
+        }
+        return hsh;
+    }
+
+    boolean hasTrailingSlash(byte[] a, int end) {
+        byte[] slashBytes = slashBytes();
+        return end >= slashBytes.length &&
+            Arrays.mismatch(a, end - slashBytes.length, end, slashBytes, 0, slashBytes.length) == -1;
+    }
+
+    private byte[] slashBytes;
+    private final Charset cs;
+    protected CharsetDecoder dec;
+    private CharsetEncoder enc;
+
+    private ZipCoder(Charset cs) {
+        this.cs = cs;
+    }
+
+    protected CharsetDecoder decoder() {
+        if (dec == null) {
+            dec = cs.newDecoder()
+              .onMalformedInput(CodingErrorAction.REPORT)
+              .onUnmappableCharacter(CodingErrorAction.REPORT);
+        }
+        return dec;
+    }
+
+    private CharsetEncoder encoder() {
+        if (enc == null) {
+            enc = cs.newEncoder()
+              .onMalformedInput(CodingErrorAction.REPORT)
+              .onUnmappableCharacter(CodingErrorAction.REPORT);
+        }
+        return enc;
+    }
+
+    // This method produces an array with the bytes that will correspond to a
+    // trailing '/' in the chosen character encoding.
+    //
+    // While in most charsets a trailing slash will be encoded as the byte
+    // value of '/', this does not hold in the general case. E.g., in charsets
+    // such as UTF-16 and UTF-32 it will be represented by a sequence of 2 or 4
+    // bytes, respectively.
+    private byte[] slashBytes() {
+        if (slashBytes == null) {
+            // Take into account charsets that produce a BOM, e.g., UTF-16
+            byte[] slash = "/".getBytes(cs);
+            byte[] doubleSlash = "//".getBytes(cs);
+            slashBytes = Arrays.copyOfRange(doubleSlash, slash.length, doubleSlash.length);
+        }
+        return slashBytes;
+    }
+
+    static final class UTF8ZipCoder extends ZipCoder {
+
+        private UTF8ZipCoder(Charset utf8) {
+            super(utf8);
+        }
+
+        @Override
+        boolean isUTF8() {
+            return true;
+        }
+
+        @Override
+        String toString(byte[] ba, int off, int length) {
+            // Android-changed: JLA is not yet available.
+            // return JLA.newStringUTF8NoRepl(ba, off, length);
+            return new String(ba, off, length, StandardCharsets.UTF_8);
+        }
+
+        @Override
+        byte[] getBytes(String s) {
+            // Android-changed: JLA is not yet available.
+            // return JLA.getBytesUTF8NoRepl(s);
+            return s.getBytes(StandardCharsets.UTF_8);
+        }
+
+        @Override
+        int checkedHash(byte[] a, int off, int len) throws Exception {
+            if (len == 0) {
+                return 0;
+            }
+
+            int end = off + len;
+            int h = 0;
+            while (off < end) {
+                byte b = a[off];
+                if (b >= 0) {
+                    // ASCII, keep going
+                    h = 31 * h + b;
+                    off++;
+                } else {
+                    // Non-ASCII, fall back to decoding a String
+                    // We avoid using decoder() here since the UTF8ZipCoder is
+                    // shared and that decoder is not thread safe.
+                    // We use the JLA.newStringUTF8NoRepl variant to throw
+                    // exceptions eagerly when opening ZipFiles
+                    // Android-changed: JLA is not yet available.
+                    // return hash(JLA.newStringUTF8NoRepl(a, end - len, len));
+                    return hash(new String(a, end - len, len, StandardCharsets.UTF_8));
+                }
+            }
+
+            if (a[end - 1] != '/') {
+                h = 31 * h + '/';
+            }
+            return h;
+        }
+
+        @Override
+        boolean hasTrailingSlash(byte[] a, int end) {
+            return end > 0 && a[end - 1] == '/';
+        }
+    }
+}
diff --git a/android-35/java/util/zip/ZipConstants.java b/android-35/java/util/zip/ZipConstants.java
new file mode 100644
index 0000000..9eac888
--- /dev/null
+++ b/android-35/java/util/zip/ZipConstants.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+/*
+ * This interface defines the constants that are used by the classes
+ * which manipulate ZIP files.
+ *
+ * @author      David Connelly
+ * @since 1.1
+ */
+interface ZipConstants {
+
+    /**
+     * Local file (LOC) header signature.
+     */
+    static long LOCSIG = 0x04034b50L;   // "PK\003\004"
+
+    /**
+     * Extra local (EXT) header signature.
+     */
+    static long EXTSIG = 0x08074b50L;   // "PK\007\008"
+
+    /**
+     * Central directory (CEN) header signature.
+     */
+    static long CENSIG = 0x02014b50L;   // "PK\001\002"
+
+    /**
+     * End of central directory (END) header signature.
+     */
+    static long ENDSIG = 0x06054b50L;   // "PK\005\006"
+
+    /**
+     * Local file (LOC) header size in bytes (including signature).
+     */
+    static final int LOCHDR = 30;
+
+    /**
+     * Extra local (EXT) header size in bytes (including signature).
+     */
+    static final int EXTHDR = 16;
+
+    /**
+     * Central directory (CEN) header size in bytes (including signature).
+     */
+    static final int CENHDR = 46;
+
+    /**
+     * End of central directory (END) header size in bytes (including signature).
+     */
+    static final int ENDHDR = 22;
+
+    /**
+     * Local file (LOC) header version needed to extract field offset.
+     */
+    static final int LOCVER = 4;
+
+    /**
+     * Local file (LOC) header general purpose bit flag field offset.
+     */
+    static final int LOCFLG = 6;
+
+    /**
+     * Local file (LOC) header compression method field offset.
+     */
+    static final int LOCHOW = 8;
+
+    /**
+     * Local file (LOC) header modification time field offset.
+     */
+    static final int LOCTIM = 10;
+
+    /**
+     * Local file (LOC) header uncompressed file crc-32 value field offset.
+     */
+    static final int LOCCRC = 14;
+
+    /**
+     * Local file (LOC) header compressed size field offset.
+     */
+    static final int LOCSIZ = 18;
+
+    /**
+     * Local file (LOC) header uncompressed size field offset.
+     */
+    static final int LOCLEN = 22;
+
+    /**
+     * Local file (LOC) header filename length field offset.
+     */
+    static final int LOCNAM = 26;
+
+    /**
+     * Local file (LOC) header extra field length field offset.
+     */
+    static final int LOCEXT = 28;
+
+    /**
+     * Extra local (EXT) header uncompressed file crc-32 value field offset.
+     */
+    static final int EXTCRC = 4;
+
+    /**
+     * Extra local (EXT) header compressed size field offset.
+     */
+    static final int EXTSIZ = 8;
+
+    /**
+     * Extra local (EXT) header uncompressed size field offset.
+     */
+    static final int EXTLEN = 12;
+
+    /**
+     * Central directory (CEN) header version made by field offset.
+     */
+    static final int CENVEM = 4;
+
+    /**
+     * Central directory (CEN) header version needed to extract field offset.
+     */
+    static final int CENVER = 6;
+
+    /**
+     * Central directory (CEN) header encrypt, decrypt flags field offset.
+     */
+    static final int CENFLG = 8;
+
+    /**
+     * Central directory (CEN) header compression method field offset.
+     */
+    static final int CENHOW = 10;
+
+    /**
+     * Central directory (CEN) header modification time field offset.
+     */
+    static final int CENTIM = 12;
+
+    /**
+     * Central directory (CEN) header uncompressed file crc-32 value field offset.
+     */
+    static final int CENCRC = 16;
+
+    /**
+     * Central directory (CEN) header compressed size field offset.
+     */
+    static final int CENSIZ = 20;
+
+    /**
+     * Central directory (CEN) header uncompressed size field offset.
+     */
+    static final int CENLEN = 24;
+
+    /**
+     * Central directory (CEN) header filename length field offset.
+     */
+    static final int CENNAM = 28;
+
+    /**
+     * Central directory (CEN) header extra field length field offset.
+     */
+    static final int CENEXT = 30;
+
+    /**
+     * Central directory (CEN) header comment length field offset.
+     */
+    static final int CENCOM = 32;
+
+    /**
+     * Central directory (CEN) header disk number start field offset.
+     */
+    static final int CENDSK = 34;
+
+    /**
+     * Central directory (CEN) header internal file attributes field offset.
+     */
+    static final int CENATT = 36;
+
+    /**
+     * Central directory (CEN) header external file attributes field offset.
+     */
+    static final int CENATX = 38;
+
+    /**
+     * Central directory (CEN) header LOC header offset field offset.
+     */
+    static final int CENOFF = 42;
+
+    /**
+     * End of central directory (END) header number of entries on this disk field offset.
+     */
+    static final int ENDSUB = 8;
+
+    /**
+     * End of central directory (END) header total number of entries field offset.
+     */
+    static final int ENDTOT = 10;
+
+    /**
+     * End of central directory (END) header central directory size in bytes field offset.
+     */
+    static final int ENDSIZ = 12;
+
+    /**
+     * End of central directory (END) header offset for the first CEN header field offset.
+     */
+    static final int ENDOFF = 16;
+
+    /**
+     * End of central directory (END) header zip file comment length field offset.
+     */
+    static final int ENDCOM = 20;
+}
diff --git a/android-35/java/util/zip/ZipConstants64.java b/android-35/java/util/zip/ZipConstants64.java
new file mode 100644
index 0000000..d78eaf4
--- /dev/null
+++ b/android-35/java/util/zip/ZipConstants64.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+/*
+ * This class defines the constants that are used by the classes
+ * which manipulate Zip64 files.
+ */
+
+class ZipConstants64 {
+
+    /*
+     * ZIP64 constants
+     */
+    static final long ZIP64_ENDSIG = 0x06064b50L;  // "PK\006\006"
+    static final long ZIP64_LOCSIG = 0x07064b50L;  // "PK\006\007"
+    static final int  ZIP64_ENDHDR = 56;           // ZIP64 end header size
+    static final int  ZIP64_LOCHDR = 20;           // ZIP64 end loc header size
+    static final int  ZIP64_EXTHDR = 24;           // EXT header size
+    static final int  ZIP64_EXTID  = 0x0001;       // Extra field Zip64 header ID
+
+    static final int  ZIP64_MAGICCOUNT = 0xFFFF;
+    static final long ZIP64_MAGICVAL = 0xFFFFFFFFL;
+
+    /*
+     * Zip64 End of central directory (END) header field offsets
+     */
+    static final int  ZIP64_ENDLEN = 4;       // size of zip64 end of central dir
+    static final int  ZIP64_ENDVEM = 12;      // version made by
+    static final int  ZIP64_ENDVER = 14;      // version needed to extract
+    static final int  ZIP64_ENDNMD = 16;      // number of this disk
+    static final int  ZIP64_ENDDSK = 20;      // disk number of start
+    static final int  ZIP64_ENDTOD = 24;      // total number of entries on this disk
+    static final int  ZIP64_ENDTOT = 32;      // total number of entries
+    static final int  ZIP64_ENDSIZ = 40;      // central directory size in bytes
+    static final int  ZIP64_ENDOFF = 48;      // offset of first CEN header
+    static final int  ZIP64_ENDEXT = 56;      // zip64 extensible data sector
+
+    /*
+     * Zip64 End of central directory locator field offsets
+     */
+    static final int  ZIP64_LOCDSK = 4;       // disk number start
+    static final int  ZIP64_LOCOFF = 8;       // offset of zip64 end
+    static final int  ZIP64_LOCTOT = 16;      // total number of disks
+
+    /*
+     * Zip64 Extra local (EXT) header field offsets
+     */
+    static final int  ZIP64_EXTCRC = 4;       // uncompressed file crc-32 value
+    static final int  ZIP64_EXTSIZ = 8;       // compressed size, 8-byte
+    static final int  ZIP64_EXTLEN = 16;      // uncompressed size, 8-byte
+
+    /*
+     * Language encoding flag (general purpose flag bit 11)
+     *
+     * If this bit is set the filename and comment fields for this
+     * entry must be encoded using UTF-8.
+     */
+    static final int USE_UTF8 = 0x800;
+
+    /*
+     * Constants below are defined here (instead of in ZipConstants)
+     * to avoid being exposed as public fields of ZipFile, ZipEntry,
+     * ZipInputStream and ZipOutputstream.
+     */
+
+    /*
+     * Extra field header ID
+     */
+    static final int  EXTID_ZIP64 = 0x0001;    // Zip64
+    static final int  EXTID_NTFS  = 0x000a;    // NTFS
+    static final int  EXTID_UNIX  = 0x000d;    // UNIX
+    static final int  EXTID_EXTT  = 0x5455;    // Info-ZIP Extended Timestamp
+
+    /*
+     * EXTT timestamp flags
+     */
+    static final int  EXTT_FLAG_LMT = 0x1;       // LastModifiedTime
+    static final int  EXTT_FLAG_LAT = 0x2;       // LastAccessTime
+    static final int  EXTT_FLAT_CT  = 0x4;       // CreationTime
+
+    private ZipConstants64() {}
+}
diff --git a/android-35/java/util/zip/ZipEntry.java b/android-35/java/util/zip/ZipEntry.java
new file mode 100644
index 0000000..3d1d522
--- /dev/null
+++ b/android-35/java/util/zip/ZipEntry.java
@@ -0,0 +1,756 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import static java.util.zip.ZipUtils.*;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.attribute.FileTime;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+import java.time.LocalDateTime;
+import java.time.ZonedDateTime;
+import java.time.ZoneId;
+
+import static java.util.zip.ZipConstants64.*;
+
+/**
+ * This class is used to represent a ZIP file entry.
+ *
+ * @author      David Connelly
+ * @since 1.1
+ */
+public class ZipEntry implements ZipConstants, Cloneable {
+
+    String name;        // entry name
+    long xdostime = -1; // last modification time (in extended DOS time,
+                        // where milliseconds lost in conversion might
+                        // be encoded into the upper half)
+    FileTime mtime;     // last modification time, from extra field data
+    FileTime atime;     // last access time, from extra field data
+    FileTime ctime;     // creation time, from extra field data
+    long crc = -1;      // crc-32 of entry data
+    long size = -1;     // uncompressed size of entry data
+    long csize = -1;    // compressed size of entry data
+    boolean csizeSet = false; // Only true if csize was explicitely set by
+                        // a call to setCompressedSize()
+    int method = -1;    // compression method
+    int flag = 0;       // general purpose flag
+    byte[] extra;       // optional extra field data for entry
+    String comment;     // optional comment string for entry
+    int extraAttributes = -1; // e.g. POSIX permissions, sym links.
+    // Android-added: Add dataOffset for internal use.
+    // Used by android.util.jar.StrictJarFile from frameworks.
+    long dataOffset;
+
+    /**
+     * Compression method for uncompressed entries.
+     */
+    public static final int STORED = 0;
+
+    /**
+     * Compression method for compressed (deflated) entries.
+     */
+    public static final int DEFLATED = 8;
+
+    /**
+     * DOS time constant for representing timestamps before 1980.
+     */
+    static final long DOSTIME_BEFORE_1980 = (1 << 21) | (1 << 16);
+
+    /**
+     * Approximately 128 years, in milliseconds (ignoring leap years etc).
+     *
+     * This establish an approximate high-bound value for DOS times in
+     * milliseconds since epoch, used to enable an efficient but
+     * sufficient bounds check to avoid generating extended last modified
+     * time entries.
+     *
+     * Calculating the exact number is locale dependent, would require loading
+     * TimeZone data eagerly, and would make little practical sense. Since DOS
+     * times theoretically go to 2107 - with compatibility not guaranteed
+     * after 2099 - setting this to a time that is before but near 2099
+     * should be sufficient.
+     * @hide
+     */
+    // Android-changed: Make UPPER_DOSTIME_BOUND public hidden for testing purposes.
+    public static final long UPPER_DOSTIME_BOUND =
+            128L * 365 * 24 * 60 * 60 * 1000;
+
+    // Android-added: New constructor for use by StrictJarFile native code.
+    /** @hide */
+    public ZipEntry(String name, String comment, long crc, long compressedSize,
+            long size, int compressionMethod, int xdostime, byte[] extra,
+            long dataOffset) {
+        this.name = name;
+        this.comment = comment;
+        this.crc = crc;
+        this.csize = compressedSize;
+        this.size = size;
+        this.method = compressionMethod;
+        this.xdostime = xdostime;
+        this.dataOffset = dataOffset;
+        this.setExtra0(extra, false, false);
+    }
+
+    /**
+     * Creates a new zip entry with the specified name.
+     *
+     * @param  name
+     *         The entry name
+     *
+     * @throws NullPointerException if the entry name is null
+     * @throws IllegalArgumentException if the entry name is longer than
+     *         0xFFFF bytes
+     */
+    public ZipEntry(String name) {
+        Objects.requireNonNull(name, "name");
+        // Android-changed: Explicitly use UTF_8 instead of the default charset.
+        // if (name.length() > 0xFFFF) {
+        //     throw new IllegalArgumentException("entry name too long");
+        // }
+        if (name.getBytes(StandardCharsets.UTF_8).length > 0xffff) {
+            throw new IllegalArgumentException(name + " too long: " +
+                    name.getBytes(StandardCharsets.UTF_8).length);
+        }
+        this.name = name;
+    }
+
+    /**
+     * Creates a new zip entry with fields taken from the specified
+     * zip entry.
+     *
+     * @param  e
+     *         A zip Entry object
+     *
+     * @throws NullPointerException if the entry object is null
+     */
+    public ZipEntry(ZipEntry e) {
+        Objects.requireNonNull(e, "entry");
+        name = e.name;
+        xdostime = e.xdostime;
+        mtime = e.mtime;
+        atime = e.atime;
+        ctime = e.ctime;
+        crc = e.crc;
+        size = e.size;
+        csize = e.csize;
+        csizeSet = e.csizeSet;
+        method = e.method;
+        flag = e.flag;
+        extra = e.extra;
+        comment = e.comment;
+        extraAttributes = e.extraAttributes;
+        // Android-added: Add dataOffset for internal use.
+        dataOffset = e.dataOffset;
+    }
+
+    /**
+     * Creates a new un-initialized zip entry
+     */
+    ZipEntry() {}
+
+    // BEGIN Android-added: Add dataOffset for internal use.
+    /** @hide */
+    public long getDataOffset() {
+        return dataOffset;
+    }
+    // END Android-added: Add dataOffset for internal use.
+
+    /**
+     * Returns the name of the entry.
+     * @return the name of the entry
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Sets the last modification time of the entry.
+     *
+     * <p> If the entry is output to a ZIP file or ZIP file formatted
+     * output stream the last modification time set by this method will
+     * be stored into the {@code date and time fields} of the zip file
+     * entry and encoded in standard {@code MS-DOS date and time format}.
+     * The {@link java.util.TimeZone#getDefault() default TimeZone} is
+     * used to convert the epoch time to the MS-DOS data and time.
+     *
+     * @param  time
+     *         The last modification time of the entry in milliseconds
+     *         since the epoch
+     *
+     * @see #getTime()
+     * @see #getLastModifiedTime()
+     */
+    public void setTime(long time) {
+        this.xdostime = javaToExtendedDosTime(time);
+        // Avoid setting the mtime field if time is in the valid
+        // range for a DOS time
+        if (this.xdostime != DOSTIME_BEFORE_1980 && time <= UPPER_DOSTIME_BOUND) {
+            this.mtime = null;
+        } else {
+            int localYear = javaEpochToLocalDateTime(time).getYear();
+            if (localYear >= 1980 && localYear <= 2099) {
+                this.mtime = null;
+            } else {
+                this.mtime = FileTime.from(time, TimeUnit.MILLISECONDS);
+            }
+        }
+    }
+
+    /**
+     * Returns the last modification time of the entry.
+     *
+     * <p> If the entry is read from a ZIP file or ZIP file formatted
+     * input stream, this is the last modification time from the {@code
+     * date and time fields} of the zip file entry. The
+     * {@link java.util.TimeZone#getDefault() default TimeZone} is used
+     * to convert the standard MS-DOS formatted date and time to the
+     * epoch time.
+     *
+     * @return  The last modification time of the entry in milliseconds
+     *          since the epoch, or -1 if not specified
+     *
+     * @see #setTime(long)
+     * @see #setLastModifiedTime(FileTime)
+     */
+    public long getTime() {
+        if (mtime != null) {
+            return mtime.toMillis();
+        }
+        return (xdostime != -1) ? extendedDosToJavaTime(xdostime) : -1;
+    }
+
+    /**
+     * Sets the last modification time of the entry in local date-time.
+     *
+     * <p> If the entry is output to a ZIP file or ZIP file formatted
+     * output stream the last modification time set by this method will
+     * be stored into the {@code date and time fields} of the zip file
+     * entry and encoded in standard {@code MS-DOS date and time format}.
+     * If the date-time set is out of the range of the standard {@code
+     * MS-DOS date and time format}, the time will also be stored into
+     * zip file entry's extended timestamp fields in {@code optional
+     * extra data} in UTC time. The {@link java.time.ZoneId#systemDefault()
+     * system default TimeZone} is used to convert the local date-time
+     * to UTC time.
+     *
+     * <p> {@code LocalDateTime} uses a precision of nanoseconds, whereas
+     * this class uses a precision of milliseconds. The conversion will
+     * truncate any excess precision information as though the amount in
+     * nanoseconds was subject to integer division by one million.
+     *
+     * @param  time
+     *         The last modification time of the entry in local date-time
+     *
+     * @see #getTimeLocal()
+     * @since 9
+     */
+    public void setTimeLocal(LocalDateTime time) {
+        int year = time.getYear() - 1980;
+        if (year < 0) {
+            this.xdostime = DOSTIME_BEFORE_1980;
+        } else {
+            this.xdostime = ((year << 25 |
+                time.getMonthValue() << 21 |
+                time.getDayOfMonth() << 16 |
+                time.getHour() << 11 |
+                time.getMinute() << 5 |
+                time.getSecond() >> 1) & 0xffffffffL)
+                + ((long)(((time.getSecond() & 0x1) * 1000) +
+                      time.getNano() / 1000_000) << 32);
+        }
+        if (xdostime != DOSTIME_BEFORE_1980 && year <= 0x7f) {
+            this.mtime = null;
+        } else {
+            this.mtime = FileTime.from(
+                ZonedDateTime.of(time, ZoneId.systemDefault()).toInstant());
+        }
+    }
+
+    /**
+     * Returns the last modification time of the entry in local date-time.
+     *
+     * <p> If the entry is read from a ZIP file or ZIP file formatted
+     * input stream, this is the last modification time from the zip
+     * file entry's {@code optional extra data} if the extended timestamp
+     * fields are present. Otherwise, the last modification time is read
+     * from entry's standard MS-DOS formatted {@code date and time fields}.
+     *
+     * <p> The {@link java.time.ZoneId#systemDefault() system default TimeZone}
+     * is used to convert the UTC time to local date-time.
+     *
+     * @return  The last modification time of the entry in local date-time
+     *
+     * @see #setTimeLocal(LocalDateTime)
+     * @since 9
+     */
+    public LocalDateTime getTimeLocal() {
+        if (mtime != null) {
+            return LocalDateTime.ofInstant(mtime.toInstant(), ZoneId.systemDefault());
+        }
+        int ms = (int)(xdostime >> 32);
+        return LocalDateTime.of((int)(((xdostime >> 25) & 0x7f) + 1980),
+                             (int)((xdostime >> 21) & 0x0f),
+                             (int)((xdostime >> 16) & 0x1f),
+                             (int)((xdostime >> 11) & 0x1f),
+                             (int)((xdostime >> 5) & 0x3f),
+                             (int)((xdostime << 1) & 0x3e) + ms / 1000,
+                             (ms % 1000) * 1000_000);
+    }
+
+
+    /**
+     * Sets the last modification time of the entry.
+     *
+     * <p> When output to a ZIP file or ZIP file formatted output stream
+     * the last modification time set by this method will be stored into
+     * zip file entry's {@code date and time fields} in {@code standard
+     * MS-DOS date and time format}), and the extended timestamp fields
+     * in {@code optional extra data} in UTC time.
+     *
+     * @param  time
+     *         The last modification time of the entry
+     * @return This zip entry
+     *
+     * @throws NullPointerException if the {@code time} is null
+     *
+     * @see #getLastModifiedTime()
+     * @since 1.8
+     */
+    public ZipEntry setLastModifiedTime(FileTime time) {
+        this.mtime = Objects.requireNonNull(time, "lastModifiedTime");
+        this.xdostime = javaToExtendedDosTime(time.to(TimeUnit.MILLISECONDS));
+        return this;
+    }
+
+    /**
+     * Returns the last modification time of the entry.
+     *
+     * <p> If the entry is read from a ZIP file or ZIP file formatted
+     * input stream, this is the last modification time from the zip
+     * file entry's {@code optional extra data} if the extended timestamp
+     * fields are present. Otherwise the last modification time is read
+     * from the entry's {@code date and time fields}, the {@link
+     * java.util.TimeZone#getDefault() default TimeZone} is used to convert
+     * the standard MS-DOS formatted date and time to the epoch time.
+     *
+     * @return The last modification time of the entry, null if not specified
+     *
+     * @see #setLastModifiedTime(FileTime)
+     * @since 1.8
+     */
+    public FileTime getLastModifiedTime() {
+        if (mtime != null)
+            return mtime;
+        if (xdostime == -1)
+            return null;
+        return FileTime.from(getTime(), TimeUnit.MILLISECONDS);
+    }
+
+    /**
+     * Sets the last access time of the entry.
+     *
+     * <p> If set, the last access time will be stored into the extended
+     * timestamp fields of entry's {@code optional extra data}, when output
+     * to a ZIP file or ZIP file formatted stream.
+     *
+     * @param  time
+     *         The last access time of the entry
+     * @return This zip entry
+     *
+     * @throws NullPointerException if the {@code time} is null
+     *
+     * @see #getLastAccessTime()
+     * @since 1.8
+     */
+    public ZipEntry setLastAccessTime(FileTime time) {
+        this.atime = Objects.requireNonNull(time, "lastAccessTime");
+        return this;
+    }
+
+    /**
+     * Returns the last access time of the entry.
+     *
+     * <p> The last access time is from the extended timestamp fields
+     * of entry's {@code optional extra data} when read from a ZIP file
+     * or ZIP file formatted stream.
+     *
+     * @return The last access time of the entry, null if not specified
+     * @see #setLastAccessTime(FileTime)
+     * @since 1.8
+     */
+    public FileTime getLastAccessTime() {
+        return atime;
+    }
+
+    /**
+     * Sets the creation time of the entry.
+     *
+     * <p> If set, the creation time will be stored into the extended
+     * timestamp fields of entry's {@code optional extra data}, when
+     * output to a ZIP file or ZIP file formatted stream.
+     *
+     * @param  time
+     *         The creation time of the entry
+     * @return This zip entry
+     *
+     * @throws NullPointerException if the {@code time} is null
+     *
+     * @see #getCreationTime()
+     * @since 1.8
+     */
+    public ZipEntry setCreationTime(FileTime time) {
+        this.ctime = Objects.requireNonNull(time, "creationTime");
+        return this;
+    }
+
+    /**
+     * Returns the creation time of the entry.
+     *
+     * <p> The creation time is from the extended timestamp fields of
+     * entry's {@code optional extra data} when read from a ZIP file
+     * or ZIP file formatted stream.
+     *
+     * @return the creation time of the entry, null if not specified
+     * @see #setCreationTime(FileTime)
+     * @since 1.8
+     */
+    public FileTime getCreationTime() {
+        return ctime;
+    }
+
+    /**
+     * Sets the uncompressed size of the entry data.
+     *
+     * @param size the uncompressed size in bytes
+     *
+     * @throws IllegalArgumentException if the specified size is less
+     *         than 0, is greater than 0xFFFFFFFF when
+     *         <a href="package-summary.html#zip64">ZIP64 format</a> is not supported,
+     *         or is less than 0 when ZIP64 is supported
+     * @see #getSize()
+     */
+    public void setSize(long size) {
+        if (size < 0) {
+            throw new IllegalArgumentException("invalid entry size");
+        }
+        this.size = size;
+    }
+
+    /**
+     * Returns the uncompressed size of the entry data.
+     *
+     * @return the uncompressed size of the entry data, or -1 if not known
+     * @see #setSize(long)
+     */
+    public long getSize() {
+        return size;
+    }
+
+    /**
+     * Returns the size of the compressed entry data.
+     *
+     * <p> In the case of a stored entry, the compressed size will be the same
+     * as the uncompressed size of the entry.
+     *
+     * @return the size of the compressed entry data, or -1 if not known
+     * @see #setCompressedSize(long)
+     */
+    public long getCompressedSize() {
+        return csize;
+    }
+
+    /**
+     * Sets the size of the compressed entry data.
+     *
+     * @param csize the compressed size to set
+     *
+     * @see #getCompressedSize()
+     */
+    public void setCompressedSize(long csize) {
+        this.csize = csize;
+        this.csizeSet = true;
+    }
+
+    /**
+     * Sets the CRC-32 checksum of the uncompressed entry data.
+     *
+     * @param crc the CRC-32 value
+     *
+     * @throws IllegalArgumentException if the specified CRC-32 value is
+     *         less than 0 or greater than 0xFFFFFFFF
+     * @see #getCrc()
+     */
+    public void setCrc(long crc) {
+        if (crc < 0 || crc > 0xFFFFFFFFL) {
+            throw new IllegalArgumentException("invalid entry crc-32");
+        }
+        this.crc = crc;
+    }
+
+    /**
+     * Returns the CRC-32 checksum of the uncompressed entry data.
+     *
+     * @return the CRC-32 checksum of the uncompressed entry data, or -1 if
+     * not known
+     *
+     * @see #setCrc(long)
+     */
+    public long getCrc() {
+        return crc;
+    }
+
+    /**
+     * Sets the compression method for the entry.
+     *
+     * @param method the compression method, either STORED or DEFLATED
+     *
+     * @throws  IllegalArgumentException if the specified compression
+     *          method is invalid
+     * @see #getMethod()
+     */
+    public void setMethod(int method) {
+        if (method != STORED && method != DEFLATED) {
+            throw new IllegalArgumentException("invalid compression method");
+        }
+        this.method = method;
+    }
+
+    /**
+     * Returns the compression method of the entry.
+     *
+     * @return the compression method of the entry, or -1 if not specified
+     * @see #setMethod(int)
+     */
+    public int getMethod() {
+        return method;
+    }
+
+    /**
+     * Sets the optional extra field data for the entry.
+     *
+     * <p> Invoking this method may change this entry's last modification
+     * time, last access time and creation time, if the {@code extra} field
+     * data includes the extensible timestamp fields, such as {@code NTFS tag
+     * 0x0001} or {@code Info-ZIP Extended Timestamp}, as specified in
+     * <a href="http://www.info-zip.org/doc/appnote-19970311-iz.zip">Info-ZIP
+     * Application Note 970311</a>.
+     *
+     * @param  extra
+     *         The extra field data bytes
+     *
+     * @throws IllegalArgumentException if the length of the specified
+     *         extra field data is greater than 0xFFFF bytes
+     *
+     * @see #getExtra()
+     */
+    public void setExtra(byte[] extra) {
+        setExtra0(extra, false, true);
+    }
+
+    /**
+     * Sets the optional extra field data for the entry.
+     *
+     * @param extra
+     *        the extra field data bytes
+     * @param doZIP64
+     *        if true, set size and csize from ZIP64 fields if present
+     * @param isLOC
+     *        true if setting the extra field for a LOC, false if for
+     *        a CEN
+     */
+    void setExtra0(byte[] extra, boolean doZIP64, boolean isLOC) {
+        if (extra != null) {
+            if (extra.length > 0xFFFF) {
+                throw new IllegalArgumentException("invalid extra field length");
+            }
+            // extra fields are in "HeaderID(2)DataSize(2)Data... format
+            int off = 0;
+            int len = extra.length;
+            while (off + 4 < len) {
+                int tag = get16(extra, off);
+                int sz = get16(extra, off + 2);
+                off += 4;
+                if (off + sz > len)         // invalid data
+                    break;
+                switch (tag) {
+                case EXTID_ZIP64:
+                    if (doZIP64) {
+                        if (isLOC) {
+                            // LOC extra zip64 entry MUST include BOTH original
+                            // and compressed file size fields.
+                            // If invalid zip64 extra fields, simply skip. Even
+                            // it's rare, it's possible the entry size happens to
+                            // be the magic value and it "accidently" has some
+                            // bytes in extra match the id.
+                            if (sz >= 16) {
+                                size = get64(extra, off);
+                                csize = get64(extra, off + 8);
+                            }
+                        } else {
+                            // CEN extra zip64
+                            if (size == ZIP64_MAGICVAL) {
+                                if (off + 8 > len)  // invalid zip64 extra
+                                    break;          // fields, just skip
+                                size = get64(extra, off);
+                            }
+                            if (csize == ZIP64_MAGICVAL) {
+                                if (off + 16 > len)  // invalid zip64 extra
+                                    break;           // fields, just skip
+                                csize = get64(extra, off + 8);
+                            }
+                        }
+                    }
+                    break;
+                case EXTID_NTFS:
+                    if (sz < 32) // reserved  4 bytes + tag 2 bytes + size 2 bytes
+                        break;   // m[a|c]time 24 bytes
+                    int pos = off + 4;               // reserved 4 bytes
+                    if (get16(extra, pos) !=  0x0001 || get16(extra, pos + 2) != 24)
+                        break;
+                    long wtime = get64(extra, pos + 4);
+                    if (wtime != WINDOWS_TIME_NOT_AVAILABLE) {
+                        mtime = winTimeToFileTime(wtime);
+                    }
+                    wtime = get64(extra, pos + 12);
+                    if (wtime != WINDOWS_TIME_NOT_AVAILABLE) {
+                        atime = winTimeToFileTime(wtime);
+                    }
+                    wtime = get64(extra, pos + 20);
+                    if (wtime != WINDOWS_TIME_NOT_AVAILABLE) {
+                        ctime = winTimeToFileTime(wtime);
+                    }
+                    break;
+                case EXTID_EXTT:
+                    int flag = Byte.toUnsignedInt(extra[off]);
+                    int sz0 = 1;
+                    // The CEN-header extra field contains the modification
+                    // time only, or no timestamp at all. 'sz' is used to
+                    // flag its presence or absence. But if mtime is present
+                    // in LOC it must be present in CEN as well.
+                    if ((flag & 0x1) != 0 && (sz0 + 4) <= sz) {
+                        mtime = unixTimeToFileTime(get32S(extra, off + sz0));
+                        sz0 += 4;
+                    }
+                    if ((flag & 0x2) != 0 && (sz0 + 4) <= sz) {
+                        atime = unixTimeToFileTime(get32S(extra, off + sz0));
+                        sz0 += 4;
+                    }
+                    if ((flag & 0x4) != 0 && (sz0 + 4) <= sz) {
+                        ctime = unixTimeToFileTime(get32S(extra, off + sz0));
+                        sz0 += 4;
+                    }
+                    break;
+                 default:
+                }
+                off += sz;
+            }
+        }
+        this.extra = extra;
+    }
+
+    /**
+     * Returns the extra field data for the entry.
+     *
+     * @return the extra field data for the entry, or null if none
+     *
+     * @see #setExtra(byte[])
+     */
+    public byte[] getExtra() {
+        return extra;
+    }
+
+    /**
+     * Sets the optional comment string for the entry.
+     *
+     * <p>ZIP entry comments have maximum length of 0xffff. If the length of the
+     * specified comment string is greater than 0xFFFF bytes after encoding, only
+     * the first 0xFFFF bytes are output to the ZIP file entry.
+     *
+     * @param comment the comment string
+     *
+     * @see #getComment()
+     */
+    public void setComment(String comment) {
+        // BEGIN Android-added: Explicitly use UTF_8 instead of the default charset.
+        if (comment != null && comment.getBytes(StandardCharsets.UTF_8).length > 0xffff) {
+            throw new IllegalArgumentException(comment + " too long: " +
+                    comment.getBytes(StandardCharsets.UTF_8).length);
+        }
+        // END Android-added: Explicitly use UTF_8 instead of the default charset.
+
+        this.comment = comment;
+    }
+
+    /**
+     * Returns the comment string for the entry.
+     *
+     * @return the comment string for the entry, or null if none
+     *
+     * @see #setComment(String)
+     */
+    public String getComment() {
+        return comment;
+    }
+
+    /**
+     * Returns true if this is a directory entry. A directory entry is
+     * defined to be one whose name ends with a '/'.
+     * @return true if this is a directory entry
+     */
+    public boolean isDirectory() {
+        return name.endsWith("/");
+    }
+
+    /**
+     * Returns a string representation of the ZIP entry.
+     */
+    public String toString() {
+        return getName();
+    }
+
+    /**
+     * Returns the hash code value for this entry.
+     */
+    public int hashCode() {
+        return name.hashCode();
+    }
+
+    /**
+     * Returns a copy of this entry.
+     */
+    public Object clone() {
+        try {
+            ZipEntry e = (ZipEntry)super.clone();
+            e.extra = (extra == null) ? null : extra.clone();
+            return e;
+        } catch (CloneNotSupportedException e) {
+            // This should never happen, since we are Cloneable
+            throw new InternalError(e);
+        }
+    }
+}
diff --git a/android-35/java/util/zip/ZipError.java b/android-35/java/util/zip/ZipError.java
new file mode 100644
index 0000000..2aa37be
--- /dev/null
+++ b/android-35/java/util/zip/ZipError.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2006, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+/**
+ * Signals that an unrecoverable error has occurred.
+ *
+ * @author  Dave Bristor
+ * @since   1.6
+ */
+public class ZipError extends InternalError {
+    @java.io.Serial
+    private static final long serialVersionUID = 853973422266861979L;
+
+    /**
+     * Constructs a ZipError with the given detail message.
+     * @param s the {@code String} containing a detail message
+     */
+    public ZipError(String s) {
+        super(s);
+    }
+}
diff --git a/android-35/java/util/zip/ZipException.java b/android-35/java/util/zip/ZipException.java
new file mode 100644
index 0000000..45048a4
--- /dev/null
+++ b/android-35/java/util/zip/ZipException.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.IOException;
+
+/**
+ * Signals that a Zip exception of some sort has occurred.
+ *
+ * @see     java.io.IOException
+ * @since   1.1
+ */
+
+public class ZipException extends IOException {
+    @java.io.Serial
+    private static final long serialVersionUID = 8000196834066748623L;
+
+    /**
+     * Constructs a {@code ZipException} with {@code null}
+     * as its error detail message.
+     */
+    public ZipException() {
+        super();
+    }
+
+    /**
+     * Constructs a {@code ZipException} with the specified detail
+     * message.
+     *
+     * @param   s   the detail message.
+     */
+
+    public ZipException(String s) {
+        super(s);
+    }
+}
diff --git a/android-35/java/util/zip/ZipFile.java b/android-35/java/util/zip/ZipFile.java
new file mode 100644
index 0000000..ee23e55
--- /dev/null
+++ b/android-35/java/util/zip/ZipFile.java
@@ -0,0 +1,1941 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1995, 2021, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.Closeable;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.RandomAccessFile;
+import java.io.UncheckedIOException;
+import java.lang.ref.Cleaner.Cleanable;
+import java.nio.charset.CharacterCodingException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.InvalidPathException;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.nio.file.Files;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.TreeSet;
+import java.util.WeakHashMap;
+import java.util.function.Consumer;
+import java.util.function.IntFunction;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import jdk.internal.access.SharedSecrets;
+import jdk.internal.misc.VM;
+import jdk.internal.ref.CleanerFactory;
+import jdk.internal.vm.annotation.Stable;
+import sun.security.util.SignatureFileVerifier;
+
+import dalvik.system.CloseGuard;
+import dalvik.system.ZipPathValidator;
+
+import static java.util.zip.ZipConstants64.*;
+import static java.util.zip.ZipUtils.*;
+
+/**
+ * This class is used to read entries from a zip file.
+ *
+ * <p> Unless otherwise noted, passing a {@code null} argument to a constructor
+ * or method in this class will cause a {@link NullPointerException} to be
+ * thrown.
+ *
+ * @apiNote
+ * To release resources used by this {@code ZipFile}, the {@link #close()} method
+ * should be called explicitly or by try-with-resources. Subclasses are responsible
+ * for the cleanup of resources acquired by the subclass. Subclasses that override
+ * {@link #finalize()} in order to perform cleanup should be modified to use alternative
+ * cleanup mechanisms such as {@link java.lang.ref.Cleaner} and remove the overriding
+ * {@code finalize} method.
+ *
+ * @author      David Connelly
+ * @since 1.1
+ */
+public class ZipFile implements ZipConstants, Closeable {
+
+    private final String name;     // zip file name
+    private volatile boolean closeRequested;
+
+    // The "resource" used by this zip file that needs to be
+    // cleaned after use.
+    // a) the input streams that need to be closed
+    // b) the list of cached Inflater objects
+    // c) the "native" source of this zip file.
+    private final @Stable CleanableResource res;
+
+    // Android-added: CloseGuard support.
+    private final CloseGuard guard = CloseGuard.get();
+
+    private static final int STORED = ZipEntry.STORED;
+    private static final int DEFLATED = ZipEntry.DEFLATED;
+
+    /**
+     * Mode flag to open a zip file for reading.
+     */
+    public static final int OPEN_READ = 0x1;
+
+    /**
+     * Mode flag to open a zip file and mark it for deletion.  The file will be
+     * deleted some time between the moment that it is opened and the moment
+     * that it is closed, but its contents will remain accessible via the
+     * {@code ZipFile} object until either the close method is invoked or the
+     * virtual machine exits.
+     */
+    public static final int OPEN_DELETE = 0x4;
+
+    // Android-changed: Additional ZipException throw scenario with ZipPathValidator.
+    /**
+     * Opens a zip file for reading.
+     *
+     * <p>First, if there is a security manager, its {@code checkRead}
+     * method is called with the {@code name} argument as its argument
+     * to ensure the read is allowed.
+     *
+     * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
+     * decode the entry names and comments.
+     *
+     * <p>If the app targets Android U or above, zip file entry names containing
+     * ".." or starting with "/" passed here will throw a {@link ZipException}.
+     * For more details, see {@link dalvik.system.ZipPathValidator}.
+     *
+     * @param name the name of the zip file
+     * @throws ZipException if (1) a ZIP format error has occurred or
+     *         (2) <code>targetSdkVersion >= BUILD.VERSION_CODES.UPSIDE_DOWN_CAKE</code>
+     *         and (the <code>name</code> argument contains ".." or starts with "/").
+     * @throws IOException if an I/O error has occurred
+     * @throws SecurityException if a security manager exists and its
+     *         {@code checkRead} method doesn't allow read access to the file.
+     *
+     * @see SecurityManager#checkRead(java.lang.String)
+     */
+    public ZipFile(String name) throws IOException {
+        this(new File(name), OPEN_READ);
+    }
+
+    /**
+     * Opens a new {@code ZipFile} to read from the specified
+     * {@code File} object in the specified mode.  The mode argument
+     * must be either {@code OPEN_READ} or {@code OPEN_READ | OPEN_DELETE}.
+     *
+     * <p>First, if there is a security manager, its {@code checkRead}
+     * method is called with the {@code name} argument as its argument to
+     * ensure the read is allowed.
+     *
+     * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
+     * decode the entry names and comments
+     *
+     * @param file the ZIP file to be opened for reading
+     * @param mode the mode in which the file is to be opened
+     * @throws ZipException if a ZIP format error has occurred
+     * @throws IOException if an I/O error has occurred
+     * @throws SecurityException if a security manager exists and
+     *         its {@code checkRead} method
+     *         doesn't allow read access to the file,
+     *         or its {@code checkDelete} method doesn't allow deleting
+     *         the file when the {@code OPEN_DELETE} flag is set.
+     * @throws IllegalArgumentException if the {@code mode} argument is invalid
+     * @see SecurityManager#checkRead(java.lang.String)
+     * @since 1.3
+     */
+    public ZipFile(File file, int mode) throws IOException {
+        // Android-changed: Use StandardCharsets.UTF_8.
+        // this(file, mode, UTF_8.INSTANCE);
+        this(file, mode, StandardCharsets.UTF_8);
+    }
+
+    /**
+     * Opens a ZIP file for reading given the specified File object.
+     *
+     * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
+     * decode the entry names and comments.
+     *
+     * @param file the ZIP file to be opened for reading
+     * @throws ZipException if a ZIP format error has occurred
+     * @throws IOException if an I/O error has occurred
+     */
+    public ZipFile(File file) throws ZipException, IOException {
+        this(file, OPEN_READ);
+    }
+
+    // Android-changed: Use of the hidden constructor with a new argument for zip path validation.
+    /**
+     * Opens a new {@code ZipFile} to read from the specified
+     * {@code File} object in the specified mode.  The mode argument
+     * must be either {@code OPEN_READ} or {@code OPEN_READ | OPEN_DELETE}.
+     *
+     * <p>First, if there is a security manager, its {@code checkRead}
+     * method is called with the {@code name} argument as its argument to
+     * ensure the read is allowed.
+     *
+     * @param file the ZIP file to be opened for reading
+     * @param mode the mode in which the file is to be opened
+     * @param charset
+     *        the {@linkplain java.nio.charset.Charset charset} to
+     *        be used to decode the ZIP entry name and comment that are not
+     *        encoded by using UTF-8 encoding (indicated by entry's general
+     *        purpose flag).
+     *
+     * @throws ZipException if a ZIP format error has occurred
+     * @throws IOException if an I/O error has occurred
+     *
+     * @throws SecurityException
+     *         if a security manager exists and its {@code checkRead}
+     *         method doesn't allow read access to the file,or its
+     *         {@code checkDelete} method doesn't allow deleting the
+     *         file when the {@code OPEN_DELETE} flag is set
+     *
+     * @throws IllegalArgumentException if the {@code mode} argument is invalid
+     *
+     * @see SecurityManager#checkRead(java.lang.String)
+     *
+     * @since 1.7
+     */
+    public ZipFile(File file, int mode, Charset charset) throws IOException
+    {
+        this(file, mode, charset, /* enableZipPathValidator */ true);
+    }
+
+    // Android-added: New hidden constructor with an argument for zip path validation.
+    /** @hide */
+    public ZipFile(File file, int mode, boolean enableZipPathValidator) throws IOException {
+        this(file, mode, StandardCharsets.UTF_8, enableZipPathValidator);
+    }
+
+    // Android-changed: Change existing constructor ZipFile(File file, int mode, Charset charset)
+    // to have a new argument enableZipPathValidator in order to set the isZipPathValidatorEnabled
+    // variable before calling the native method open().
+    /** @hide */
+    public ZipFile(File file, int mode, Charset charset, boolean enableZipPathValidator)
+            throws IOException {
+        if (((mode & OPEN_READ) == 0) ||
+            ((mode & ~(OPEN_READ | OPEN_DELETE)) != 0)) {
+            throw new IllegalArgumentException("Illegal mode: 0x"+
+                                               Integer.toHexString(mode));
+        }
+        String name = file.getPath();
+        file = new File(name);
+        // Android-removed: SecurityManager is always null.
+        /*
+        @SuppressWarnings("removal")
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkRead(name);
+            if ((mode & OPEN_DELETE) != 0) {
+                sm.checkDelete(name);
+            }
+        }
+        */
+
+        Objects.requireNonNull(charset, "charset");
+
+        this.name = name;
+        // Android-removed: Skip perf counters.
+        // long t0 = System.nanoTime();
+
+        // Android-changed: pass isZipPathValidatorEnabled flag.
+        // this.res = new CleanableResource(this, ZipCoder.get(charset), file, mode);
+        boolean isZipPathValidatorEnabled = enableZipPathValidator && !ZipPathValidator.isClear();
+        this.res = new CleanableResource(
+                this, ZipCoder.get(charset), file, mode, isZipPathValidatorEnabled);
+
+        // Android-removed: Skip perf counters.
+        // PerfCounter.getZipFileOpenTime().addElapsedTimeFrom(t0);
+        // PerfCounter.getZipFileCount().increment();
+    }
+
+    /**
+     * Opens a zip file for reading.
+     *
+     * <p>First, if there is a security manager, its {@code checkRead}
+     * method is called with the {@code name} argument as its argument
+     * to ensure the read is allowed.
+     *
+     * @param name the name of the zip file
+     * @param charset
+     *        the {@linkplain java.nio.charset.Charset charset} to
+     *        be used to decode the ZIP entry name and comment that are not
+     *        encoded by using UTF-8 encoding (indicated by entry's general
+     *        purpose flag).
+     *
+     * @throws ZipException if a ZIP format error has occurred
+     * @throws IOException if an I/O error has occurred
+     * @throws SecurityException
+     *         if a security manager exists and its {@code checkRead}
+     *         method doesn't allow read access to the file
+     *
+     * @see SecurityManager#checkRead(java.lang.String)
+     *
+     * @since 1.7
+     */
+    public ZipFile(String name, Charset charset) throws IOException
+    {
+        this(new File(name), OPEN_READ, charset);
+    }
+
+    /**
+     * Opens a ZIP file for reading given the specified File object.
+     *
+     * @param file the ZIP file to be opened for reading
+     * @param charset
+     *        The {@linkplain java.nio.charset.Charset charset} to be
+     *        used to decode the ZIP entry name and comment (ignored if
+     *        the <a href="package-summary.html#lang_encoding"> language
+     *        encoding bit</a> of the ZIP entry's general purpose bit
+     *        flag is set).
+     *
+     * @throws ZipException if a ZIP format error has occurred
+     * @throws IOException if an I/O error has occurred
+     *
+     * @since 1.7
+     */
+    public ZipFile(File file, Charset charset) throws IOException
+    {
+        this(file, OPEN_READ, charset);
+    }
+
+    /**
+     * Returns the zip file comment, or null if none.
+     *
+     * @return the comment string for the zip file, or null if none
+     *
+     * @throws IllegalStateException if the zip file has been closed
+     *
+     * @since 1.7
+     */
+    public String getComment() {
+        synchronized (this) {
+            ensureOpen();
+            if (res.zsrc.comment == null) {
+                return null;
+            }
+            return res.zsrc.zc.toString(res.zsrc.comment);
+        }
+    }
+
+    /**
+     * Returns the zip file entry for the specified name, or null
+     * if not found.
+     *
+     * @param name the name of the entry
+     * @return the zip file entry, or null if not found
+     * @throws IllegalStateException if the zip file has been closed
+     */
+    public ZipEntry getEntry(String name) {
+        Objects.requireNonNull(name, "name");
+        ZipEntry entry = null;
+        synchronized (this) {
+            ensureOpen();
+            int pos = res.zsrc.getEntryPos(name, true);
+            if (pos != -1) {
+                entry = getZipEntry(name, pos);
+            }
+        }
+        return entry;
+    }
+
+    /**
+     * Returns an input stream for reading the contents of the specified
+     * zip file entry.
+     * <p>
+     * Closing this ZIP file will, in turn, close all input streams that
+     * have been returned by invocations of this method.
+     *
+     * @param entry the zip file entry
+     * @return the input stream for reading the contents of the specified
+     * zip file entry.
+     * @throws ZipException if a ZIP format error has occurred
+     * @throws IOException if an I/O error has occurred
+     * @throws IllegalStateException if the zip file has been closed
+     */
+    public InputStream getInputStream(ZipEntry entry) throws IOException {
+        Objects.requireNonNull(entry, "entry");
+        int pos;
+        ZipFileInputStream in;
+        Source zsrc = res.zsrc;
+        Set<InputStream> istreams = res.istreams;
+        synchronized (this) {
+            ensureOpen();
+            if (Objects.equals(lastEntryName, entry.name)) {
+                pos = lastEntryPos;
+            } else {
+                pos = zsrc.getEntryPos(entry.name, false);
+            }
+            if (pos == -1) {
+                return null;
+            }
+            in = new ZipFileInputStream(zsrc.cen, pos);
+            switch (CENHOW(zsrc.cen, pos)) {
+            case STORED:
+                synchronized (istreams) {
+                    istreams.add(in);
+                }
+                return in;
+            case DEFLATED:
+                // Inflater likes a bit of slack
+                // MORE: Compute good size for inflater stream:
+                long size = CENLEN(zsrc.cen, pos) + 2;
+                if (size > 65536) {
+                    // Android-changed: Use 64k buffer size, performs
+                    // better than 8k. See http://b/65491407.
+                    // size = 8192;
+                    size = 65536;
+                }
+                if (size <= 0) {
+                    size = 4096;
+                }
+                InputStream is = new ZipFileInflaterInputStream(in, res, (int)size);
+                synchronized (istreams) {
+                    istreams.add(is);
+                }
+                return is;
+            default:
+                throw new ZipException("invalid compression method");
+            }
+        }
+    }
+
+    private static class InflaterCleanupAction implements Runnable {
+        private final Inflater inf;
+        private final CleanableResource res;
+
+        InflaterCleanupAction(Inflater inf, CleanableResource res) {
+            this.inf = inf;
+            this.res = res;
+        }
+
+        @Override
+        public void run() {
+            res.releaseInflater(inf);
+        }
+    }
+
+    private class ZipFileInflaterInputStream extends InflaterInputStream {
+        private volatile boolean closeRequested;
+        private boolean eof = false;
+        private final Cleanable cleanable;
+
+        ZipFileInflaterInputStream(ZipFileInputStream zfin,
+                                   CleanableResource res, int size) {
+            this(zfin, res, res.getInflater(), size);
+        }
+
+        private ZipFileInflaterInputStream(ZipFileInputStream zfin,
+                                           CleanableResource res,
+                                           Inflater inf, int size) {
+            // Android-changed: ZipFileInflaterInputStream does not control its inflater's lifetime
+            // and hence it shouldn't be closed when the stream is closed.
+            // super(zfin, inf, size);
+            super(zfin, inf, size, /* ownsInflater */ false);
+            this.cleanable = CleanerFactory.cleaner().register(this,
+                    new InflaterCleanupAction(inf, res));
+        }
+
+        public void close() throws IOException {
+            if (closeRequested)
+                return;
+            closeRequested = true;
+            super.close();
+            synchronized (res.istreams) {
+                res.istreams.remove(this);
+            }
+            cleanable.clean();
+        }
+
+        // Override fill() method to provide an extra "dummy" byte
+        // at the end of the input stream. This is required when
+        // using the "nowrap" Inflater option.
+        protected void fill() throws IOException {
+            if (eof) {
+                throw new EOFException("Unexpected end of ZLIB input stream");
+            }
+            len = in.read(buf, 0, buf.length);
+            if (len == -1) {
+                buf[0] = 0;
+                len = 1;
+                eof = true;
+            }
+            inf.setInput(buf, 0, len);
+        }
+
+        public int available() throws IOException {
+            if (closeRequested)
+                return 0;
+            long avail = ((ZipFileInputStream)in).size() - inf.getBytesWritten();
+            return (avail > (long) Integer.MAX_VALUE ?
+                    Integer.MAX_VALUE : (int) avail);
+        }
+    }
+
+    /**
+     * Returns the path name of the ZIP file.
+     * @return the path name of the ZIP file
+     */
+    public String getName() {
+        return name;
+    }
+
+    private class ZipEntryIterator<T extends ZipEntry>
+            implements Enumeration<T>, Iterator<T> {
+
+        private int i = 0;
+        private final int entryCount;
+
+        public ZipEntryIterator(int entryCount) {
+            this.entryCount = entryCount;
+        }
+
+        @Override
+        public boolean hasMoreElements() {
+            return hasNext();
+        }
+
+        @Override
+        public boolean hasNext() {
+            // Android-changed: check that file is open.
+            // return i < entryCount;
+            synchronized (ZipFile.this) {
+                ensureOpen();
+                return i < entryCount;
+            }
+        }
+
+        @Override
+        public T nextElement() {
+            return next();
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public T next() {
+            synchronized (ZipFile.this) {
+                ensureOpen();
+                if (!hasNext()) {
+                    throw new NoSuchElementException();
+                }
+                // each "entry" has 3 ints in table entries
+                return (T)getZipEntry(null, res.zsrc.getEntryPos(i++ * 3));
+            }
+        }
+
+        @Override
+        public Iterator<T> asIterator() {
+            return this;
+        }
+    }
+
+    /**
+     * Returns an enumeration of the ZIP file entries.
+     * @return an enumeration of the ZIP file entries
+     * @throws IllegalStateException if the zip file has been closed
+     */
+    public Enumeration<? extends ZipEntry> entries() {
+        synchronized (this) {
+            ensureOpen();
+            return new ZipEntryIterator<ZipEntry>(res.zsrc.total);
+        }
+    }
+
+    private Enumeration<JarEntry> jarEntries() {
+        synchronized (this) {
+            ensureOpen();
+            return new ZipEntryIterator<JarEntry>(res.zsrc.total);
+        }
+    }
+
+    private class EntrySpliterator<T> extends Spliterators.AbstractSpliterator<T> {
+        private int index;
+        private final int fence;
+        private final IntFunction<T> gen;
+
+        EntrySpliterator(int index, int fence, IntFunction<T> gen) {
+            super((long)fence,
+                  Spliterator.ORDERED | Spliterator.DISTINCT | Spliterator.IMMUTABLE |
+                  Spliterator.NONNULL);
+            this.index = index;
+            this.fence = fence;
+            this.gen = gen;
+        }
+
+        @Override
+        public boolean tryAdvance(Consumer<? super T> action) {
+            if (action == null)
+                throw new NullPointerException();
+            if (index >= 0 && index < fence) {
+                synchronized (ZipFile.this) {
+                    ensureOpen();
+                    action.accept(gen.apply(res.zsrc.getEntryPos(index++ * 3)));
+                }
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Returns an ordered {@code Stream} over the ZIP file entries.
+     *
+     * Entries appear in the {@code Stream} in the order they appear in
+     * the central directory of the ZIP file.
+     *
+     * @return an ordered {@code Stream} of entries in this ZIP file
+     * @throws IllegalStateException if the zip file has been closed
+     * @since 1.8
+     */
+    public Stream<? extends ZipEntry> stream() {
+        synchronized (this) {
+            ensureOpen();
+            return StreamSupport.stream(new EntrySpliterator<>(0, res.zsrc.total,
+                pos -> getZipEntry(null, pos)), false);
+       }
+    }
+
+    private String getEntryName(int pos) {
+        byte[] cen = res.zsrc.cen;
+        int nlen = CENNAM(cen, pos);
+        ZipCoder zc = res.zsrc.zipCoderForPos(pos);
+        return zc.toString(cen, pos + CENHDR, nlen);
+    }
+
+    /*
+     * Returns an ordered {@code Stream} over the zip file entry names.
+     *
+     * Entry names appear in the {@code Stream} in the order they appear in
+     * the central directory of the ZIP file.
+     *
+     * @return an ordered {@code Stream} of entry names in this zip file
+     * @throws IllegalStateException if the zip file has been closed
+     * @since 10
+     */
+    private Stream<String> entryNameStream() {
+        synchronized (this) {
+            ensureOpen();
+            return StreamSupport.stream(
+                new EntrySpliterator<>(0, res.zsrc.total, this::getEntryName), false);
+        }
+    }
+
+    /*
+     * Returns an ordered {@code Stream} over the zip file entries.
+     *
+     * Entries appear in the {@code Stream} in the order they appear in
+     * the central directory of the jar file.
+     *
+     * @return an ordered {@code Stream} of entries in this zip file
+     * @throws IllegalStateException if the zip file has been closed
+     * @since 10
+     */
+    private Stream<JarEntry> jarStream() {
+        synchronized (this) {
+            ensureOpen();
+            return StreamSupport.stream(new EntrySpliterator<>(0, res.zsrc.total,
+                pos -> (JarEntry)getZipEntry(null, pos)), false);
+        }
+    }
+
+    private String lastEntryName;
+    private int lastEntryPos;
+
+    /* Check ensureOpen() before invoking this method */
+    private ZipEntry getZipEntry(String name, int pos) {
+        byte[] cen = res.zsrc.cen;
+        int nlen = CENNAM(cen, pos);
+        int elen = CENEXT(cen, pos);
+        int clen = CENCOM(cen, pos);
+
+        ZipCoder zc = res.zsrc.zipCoderForPos(pos);
+        if (name != null) {
+            // only need to check for mismatch of trailing slash
+            if (nlen > 0 &&
+                !name.isEmpty() &&
+                zc.hasTrailingSlash(cen, pos + CENHDR + nlen) &&
+                !name.endsWith("/"))
+            {
+                name += '/';
+            }
+        } else {
+            // invoked from iterator, use the entry name stored in cen
+            name = zc.toString(cen, pos + CENHDR, nlen);
+        }
+        ZipEntry e;
+        if (this instanceof JarFile) {
+            // Android-changed: access method directly.
+            // e = Source.JUJA.entryFor((JarFile)this, name);
+            e = ((JarFile) this).entryFor(name);
+        } else {
+            e = new ZipEntry(name);
+        }
+        e.flag = CENFLG(cen, pos);
+        e.xdostime = CENTIM(cen, pos);
+        e.crc = CENCRC(cen, pos);
+        e.size = CENLEN(cen, pos);
+        e.csize = CENSIZ(cen, pos);
+        e.method = CENHOW(cen, pos);
+        if (CENVEM_FA(cen, pos) == FILE_ATTRIBUTES_UNIX) {
+            // read all bits in this field, including sym link attributes
+            e.extraAttributes = CENATX_PERMS(cen, pos) & 0xFFFF;
+        }
+
+        if (elen != 0) {
+            int start = pos + CENHDR + nlen;
+            e.setExtra0(Arrays.copyOfRange(cen, start, start + elen), true, false);
+        }
+        if (clen != 0) {
+            int start = pos + CENHDR + nlen + elen;
+            e.comment = zc.toString(cen, start, clen);
+        }
+        lastEntryName = e.name;
+        lastEntryPos = pos;
+        return e;
+    }
+
+    /**
+     * Returns the number of entries in the ZIP file.
+     *
+     * @return the number of entries in the ZIP file
+     * @throws IllegalStateException if the zip file has been closed
+     */
+    public int size() {
+        synchronized (this) {
+            ensureOpen();
+            return res.zsrc.total;
+        }
+    }
+
+    private static class CleanableResource implements Runnable {
+        // The outstanding inputstreams that need to be closed
+        final Set<InputStream> istreams;
+
+        // List of cached Inflater objects for decompression
+        Deque<Inflater> inflaterCache;
+
+        final Cleanable cleanable;
+
+        Source zsrc;
+
+        CleanableResource(ZipFile zf, ZipCoder zc, File file, int mode) throws IOException {
+            this(zf, zc, file, mode, false);
+        }
+
+        // Android-added: added extra enableZipPathValidator argument.
+        CleanableResource(ZipFile zf, ZipCoder zc, File file,
+                int mode, boolean enableZipPathValidator) throws IOException {
+            this.cleanable = CleanerFactory.cleaner().register(zf, this);
+            this.istreams = Collections.newSetFromMap(new WeakHashMap<>());
+            this.inflaterCache = new ArrayDeque<>();
+            this.zsrc = Source.get(file, (mode & OPEN_DELETE) != 0, zc, enableZipPathValidator);
+        }
+
+        void clean() {
+            cleanable.clean();
+        }
+
+        /*
+         * Gets an inflater from the list of available inflaters or allocates
+         * a new one.
+         */
+        Inflater getInflater() {
+            Inflater inf;
+            synchronized (inflaterCache) {
+                if ((inf = inflaterCache.poll()) != null) {
+                    return inf;
+                }
+            }
+            return new Inflater(true);
+        }
+
+        /*
+         * Releases the specified inflater to the list of available inflaters.
+         */
+        void releaseInflater(Inflater inf) {
+            Deque<Inflater> inflaters = this.inflaterCache;
+            if (inflaters != null) {
+                synchronized (inflaters) {
+                    // double checked!
+                    if (inflaters == this.inflaterCache) {
+                        inf.reset();
+                        inflaters.add(inf);
+                        return;
+                    }
+                }
+            }
+            // inflaters cache already closed - just end it.
+            inf.end();
+        }
+
+        public void run() {
+            IOException ioe = null;
+
+            // Release cached inflaters and close the cache first
+            Deque<Inflater> inflaters = this.inflaterCache;
+            if (inflaters != null) {
+                synchronized (inflaters) {
+                    // no need to double-check as only one thread gets a
+                    // chance to execute run() (Cleaner guarantee)...
+                    Inflater inf;
+                    while ((inf = inflaters.poll()) != null) {
+                        inf.end();
+                    }
+                    // close inflaters cache
+                    this.inflaterCache = null;
+                }
+            }
+
+            // Close streams, release their inflaters
+            if (istreams != null) {
+                synchronized (istreams) {
+                    if (!istreams.isEmpty()) {
+                        InputStream[] copy = istreams.toArray(new InputStream[0]);
+                        istreams.clear();
+                        for (InputStream is : copy) {
+                            try {
+                                is.close();
+                            } catch (IOException e) {
+                                if (ioe == null) ioe = e;
+                                else ioe.addSuppressed(e);
+                            }
+                        }
+                    }
+                }
+            }
+
+            // Release zip src
+            if (zsrc != null) {
+                synchronized (zsrc) {
+                    try {
+                        Source.release(zsrc);
+                        zsrc = null;
+                    } catch (IOException e) {
+                        if (ioe == null) ioe = e;
+                        else ioe.addSuppressed(e);
+                    }
+                }
+            }
+            if (ioe != null) {
+                throw new UncheckedIOException(ioe);
+            }
+        }
+    }
+
+    /**
+     * Closes the ZIP file.
+     *
+     * <p> Closing this ZIP file will close all of the input streams
+     * previously returned by invocations of the {@link #getInputStream
+     * getInputStream} method.
+     *
+     * @throws IOException if an I/O error has occurred
+     */
+    public void close() throws IOException {
+        if (closeRequested) {
+            return;
+        }
+        // Android-added: CloseGuard support.
+        if (guard != null) {
+            guard.close();
+        }
+        closeRequested = true;
+
+        synchronized (this) {
+            // Close streams, release their inflaters, release cached inflaters
+            // and release zip source
+            try {
+                res.clean();
+            } catch (UncheckedIOException ioe) {
+                throw ioe.getCause();
+            }
+        }
+    }
+
+    private void ensureOpen() {
+        if (closeRequested) {
+            throw new IllegalStateException("zip file closed");
+        }
+        if (res.zsrc == null) {
+            throw new IllegalStateException("The object is not initialized.");
+        }
+    }
+
+    private void ensureOpenOrZipException() throws IOException {
+        if (closeRequested) {
+            throw new ZipException("ZipFile closed");
+        }
+    }
+
+    /*
+     * Inner class implementing the input stream used to read a
+     * (possibly compressed) zip file entry.
+     */
+    private class ZipFileInputStream extends InputStream {
+        private volatile boolean closeRequested;
+        private   long pos;     // current position within entry data
+        private   long startingPos; // Start position for the entry data
+        protected long rem;     // number of remaining bytes within entry
+        protected long size;    // uncompressed size of this entry
+
+        ZipFileInputStream(byte[] cen, int cenpos) {
+            rem = CENSIZ(cen, cenpos);
+            size = CENLEN(cen, cenpos);
+            pos = CENOFF(cen, cenpos);
+            // zip64
+            if (rem == ZIP64_MAGICVAL || size == ZIP64_MAGICVAL ||
+                pos == ZIP64_MAGICVAL) {
+                checkZIP64(cen, cenpos);
+            }
+            // negative for lazy initialization, see getDataOffset();
+            pos = - (pos + ZipFile.this.res.zsrc.locpos);
+        }
+
+        private void checkZIP64(byte[] cen, int cenpos) {
+            int off = cenpos + CENHDR + CENNAM(cen, cenpos);
+            int end = off + CENEXT(cen, cenpos);
+            while (off + 4 < end) {
+                int tag = get16(cen, off);
+                int sz = get16(cen, off + 2);
+                off += 4;
+                if (off + sz > end)         // invalid data
+                    break;
+                if (tag == EXTID_ZIP64) {
+                    if (size == ZIP64_MAGICVAL) {
+                        if (sz < 8 || (off + 8) > end)
+                            break;
+                        size = get64(cen, off);
+                        sz -= 8;
+                        off += 8;
+                    }
+                    if (rem == ZIP64_MAGICVAL) {
+                        if (sz < 8 || (off + 8) > end)
+                            break;
+                        rem = get64(cen, off);
+                        sz -= 8;
+                        off += 8;
+                    }
+                    if (pos == ZIP64_MAGICVAL) {
+                        if (sz < 8 || (off + 8) > end)
+                            break;
+                        pos = get64(cen, off);
+                        sz -= 8;
+                        off += 8;
+                    }
+                    break;
+                }
+                off += sz;
+            }
+        }
+
+        /*
+         * The Zip file spec explicitly allows the LOC extra data size to
+         * be different from the CEN extra data size. Since we cannot trust
+         * the CEN extra data size, we need to read the LOC to determine
+         * the entry data offset.
+         */
+        private long initDataOffset() throws IOException {
+            if (pos <= 0) {
+                byte[] loc = new byte[LOCHDR];
+                pos = -pos;
+                int len = ZipFile.this.res.zsrc.readFullyAt(loc, 0, loc.length, pos);
+                if (len != LOCHDR) {
+                    throw new ZipException("ZipFile error reading zip file");
+                }
+                if (LOCSIG(loc) != LOCSIG) {
+                    throw new ZipException("ZipFile invalid LOC header (bad signature)");
+                }
+                pos += LOCHDR + LOCNAM(loc) + LOCEXT(loc);
+                startingPos = pos; // Save starting position for the entry
+            }
+            return pos;
+        }
+
+        public int read(byte b[], int off, int len) throws IOException {
+            synchronized (ZipFile.this) {
+                ensureOpenOrZipException();
+                initDataOffset();
+                if (rem == 0) {
+                    return -1;
+                }
+                if (len > rem) {
+                    len = (int) rem;
+                }
+                if (len <= 0) {
+                    return 0;
+                }
+                len = ZipFile.this.res.zsrc.readAt(b, off, len, pos);
+                if (len > 0) {
+                    pos += len;
+                    rem -= len;
+                }
+            }
+            if (rem == 0) {
+                close();
+            }
+            return len;
+        }
+
+        public int read() throws IOException {
+            byte[] b = new byte[1];
+            if (read(b, 0, 1) == 1) {
+                return b[0] & 0xff;
+            } else {
+                return -1;
+            }
+        }
+
+        public long skip(long n) throws IOException {
+            synchronized (ZipFile.this) {
+                initDataOffset();
+                long newPos = pos + n;
+                if (n > 0) {
+                    // If we overflowed adding the skip value or are moving
+                    // past EOF, set the skip value to number of bytes remaining
+                    // to reach EOF
+                    if (newPos < 0 || n > rem) {
+                        n = rem;
+                    }
+                } else if (newPos < startingPos) {
+                    // Tried to position before BOF so set position to the
+                    // BOF and return the number of bytes we moved backwards
+                    // to reach BOF
+                    n = startingPos - pos;
+                }
+                pos += n;
+                rem -= n;
+            }
+            if (rem == 0) {
+                close();
+            }
+            return n;
+        }
+
+        public int available() {
+            return rem > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) rem;
+        }
+
+        public long size() {
+            return size;
+        }
+
+        public void close() {
+            if (closeRequested) {
+                return;
+            }
+            closeRequested = true;
+            rem = 0;
+            synchronized (res.istreams) {
+                res.istreams.remove(this);
+            }
+        }
+
+    }
+
+    /**
+     * Returns {@code true} if, and only if, the zip file begins with {@code
+     * LOCSIG}.
+     * @hide
+     */
+    // Android-added: Access startsWithLocHeader() directly.
+    // Make hidden public for use by sun.misc.URLClassPath
+    public boolean startsWithLocHeader() {
+        return res.zsrc.startsWithLoc;
+    }
+
+    // Android-changed: marked as protected so JarFile can access it.
+    /**
+     * Returns the names of the META-INF/MANIFEST.MF entry - if exists -
+     * and any signature-related files under META-INF. This method is used in
+     * JarFile, via SharedSecrets, as an optimization.
+     * @hide
+     */
+    protected List<String> getManifestAndSignatureRelatedFiles() {
+        synchronized (this) {
+            ensureOpen();
+            Source zsrc = res.zsrc;
+            int[] metanames = zsrc.signatureMetaNames;
+            List<String> files = null;
+            if (zsrc.manifestPos >= 0) {
+                files = new ArrayList<>();
+                files.add(getEntryName(zsrc.manifestPos));
+            }
+            if (metanames != null) {
+                if (files == null) {
+                    files = new ArrayList<>();
+                }
+                for (int i = 0; i < metanames.length; i++) {
+                    files.add(getEntryName(metanames[i]));
+                }
+            }
+            return files == null ? List.of() : files;
+        }
+    }
+
+    /**
+     * Returns the number of the META-INF/MANIFEST.MF entries, case insensitive.
+     * When this number is greater than 1, JarVerifier will treat a file as
+     * unsigned.
+     */
+    private int getManifestNum() {
+        synchronized (this) {
+            ensureOpen();
+            return res.zsrc.manifestNum;
+        }
+    }
+
+    // Android-changed: marked public and @hide as alternative to JavaUtilZipFileAccess.getManifestName.
+    /**
+     * Returns the name of the META-INF/MANIFEST.MF entry, ignoring
+     * case. If {@code onlyIfSignatureRelatedFiles} is true, we only return the
+     * manifest if there is also at least one signature-related file.
+     * This method is used in JarFile, via SharedSecrets, as an optimization
+     * when looking up the manifest file.
+     * @hide
+     */
+    protected String getManifestName(boolean onlyIfSignatureRelatedFiles) {
+        synchronized (this) {
+            ensureOpen();
+            Source zsrc = res.zsrc;
+            int pos = zsrc.manifestPos;
+            if (pos >= 0 && (!onlyIfSignatureRelatedFiles || zsrc.signatureMetaNames != null)) {
+                return getEntryName(pos);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns the versions for which there exists a non-directory
+     * entry that begin with "META-INF/versions/" (case ignored).
+     * This method is used in JarFile, via SharedSecrets, as an
+     * optimization when looking up potentially versioned entries.
+     * Returns an empty array if no versioned entries exist.
+     */
+    private int[] getMetaInfVersions() {
+        synchronized (this) {
+            ensureOpen();
+            return res.zsrc.metaVersions;
+        }
+    }
+
+    // Android-removed: this code does not run on Windows and JavaUtilZipFileAccess is not imported.
+    /*
+    private static boolean isWindows;
+
+    static {
+        SharedSecrets.setJavaUtilZipFileAccess(
+            new JavaUtilZipFileAccess() {
+                @Override
+                public boolean startsWithLocHeader(ZipFile zip) {
+                    return zip.res.zsrc.startsWithLoc;
+                }
+                @Override
+                public List<String> getManifestAndSignatureRelatedFiles(JarFile jar) {
+                    return ((ZipFile)jar).getManifestAndSignatureRelatedFiles();
+                }
+                @Override
+                public int getManifestNum(JarFile jar) {
+                    return ((ZipFile)jar).getManifestNum();
+                }
+                @Override
+                public String getManifestName(JarFile jar, boolean onlyIfHasSignatureRelatedFiles) {
+                    return ((ZipFile)jar).getManifestName(onlyIfHasSignatureRelatedFiles);
+                }
+                @Override
+                public int[] getMetaInfVersions(JarFile jar) {
+                    return ((ZipFile)jar).getMetaInfVersions();
+                }
+                @Override
+                public Enumeration<JarEntry> entries(ZipFile zip) {
+                    return zip.jarEntries();
+                }
+                @Override
+                public Stream<JarEntry> stream(ZipFile zip) {
+                    return zip.jarStream();
+                }
+                @Override
+                public Stream<String> entryNameStream(ZipFile zip) {
+                    return zip.entryNameStream();
+                }
+                @Override
+                public int getExtraAttributes(ZipEntry ze) {
+                    return ze.extraAttributes;
+                }
+                @Override
+                public void setExtraAttributes(ZipEntry ze, int extraAttrs) {
+                    ze.extraAttributes = extraAttrs;
+                }
+
+             }
+        );
+        isWindows = VM.getSavedProperty("os.name").contains("Windows");
+    }
+    */
+
+    private static class Source {
+        // While this is only used from ZipFile, defining it there would cause
+        // a bootstrap cycle that would leave this initialized as null
+        // Android-removed: JavaUtilJarAccess is not available.
+        // private static final JavaUtilJarAccess JUJA = SharedSecrets.javaUtilJarAccess();
+        // "META-INF/".length()
+        private static final int META_INF_LEN = 9;
+        private static final int[] EMPTY_META_VERSIONS = new int[0];
+
+        private final Key key;               // the key in files
+        private final @Stable ZipCoder zc;   // zip coder used to decode/encode
+
+        private int refs = 1;
+
+        private RandomAccessFile zfile;      // zfile of the underlying zip file
+        private byte[] cen;                  // CEN & ENDHDR
+        private long locpos;                 // position of first LOC header (usually 0)
+        private byte[] comment;              // zip file comment
+                                             // list of meta entries in META-INF dir
+        private int   manifestPos = -1;      // position of the META-INF/MANIFEST.MF, if exists
+        private int   manifestNum = 0;       // number of META-INF/MANIFEST.MF, case insensitive
+        private int[] signatureMetaNames;    // positions of signature related entries, if such exist
+        private int[] metaVersions;          // list of unique versions found in META-INF/versions/
+        private final boolean startsWithLoc; // true, if zip file starts with LOCSIG (usually true)
+
+        // A Hashmap for all entries.
+        //
+        // A cen entry of Zip/JAR file. As we have one for every entry in every active Zip/JAR,
+        // We might have a lot of these in a typical system. In order to save space we don't
+        // keep the name in memory, but merely remember a 32 bit {@code hash} value of the
+        // entry name and its offset {@code pos} in the central directory hdeader.
+        //
+        // private static class Entry {
+        //     int hash;       // 32 bit hashcode on name
+        //     int next;       // hash chain: index into entries
+        //     int pos;        // Offset of central directory file header
+        // }
+        // private Entry[] entries;             // array of hashed cen entry
+        //
+        // To reduce the total size of entries further, we use a int[] here to store 3 "int"
+        // {@code hash}, {@code next} and {@code pos} for each entry. The entry can then be
+        // referred by their index of their positions in the {@code entries}.
+        //
+        private int[] entries;                  // array of hashed cen entry
+
+        // Checks the entry at offset pos in the CEN, calculates the Entry values as per above,
+        // then returns the length of the entry name.
+        private int checkAndAddEntry(int pos, int index)
+            throws ZipException
+        {
+            byte[] cen = this.cen;
+            if (CENSIG(cen, pos) != CENSIG) {
+                zerror("invalid CEN header (bad signature)");
+            }
+            int method = CENHOW(cen, pos);
+            int flag   = CENFLG(cen, pos);
+            if ((flag & 1) != 0) {
+                zerror("invalid CEN header (encrypted entry)");
+            }
+            if (method != STORED && method != DEFLATED) {
+                zerror("invalid CEN header (bad compression method: " + method + ")");
+            }
+            int entryPos = pos + CENHDR;
+            int nlen = CENNAM(cen, pos);
+            if (entryPos + nlen > cen.length - ENDHDR) {
+                zerror("invalid CEN header (bad header size)");
+            }
+            try {
+                ZipCoder zcp = zipCoderForPos(pos);
+                int hash = zcp.checkedHash(cen, entryPos, nlen);
+                int hsh = (hash & 0x7fffffff) % tablelen;
+                int next = table[hsh];
+                table[hsh] = index;
+                // Record the CEN offset and the name hash in our hash cell.
+                entries[index++] = hash;
+                entries[index++] = next;
+                entries[index  ] = pos;
+            } catch (Exception e) {
+                zerror("invalid CEN header (bad entry name)");
+            }
+            return nlen;
+        }
+
+        private int getEntryHash(int index) { return entries[index]; }
+        private int getEntryNext(int index) { return entries[index + 1]; }
+        private int getEntryPos(int index)  { return entries[index + 2]; }
+        private static final int ZIP_ENDCHAIN  = -1;
+        private int total;                   // total number of entries
+        private int[] table;                 // Hash chain heads: indexes into entries
+        private int tablelen;                // number of hash heads
+
+        // Android-changed: isZipFilePathValidatorEnabled added as Key part. Key is used as key in
+        // files HashMap, so not including it could lead to opening ZipFile w/o entry names
+        // validation.
+        private static class Key {
+            final BasicFileAttributes attrs;
+            File file;
+            final boolean utf8;
+            // Android-added: isZipFilePathValidatorEnabled added as Key part.
+            final boolean isZipFilePathValidatorEnabled;
+
+            public Key(File file, BasicFileAttributes attrs, ZipCoder zc) {
+                this(file, attrs, zc, /* isZipFilePathValidatorEnabled= */ false);
+            }
+
+            // Android-added: added constructor with isZipFilePathValidatorEnabled argument.
+            public Key(File file, BasicFileAttributes attrs, ZipCoder zc,
+                    boolean isZipFilePathValidatorEnabled) {
+                this.attrs = attrs;
+                this.file = file;
+                this.utf8 = zc.isUTF8();
+                this.isZipFilePathValidatorEnabled = isZipFilePathValidatorEnabled;
+            }
+
+            public int hashCode() {
+                long t = utf8 ? 0 : Long.MAX_VALUE;
+                t += attrs.lastModifiedTime().toMillis();
+                // Android-changed: include izZipFilePathValidatorEnabled in hash computation.
+                // return ((int)(t ^ (t >>> 32))) + file.hashCode();
+                return ((int)(t ^ (t >>> 32))) + file.hashCode()
+                        + Boolean.hashCode(isZipFilePathValidatorEnabled);
+            }
+
+            public boolean equals(Object obj) {
+                if (obj instanceof Key key) {
+                    if (key.utf8 != utf8) {
+                        return false;
+                    }
+                    if (!attrs.lastModifiedTime().equals(key.attrs.lastModifiedTime())) {
+                        return false;
+                    }
+                    // Android-added: include isZipFilePathValidatorEnabled as equality part.
+                    if (key.isZipFilePathValidatorEnabled != isZipFilePathValidatorEnabled) {
+                        return false;
+                    }
+                    Object fk = attrs.fileKey();
+                    if (fk != null) {
+                        return fk.equals(key.attrs.fileKey());
+                    } else {
+                        return file.equals(key.file);
+                    }
+                }
+                return false;
+            }
+        }
+        private static final HashMap<Key, Source> files = new HashMap<>();
+
+
+        // Android-changed: pass izZipFilePathValidatorEnabled argument.
+        // static Source get(File file, boolean toDelete, ZipCoder zc) throws IOException {
+        static Source get(File file, boolean toDelete, ZipCoder zc,
+                boolean isZipPathValidatorEnabled) throws IOException {
+            final Key key;
+            try {
+                // BEGIN Android-changed: isZipFilePathValidatorEnabled passed as part of Key.
+                /*
+                key = new Key(file,
+                        Files.readAttributes(file.toPath(), BasicFileAttributes.class),
+                        zc);
+                */
+                key = new Key(file,
+                        Files.readAttributes(file.toPath(), BasicFileAttributes.class),
+                        zc, isZipPathValidatorEnabled);
+                // END Android-changed: isZipFilePathValidatorEnabled passed as part of Key.
+            } catch (InvalidPathException ipe) {
+                throw new IOException(ipe);
+            }
+            Source src;
+            synchronized (files) {
+                src = files.get(key);
+                if (src != null) {
+                    src.refs++;
+                    return src;
+                }
+            }
+            src = new Source(key, toDelete, zc);
+
+            synchronized (files) {
+                if (files.containsKey(key)) {    // someone else put in first
+                    src.close();                 // close the newly created one
+                    src = files.get(key);
+                    src.refs++;
+                    return src;
+                }
+                files.put(key, src);
+                return src;
+            }
+        }
+
+        static void release(Source src) throws IOException {
+            synchronized (files) {
+                if (src != null && --src.refs == 0) {
+                    files.remove(src.key);
+                    src.close();
+                }
+            }
+        }
+
+        private Source(Key key, boolean toDelete, ZipCoder zc) throws IOException {
+            this.zc = zc;
+            this.key = key;
+            if (toDelete) {
+                // BEGIN Android-changed: we are not targeting Windows, keep else branch only. Also
+                // open file with O_CLOEXEC flag set.
+                /*
+                if (isWindows) {
+                    this.zfile = SharedSecrets.getJavaIORandomAccessFileAccess()
+                                              .openAndDelete(key.file, "r");
+                } else {
+                    this.zfile = new RandomAccessFile(key.file, "r");
+                    key.file.delete();
+                }
+                */
+                this.zfile = new RandomAccessFile(key.file, "r", /* setCloExecFlag= */ true);
+                key.file.delete();
+                // END Android-changed: we are not targeting Windows, keep else branch only.
+            } else {
+                // Android-changed: open with O_CLOEXEC flag set.
+                // this.zfile = new RandomAccessFile(key.file, "r");
+                this.zfile = new RandomAccessFile(key.file, "r", /* setCloExecFlag= */ true);
+            }
+            try {
+                initCEN(-1);
+                byte[] buf = new byte[4];
+                readFullyAt(buf, 0, 4, 0);
+                // BEGIN Android-changed: do not accept files with invalid header
+                // this.startsWithLoc = (LOCSIG(buf) == LOCSIG);
+                long locsig = LOCSIG(buf);
+                this.startsWithLoc = (locsig == LOCSIG);
+                // If a zip file starts with "end of central directory record" it means that such
+                // file is empty.
+                if (locsig != LOCSIG && locsig != ENDSIG) {
+                    String msg = "Entry at offset zero has invalid LFH signature "
+                                    + Long.toHexString(locsig);
+                    throw new ZipException(msg);
+                }
+                // END Android-changed: do not accept files with invalid header
+            } catch (IOException x) {
+                try {
+                    this.zfile.close();
+                } catch (IOException xx) {}
+                throw x;
+            }
+        }
+
+        private void close() throws IOException {
+            zfile.close();
+            zfile = null;
+            cen = null;
+            entries = null;
+            table = null;
+            manifestPos = -1;
+            manifestNum = 0;
+            signatureMetaNames = null;
+            metaVersions = EMPTY_META_VERSIONS;
+        }
+
+        private static final int BUF_SIZE = 8192;
+        private final int readFullyAt(byte[] buf, int off, int len, long pos)
+            throws IOException
+        {
+            synchronized (zfile) {
+                zfile.seek(pos);
+                int N = len;
+                while (N > 0) {
+                    int n = Math.min(BUF_SIZE, N);
+                    zfile.readFully(buf, off, n);
+                    off += n;
+                    N -= n;
+                }
+                return len;
+            }
+        }
+
+        private final int readAt(byte[] buf, int off, int len, long pos)
+            throws IOException
+        {
+            synchronized (zfile) {
+                zfile.seek(pos);
+                return zfile.read(buf, off, len);
+            }
+        }
+
+
+        private static class End {
+            int  centot;     // 4 bytes
+            long cenlen;     // 4 bytes
+            long cenoff;     // 4 bytes
+            long endpos;     // 4 bytes
+        }
+
+        /*
+         * Searches for end of central directory (END) header. The contents of
+         * the END header will be read and placed in endbuf. Returns the file
+         * position of the END header, otherwise returns -1 if the END header
+         * was not found or an error occurred.
+         */
+        private End findEND() throws IOException {
+            long ziplen = zfile.length();
+            if (ziplen <= 0)
+                zerror("zip file is empty");
+            End end = new End();
+            byte[] buf = new byte[READBLOCKSZ];
+            long minHDR = (ziplen - END_MAXLEN) > 0 ? ziplen - END_MAXLEN : 0;
+            long minPos = minHDR - (buf.length - ENDHDR);
+            for (long pos = ziplen - buf.length; pos >= minPos; pos -= (buf.length - ENDHDR)) {
+                int off = 0;
+                if (pos < 0) {
+                    // Pretend there are some NUL bytes before start of file
+                    off = (int)-pos;
+                    Arrays.fill(buf, 0, off, (byte)0);
+                }
+                int len = buf.length - off;
+                if (readFullyAt(buf, off, len, pos + off) != len ) {
+                    zerror("zip END header not found");
+                }
+                // Now scan the block backwards for END header signature
+                for (int i = buf.length - ENDHDR; i >= 0; i--) {
+                    if (buf[i+0] == (byte)'P'    &&
+                        buf[i+1] == (byte)'K'    &&
+                        buf[i+2] == (byte)'\005' &&
+                        buf[i+3] == (byte)'\006') {
+                        // Found ENDSIG header
+                        byte[] endbuf = Arrays.copyOfRange(buf, i, i + ENDHDR);
+                        end.centot = ENDTOT(endbuf);
+                        end.cenlen = ENDSIZ(endbuf);
+                        end.cenoff = ENDOFF(endbuf);
+                        end.endpos = pos + i;
+                        int comlen = ENDCOM(endbuf);
+                        if (end.endpos + ENDHDR + comlen != ziplen) {
+                            // ENDSIG matched, however the size of file comment in it does
+                            // not match the real size. One "common" cause for this problem
+                            // is some "extra" bytes are padded at the end of the zipfile.
+                            // Let's do some extra verification, we don't care about the
+                            // performance in this situation.
+                            byte[] sbuf = new byte[4];
+                            long cenpos = end.endpos - end.cenlen;
+                            long locpos = cenpos - end.cenoff;
+                            if  (cenpos < 0 ||
+                                 locpos < 0 ||
+                                 readFullyAt(sbuf, 0, sbuf.length, cenpos) != 4 ||
+                                 GETSIG(sbuf) != CENSIG ||
+                                 readFullyAt(sbuf, 0, sbuf.length, locpos) != 4 ||
+                                 GETSIG(sbuf) != LOCSIG) {
+                                continue;
+                            }
+                        }
+                        if (comlen > 0) {    // this zip file has comlen
+                            comment = new byte[comlen];
+                            if (readFullyAt(comment, 0, comlen, end.endpos + ENDHDR) != comlen) {
+                                zerror("zip comment read failed");
+                            }
+                        }
+                        // must check for a zip64 end record; it is always permitted to be present
+                        try {
+                            byte[] loc64 = new byte[ZIP64_LOCHDR];
+                            if (end.endpos < ZIP64_LOCHDR ||
+                                readFullyAt(loc64, 0, loc64.length, end.endpos - ZIP64_LOCHDR)
+                                != loc64.length || GETSIG(loc64) != ZIP64_LOCSIG) {
+                                return end;
+                            }
+                            long end64pos = ZIP64_LOCOFF(loc64);
+                            byte[] end64buf = new byte[ZIP64_ENDHDR];
+                            if (readFullyAt(end64buf, 0, end64buf.length, end64pos)
+                                != end64buf.length || GETSIG(end64buf) != ZIP64_ENDSIG) {
+                                return end;
+                            }
+                            // end64 candidate found,
+                            long cenlen64 = ZIP64_ENDSIZ(end64buf);
+                            long cenoff64 = ZIP64_ENDOFF(end64buf);
+                            long centot64 = ZIP64_ENDTOT(end64buf);
+                            // double-check
+                            if (cenlen64 != end.cenlen && end.cenlen != ZIP64_MAGICVAL ||
+                                cenoff64 != end.cenoff && end.cenoff != ZIP64_MAGICVAL ||
+                                centot64 != end.centot && end.centot != ZIP64_MAGICCOUNT) {
+                                return end;
+                            }
+                            // to use the end64 values
+                            end.cenlen = cenlen64;
+                            end.cenoff = cenoff64;
+                            end.centot = (int)centot64; // assume total < 2g
+                            end.endpos = end64pos;
+                        } catch (IOException x) {}    // no zip64 loc/end
+                        return end;
+                    }
+                }
+            }
+            throw new ZipException("zip END header not found");
+        }
+
+        // Reads zip file central directory.
+        private void initCEN(int knownTotal) throws IOException {
+            // Prefer locals for better performance during startup
+            byte[] cen;
+            if (knownTotal == -1) {
+                End end = findEND();
+                if (end.endpos == 0) {
+                    locpos = 0;
+                    total = 0;
+                    entries = new int[0];
+                    this.cen = null;
+                    return;         // only END header present
+                }
+                if (end.cenlen > end.endpos)
+                    zerror("invalid END header (bad central directory size)");
+                long cenpos = end.endpos - end.cenlen;     // position of CEN table
+                // Get position of first local file (LOC) header, taking into
+                // account that there may be a stub prefixed to the zip file.
+                locpos = cenpos - end.cenoff;
+                if (locpos < 0) {
+                    zerror("invalid END header (bad central directory offset)");
+                }
+                // read in the CEN and END
+                cen = this.cen = new byte[(int)(end.cenlen + ENDHDR)];
+                if (readFullyAt(cen, 0, cen.length, cenpos) != end.cenlen + ENDHDR) {
+                    zerror("read CEN tables failed");
+                }
+                this.total = end.centot;
+            } else {
+                cen = this.cen;
+                this.total = knownTotal;
+            }
+            // hash table for entries
+            int entriesLength = this.total * 3;
+            entries = new int[entriesLength];
+
+            int tablelen = ((total/2) | 1); // Odd -> fewer collisions
+            this.tablelen = tablelen;
+
+            int[] table = new int[tablelen];
+            this.table = table;
+
+            Arrays.fill(table, ZIP_ENDCHAIN);
+
+            // list for all meta entries
+            ArrayList<Integer> signatureNames = null;
+            // Set of all version numbers seen in META-INF/versions/
+            Set<Integer> metaVersionsSet = null;
+
+            // Iterate through the entries in the central directory
+            int idx = 0; // Index into the entries array
+            int pos = 0;
+            int entryPos = CENHDR;
+            int limit = cen.length - ENDHDR;
+            manifestNum = 0;
+            // Android-added: duplicate entries are not allowed. See CVE-2013-4787 and b/8219321
+            Set<String> entriesNames = new HashSet<>();
+            while (entryPos <= limit) {
+                if (idx >= entriesLength) {
+                    // This will only happen if the zip file has an incorrect
+                    // ENDTOT field, which usually means it contains more than
+                    // 65535 entries.
+                    initCEN(countCENHeaders(cen, limit));
+                    return;
+                }
+
+                // Checks the entry and adds values to entries[idx ... idx+2]
+                int nlen = checkAndAddEntry(pos, idx);
+
+                // BEGIN Android-added: duplicate entries are not allowed. See CVE-2013-4787
+                // and b/8219321.
+                // zipCoderForPos takes USE_UTF8 flag into account.
+                ZipCoder zcp = zipCoderForPos(entryPos);
+                String name = zcp.toString(cen, pos + CENHDR, nlen);
+                if (!entriesNames.add(name)) {
+                    zerror("Duplicate entry name: " + name);
+                }
+                // END Android-added: duplicate entries are not allowed. See CVE-2013-4787
+                // and b/8219321
+                // BEGIN Android-added: don't allow NUL in entry names. We can handle it in Java fine,
+                // but it is of questionable utility as a valid pathname can't contain NUL.
+                for (int nameIdx = 0; nameIdx < nlen; ++nameIdx) {
+                    byte b = cen[pos + CENHDR + nameIdx];
+
+                    if (b == 0) {
+                        zerror("Filename contains NUL byte: " + name);
+                    }
+                }
+                // END Android-added: don't allow NUL in entry names.
+                // BEGIN Android-changed: validation of zip entry names.
+                if (key.isZipFilePathValidatorEnabled && !ZipPathValidator.isClear()) {
+                    ZipPathValidator.getInstance().onZipEntryAccess(name);
+                }
+                // END Android-changed: validation of zip entry names.
+                idx += 3;
+
+                // Adds name to metanames.
+                if (isMetaName(cen, entryPos, nlen)) {
+                    // nlen is at least META_INF_LENGTH
+                    if (isManifestName(entryPos + META_INF_LEN, nlen - META_INF_LEN)) {
+                        manifestPos = pos;
+                        manifestNum++;
+                    } else {
+                        if (isSignatureRelated(entryPos, nlen)) {
+                            if (signatureNames == null)
+                                signatureNames = new ArrayList<>(4);
+                            signatureNames.add(pos);
+                        }
+
+                        // If this is a versioned entry, parse the version
+                        // and store it for later. This optimizes lookup
+                        // performance in multi-release jar files
+                        int version = getMetaVersion(entryPos + META_INF_LEN, nlen - META_INF_LEN);
+                        if (version > 0) {
+                            if (metaVersionsSet == null)
+                                metaVersionsSet = new TreeSet<>();
+                            metaVersionsSet.add(version);
+                        }
+                    }
+                }
+                // skip to the start of the next entry
+                pos = nextEntryPos(pos, entryPos, nlen);
+                entryPos = pos + CENHDR;
+            }
+
+            // Adjust the total entries
+            this.total = idx / 3;
+
+            if (signatureNames != null) {
+                int len = signatureNames.size();
+                signatureMetaNames = new int[len];
+                for (int j = 0; j < len; j++) {
+                    signatureMetaNames[j] = signatureNames.get(j);
+                }
+            }
+            if (metaVersionsSet != null) {
+                metaVersions = new int[metaVersionsSet.size()];
+                int c = 0;
+                for (Integer version : metaVersionsSet) {
+                    metaVersions[c++] = version;
+                }
+            } else {
+                metaVersions = EMPTY_META_VERSIONS;
+            }
+            if (pos + ENDHDR != cen.length) {
+                zerror("invalid CEN header (bad header size)");
+            }
+        }
+
+        private int nextEntryPos(int pos, int entryPos, int nlen) {
+            return entryPos + nlen + CENCOM(cen, pos) + CENEXT(cen, pos);
+        }
+
+        private static void zerror(String msg) throws ZipException {
+            throw new ZipException(msg);
+        }
+
+        /*
+         * Returns the {@code pos} of the zip cen entry corresponding to the
+         * specified entry name, or -1 if not found.
+         */
+        private int getEntryPos(String name, boolean addSlash) {
+            if (total == 0) {
+                return -1;
+            }
+
+            int hsh = ZipCoder.hash(name);
+            int idx = table[(hsh & 0x7fffffff) % tablelen];
+
+            // Search down the target hash chain for a entry whose
+            // 32 bit hash matches the hashed name.
+            while (idx != ZIP_ENDCHAIN) {
+                if (getEntryHash(idx) == hsh) {
+                    // The CEN name must match the specfied one
+                    int pos = getEntryPos(idx);
+
+                    try {
+                        ZipCoder zc = zipCoderForPos(pos);
+                        String entry = zc.toString(cen, pos + CENHDR, CENNAM(cen, pos));
+
+                        // If addSlash is true we'll test for name+/ in addition to
+                        // name, unless name is the empty string or already ends with a
+                        // slash
+                        int entryLen = entry.length();
+                        int nameLen = name.length();
+                        if ((entryLen == nameLen && entry.equals(name)) ||
+                                (addSlash &&
+                                nameLen + 1 == entryLen &&
+                                entry.startsWith(name) &&
+                                entry.charAt(entryLen - 1) == '/')) {
+                            return pos;
+                        }
+                    } catch (IllegalArgumentException iae) {
+                        // Ignore
+                    }
+                }
+                idx = getEntryNext(idx);
+            }
+            return -1;
+        }
+
+        private ZipCoder zipCoderForPos(int pos) {
+            if (zc.isUTF8()) {
+                return zc;
+            }
+            if ((CENFLG(cen, pos) & USE_UTF8) != 0) {
+                return ZipCoder.UTF8;
+            }
+            return zc;
+        }
+
+        /**
+         * Returns true if the bytes represent a non-directory name
+         * beginning with "META-INF/", disregarding ASCII case.
+         */
+        private static boolean isMetaName(byte[] name, int off, int len) {
+            // Use the "oldest ASCII trick in the book":
+            // ch | 0x20 == Character.toLowerCase(ch)
+            return len > META_INF_LEN       // "META-INF/".length()
+                && name[off + len - 1] != '/'  // non-directory
+                && (name[off++] | 0x20) == 'm'
+                && (name[off++] | 0x20) == 'e'
+                && (name[off++] | 0x20) == 't'
+                && (name[off++] | 0x20) == 'a'
+                && (name[off++]       ) == '-'
+                && (name[off++] | 0x20) == 'i'
+                && (name[off++] | 0x20) == 'n'
+                && (name[off++] | 0x20) == 'f'
+                && (name[off]         ) == '/';
+        }
+
+        /*
+         * Check if the bytes represents a name equals to MANIFEST.MF
+         */
+        private boolean isManifestName(int off, int len) {
+            byte[] name = cen;
+            return (len == 11 // "MANIFEST.MF".length()
+                    && (name[off++] | 0x20) == 'm'
+                    && (name[off++] | 0x20) == 'a'
+                    && (name[off++] | 0x20) == 'n'
+                    && (name[off++] | 0x20) == 'i'
+                    && (name[off++] | 0x20) == 'f'
+                    && (name[off++] | 0x20) == 'e'
+                    && (name[off++] | 0x20) == 's'
+                    && (name[off++] | 0x20) == 't'
+                    && (name[off++]       ) == '.'
+                    && (name[off++] | 0x20) == 'm'
+                    && (name[off]   | 0x20) == 'f');
+        }
+
+        private boolean isSignatureRelated(int off, int len) {
+            // Only called when isMetaName(name, off, len) is true, which means
+            // len is at least META_INF_LENGTH
+            // assert isMetaName(name, off, len)
+            boolean signatureRelated = false;
+            byte[] name = cen;
+            if (name[off + len - 3] == '.') {
+                // Check if entry ends with .EC and .SF
+                int b1 = name[off + len - 2] | 0x20;
+                int b2 = name[off + len - 1] | 0x20;
+                if ((b1 == 'e' && b2 == 'c') || (b1 == 's' && b2 == 'f')) {
+                    signatureRelated = true;
+                }
+            } else if (name[off + len - 4] == '.') {
+                // Check if entry ends with .DSA and .RSA
+                int b1 = name[off + len - 3] | 0x20;
+                int b2 = name[off + len - 2] | 0x20;
+                int b3 = name[off + len - 1] | 0x20;
+                if ((b1 == 'r' || b1 == 'd') && b2 == 's' && b3 == 'a') {
+                    signatureRelated = true;
+                }
+            }
+            // Above logic must match SignatureFileVerifier.isBlockOrSF
+            assert(signatureRelated == SignatureFileVerifier
+                // Android-changed: use StandardCharsets.
+                // .isBlockOrSF(new String(name, off, len, UTF_8.INSTANCE)
+                .isBlockOrSF(new String(name, off, len, StandardCharsets.UTF_8)
+                    .toUpperCase(Locale.ENGLISH)));
+            return signatureRelated;
+        }
+
+        /*
+         * If the bytes represents a non-directory name beginning
+         * with "versions/", continuing with a positive integer,
+         * followed by a '/', then return that integer value.
+         * Otherwise, return 0
+         */
+        private int getMetaVersion(int off, int len) {
+            byte[] name = cen;
+            int nend = off + len;
+            if (!(len > 10                         // "versions//".length()
+                    && name[off + len - 1] != '/'  // non-directory
+                    && (name[off++] | 0x20) == 'v'
+                    && (name[off++] | 0x20) == 'e'
+                    && (name[off++] | 0x20) == 'r'
+                    && (name[off++] | 0x20) == 's'
+                    && (name[off++] | 0x20) == 'i'
+                    && (name[off++] | 0x20) == 'o'
+                    && (name[off++] | 0x20) == 'n'
+                    && (name[off++] | 0x20) == 's'
+                    && (name[off++]       ) == '/')) {
+                return 0;
+            }
+            int version = 0;
+            while (off < nend) {
+                final byte c = name[off++];
+                if (c == '/') {
+                    return version;
+                }
+                if (c < '0' || c > '9') {
+                    return 0;
+                }
+                version = version * 10 + c - '0';
+                // Check for overflow and leading zeros
+                if (version <= 0) {
+                    return 0;
+                }
+            }
+            return 0;
+        }
+
+        /**
+         * Returns the number of CEN headers in a central directory.
+         * Will not throw, even if the zip file is corrupt.
+         *
+         * @param cen copy of the bytes in a zip file's central directory
+         * @param size number of bytes in central directory
+         */
+        private static int countCENHeaders(byte[] cen, int size) {
+            int count = 0;
+            for (int p = 0;
+                 p + CENHDR <= size;
+                 p += CENHDR + CENNAM(cen, p) + CENEXT(cen, p) + CENCOM(cen, p))
+                count++;
+            return count;
+        }
+    }
+}
diff --git a/android-35/java/util/zip/ZipInputStream.java b/android-35/java/util/zip/ZipInputStream.java
new file mode 100644
index 0000000..617e7ff
--- /dev/null
+++ b/android-35/java/util/zip/ZipInputStream.java
@@ -0,0 +1,464 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.EOFException;
+import java.io.PushbackInputStream;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+import static java.util.zip.ZipConstants64.*;
+import static java.util.zip.ZipUtils.*;
+
+import dalvik.system.ZipPathValidator;
+
+/**
+ * This class implements an input stream filter for reading files in the
+ * ZIP file format. Includes support for both compressed and uncompressed
+ * entries.
+ *
+ * @author      David Connelly
+ * @since 1.1
+ */
+public class ZipInputStream extends InflaterInputStream implements ZipConstants {
+    private ZipEntry entry;
+    private int flag;
+    private CRC32 crc = new CRC32();
+    private long remaining;
+    private byte[] tmpbuf = new byte[512];
+
+    private static final int STORED = ZipEntry.STORED;
+    private static final int DEFLATED = ZipEntry.DEFLATED;
+
+    private boolean closed = false;
+    // this flag is set to true after EOF has reached for
+    // one entry
+    private boolean entryEOF = false;
+
+    private ZipCoder zc;
+
+    /**
+     * Check to make sure that this stream has not been closed
+     */
+    private void ensureOpen() throws IOException {
+        if (closed) {
+            throw new IOException("Stream closed");
+        }
+    }
+
+    /**
+     * Creates a new ZIP input stream.
+     *
+     * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used to
+     * decode the entry names.
+     *
+     * @param in the actual input stream
+     */
+    public ZipInputStream(InputStream in) {
+        // Android-changed: use StandardCharsets.
+        // this(in, UTF_8.INSTANCE);
+        this(in, StandardCharsets.UTF_8);
+    }
+
+    /**
+     * Creates a new ZIP input stream.
+     *
+     * @param in the actual input stream
+     *
+     * @param charset
+     *        The {@linkplain java.nio.charset.Charset charset} to be
+     *        used to decode the ZIP entry name (ignored if the
+     *        <a href="package-summary.html#lang_encoding"> language
+     *        encoding bit</a> of the ZIP entry's general purpose bit
+     *        flag is set).
+     *
+     * @since 1.7
+     */
+    public ZipInputStream(InputStream in, Charset charset) {
+        super(new PushbackInputStream(in, 512), new Inflater(true), 512);
+        // Android-changed: Unconditionally close external inflaters (b/26462400)
+        // usesDefaultInflater = true;
+        if(in == null) {
+            throw new NullPointerException("in is null");
+        }
+        if (charset == null)
+            throw new NullPointerException("charset is null");
+        this.zc = ZipCoder.get(charset);
+    }
+
+    // Android-changed: Additional ZipException throw scenario with ZipPathValidator.
+    /**
+     * Reads the next ZIP file entry and positions the stream at the
+     * beginning of the entry data.
+     *
+     * <p>If the app targets Android U or above, zip file entry names containing
+     * ".." or starting with "/" passed here will throw a {@link ZipException}.
+     * For more details, see {@link dalvik.system.ZipPathValidator}.
+     *
+     * @return the next ZIP file entry, or null if there are no more entries
+     * @throws ZipException if (1) a ZIP file error has occurred or
+     *            (2) <code>targetSdkVersion >= BUILD.VERSION_CODES.UPSIDE_DOWN_CAKE</code>
+     *            and (the <code>name</code> argument contains ".." or starts with "/").
+     * @throws IOException if an I/O error has occurred
+     */
+    public ZipEntry getNextEntry() throws IOException {
+        ensureOpen();
+        if (entry != null) {
+            closeEntry();
+        }
+        crc.reset();
+        inf.reset();
+        if ((entry = readLOC()) == null) {
+            return null;
+        }
+        // Android-changed: Return more accurate value from available().
+        // Initialize the remaining field with the number of bytes that can be read from the entry
+        // for both uncompressed and compressed entries so that it can be used to provide a more
+        // accurate return value for available().
+        // if (entry.method == STORED) {
+        if (entry.method == STORED || entry.method == DEFLATED) {
+            remaining = entry.size;
+        }
+        entryEOF = false;
+        return entry;
+    }
+
+    /**
+     * Closes the current ZIP entry and positions the stream for reading the
+     * next entry.
+     * @throws    ZipException if a ZIP file error has occurred
+     * @throws    IOException if an I/O error has occurred
+     */
+    public void closeEntry() throws IOException {
+        ensureOpen();
+        while (read(tmpbuf, 0, tmpbuf.length) != -1) ;
+        entryEOF = true;
+    }
+
+    /**
+     * Returns 0 after EOF has reached for the current entry data,
+     * otherwise always return 1.
+     * <p>
+     * Programs should not count on this method to return the actual number
+     * of bytes that could be read without blocking.
+     *
+     * @return     1 before EOF and 0 after EOF has reached for current entry.
+     * @throws     IOException  if an I/O error occurs.
+     *
+     */
+    public int available() throws IOException {
+        ensureOpen();
+        // Android-changed: Return more accurate value from available().
+        // Tracks the remaining bytes in order to return a more accurate value for the available
+        // bytes. Given an entry of size N both Android and upstream will return 1 until N bytes
+        // have been read at which point Android will return 0 and upstream will return 1.
+        // Upstream will only return 0 after an attempt to read a byte fails because the EOF has
+        // been reached. See http://b/111439440 for more details.
+        // if (entryEOF) {
+        if (entryEOF || (entry != null && remaining == 0)) {
+            return 0;
+        } else {
+            return 1;
+        }
+    }
+
+    /**
+     * Reads from the current ZIP entry into an array of bytes.
+     * If {@code len} is not zero, the method
+     * blocks until some input is available; otherwise, no
+     * bytes are read and {@code 0} is returned.
+     * @param b the buffer into which the data is read
+     * @param off the start offset in the destination array {@code b}
+     * @param len the maximum number of bytes read
+     * @return the actual number of bytes read, or -1 if the end of the
+     *         entry is reached
+     * @throws     NullPointerException if {@code b} is {@code null}.
+     * @throws     IndexOutOfBoundsException if {@code off} is negative,
+     * {@code len} is negative, or {@code len} is greater than
+     * {@code b.length - off}
+     * @throws    ZipException if a ZIP file error has occurred
+     * @throws    IOException if an I/O error has occurred
+     */
+    public int read(byte[] b, int off, int len) throws IOException {
+        ensureOpen();
+        if (off < 0 || len < 0 || off > b.length - len) {
+            throw new IndexOutOfBoundsException();
+        } else if (len == 0) {
+            return 0;
+        }
+
+        if (entry == null) {
+            return -1;
+        }
+        switch (entry.method) {
+        case DEFLATED:
+            len = super.read(b, off, len);
+            if (len == -1) {
+                readEnd(entry);
+                entryEOF = true;
+                entry = null;
+            } else {
+                crc.update(b, off, len);
+                // Android-added: Return more accurate value from available().
+                // Update the remaining field so it is an accurate count of the number of bytes
+                // remaining in this stream, after deflation.
+                remaining -= len;
+            }
+            return len;
+        case STORED:
+            if (remaining <= 0) {
+                entryEOF = true;
+                entry = null;
+                return -1;
+            }
+            if (len > remaining) {
+                len = (int)remaining;
+            }
+            len = in.read(b, off, len);
+            if (len == -1) {
+                throw new ZipException("unexpected EOF");
+            }
+            crc.update(b, off, len);
+            remaining -= len;
+            if (remaining == 0 && entry.crc != crc.getValue()) {
+                throw new ZipException(
+                    "invalid entry CRC (expected 0x" + Long.toHexString(entry.crc) +
+                    " but got 0x" + Long.toHexString(crc.getValue()) + ")");
+            }
+            return len;
+        default:
+            throw new ZipException("invalid compression method");
+        }
+    }
+
+    /**
+     * Skips specified number of bytes in the current ZIP entry.
+     * @param n the number of bytes to skip
+     * @return the actual number of bytes skipped
+     * @throws    ZipException if a ZIP file error has occurred
+     * @throws    IOException if an I/O error has occurred
+     * @throws    IllegalArgumentException if {@code n < 0}
+     */
+    public long skip(long n) throws IOException {
+        if (n < 0) {
+            throw new IllegalArgumentException("negative skip length");
+        }
+        ensureOpen();
+        int max = (int)Math.min(n, Integer.MAX_VALUE);
+        int total = 0;
+        while (total < max) {
+            int len = max - total;
+            if (len > tmpbuf.length) {
+                len = tmpbuf.length;
+            }
+            len = read(tmpbuf, 0, len);
+            if (len == -1) {
+                entryEOF = true;
+                break;
+            }
+            total += len;
+        }
+        return total;
+    }
+
+    /**
+     * Closes this input stream and releases any system resources associated
+     * with the stream.
+     * @throws    IOException if an I/O error has occurred
+     */
+    public void close() throws IOException {
+        if (!closed) {
+            super.close();
+            closed = true;
+        }
+    }
+
+    private byte[] b = new byte[256];
+
+    /*
+     * Reads local file (LOC) header for next entry.
+     */
+    private ZipEntry readLOC() throws IOException {
+        try {
+            readFully(tmpbuf, 0, LOCHDR);
+        } catch (EOFException e) {
+            return null;
+        }
+        if (get32(tmpbuf, 0) != LOCSIG) {
+            return null;
+        }
+        // get flag first, we need check USE_UTF8.
+        flag = get16(tmpbuf, LOCFLG);
+        // get the entry name and create the ZipEntry first
+        int len = get16(tmpbuf, LOCNAM);
+        int blen = b.length;
+        if (len > blen) {
+            do {
+                blen = blen * 2;
+            } while (len > blen);
+            b = new byte[blen];
+        }
+        readFully(b, 0, len);
+        // Force to use UTF-8 if the USE_UTF8 bit is ON
+        ZipEntry e = createZipEntry(((flag & USE_UTF8) != 0)
+                                    ? ZipCoder.toStringUTF8(b, len)
+                                    : zc.toString(b, len));
+        // now get the remaining fields for the entry
+        if ((flag & 1) == 1) {
+            throw new ZipException("encrypted ZIP entry not supported");
+        }
+        // BEGIN Android-added: Use ZipPathValidator to validate zip entry name.
+        ZipPathValidator.getInstance().onZipEntryAccess(e.name);
+        // END Android-added: Use ZipPathValidator to validate zip entry name.
+        e.method = get16(tmpbuf, LOCHOW);
+        e.xdostime = get32(tmpbuf, LOCTIM);
+        if ((flag & 8) == 8) {
+            // Android-changed: Remove the requirement that only DEFLATED entries
+            // can have data descriptors. This is not required by the ZIP spec and
+            // is inconsistent with the behaviour of ZipFile and versions of Android
+            // prior to Android N.
+            //
+            // /* "Data Descriptor" present */
+            // if (e.method != DEFLATED) {
+            //     throw new ZipException(
+            //             "only DEFLATED entries can have EXT descriptor");
+            // }
+        } else {
+            e.crc = get32(tmpbuf, LOCCRC);
+            e.csize = get32(tmpbuf, LOCSIZ);
+            e.size = get32(tmpbuf, LOCLEN);
+        }
+        len = get16(tmpbuf, LOCEXT);
+        if (len > 0) {
+            byte[] extra = new byte[len];
+            readFully(extra, 0, len);
+            e.setExtra0(extra,
+                        e.csize == ZIP64_MAGICVAL || e.size == ZIP64_MAGICVAL, true);
+        }
+        return e;
+    }
+
+    /**
+     * Creates a new {@code ZipEntry} object for the specified
+     * entry name.
+     *
+     * @param name the ZIP file entry name
+     * @return the ZipEntry just created
+     */
+    protected ZipEntry createZipEntry(String name) {
+        return new ZipEntry(name);
+    }
+
+    /**
+     * Reads end of deflated entry as well as EXT descriptor if present.
+     *
+     * Local headers for DEFLATED entries may optionally be followed by a
+     * data descriptor, and that data descriptor may optionally contain a
+     * leading signature (EXTSIG).
+     *
+     * From the zip spec http://www.pkware.com/documents/casestudies/APPNOTE.TXT
+     *
+     * """Although not originally assigned a signature, the value 0x08074b50
+     * has commonly been adopted as a signature value for the data descriptor
+     * record.  Implementers should be aware that ZIP files may be
+     * encountered with or without this signature marking data descriptors
+     * and should account for either case when reading ZIP files to ensure
+     * compatibility."""
+     */
+    private void readEnd(ZipEntry e) throws IOException {
+        int n = inf.getRemaining();
+        if (n > 0) {
+            ((PushbackInputStream)in).unread(buf, len - n, n);
+        }
+        if ((flag & 8) == 8) {
+            /* "Data Descriptor" present */
+            if (inf.getBytesWritten() > ZIP64_MAGICVAL ||
+                inf.getBytesRead() > ZIP64_MAGICVAL) {
+                // ZIP64 format
+                readFully(tmpbuf, 0, ZIP64_EXTHDR);
+                long sig = get32(tmpbuf, 0);
+                if (sig != EXTSIG) { // no EXTSIG present
+                    e.crc = sig;
+                    e.csize = get64(tmpbuf, ZIP64_EXTSIZ - ZIP64_EXTCRC);
+                    e.size = get64(tmpbuf, ZIP64_EXTLEN - ZIP64_EXTCRC);
+                    ((PushbackInputStream)in).unread(
+                        tmpbuf, ZIP64_EXTHDR - ZIP64_EXTCRC, ZIP64_EXTCRC);
+                } else {
+                    e.crc = get32(tmpbuf, ZIP64_EXTCRC);
+                    e.csize = get64(tmpbuf, ZIP64_EXTSIZ);
+                    e.size = get64(tmpbuf, ZIP64_EXTLEN);
+                }
+            } else {
+                readFully(tmpbuf, 0, EXTHDR);
+                long sig = get32(tmpbuf, 0);
+                if (sig != EXTSIG) { // no EXTSIG present
+                    e.crc = sig;
+                    e.csize = get32(tmpbuf, EXTSIZ - EXTCRC);
+                    e.size = get32(tmpbuf, EXTLEN - EXTCRC);
+                    ((PushbackInputStream)in).unread(
+                                               tmpbuf, EXTHDR - EXTCRC, EXTCRC);
+                } else {
+                    e.crc = get32(tmpbuf, EXTCRC);
+                    e.csize = get32(tmpbuf, EXTSIZ);
+                    e.size = get32(tmpbuf, EXTLEN);
+                }
+            }
+        }
+        if (e.size != inf.getBytesWritten()) {
+            throw new ZipException(
+                "invalid entry size (expected " + e.size +
+                " but got " + inf.getBytesWritten() + " bytes)");
+        }
+        if (e.csize != inf.getBytesRead()) {
+            throw new ZipException(
+                "invalid entry compressed size (expected " + e.csize +
+                " but got " + inf.getBytesRead() + " bytes)");
+        }
+        if (e.crc != crc.getValue()) {
+            throw new ZipException(
+                "invalid entry CRC (expected 0x" + Long.toHexString(e.crc) +
+                " but got 0x" + Long.toHexString(crc.getValue()) + ")");
+        }
+    }
+
+    /*
+     * Reads bytes, blocking until all bytes are read.
+     */
+    private void readFully(byte[] b, int off, int len) throws IOException {
+        while (len > 0) {
+            int n = in.read(b, off, len);
+            if (n == -1) {
+                throw new EOFException();
+            }
+            off += n;
+            len -= n;
+        }
+    }
+
+}
diff --git a/android-35/java/util/zip/ZipOutputStream.java b/android-35/java/util/zip/ZipOutputStream.java
new file mode 100644
index 0000000..6b6a68c
--- /dev/null
+++ b/android-35/java/util/zip/ZipOutputStream.java
@@ -0,0 +1,836 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.io.OutputStream;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Vector;
+import java.util.HashSet;
+import static java.util.zip.ZipConstants64.*;
+import static java.util.zip.ZipUtils.*;
+import sun.security.action.GetPropertyAction;
+
+/**
+ * This class implements an output stream filter for writing files in the
+ * ZIP file format. Includes support for both compressed and uncompressed
+ * entries.
+ *
+ * @author      David Connelly
+ * @since 1.1
+ */
+public class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {
+
+    /**
+     * Whether to use ZIP64 for zip files with more than 64k entries.
+     * Until ZIP64 support in zip implementations is ubiquitous, this
+     * system property allows the creation of zip files which can be
+     * read by legacy zip implementations which tolerate "incorrect"
+     * total entry count fields, such as the ones in jdk6, and even
+     * some in jdk7.
+     */
+    // Android-changed: Always allow use of Zip64.
+    private static final boolean inhibitZip64 = false;
+        // Boolean.parseBoolean(
+        //    GetPropertyAction.privilegedGetProperty("jdk.util.zip.inhibitZip64"));
+
+    private static class XEntry {
+        final ZipEntry entry;
+        final long offset;
+        public XEntry(ZipEntry entry, long offset) {
+            this.entry = entry;
+            this.offset = offset;
+        }
+    }
+
+    private XEntry current;
+    private Vector<XEntry> xentries = new Vector<>();
+    private HashSet<String> names = new HashSet<>();
+    private CRC32 crc = new CRC32();
+    private long written = 0;
+    private long locoff = 0;
+    private byte[] comment;
+    private int method = DEFLATED;
+    private boolean finished;
+
+    private boolean closed = false;
+
+    private final ZipCoder zc;
+
+    private static int version(ZipEntry e) throws ZipException {
+        return switch (e.method) {
+        case DEFLATED -> 20;
+        case STORED   -> 10;
+        default       -> throw new ZipException("unsupported compression method");
+        };
+    }
+
+    /**
+     * Checks to make sure that this stream has not been closed.
+     */
+    private void ensureOpen() throws IOException {
+        if (closed) {
+            throw new IOException("Stream closed");
+        }
+    }
+
+    /**
+     * Compression method for uncompressed (STORED) entries.
+     */
+    public static final int STORED = ZipEntry.STORED;
+
+    /**
+     * Compression method for compressed (DEFLATED) entries.
+     */
+    public static final int DEFLATED = ZipEntry.DEFLATED;
+
+    /**
+     * Creates a new ZIP output stream.
+     *
+     * <p>The UTF-8 {@link java.nio.charset.Charset charset} is used
+     * to encode the entry names and comments.
+     *
+     * @param out the actual output stream
+     */
+    public ZipOutputStream(OutputStream out) {
+        this(out, StandardCharsets.UTF_8);
+    }
+
+    /**
+     * Creates a new ZIP output stream.
+     *
+     * @param out the actual output stream
+     *
+     * @param charset the {@linkplain java.nio.charset.Charset charset}
+     *                to be used to encode the entry names and comments
+     *
+     * @since 1.7
+     */
+    public ZipOutputStream(OutputStream out, Charset charset) {
+        super(out, out != null ? new Deflater(Deflater.DEFAULT_COMPRESSION, true) : null);
+        if (charset == null)
+            throw new NullPointerException("charset is null");
+        this.zc = ZipCoder.get(charset);
+        usesDefaultDeflater = true;
+    }
+
+    /**
+     * Sets the ZIP file comment.
+     * @param     comment the comment string
+     * @throws    IllegalArgumentException if the length of the specified
+     *            ZIP file comment is greater than 0xFFFF bytes
+     */
+    public void setComment(String comment) {
+        byte[] bytes = null;
+        if (comment != null) {
+            bytes = zc.getBytes(comment);
+            if (bytes.length > 0xffff) {
+                throw new IllegalArgumentException("ZIP file comment too long");
+            }
+        }
+        this.comment = bytes;
+    }
+
+    /**
+     * Sets the default compression method for subsequent entries. This
+     * default will be used whenever the compression method is not specified
+     * for an individual ZIP file entry, and is initially set to DEFLATED.
+     * @param     method the default compression method
+     * @throws    IllegalArgumentException if the specified compression method
+     *            is invalid
+     */
+    public void setMethod(int method) {
+        if (method != DEFLATED && method != STORED) {
+            throw new IllegalArgumentException("invalid compression method");
+        }
+        this.method = method;
+    }
+
+    /**
+     * Sets the compression level for subsequent entries which are DEFLATED.
+     * The default setting is DEFAULT_COMPRESSION.
+     * @param     level the compression level (0-9)
+     * @throws    IllegalArgumentException if the compression level is invalid
+     */
+    public void setLevel(int level) {
+        def.setLevel(level);
+    }
+
+    /**
+     * Begins writing a new ZIP file entry and positions the stream to the
+     * start of the entry data. Closes the current entry if still active.
+     * <p>
+     * The default compression method will be used if no compression method
+     * was specified for the entry. When writing a compressed (DEFLATED)
+     * entry, and the compressed size has not been explicitly set with the
+     * {@link ZipEntry#setCompressedSize(long)} method, then the compressed
+     * size will be set to the actual compressed size after deflation.
+     * <p>
+     * The current time will be used if the entry has no set modification time.
+     *
+     * @param     e the ZIP entry to be written
+     * @throws    ZipException if a ZIP format error has occurred
+     * @throws    IOException if an I/O error has occurred
+     */
+    public void putNextEntry(ZipEntry e) throws IOException {
+        ensureOpen();
+        if (current != null) {
+            closeEntry();       // close previous entry
+        }
+        if (e.xdostime == -1) {
+            // by default, do NOT use extended timestamps in extra
+            // data, for now.
+            e.setTime(System.currentTimeMillis());
+        }
+        if (e.method == -1) {
+            e.method = method;  // use default method
+        }
+        // store size, compressed size, and crc-32 in LOC header
+        e.flag = 0;
+        switch (e.method) {
+        case DEFLATED:
+            // If not set, store size, compressed size, and crc-32 in data
+            // descriptor immediately following the compressed entry data.
+            // Ignore the compressed size of a ZipEntry if it was implcitely set
+            // while reading that ZipEntry from a  ZipFile or ZipInputStream because
+            // we can't know the compression level of the source zip file/stream.
+            if (e.size  == -1 || e.csize == -1 || e.crc   == -1 || !e.csizeSet) {
+                e.flag = 8;
+            }
+            break;
+        case STORED:
+            // compressed size, uncompressed size, and crc-32 must all be
+            // set for entries using STORED compression method
+            if (e.size == -1) {
+                e.size = e.csize;
+            } else if (e.csize == -1) {
+                e.csize = e.size;
+            } else if (e.size != e.csize) {
+                throw new ZipException(
+                    "STORED entry where compressed != uncompressed size");
+            }
+            if (e.size == -1 || e.crc == -1) {
+                throw new ZipException(
+                    "STORED entry missing size, compressed size, or crc-32");
+            }
+            break;
+        default:
+            throw new ZipException("unsupported compression method");
+        }
+        if (! names.add(e.name)) {
+            throw new ZipException("duplicate entry: " + e.name);
+        }
+        if (zc.isUTF8())
+            e.flag |= USE_UTF8;
+        current = new XEntry(e, written);
+        xentries.add(current);
+        writeLOC(current);
+    }
+
+    /**
+     * Closes the current ZIP entry and positions the stream for writing
+     * the next entry.
+     * @throws    ZipException if a ZIP format error has occurred
+     * @throws    IOException if an I/O error has occurred
+     */
+    public void closeEntry() throws IOException {
+        ensureOpen();
+        if (current != null) {
+            try {
+                ZipEntry e = current.entry;
+                switch (e.method) {
+                    case DEFLATED -> {
+                        def.finish();
+                        while (!def.finished()) {
+                            deflate();
+                        }
+                        if ((e.flag & 8) == 0) {
+                            // verify size, compressed size, and crc-32 settings
+                            if (e.size != def.getBytesRead()) {
+                                throw new ZipException(
+                                    "invalid entry size (expected " + e.size +
+                                    " but got " + def.getBytesRead() + " bytes)");
+                            }
+                            if (e.csize != def.getBytesWritten()) {
+                                throw new ZipException(
+                                    "invalid entry compressed size (expected " +
+                                    e.csize + " but got " + def.getBytesWritten() + " bytes)");
+                            }
+                            if (e.crc != crc.getValue()) {
+                                throw new ZipException(
+                                    "invalid entry CRC-32 (expected 0x" +
+                                    Long.toHexString(e.crc) + " but got 0x" +
+                                    Long.toHexString(crc.getValue()) + ")");
+                            }
+                        } else {
+                            e.size = def.getBytesRead();
+                            e.csize = def.getBytesWritten();
+                            e.crc = crc.getValue();
+                            writeEXT(e);
+                        }
+                        def.reset();
+                        written += e.csize;
+                    }
+                    case STORED -> {
+                        // we already know that both e.size and e.csize are the same
+                        if (e.size != written - locoff) {
+                            throw new ZipException(
+                                "invalid entry size (expected " + e.size +
+                                " but got " + (written - locoff) + " bytes)");
+                        }
+                        if (e.crc != crc.getValue()) {
+                            throw new ZipException(
+                                "invalid entry crc-32 (expected 0x" +
+                                Long.toHexString(e.crc) + " but got 0x" +
+                                Long.toHexString(crc.getValue()) + ")");
+                        }
+                    }
+                    default -> throw new ZipException("invalid compression method");
+                }
+                crc.reset();
+                current = null;
+            } catch (IOException e) {
+                if (def.shouldFinish() && usesDefaultDeflater && !(e instanceof ZipException))
+                    def.end();
+                throw e;
+            }
+        }
+    }
+
+    /**
+     * Writes an array of bytes to the current ZIP entry data. This method
+     * will block until all the bytes are written.
+     * @param     b the data to be written
+     * @param     off the start offset in the data
+     * @param     len the number of bytes that are written
+     * @throws    ZipException if a ZIP file error has occurred
+     * @throws    IOException if an I/O error has occurred
+     */
+    public synchronized void write(byte[] b, int off, int len)
+        throws IOException
+    {
+        ensureOpen();
+        if (off < 0 || len < 0 || off > b.length - len) {
+            throw new IndexOutOfBoundsException();
+        } else if (len == 0) {
+            return;
+        }
+
+        if (current == null) {
+            throw new ZipException("no current ZIP entry");
+        }
+        ZipEntry entry = current.entry;
+        switch (entry.method) {
+            case DEFLATED -> super.write(b, off, len);
+            case STORED -> {
+                written += len;
+                if (written - locoff > entry.size) {
+                    throw new ZipException(
+                        "attempt to write past end of STORED entry");
+                }
+                out.write(b, off, len);
+            }
+            default -> throw new ZipException("invalid compression method");
+        }
+        crc.update(b, off, len);
+    }
+
+    /**
+     * Finishes writing the contents of the ZIP output stream without closing
+     * the underlying stream. Use this method when applying multiple filters
+     * in succession to the same output stream.
+     * @throws    ZipException if a ZIP file error has occurred
+     * @throws    IOException if an I/O exception has occurred
+     */
+    public void finish() throws IOException {
+        ensureOpen();
+        if (finished) {
+            return;
+        }
+        if (current != null) {
+            closeEntry();
+        }
+        // write central directory
+        long off = written;
+        for (XEntry xentry : xentries)
+            writeCEN(xentry);
+        writeEND(off, written - off);
+        finished = true;
+    }
+
+    /**
+     * Closes the ZIP output stream as well as the stream being filtered.
+     * @throws    ZipException if a ZIP file error has occurred
+     * @throws    IOException if an I/O error has occurred
+     */
+    public void close() throws IOException {
+        if (!closed) {
+            super.close();
+            closed = true;
+        }
+    }
+
+    /*
+     * Writes local file (LOC) header for specified entry.
+     */
+    private void writeLOC(XEntry xentry) throws IOException {
+        ZipEntry e = xentry.entry;
+        int flag = e.flag;
+        boolean hasZip64 = false;
+        int elen = getExtraLen(e.extra);
+
+        writeInt(LOCSIG);               // LOC header signature
+        if ((flag & 8) == 8) {
+            writeShort(version(e));     // version needed to extract
+            writeShort(flag);           // general purpose bit flag
+            writeShort(e.method);       // compression method
+            writeInt(e.xdostime);       // last modification time
+            // store size, uncompressed size, and crc-32 in data descriptor
+            // immediately following compressed entry data
+            writeInt(0);
+            writeInt(0);
+            writeInt(0);
+        } else {
+            if (e.csize >= ZIP64_MAGICVAL || e.size >= ZIP64_MAGICVAL) {
+                hasZip64 = true;
+                writeShort(45);         // ver 4.5 for zip64
+            } else {
+                writeShort(version(e)); // version needed to extract
+            }
+            writeShort(flag);           // general purpose bit flag
+            writeShort(e.method);       // compression method
+            writeInt(e.xdostime);       // last modification time
+            writeInt(e.crc);            // crc-32
+            if (hasZip64) {
+                writeInt(ZIP64_MAGICVAL);
+                writeInt(ZIP64_MAGICVAL);
+                elen += 20;        //headid(2) + size(2) + size(8) + csize(8)
+            } else {
+                writeInt(e.csize);  // compressed size
+                writeInt(e.size);   // uncompressed size
+            }
+        }
+        byte[] nameBytes = zc.getBytes(e.name);
+        writeShort(nameBytes.length);
+
+        int elenEXTT = 0;         // info-zip extended timestamp
+        int flagEXTT = 0;
+        long umtime = -1;
+        long uatime = -1;
+        long uctime = -1;
+        if (e.mtime != null) {
+            elenEXTT += 4;
+            flagEXTT |= EXTT_FLAG_LMT;
+            umtime = fileTimeToUnixTime(e.mtime);
+        }
+        if (e.atime != null) {
+            elenEXTT += 4;
+            flagEXTT |= EXTT_FLAG_LAT;
+            uatime = fileTimeToUnixTime(e.atime);
+        }
+        if (e.ctime != null) {
+            elenEXTT += 4;
+            flagEXTT |= EXTT_FLAT_CT;
+            uctime = fileTimeToUnixTime(e.ctime);
+        }
+        if (flagEXTT != 0) {
+            // to use ntfs time if any m/a/ctime is beyond unixtime upper bound
+            if (umtime > UPPER_UNIXTIME_BOUND ||
+                uatime > UPPER_UNIXTIME_BOUND ||
+                uctime > UPPER_UNIXTIME_BOUND) {
+                elen += 36;                // NTFS time, total 36 bytes
+            } else {
+                elen += (elenEXTT + 5);    // headid(2) + size(2) + flag(1) + data
+            }
+        }
+        writeShort(elen);
+        writeBytes(nameBytes, 0, nameBytes.length);
+        if (hasZip64) {
+            writeShort(ZIP64_EXTID);
+            writeShort(16);
+            writeLong(e.size);
+            writeLong(e.csize);
+        }
+        if (flagEXTT != 0) {
+            if (umtime > UPPER_UNIXTIME_BOUND ||
+                uatime > UPPER_UNIXTIME_BOUND ||
+                uctime > UPPER_UNIXTIME_BOUND) {
+                writeShort(EXTID_NTFS);    // id
+                writeShort(32);            // data size
+                writeInt(0);               // reserved
+                writeShort(0x0001);        // NTFS attr tag
+                writeShort(24);
+                writeLong(e.mtime == null ? WINDOWS_TIME_NOT_AVAILABLE
+                                          : fileTimeToWinTime(e.mtime));
+                writeLong(e.atime == null ? WINDOWS_TIME_NOT_AVAILABLE
+                                          : fileTimeToWinTime(e.atime));
+                writeLong(e.ctime == null ? WINDOWS_TIME_NOT_AVAILABLE
+                                          : fileTimeToWinTime(e.ctime));
+            } else {
+                writeShort(EXTID_EXTT);
+                writeShort(elenEXTT + 1);  // flag + data
+                writeByte(flagEXTT);
+                if (e.mtime != null)
+                    writeInt(umtime);
+                if (e.atime != null)
+                    writeInt(uatime);
+                if (e.ctime != null)
+                    writeInt(uctime);
+            }
+        }
+        writeExtra(e.extra);
+        locoff = written;
+    }
+
+    /*
+     * Writes extra data descriptor (EXT) for specified entry.
+     */
+    private void writeEXT(ZipEntry e) throws IOException {
+        writeInt(EXTSIG);           // EXT header signature
+        writeInt(e.crc);            // crc-32
+        if (e.csize >= ZIP64_MAGICVAL || e.size >= ZIP64_MAGICVAL) {
+            writeLong(e.csize);
+            writeLong(e.size);
+        } else {
+            writeInt(e.csize);          // compressed size
+            writeInt(e.size);           // uncompressed size
+        }
+    }
+
+    /**
+     * Adds information about compatibility of file attribute information
+     * to a version value.
+     */
+    private int versionMadeBy(ZipEntry e, int version) {
+        return (e.extraAttributes < 0) ? version :
+                VERSION_MADE_BY_BASE_UNIX | (version & 0xff);
+    }
+
+    /*
+     * Write central directory (CEN) header for specified entry.
+     * REMIND: add support for file attributes
+     */
+    private void writeCEN(XEntry xentry) throws IOException {
+        ZipEntry e  = xentry.entry;
+        int flag = e.flag;
+        int version = version(e);
+        long csize = e.csize;
+        long size = e.size;
+        long offset = xentry.offset;
+        int elenZIP64 = 0;
+        boolean hasZip64 = false;
+
+        if (e.csize >= ZIP64_MAGICVAL) {
+            csize = ZIP64_MAGICVAL;
+            elenZIP64 += 8;              // csize(8)
+            hasZip64 = true;
+        }
+        if (e.size >= ZIP64_MAGICVAL) {
+            size = ZIP64_MAGICVAL;    // size(8)
+            elenZIP64 += 8;
+            hasZip64 = true;
+        }
+        if (xentry.offset >= ZIP64_MAGICVAL) {
+            offset = ZIP64_MAGICVAL;
+            elenZIP64 += 8;              // offset(8)
+            hasZip64 = true;
+        }
+        writeInt(CENSIG);           // CEN header signature
+        if (hasZip64) {
+            writeShort(versionMadeBy(e,45));         // ver 4.5 for zip64
+            writeShort(45);
+        } else {
+            writeShort(versionMadeBy(e, version));    // version made by
+            writeShort(version);    // version needed to extract
+        }
+        writeShort(flag);           // general purpose bit flag
+        writeShort(e.method);       // compression method
+        writeInt(e.xdostime);       // last modification time
+        writeInt(e.crc);            // crc-32
+        writeInt(csize);            // compressed size
+        writeInt(size);             // uncompressed size
+        byte[] nameBytes = zc.getBytes(e.name);
+        writeShort(nameBytes.length);
+
+        int elen = getExtraLen(e.extra);
+        if (hasZip64) {
+            elen += (elenZIP64 + 4);// + headid(2) + datasize(2)
+        }
+        // cen info-zip extended timestamp only outputs mtime
+        // but set the flag for a/ctime, if present in loc
+        int flagEXTT = 0;
+        long umtime = -1;
+        long uatime = -1;
+        long uctime = -1;
+        if (e.mtime != null) {
+            flagEXTT |= EXTT_FLAG_LMT;
+            umtime = fileTimeToUnixTime(e.mtime);
+        }
+        if (e.atime != null) {
+            flagEXTT |= EXTT_FLAG_LAT;
+            uatime = fileTimeToUnixTime(e.atime);
+        }
+        if (e.ctime != null) {
+            flagEXTT |= EXTT_FLAT_CT;
+            uctime = fileTimeToUnixTime(e.ctime);
+        }
+        if (flagEXTT != 0) {
+            // to use ntfs time if any m/a/ctime is beyond unixtime upper bound
+            if (umtime > UPPER_UNIXTIME_BOUND ||
+                uatime > UPPER_UNIXTIME_BOUND ||
+                uctime > UPPER_UNIXTIME_BOUND) {
+                elen += 36;         // NTFS time total 36 bytes
+            } else {
+                elen += 5;          // headid(2) + sz(2) + flag(1)
+                if (e.mtime != null)
+                    elen += 4;      // + mtime (4)
+            }
+        }
+        writeShort(elen);
+        byte[] commentBytes;
+        if (e.comment != null) {
+            commentBytes = zc.getBytes(e.comment);
+            writeShort(Math.min(commentBytes.length, 0xffff));
+        } else {
+            commentBytes = null;
+            writeShort(0);
+        }
+        writeShort(0);              // starting disk number
+        writeShort(0);              // internal file attributes (unused)
+        // extra file attributes, used for storing posix permissions etc.
+        writeInt(e.extraAttributes > 0 ? e.extraAttributes << 16 : 0);
+        writeInt(offset);           // relative offset of local header
+        writeBytes(nameBytes, 0, nameBytes.length);
+
+        // take care of EXTID_ZIP64 and EXTID_EXTT
+        if (hasZip64) {
+            writeShort(ZIP64_EXTID);// Zip64 extra
+            writeShort(elenZIP64);
+            if (size == ZIP64_MAGICVAL)
+                writeLong(e.size);
+            if (csize == ZIP64_MAGICVAL)
+                writeLong(e.csize);
+            if (offset == ZIP64_MAGICVAL)
+                writeLong(xentry.offset);
+        }
+        if (flagEXTT != 0) {
+            if (umtime > UPPER_UNIXTIME_BOUND ||
+                uatime > UPPER_UNIXTIME_BOUND ||
+                uctime > UPPER_UNIXTIME_BOUND) {
+                writeShort(EXTID_NTFS);    // id
+                writeShort(32);            // data size
+                writeInt(0);               // reserved
+                writeShort(0x0001);        // NTFS attr tag
+                writeShort(24);
+                writeLong(e.mtime == null ? WINDOWS_TIME_NOT_AVAILABLE
+                                          : fileTimeToWinTime(e.mtime));
+                writeLong(e.atime == null ? WINDOWS_TIME_NOT_AVAILABLE
+                                          : fileTimeToWinTime(e.atime));
+                writeLong(e.ctime == null ? WINDOWS_TIME_NOT_AVAILABLE
+                                          : fileTimeToWinTime(e.ctime));
+            } else {
+                writeShort(EXTID_EXTT);
+                if (e.mtime != null) {
+                    writeShort(5);      // flag + mtime
+                    writeByte(flagEXTT);
+                    writeInt(umtime);
+                } else {
+                    writeShort(1);      // flag only
+                    writeByte(flagEXTT);
+                }
+            }
+        }
+        writeExtra(e.extra);
+        if (commentBytes != null) {
+            writeBytes(commentBytes, 0, Math.min(commentBytes.length, 0xffff));
+        }
+    }
+
+    /*
+     * Writes end of central directory (END) header.
+     */
+    private void writeEND(long off, long len) throws IOException {
+        boolean hasZip64 = false;
+        long xlen = len;
+        long xoff = off;
+        if (xlen >= ZIP64_MAGICVAL) {
+            xlen = ZIP64_MAGICVAL;
+            hasZip64 = true;
+        }
+        if (xoff >= ZIP64_MAGICVAL) {
+            xoff = ZIP64_MAGICVAL;
+            hasZip64 = true;
+        }
+        int count = xentries.size();
+        if (count >= ZIP64_MAGICCOUNT) {
+            hasZip64 |= !inhibitZip64;
+            if (hasZip64) {
+                count = ZIP64_MAGICCOUNT;
+            }
+        }
+        if (hasZip64) {
+            long off64 = written;
+            //zip64 end of central directory record
+            writeInt(ZIP64_ENDSIG);        // zip64 END record signature
+            writeLong(ZIP64_ENDHDR - 12);  // size of zip64 end
+            writeShort(45);                // version made by
+            writeShort(45);                // version needed to extract
+            writeInt(0);                   // number of this disk
+            writeInt(0);                   // central directory start disk
+            writeLong(xentries.size());    // number of directory entires on disk
+            writeLong(xentries.size());    // number of directory entires
+            writeLong(len);                // length of central directory
+            writeLong(off);                // offset of central directory
+
+            //zip64 end of central directory locator
+            writeInt(ZIP64_LOCSIG);        // zip64 END locator signature
+            writeInt(0);                   // zip64 END start disk
+            writeLong(off64);              // offset of zip64 END
+            writeInt(1);                   // total number of disks (?)
+        }
+        writeInt(ENDSIG);                 // END record signature
+        writeShort(0);                    // number of this disk
+        writeShort(0);                    // central directory start disk
+        writeShort(count);                // number of directory entries on disk
+        writeShort(count);                // total number of directory entries
+        writeInt(xlen);                   // length of central directory
+        writeInt(xoff);                   // offset of central directory
+        if (comment != null) {            // zip file comment
+            writeShort(comment.length);
+            writeBytes(comment, 0, comment.length);
+        } else {
+            writeShort(0);
+        }
+    }
+
+    /*
+     * Returns the length of extra data without EXTT and ZIP64.
+     */
+    private int getExtraLen(byte[] extra) {
+        if (extra == null)
+            return 0;
+        int skipped = 0;
+        int len = extra.length;
+        int off = 0;
+        while (off + 4 <= len) {
+            int tag = get16(extra, off);
+            int sz = get16(extra, off + 2);
+            if (sz < 0 || (off + 4 + sz) > len) {
+                break;
+            }
+            if (tag == EXTID_EXTT || tag == EXTID_ZIP64) {
+                skipped += (sz + 4);
+            }
+            off += (sz + 4);
+        }
+        return len - skipped;
+    }
+
+    /*
+     * Writes extra data without EXTT and ZIP64.
+     *
+     * Extra timestamp and ZIP64 data is handled/output separately
+     * in writeLOC and writeCEN.
+     */
+    private void writeExtra(byte[] extra) throws IOException {
+        if (extra != null) {
+            int len = extra.length;
+            int off = 0;
+            while (off + 4 <= len) {
+                int tag = get16(extra, off);
+                int sz = get16(extra, off + 2);
+                if (sz < 0 || (off + 4 + sz) > len) {
+                    writeBytes(extra, off, len - off);
+                    return;
+                }
+                if (tag != EXTID_EXTT && tag != EXTID_ZIP64) {
+                    writeBytes(extra, off, sz + 4);
+                }
+                off += (sz + 4);
+            }
+            if (off < len) {
+                writeBytes(extra, off, len - off);
+            }
+        }
+    }
+
+    /*
+     * Writes a 8-bit byte to the output stream.
+     */
+    private void writeByte(int v) throws IOException {
+        OutputStream out = this.out;
+        out.write(v & 0xff);
+        written += 1;
+    }
+
+    /*
+     * Writes a 16-bit short to the output stream in little-endian byte order.
+     */
+    private void writeShort(int v) throws IOException {
+        OutputStream out = this.out;
+        out.write((v >>> 0) & 0xff);
+        out.write((v >>> 8) & 0xff);
+        written += 2;
+    }
+
+    /*
+     * Writes a 32-bit int to the output stream in little-endian byte order.
+     */
+    private void writeInt(long v) throws IOException {
+        OutputStream out = this.out;
+        out.write((int)((v >>>  0) & 0xff));
+        out.write((int)((v >>>  8) & 0xff));
+        out.write((int)((v >>> 16) & 0xff));
+        out.write((int)((v >>> 24) & 0xff));
+        written += 4;
+    }
+
+    /*
+     * Writes a 64-bit int to the output stream in little-endian byte order.
+     */
+    private void writeLong(long v) throws IOException {
+        OutputStream out = this.out;
+        out.write((int)((v >>>  0) & 0xff));
+        out.write((int)((v >>>  8) & 0xff));
+        out.write((int)((v >>> 16) & 0xff));
+        out.write((int)((v >>> 24) & 0xff));
+        out.write((int)((v >>> 32) & 0xff));
+        out.write((int)((v >>> 40) & 0xff));
+        out.write((int)((v >>> 48) & 0xff));
+        out.write((int)((v >>> 56) & 0xff));
+        written += 8;
+    }
+
+    /*
+     * Writes an array of bytes to the output stream.
+     */
+    private void writeBytes(byte[] b, int off, int len) throws IOException {
+        super.out.write(b, off, len);
+        written += len;
+    }
+}
diff --git a/android-35/java/util/zip/ZipUtils.java b/android-35/java/util/zip/ZipUtils.java
new file mode 100644
index 0000000..dd5106e
--- /dev/null
+++ b/android-35/java/util/zip/ZipUtils.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.util.zip;
+
+import java.nio.ByteBuffer;
+import java.nio.file.attribute.FileTime;
+import java.time.DateTimeException;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+import static java.util.zip.ZipConstants.ENDHDR;
+
+import jdk.internal.misc.Unsafe;
+
+class ZipUtils {
+
+    // used to adjust values between Windows and java epoch
+    private static final long WINDOWS_EPOCH_IN_MICROSECONDS = -11644473600000000L;
+
+    // used to indicate the corresponding windows time is not available
+    public static final long WINDOWS_TIME_NOT_AVAILABLE = Long.MIN_VALUE;
+
+    // static final ByteBuffer defaultBuf = ByteBuffer.allocateDirect(0);
+    static final ByteBuffer defaultBuf = ByteBuffer.allocate(0);
+
+    /**
+     * Converts Windows time (in microseconds, UTC/GMT) time to FileTime.
+     */
+    public static final FileTime winTimeToFileTime(long wtime) {
+        return FileTime.from(wtime / 10 + WINDOWS_EPOCH_IN_MICROSECONDS,
+                             TimeUnit.MICROSECONDS);
+    }
+
+    /**
+     * Converts FileTime to Windows time.
+     */
+    public static final long fileTimeToWinTime(FileTime ftime) {
+        return (ftime.to(TimeUnit.MICROSECONDS) - WINDOWS_EPOCH_IN_MICROSECONDS) * 10;
+    }
+
+    /**
+     * The upper bound of the 32-bit unix time, the "year 2038 problem".
+     */
+    public static final long UPPER_UNIXTIME_BOUND = 0x7fffffff;
+
+    /**
+     * Converts "standard Unix time"(in seconds, UTC/GMT) to FileTime
+     */
+    public static final FileTime unixTimeToFileTime(long utime) {
+        return FileTime.from(utime, TimeUnit.SECONDS);
+    }
+
+    /**
+     * Converts FileTime to "standard Unix time".
+     */
+    public static final long fileTimeToUnixTime(FileTime ftime) {
+        return ftime.to(TimeUnit.SECONDS);
+    }
+
+    /**
+     * Converts DOS time to Java time (number of milliseconds since epoch).
+     */
+    public static long dosToJavaTime(long dtime) {
+        int year = (int) (((dtime >> 25) & 0x7f) + 1980);
+        int month = (int) ((dtime >> 21) & 0x0f);
+        int day = (int) ((dtime >> 16) & 0x1f);
+        int hour = (int) ((dtime >> 11) & 0x1f);
+        int minute = (int) ((dtime >> 5) & 0x3f);
+        int second = (int) ((dtime << 1) & 0x3e);
+
+        if (month > 0 && month < 13 && day > 0 && hour < 24 && minute < 60 && second < 60) {
+            try {
+                LocalDateTime ldt = LocalDateTime.of(year, month, day, hour, minute, second);
+                return TimeUnit.MILLISECONDS.convert(ldt.toEpochSecond(
+                        ZoneId.systemDefault().getRules().getOffset(ldt)), TimeUnit.SECONDS);
+            } catch (DateTimeException dte) {
+                // ignore
+            }
+        }
+        return overflowDosToJavaTime(year, month, day, hour, minute, second);
+    }
+
+    /*
+     * Deal with corner cases where an arguably mal-formed DOS time is used
+     */
+    @SuppressWarnings("deprecation") // Use of Date constructor
+    private static long overflowDosToJavaTime(int year, int month, int day,
+                                              int hour, int minute, int second) {
+        return new Date(year - 1900, month - 1, day, hour, minute, second).getTime();
+    }
+
+
+    /**
+     * Converts extended DOS time to Java time, where up to 1999 milliseconds
+     * might be encoded into the upper half of the returned long.
+     *
+     * @param xdostime the extended DOS time value
+     * @return milliseconds since epoch
+     */
+    public static long extendedDosToJavaTime(long xdostime) {
+        long time = dosToJavaTime(xdostime);
+        return time + (xdostime >> 32);
+    }
+
+    /**
+     * Converts Java time to DOS time.
+     */
+    private static long javaToDosTime(LocalDateTime ldt) {
+        int year = ldt.getYear() - 1980;
+        return (year << 25 |
+            ldt.getMonthValue() << 21 |
+            ldt.getDayOfMonth() << 16 |
+            ldt.getHour() << 11 |
+            ldt.getMinute() << 5 |
+            ldt.getSecond() >> 1) & 0xffffffffL;
+    }
+
+    /**
+     * Converts Java time to DOS time, encoding any milliseconds lost
+     * in the conversion into the upper half of the returned long.
+     *
+     * @param time milliseconds since epoch
+     * @return DOS time with 2s remainder encoded into upper half
+     */
+    static long javaToExtendedDosTime(long time) {
+        LocalDateTime ldt = javaEpochToLocalDateTime(time);
+        if (ldt.getYear() >= 1980) {
+            return javaToDosTime(ldt) + ((time % 2000) << 32);
+        }
+        return ZipEntry.DOSTIME_BEFORE_1980;
+    }
+
+    static LocalDateTime javaEpochToLocalDateTime(long time) {
+        Instant instant = Instant.ofEpochMilli(time);
+        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
+    }
+
+    /**
+     * Fetches unsigned 16-bit value from byte array at specified offset.
+     * The bytes are assumed to be in Intel (little-endian) byte order.
+     */
+    public static final int get16(byte b[], int off) {
+        return (b[off] & 0xff) | ((b[off + 1] & 0xff) << 8);
+    }
+
+    /**
+     * Fetches unsigned 32-bit value from byte array at specified offset.
+     * The bytes are assumed to be in Intel (little-endian) byte order.
+     */
+    public static final long get32(byte b[], int off) {
+        return (get16(b, off) | ((long)get16(b, off+2) << 16)) & 0xffffffffL;
+    }
+
+    /**
+     * Fetches signed 64-bit value from byte array at specified offset.
+     * The bytes are assumed to be in Intel (little-endian) byte order.
+     */
+    public static final long get64(byte b[], int off) {
+        return get32(b, off) | (get32(b, off+4) << 32);
+    }
+
+    /**
+     * Fetches signed 32-bit value from byte array at specified offset.
+     * The bytes are assumed to be in Intel (little-endian) byte order.
+     *
+     */
+    public static final int get32S(byte b[], int off) {
+        return (get16(b, off) | (get16(b, off+2) << 16));
+    }
+
+    // fields access methods
+    static final int CH(byte[] b, int n) {
+        return b[n] & 0xff ;
+    }
+
+    static final int SH(byte[] b, int n) {
+        return (b[n] & 0xff) | ((b[n + 1] & 0xff) << 8);
+    }
+
+    static final long LG(byte[] b, int n) {
+        return ((SH(b, n)) | (SH(b, n + 2) << 16)) & 0xffffffffL;
+    }
+
+    static final long LL(byte[] b, int n) {
+        return (LG(b, n)) | (LG(b, n + 4) << 32);
+    }
+
+    static final long GETSIG(byte[] b) {
+        return LG(b, 0);
+    }
+
+    /*
+     * File attribute compatibility types of CEN field "version made by"
+     */
+    static final int FILE_ATTRIBUTES_UNIX = 3; // Unix
+
+    /*
+     * Base values for CEN field "version made by"
+     */
+    static final int VERSION_MADE_BY_BASE_UNIX = FILE_ATTRIBUTES_UNIX << 8; // Unix
+
+
+    // local file (LOC) header fields
+    static final long LOCSIG(byte[] b) { return LG(b, 0); } // signature
+    static final int  LOCVER(byte[] b) { return SH(b, 4); } // version needed to extract
+    static final int  LOCFLG(byte[] b) { return SH(b, 6); } // general purpose bit flags
+    static final int  LOCHOW(byte[] b) { return SH(b, 8); } // compression method
+    static final long LOCTIM(byte[] b) { return LG(b, 10);} // modification time
+    static final long LOCCRC(byte[] b) { return LG(b, 14);} // crc of uncompressed data
+    static final long LOCSIZ(byte[] b) { return LG(b, 18);} // compressed data size
+    static final long LOCLEN(byte[] b) { return LG(b, 22);} // uncompressed data size
+    static final int  LOCNAM(byte[] b) { return SH(b, 26);} // filename length
+    static final int  LOCEXT(byte[] b) { return SH(b, 28);} // extra field length
+
+    // extra local (EXT) header fields
+    static final long EXTCRC(byte[] b) { return LG(b, 4);}  // crc of uncompressed data
+    static final long EXTSIZ(byte[] b) { return LG(b, 8);}  // compressed size
+    static final long EXTLEN(byte[] b) { return LG(b, 12);} // uncompressed size
+
+    // end of central directory header (END) fields
+    static final int  ENDSUB(byte[] b) { return SH(b, 8); }  // number of entries on this disk
+    static final int  ENDTOT(byte[] b) { return SH(b, 10);}  // total number of entries
+    static final long ENDSIZ(byte[] b) { return LG(b, 12);}  // central directory size
+    static final long ENDOFF(byte[] b) { return LG(b, 16);}  // central directory offset
+    static final int  ENDCOM(byte[] b) { return SH(b, 20);}  // size of zip file comment
+    static final int  ENDCOM(byte[] b, int off) { return SH(b, off + 20);}
+
+    // zip64 end of central directory recoder fields
+    static final long ZIP64_ENDTOD(byte[] b) { return LL(b, 24);}  // total number of entries on disk
+    static final long ZIP64_ENDTOT(byte[] b) { return LL(b, 32);}  // total number of entries
+    static final long ZIP64_ENDSIZ(byte[] b) { return LL(b, 40);}  // central directory size
+    static final long ZIP64_ENDOFF(byte[] b) { return LL(b, 48);}  // central directory offset
+    static final long ZIP64_LOCOFF(byte[] b) { return LL(b, 8);}   // zip64 end offset
+
+    // central directory header (CEN) fields
+    static final long CENSIG(byte[] b, int pos) { return LG(b, pos + 0); }
+    static final int  CENVEM(byte[] b, int pos) { return SH(b, pos + 4); }
+    static final int  CENVEM_FA(byte[] b, int pos) { return CH(b, pos + 5); } // file attribute compatibility
+    static final int  CENVER(byte[] b, int pos) { return SH(b, pos + 6); }
+    static final int  CENFLG(byte[] b, int pos) { return SH(b, pos + 8); }
+    static final int  CENHOW(byte[] b, int pos) { return SH(b, pos + 10);}
+    static final long CENTIM(byte[] b, int pos) { return LG(b, pos + 12);}
+    static final long CENCRC(byte[] b, int pos) { return LG(b, pos + 16);}
+    static final long CENSIZ(byte[] b, int pos) { return LG(b, pos + 20);}
+    static final long CENLEN(byte[] b, int pos) { return LG(b, pos + 24);}
+    static final int  CENNAM(byte[] b, int pos) { return SH(b, pos + 28);}
+    static final int  CENEXT(byte[] b, int pos) { return SH(b, pos + 30);}
+    static final int  CENCOM(byte[] b, int pos) { return SH(b, pos + 32);}
+    static final int  CENDSK(byte[] b, int pos) { return SH(b, pos + 34);}
+    static final int  CENATT(byte[] b, int pos) { return SH(b, pos + 36);}
+    static final long CENATX(byte[] b, int pos) { return LG(b, pos + 38);}
+    static final int  CENATX_PERMS(byte[] b, int pos) { return SH(b, pos + 40);} // posix permission data
+    static final long CENOFF(byte[] b, int pos) { return LG(b, pos + 42);}
+
+    // The END header is followed by a variable length comment of size < 64k.
+    static final long END_MAXLEN = 0xFFFF + ENDHDR;
+    static final int READBLOCKSZ = 128;
+
+    // Android-removed: not available on Android.
+    /*
+     * Loads zip native library, if not already laoded
+     *
+    static void loadLibrary() {
+        jdk.internal.loader.BootLoader.loadLibrary("zip");
+    }
+    */
+
+    private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+    private static final long byteBufferArrayOffset = unsafe.objectFieldOffset(ByteBuffer.class, "hb");
+    private static final long byteBufferOffsetOffset = unsafe.objectFieldOffset(ByteBuffer.class, "offset");
+
+    static byte[] getBufferArray(ByteBuffer byteBuffer) {
+        return (byte[]) unsafe.getReference(byteBuffer, byteBufferArrayOffset);
+    }
+
+    static int getBufferOffset(ByteBuffer byteBuffer) {
+        return unsafe.getInt(byteBuffer, byteBufferOffsetOffset);
+    }
+}
diff --git a/android-35/java/util/zip/package-info.java b/android-35/java/util/zip/package-info.java
new file mode 100644
index 0000000..f4a38d6
--- /dev/null
+++ b/android-35/java/util/zip/package-info.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * Provides classes for reading and writing the standard ZIP and GZIP file
+ * formats. Also includes classes for compressing and decompressing data using
+ * the DEFLATE compression algorithm, which is used by the ZIP and GZIP file
+ * formats. Additionally, there are utility classes for computing the CRC-32,
+ * CRC-32C and Adler-32 checksums of arbitrary input streams.
+ *
+ * <h2>Package Specification</h2>
+ *
+ * <ul>
+ *     <li><a href="http://www.info-zip.org/doc/appnote-19970311-iz.zip">
+ *         Info-ZIP Application Note 970311</a> - a detailed description of
+ *         the Info-ZIP format upon which the {@code java.util.zip} classes
+ *         are based.
+ *     <li><a id="zip64">An implementation may optionally support the
+ *         ZIP64(tm) format extensions defined by the</a>
+ *         <a href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">
+ *         PKWARE ZIP File Format Specification</a>. The ZIP64(tm) format
+ *         extensions are used to overcome the size limitations of the
+ *         original ZIP format.
+ *     <li><a id="lang_encoding">APPENDIX D of</a>
+ *         <a href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">
+ *         PKWARE ZIP File Format Specification</a> - Language Encoding Flag
+ *         to encode ZIP entry filename and comment fields using UTF-8.
+ *     <li><a href="http://www.ietf.org/rfc/rfc1950.txt">
+ *         ZLIB Compressed Data Format Specification version 3.3</a>
+ *         &nbsp;
+ *         <a href="http://www.ietf.org/rfc/rfc1950.txt.pdf">(pdf)</a>
+ *         (RFC 1950)
+ *     <li><a href="http://www.ietf.org/rfc/rfc1951.txt">
+ *         DEFLATE Compressed Data Format Specification version 1.3</a>
+ *         &nbsp;
+ *         <a href="http://www.ietf.org/rfc/rfc1951.txt.pdf">(pdf)</a>
+ *         (RFC 1951)
+ *     <li><a href="http://www.ietf.org/rfc/rfc1952.txt">
+ *         GZIP file format specification version 4.3</a>
+ *         &nbsp;
+ *         <a href="http://www.ietf.org/rfc/rfc1952.txt.pdf">(pdf)</a>
+ *         (RFC 1952)
+ *     <li>CRC-32 checksum is described in RFC 1952 (above)
+ *     <li>CRC-32C checksum is described in
+ *         <a href="http://www.ietf.org/rfc/rfc3720.txt">Internet Small
+ *         Computer Systems Interface (iSCSI)</a>
+ *         &nbsp;
+ *         <a href="http://www.ietf.org/rfc/rfc3720.txt.pdf">(pdf)</a>
+ *         (RFC 3720)
+ *     <li>Adler-32 checksum is described in RFC 1950 (above)
+ * </ul>
+ *
+ * @since 1.1
+ */
+package java.util.zip;