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>
+ *
+ * <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>
+ *
+ * <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>
+ *
+ * <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>
+ *
+ * <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;