Merge "Modify tests to reflect different URL encoding for single quote" into nyc-mr1-dev
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java
index a9f64a2..a16c4e4 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java
@@ -30,6 +30,7 @@
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.Spliterator;
import java.util.Vector;
@@ -850,6 +851,15 @@
}
}
+ // http://b/30974375
+ public void test_listIterator_addAndPrevious() {
+ ListIterator<String> it = new Vector<String>().listIterator();
+ assertFalse(it.hasNext());
+ it.add("value");
+ assertEquals("value", it.previous());
+ assertTrue(it.hasNext());
+ }
+
/**
* java.util.Vector#remove(int)
*/
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/SocketFactoryTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/SocketFactoryTest.java
index 649be09..52d9142 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/SocketFactoryTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/javax/net/SocketFactoryTest.java
@@ -126,30 +126,12 @@
public final void test_createSocket_InetAddressIInetAddressI() throws Exception {
SocketFactory sf = SocketFactory.getDefault();
int sport = new ServerSocket(0).getLocalPort();
- int[] invalidPorts = {Integer.MIN_VALUE, -1, 65536, Integer.MAX_VALUE};
Socket s = sf.createSocket(InetAddress.getLocalHost(), sport,
- InetAddress.getLocalHost(), 0);
+ InetAddress.getLocalHost(), 0);
assertNotNull(s);
assertTrue("1: Failed to create socket", s.getPort() == sport);
int portNumber = s.getLocalPort();
-
- for (int i = 0; i < invalidPorts.length; i++) {
- try {
- sf.createSocket(InetAddress.getLocalHost(), invalidPorts[i],
- InetAddress.getLocalHost(), portNumber);
- fail("IllegalArgumentException wasn't thrown for " + invalidPorts[i]);
- } catch (IllegalArgumentException expected) {
- }
-
- try {
- sf.createSocket(InetAddress.getLocalHost(), sport,
- InetAddress.getLocalHost(), invalidPorts[i]);
- fail("IllegalArgumentException wasn't thrown for " + invalidPorts[i]);
- } catch (IllegalArgumentException expected) {
- }
- }
-
try {
sf.createSocket(InetAddress.getLocalHost(), sport,
InetAddress.getLocalHost(), portNumber);
@@ -165,6 +147,64 @@
}
}
+ // Checks the behavior of createSocket(InetAddress, int, InetAddress, int) when the
+ // ports are invalid.
+ public void test_createSocket_InetAddressIInetAddressI_IllegalArgumentException()
+ throws Exception {
+ SocketFactory sf = SocketFactory.getDefault();
+ int validPort = new ServerSocket(0).getLocalPort();
+ int[] invalidPorts = {Integer.MIN_VALUE, -1, 65536, Integer.MAX_VALUE};
+
+ for (int i = 0; i < invalidPorts.length; i++) {
+ // Check invalid server port.
+ try (Socket s = sf.createSocket(InetAddress.getLocalHost() /* ServerAddress */,
+ invalidPorts[i] /* ServerPort */,
+ InetAddress.getLocalHost() /* ClientAddress */,
+ validPort /* ClientPort */)) {
+ fail("IllegalArgumentException wasn't thrown for " + invalidPorts[i]);
+ } catch (IllegalArgumentException expected) {
+ }
+
+ // Check invalid client port.
+ try (Socket s = sf.createSocket(InetAddress.getLocalHost() /* ServerAddress */,
+ validPort /* ServerPort */,
+ InetAddress.getLocalHost() /* ClientAddress */,
+ invalidPorts[i]) /* ClientPort */){
+ fail("IllegalArgumentException wasn't thrown for " + invalidPorts[i]);
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+ }
+
+ // b/31019685
+ // Checks the ordering of port number validation (IllegalArgumentException) and binding error.
+ public void test_createSocket_InetAddressIInetAddressI_ExceptionOrder() throws IOException {
+ int invalidPort = Integer.MAX_VALUE;
+ SocketFactory sf = SocketFactory.getDefault();
+ int validServerPortNumber = new ServerSocket(0).getLocalPort();
+
+ // Create a socket with localhost as the client address so that another attempt to bind
+ // would fail.
+ Socket s = sf.createSocket(InetAddress.getLocalHost() /* ServerAddress */,
+ validServerPortNumber /* ServerPortNumber */,
+ InetAddress.getLocalHost() /* ClientAddress */,
+ 0 /* ClientPortNumber */);
+
+ int assignedLocalPortNumber = s.getLocalPort();
+
+ // Create a socket with an invalid port and localhost as the client address. Both
+ // BindException and IllegalArgumentException are expected in this case as the address is
+ // already bound to the socket above and port is invalid, however, to preserve the
+ // precedence order, IllegalArgumentException should be thrown.
+ try (Socket s1 = sf.createSocket(InetAddress.getLocalHost() /* ServerAddress */,
+ invalidPort /* ServerPortNumber */,
+ InetAddress.getLocalHost() /* ClientAddress */,
+ assignedLocalPortNumber /* ClientPortNumber */)) {
+ fail("IllegalArgumentException wasn't thrown for " + invalidPort);
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
/**
* javax.net.SocketFactory#createSocket(String host, int port,
* InetAddress localHost, int localPort)
diff --git a/luni/src/main/java/libcore/icu/TimeZoneNames.java b/luni/src/main/java/libcore/icu/TimeZoneNames.java
index daa915e..917d9ce 100644
--- a/luni/src/main/java/libcore/icu/TimeZoneNames.java
+++ b/luni/src/main/java/libcore/icu/TimeZoneNames.java
@@ -68,7 +68,7 @@
}
long nativeStart = System.nanoTime();
- fillZoneStrings(locale.toString(), result);
+ fillZoneStrings(locale.toLanguageTag(), result);
long nativeEnd = System.nanoTime();
internStrings(result);
diff --git a/luni/src/main/java/libcore/net/MimeUtils.java b/luni/src/main/java/libcore/net/MimeUtils.java
index 3b59b87..88a7392 100644
--- a/luni/src/main/java/libcore/net/MimeUtils.java
+++ b/luni/src/main/java/libcore/net/MimeUtils.java
@@ -210,6 +210,12 @@
add("application/x-xcf", "xcf");
add("application/x-xfig", "fig");
add("application/xhtml+xml", "xhtml");
+ // Video mime types for 3GPP first so they'll be default for guessMimeTypeFromExtension
+ // See RFC 3839 for 3GPP and RFC 4393 for 3GPP2
+ add("video/3gpp", "3gpp");
+ add("video/3gpp", "3gp");
+ add("video/3gpp2", "3gpp2");
+ add("video/3gpp2", "3g2");
add("audio/3gpp", "3gpp");
add("audio/aac", "aac");
add("audio/aac-adts", "aac");
@@ -353,10 +359,6 @@
add("text/x-tex", "cls");
add("text/x-vcalendar", "vcs");
add("text/x-vcard", "vcf");
- add("video/3gpp", "3gpp");
- add("video/3gpp", "3gp");
- add("video/3gpp2", "3gpp2");
- add("video/3gpp2", "3g2");
add("video/avi", "avi");
add("video/dl", "dl");
add("video/dv", "dif");
diff --git a/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java b/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java
index fce8507..ec5ca03 100644
--- a/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java
+++ b/luni/src/test/java/com/android/org/bouncycastle/crypto/digests/DigestTest.java
@@ -93,9 +93,6 @@
+ oldTime.toString());
System.out.println("Time for " + ITERATIONS + " x new hash processing: "
+ newTime.toString());
-
- assertTrue("New hash should be faster:\nold=" + oldTime.toString() + "\nnew="
- + newTime.toString(), newTime.mean() < oldTime.mean());
}
/**
diff --git a/luni/src/test/java/libcore/java/net/SocketTest.java b/luni/src/test/java/libcore/java/net/SocketTest.java
index 52638d4..e2d3964 100644
--- a/luni/src/test/java/libcore/java/net/SocketTest.java
+++ b/luni/src/test/java/libcore/java/net/SocketTest.java
@@ -21,6 +21,7 @@
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.Inet4Address;
+import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
@@ -34,6 +35,7 @@
import java.net.UnknownHostException;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
+import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
@@ -45,6 +47,10 @@
public class SocketTest extends junit.framework.TestCase {
+
+ // This hostname is required to resolve to 127.0.0.1 and ::1 for all tests to pass.
+ private static final String ALL_LOOPBACK_HOSTNAME = "loopback46.unittest.grpc.io";
+
// See http://b/2980559.
public void test_close() throws Exception {
Socket s = new Socket();
@@ -542,4 +548,51 @@
new SocketThatFailOnClose(InetAddress.getLocalHost(), 1, true);
} catch(IOException expected) {}
}
+
+ // b/30007735
+ public void testSocketTestAllAddresses() throws Exception {
+ // Socket Ctor should try all sockets.
+ //
+ // This test creates a server socket bound to 127.0.0.1 or ::1 only, and connects using a
+ // hostname that resolves to both addresses. We should be able to connect to the server
+ // socket in either setup.
+ final String loopbackHost = ALL_LOOPBACK_HOSTNAME;
+
+ assertTrue("Loopback DNS record is unreachable or is invalid.", checkLoopbackHost(
+ loopbackHost));
+
+ final int port = 9999;
+ for (InetAddress addr : new InetAddress[]{ Inet4Address.LOOPBACK, Inet6Address.LOOPBACK }) {
+ try (ServerSocket ss = new ServerSocket(port, 0, addr)) {
+ new Thread(() -> {
+ try {
+ ss.accept();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }).start();
+
+ assertTrue(canConnect(loopbackHost, port));
+ }
+ }
+ }
+
+ /** Confirm the supplied hostname maps to only loopback addresses. */
+ private static boolean checkLoopbackHost(String host) {
+ try {
+ List<InetAddress> addrs = Arrays.asList(InetAddress.getAllByName(host));
+ return addrs.stream().allMatch(InetAddress::isLoopbackAddress) &&
+ addrs.contains(Inet4Address.LOOPBACK) && addrs.contains(Inet6Address.LOOPBACK);
+ } catch (UnknownHostException e) {
+ return false;
+ }
+ }
+
+ private static boolean canConnect(String host, int port) {
+ try(Socket sock = new Socket(host, port)) {
+ return sock.isConnected();
+ } catch (IOException e) {
+ return false;
+ }
+ }
}
diff --git a/luni/src/test/java/libcore/java/util/TimeZoneTest.java b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
index 5a6fa7f..cb2cd0f 100644
--- a/luni/src/test/java/libcore/java/util/TimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
@@ -16,13 +16,20 @@
package libcore.java.util;
+import junit.framework.TestCase;
+
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Calendar;
+import java.util.Collections;
import java.util.Date;
+import java.util.List;
import java.util.Locale;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
-import junit.framework.TestCase;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.atomic.AtomicInteger;
public class TimeZoneTest extends TestCase {
// http://code.google.com/p/android/issues/detail?id=877
@@ -261,6 +268,31 @@
assertEquals("", failures.toString());
}
+ // http://b/30527513
+ public void testDisplayNamesWithScript() throws Exception {
+ Locale latinLocale = Locale.forLanguageTag("sr-Latn-RS");
+ Locale cyrillicLocale = Locale.forLanguageTag("sr-Cyrl-RS");
+ Locale noScriptLocale = Locale.forLanguageTag("sr-RS");
+ TimeZone tz = TimeZone.getTimeZone("Europe/London");
+
+ final String latinName = "Srednje vreme po Griniču";
+ final String cyrillicName = "Средње време по Гриничу";
+
+ // Check java.util.TimeZone
+ assertEquals(latinName, tz.getDisplayName(latinLocale));
+ assertEquals(cyrillicName, tz.getDisplayName(cyrillicLocale));
+ assertEquals(cyrillicName, tz.getDisplayName(noScriptLocale));
+
+ // Check ICU TimeZoneNames
+ // The one-argument getDisplayName() override uses LONG_GENERIC style which is different
+ // from what java.util.TimeZone uses. Force the LONG style to get equivalent results.
+ final int style = android.icu.util.TimeZone.LONG;
+ android.icu.util.TimeZone utz = android.icu.util.TimeZone.getTimeZone(tz.getID());
+ assertEquals(latinName, utz.getDisplayName(false, style, latinLocale));
+ assertEquals(cyrillicName, utz.getDisplayName(false, style, cyrillicLocale));
+ assertEquals(cyrillicName, utz.getDisplayName(false, style, noScriptLocale));
+ }
+
// http://b/7955614
public void testApia() throws Exception {
TimeZone tz = TimeZone.getTimeZone("Pacific/Apia");
@@ -340,4 +372,107 @@
TimeZone.setDefault(origTz);
}
}
+
+ // http://b/30937209
+ public void testSetDefaultDeadlock() throws InterruptedException, BrokenBarrierException {
+ // Since this tests a deadlock, the test has two fundamental problems:
+ // - it is probabilistic: it's not guaranteed to fail if the problem exists
+ // - if it fails, it will effectively hang the current runtime, as no other thread will
+ // be able to call TimeZone.getDefault()/setDefault() successfully any more.
+
+ // 10 was too low to be reliable, 100 failed more than half the time (on a bullhead).
+ final int iterations = 100;
+ TimeZone otherTimeZone = TimeZone.getTimeZone("Europe/London");
+ AtomicInteger setterCount = new AtomicInteger();
+ CyclicBarrier startBarrier = new CyclicBarrier(2);
+ Thread setter = new Thread(() -> {
+ waitFor(startBarrier);
+ for (int i = 0; i < iterations; i++) {
+ TimeZone.setDefault(otherTimeZone);
+ TimeZone.setDefault(null);
+ setterCount.set(i+1);
+ }
+ });
+ setter.setName("testSetDefaultDeadlock setter");
+
+ AtomicInteger getterCount = new AtomicInteger();
+ Thread getter = new Thread(() -> {
+ waitFor(startBarrier);
+ for (int i = 0; i < iterations; i++) {
+ android.icu.util.TimeZone.getDefault();
+ getterCount.set(i+1);
+ }
+ });
+ getter.setName("testSetDefaultDeadlock getter");
+
+ setter.start();
+ getter.start();
+
+ // 2 seconds is plenty: If successful, we usually complete much faster.
+ setter.join(1000);
+ getter.join(1000);
+ if (setter.isAlive() || getter.isAlive()) {
+ fail("Threads are still alive. Getter iteration count: " + getterCount.get()
+ + ", setter iteration count: " + setterCount.get());
+ }
+ // Guard against unexpected uncaught exceptions.
+ assertEquals("Setter iterations", iterations, setterCount.get());
+ assertEquals("Getter iterations", iterations, getterCount.get());
+ }
+
+ // http://b/30979219
+ public void testSetDefaultRace() throws InterruptedException {
+ // Since this tests a race condition, the test is probabilistic: it's not guaranteed to
+ // fail if the problem exists
+
+ // These iterations are significantly faster than the ones in #testSetDefaultDeadlock
+ final int iterations = 10000;
+ List<Throwable> exceptions = Collections.synchronizedList(new ArrayList<>());
+ Thread.UncaughtExceptionHandler handler = (t, e) -> exceptions.add(e);
+
+ CyclicBarrier startBarrier = new CyclicBarrier(2);
+ Thread clearer = new Thread(() -> {
+ waitFor(startBarrier);
+ for (int i = 0; i < iterations; i++) {
+ // This is not public API but can effectively be invoked via
+ // java.util.TimeZone.setDefault. Call it directly to reduce the amount of code
+ // involved in this test.
+ android.icu.util.TimeZone.clearCachedDefault();
+ }
+ });
+ clearer.setName("testSetDefaultRace clearer");
+ clearer.setUncaughtExceptionHandler(handler);
+
+ Thread getter = new Thread(() -> {
+ waitFor(startBarrier);
+ for (int i = 0; i < iterations; i++) {
+ android.icu.util.TimeZone.getDefault();
+ }
+ });
+ getter.setName("testSetDefaultRace getter");
+ getter.setUncaughtExceptionHandler(handler);
+
+ clearer.start();
+ getter.start();
+
+ // 2 seconds is plenty: If successful, we usually complete much faster.
+ clearer.join(1000);
+ getter.join(1000);
+
+ if (!exceptions.isEmpty()) {
+ Throwable firstException = exceptions.get(0);
+ firstException.printStackTrace();
+ fail("Threads did not succeed successfully: " + firstException);
+ }
+ assertFalse("clearer thread is still alive", clearer.isAlive());
+ assertFalse("getter thread is still alive", getter.isAlive());
+ }
+
+ private static void waitFor(CyclicBarrier barrier) {
+ try {
+ barrier.await();
+ } catch (InterruptedException | BrokenBarrierException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
index 02210ac..03fa29a 100644
--- a/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
+++ b/luni/src/test/java/libcore/java/util/zip/ZipFileTest.java
@@ -16,7 +16,18 @@
package libcore.java.util.zip;
+import android.system.OsConstants;
+import libcore.io.Libcore;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.InputStream;
import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
public final class ZipFileTest extends AbstractZipFileTest {
@@ -25,4 +36,42 @@
protected ZipOutputStream createZipOutputStream(OutputStream wrapped) {
return new ZipOutputStream(wrapped);
}
+
+ // http://b/30407219
+ public void testZipFileOffsetNeverChangesAfterInit() throws Exception {
+ final File f = createTemporaryZipFile();
+ writeEntries(createZipOutputStream(new BufferedOutputStream(new FileOutputStream(f))),
+ 2 /* number of entries */, 1024 /* entry size */, true /* setEntrySize */);
+
+ ZipFile zipFile = new ZipFile(f);
+ FileDescriptor fd = new FileDescriptor();
+ fd.setInt$(zipFile.getFileDescriptor());
+
+ long initialOffset = android.system.Os.lseek(fd, 0, OsConstants.SEEK_CUR);
+
+ Enumeration<? extends ZipEntry> entries = zipFile.entries();
+ assertOffset(initialOffset, fd);
+
+ // Get references to the two elements in the file.
+ ZipEntry entry1 = entries.nextElement();
+ ZipEntry entry2 = entries.nextElement();
+ assertFalse(entries.hasMoreElements());
+ assertOffset(initialOffset, fd);
+
+ InputStream is1 = zipFile.getInputStream(entry1);
+ assertOffset(initialOffset, fd);
+ is1.read(new byte[256]);
+ assertOffset(initialOffset, fd);
+ is1.close();
+
+ assertNotNull(zipFile.getEntry(entry2.getName()));
+ assertOffset(initialOffset, fd);
+
+ zipFile.close();
+ }
+
+ private static void assertOffset(long initialOffset, FileDescriptor fd) throws Exception {
+ long currentOffset = android.system.Os.lseek(fd, 0, OsConstants.SEEK_CUR);
+ assertEquals(initialOffset, currentOffset);
+ }
}
diff --git a/luni/src/test/java/libcore/javax/crypto/CipherTest.java b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
index a75a66d..4ce883b 100644
--- a/luni/src/test/java/libcore/javax/crypto/CipherTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/CipherTest.java
@@ -2683,6 +2683,27 @@
/*
* Test vector generation:
+ * openssl rand -hex 16 | sed 's/\(..\)/(byte) 0x\1, /g'
+ */
+ private static final byte[] DES_112_KEY = new byte[] {
+ (byte) 0x6b, (byte) 0xb3, (byte) 0x85, (byte) 0x1c, (byte) 0x3d, (byte) 0x50,
+ (byte) 0xd4, (byte) 0x95, (byte) 0x39, (byte) 0x48, (byte) 0x77, (byte) 0x30,
+ (byte) 0x1a, (byte) 0xd7, (byte) 0x86, (byte) 0x57,
+ };
+
+ /*
+ * Test vector generation:
+ * openssl rand -hex 24 | sed 's/\(..\)/(byte) 0x\1, /g'
+ */
+ private static final byte[] DES_168_KEY = new byte[] {
+ (byte) 0xfe, (byte) 0xd4, (byte) 0xd7, (byte) 0xc9, (byte) 0x8a, (byte) 0x13,
+ (byte) 0x6a, (byte) 0xa8, (byte) 0x5a, (byte) 0xb8, (byte) 0x19, (byte) 0xb8,
+ (byte) 0xcf, (byte) 0x3c, (byte) 0x5f, (byte) 0xe0, (byte) 0xa2, (byte) 0xf7,
+ (byte) 0x7b, (byte) 0x65, (byte) 0x43, (byte) 0xc0, (byte) 0xc4, (byte) 0xe1,
+ };
+
+ /*
+ * Test vector generation:
* openssl rand -hex 16
* echo '3d4f8970b1f27537f40a39298a41555f' | sed 's/\(..\)/(byte) 0x\1, /g'
*/
@@ -2727,6 +2748,61 @@
};
/*
+ * Test vector generation:
+ * echo -n 'Testing rocks!' | recode ../x1 | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] DES_Plaintext1 = new byte[] {
+ (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x69, (byte) 0x6E,
+ (byte) 0x67, (byte) 0x20, (byte) 0x72, (byte) 0x6F, (byte) 0x63, (byte) 0x6B,
+ (byte) 0x73, (byte) 0x21
+ };
+
+ /*
+ * Test vector generation: take DES_Plaintext1 and PKCS #5 pad it manually (it's not hard).
+ */
+ private static final byte[] DES_Plaintext1_PKCS5_Padded = new byte[] {
+ (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x69, (byte) 0x6E,
+ (byte) 0x67, (byte) 0x20, (byte) 0x72, (byte) 0x6F, (byte) 0x63, (byte) 0x6B,
+ (byte) 0x73, (byte) 0x21, (byte) 0x02, (byte) 0x02,
+ };
+
+ /*
+ * Test vector generation:
+ * openssl rand -hex 8 | sed 's/\(..\)/(byte) 0x\1, /g'
+ */
+ private static final byte[] DES_IV1 = new byte[] {
+ (byte) 0x5c, (byte) 0x47, (byte) 0x5e, (byte) 0x57, (byte) 0x0c, (byte) 0x46,
+ (byte) 0xcb, (byte) 0x47,
+ };
+
+ /*
+ * Test vector generation:
+ * openssl enc -des-ede-cbc -K 6bb3851c3d50d495394877301ad78657 -iv 5c475e570c46cb47 -in blah
+ * | recode ../x1 | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[]
+ DES_Plaintext1_Encrypted_With_DES_112_KEY_And_DESEDE_CBC_PKCS5PADDING_With_DES_IV1 =
+ new byte[] {
+ (byte) 0x09, (byte) 0xA5, (byte) 0x5D, (byte) 0x94, (byte) 0x94, (byte) 0xAA,
+ (byte) 0x3F, (byte) 0xC8, (byte) 0xB7, (byte) 0x73, (byte) 0x94, (byte) 0x0E,
+ (byte) 0xFC, (byte) 0xF4, (byte) 0xA5, (byte) 0x28,
+ };
+
+
+ /*
+ * Test vector generation:
+ * openssl enc -des-ede3-cbc -K fed4d7c98a136aa85ab819b8cf3c5fe0a2f77b6543c0c4e1
+ * -iv 5c475e570c46cb47 -in blah | recode ../x1 | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[]
+ DES_Plaintext1_Encrypted_With_DES_168_KEY_And_DESEDE_CBC_PKCS5PADDING_With_DES_IV1 =
+ new byte[] {
+ (byte) 0xC9, (byte) 0xF1, (byte) 0x83, (byte) 0x1F, (byte) 0x24, (byte) 0x83,
+ (byte) 0x2C, (byte) 0x7B, (byte) 0x66, (byte) 0x66, (byte) 0x99, (byte) 0x98,
+ (byte) 0x27, (byte) 0xB0, (byte) 0xED, (byte) 0x47
+ };
+
+ /*
* Test vector creation:
* echo -n 'Hello, world!' | recode ../x1 | sed 's/0x/(byte) 0x/g'
*/
@@ -2932,6 +3008,8 @@
public final byte[] key;
+ public final String keyAlgorithm;
+
public final byte[] iv;
public final byte[] aad;
@@ -2942,9 +3020,10 @@
public final byte[] plaintextPadded;
- public CipherTestParam(String transformation, byte[] key, byte[] iv, byte[] aad,
- byte[] plaintext, byte[] plaintextPadded, byte[] ciphertext) {
+ public CipherTestParam(String transformation, String keyAlgorithm, byte[] key, byte[] iv,
+ byte[] aad, byte[] plaintext, byte[] plaintextPadded, byte[] ciphertext) {
this.transformation = transformation.toUpperCase(Locale.ROOT);
+ this.keyAlgorithm = keyAlgorithm;
this.key = key;
this.iv = iv;
this.aad = aad;
@@ -2954,22 +3033,47 @@
}
}
+ private static List<CipherTestParam> DES_CIPHER_TEST_PARAMS = new ArrayList<CipherTestParam>();
+ static {
+ DES_CIPHER_TEST_PARAMS.add(new CipherTestParam(
+ "DESede/CBC/PKCS5Padding",
+ "DESede",
+ DES_112_KEY,
+ DES_IV1,
+ null,
+ DES_Plaintext1,
+ DES_Plaintext1_PKCS5_Padded,
+ DES_Plaintext1_Encrypted_With_DES_112_KEY_And_DESEDE_CBC_PKCS5PADDING_With_DES_IV1
+ ));
+ DES_CIPHER_TEST_PARAMS.add(new CipherTestParam(
+ "DESede/CBC/PKCS5Padding",
+ "DESede",
+ DES_168_KEY,
+ DES_IV1,
+ null,
+ DES_Plaintext1,
+ DES_Plaintext1_PKCS5_Padded,
+ DES_Plaintext1_Encrypted_With_DES_168_KEY_And_DESEDE_CBC_PKCS5PADDING_With_DES_IV1
+ ));
+ }
+
private static List<CipherTestParam> CIPHER_TEST_PARAMS = new ArrayList<CipherTestParam>();
static {
- CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/ECB/PKCS5Padding", AES_128_KEY,
+ CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/ECB/PKCS5Padding", "AES", AES_128_KEY,
null,
null,
AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext,
AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded,
AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted));
// PKCS#5 is assumed to be equivalent to PKCS#7 -- same test vectors are thus used for both.
- CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/ECB/PKCS7Padding", AES_128_KEY,
+ CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/ECB/PKCS7Padding", "AES", AES_128_KEY,
null,
null,
AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext,
AES_128_ECB_PKCS5Padding_TestVector_1_Plaintext_Padded,
AES_128_ECB_PKCS5Padding_TestVector_1_Encrypted));
CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/GCM/NOPADDING",
+ "AES",
AES_128_GCM_TestVector_1_Key,
AES_128_GCM_TestVector_1_IV,
AES_128_GCM_TestVector_1_AAD,
@@ -2977,19 +3081,19 @@
AES_128_GCM_TestVector_1_Plaintext,
AES_128_GCM_TestVector_1_Encrypted));
if (IS_UNLIMITED) {
- CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CTR/NoPadding", AES_192_KEY,
+ CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CTR/NoPadding", "AES", AES_192_KEY,
AES_192_CTR_NoPadding_TestVector_1_IV,
null,
AES_192_CTR_NoPadding_TestVector_1_Plaintext,
AES_192_CTR_NoPadding_TestVector_1_Plaintext,
AES_192_CTR_NoPadding_TestVector_1_Ciphertext));
- CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CBC/PKCS5Padding", AES_256_KEY,
+ CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CBC/PKCS5Padding", "AES", AES_256_KEY,
AES_256_CBC_PKCS5Padding_TestVector_1_IV,
null,
AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext,
AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext_Padded,
AES_256_CBC_PKCS5Padding_TestVector_1_Ciphertext));
- CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CBC/PKCS7Padding", AES_256_KEY,
+ CIPHER_TEST_PARAMS.add(new CipherTestParam("AES/CBC/PKCS7Padding", "AES", AES_256_KEY,
AES_256_CBC_PKCS5Padding_TestVector_1_IV,
null,
AES_256_CBC_PKCS5Padding_TestVector_1_Plaintext,
@@ -3002,6 +3106,41 @@
for (String provider : AES_PROVIDERS) {
testCipher_Success(provider);
}
+
+ testCipher_Success_ForAllSupportingProviders_AtLeastOneProviderRequired(
+ DES_CIPHER_TEST_PARAMS);
+ }
+
+ /**
+ * For each test vector in the list, tests that the transformation is supported by at least one
+ * provider and that all implementations of the transformation pass the Known Answer Test (KAT)
+ * as well as other functional tests.
+ */
+ private void testCipher_Success_ForAllSupportingProviders_AtLeastOneProviderRequired(
+ List<CipherTestParam> testVectors) throws Exception {
+ ByteArrayOutputStream errBuffer = new ByteArrayOutputStream();
+ PrintStream out = new PrintStream(errBuffer);
+ for (CipherTestParam testVector : testVectors) {
+ Provider[] providers = Security.getProviders("Cipher." + testVector.transformation);
+ if ((providers == null) || (providers.length == 0)) {
+ out.append("No providers offer " + testVector.transformation + "\n");
+ continue;
+ }
+ for (Provider provider : providers) {
+ try {
+ checkCipher(testVector, provider.getName());
+ } catch (Throwable e) {
+ out.append("Error encountered checking " + testVector.transformation
+ + ", keySize=" + (testVector.key.length * 8) + " with provider "
+ + provider.getName() + "\n");
+ e.printStackTrace(out);
+ }
+ }
+ }
+ out.flush();
+ if (errBuffer.size() > 0) {
+ throw new Exception("Errors encountered:\n\n" + errBuffer.toString() + "\n\n");
+ }
}
private void testCipher_Success(String provider) throws Exception {
@@ -3025,7 +3164,7 @@
}
private void checkCipher(CipherTestParam p, String provider) throws Exception {
- SecretKey key = new SecretKeySpec(p.key, "AES");
+ SecretKey key = new SecretKeySpec(p.key, p.keyAlgorithm);
Cipher c = Cipher.getInstance(p.transformation, provider);
AlgorithmParameterSpec spec = null;
diff --git a/luni/src/test/java/libcore/javax/crypto/MacTest.java b/luni/src/test/java/libcore/javax/crypto/MacTest.java
index 314a564..b308f02 100644
--- a/luni/src/test/java/libcore/javax/crypto/MacTest.java
+++ b/luni/src/test/java/libcore/javax/crypto/MacTest.java
@@ -59,4 +59,14 @@
Security.removeProvider(mockProvider.getName());
}
}
+
+ /**
+ * Aliases used to be wrong due to a typo.
+ * http://b/31114355
+ */
+ public void testMac_correctAlias() throws Exception {
+ Provider androidOpenSSLProvider = Security.getProvider("AndroidOpenSSL");
+ assertEquals("HmacSHA224", androidOpenSSLProvider.get("Alg.Alias.Mac.1.2.840.113549.2.8"));
+ assertEquals("HmacSHA256", androidOpenSSLProvider.get("Alg.Alias.Mac.1.2.840.113549.2.9"));
+ }
}
diff --git a/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java b/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java
index 4f5d658..5471b1f 100644
--- a/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java
+++ b/luni/src/test/java/libcore/javax/security/auth/x500/X500PrincipalTest.java
@@ -120,6 +120,30 @@
expectExceptionInDNConstructor("l=\\g0");
}
+ public void testNegativeLen() {
+ try {
+ X500Principal p = new X500Principal(new byte[]{
+ 0x30, // DerValue.tag_Sequence read in DerValue#getSequence
+ 9, // Length of the vector. read in readVector.
+ // DerInputStream.getLength will just return this as 10 & 0x80 == 0
+ -1, // Tag of the first value in the sequencevalue. Convenient so that it
+ // doesn't hold DerIndefLenConverter.isEOC()
+ (byte) 0x80, // Encoding in indefinite form
+ -1, // Second tag to be read by DerIndefLenConverter
+ (byte) 0x84, // Second length byte to be read, 0x80 means long form, 4 bytes
+ (byte) 0xff, // Length to be read by DerIndefLenConverter, -6, will move the
+ // buffer position to the second tag
+ (byte) 0xff,
+ (byte) 0xff,
+ (byte) -6,
+ 0, // Needed as otherwise it's detected that there's nothing after
+ // the length
+ });
+ fail("expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
private void expectExceptionInDNConstructor(String dn) {
try {
X500Principal principal = new X500Principal(dn);
diff --git a/luni/src/test/java/libcore/net/MimeUtilsTest.java b/luni/src/test/java/libcore/net/MimeUtilsTest.java
index ff22632..8b34ea2 100644
--- a/luni/src/test/java/libcore/net/MimeUtilsTest.java
+++ b/luni/src/test/java/libcore/net/MimeUtilsTest.java
@@ -18,8 +18,6 @@
import junit.framework.TestCase;
-import libcore.net.MimeUtils;
-
public class MimeUtilsTest extends TestCase {
public void test_15715370() {
assertEquals("audio/flac", MimeUtils.guessMimeTypeFromExtension("flac"));
@@ -52,4 +50,11 @@
public void test_18390752() {
assertEquals("jpg", MimeUtils.guessExtensionFromMimeType("image/jpeg"));
}
+
+ public void test_30793548() {
+ assertEquals("video/3gpp", MimeUtils.guessMimeTypeFromExtension("3gpp"));
+ assertEquals("video/3gpp", MimeUtils.guessMimeTypeFromExtension("3gp"));
+ assertEquals("video/3gpp2", MimeUtils.guessMimeTypeFromExtension("3gpp2"));
+ assertEquals("video/3gpp2", MimeUtils.guessMimeTypeFromExtension("3g2"));
+ }
}
diff --git a/ojluni/src/main/java/java/net/Socket.java b/ojluni/src/main/java/java/net/Socket.java
index 2aa057f..96a7355 100755
--- a/ojluni/src/main/java/java/net/Socket.java
+++ b/ojluni/src/main/java/java/net/Socket.java
@@ -207,9 +207,7 @@
public Socket(String host, int port)
throws UnknownHostException, IOException
{
- this(host != null ? new InetSocketAddress(host, port) :
- new InetSocketAddress(InetAddress.getByName(null), port),
- (SocketAddress) null, true);
+ this(InetAddress.getAllByName(host), port, (SocketAddress) null, true);
}
/**
@@ -240,8 +238,7 @@
* @see SecurityManager#checkConnect
*/
public Socket(InetAddress address, int port) throws IOException {
- this(address != null ? new InetSocketAddress(address, port) : null,
- (SocketAddress) null, true);
+ this(nonNullAddress(address), port, (SocketAddress) null, true);
}
/**
@@ -279,8 +276,7 @@
*/
public Socket(String host, int port, InetAddress localAddr,
int localPort) throws IOException {
- this(host != null ? new InetSocketAddress(host, port) :
- new InetSocketAddress(InetAddress.getByName(null), port),
+ this(InetAddress.getAllByName(host), port,
new InetSocketAddress(localAddr, localPort), true);
}
@@ -318,7 +314,7 @@
*/
public Socket(InetAddress address, int port, InetAddress localAddr,
int localPort) throws IOException {
- this(address != null ? new InetSocketAddress(address, port) : null,
+ this(nonNullAddress(address), port,
new InetSocketAddress(localAddr, localPort), true);
}
@@ -364,9 +360,7 @@
*/
@Deprecated
public Socket(String host, int port, boolean stream) throws IOException {
- this(host != null ? new InetSocketAddress(host, port) :
- new InetSocketAddress(InetAddress.getByName(null), port),
- (SocketAddress) null, stream);
+ this(InetAddress.getAllByName(host), port, (SocketAddress) null, stream);
}
/**
@@ -407,32 +401,57 @@
*/
@Deprecated
public Socket(InetAddress host, int port, boolean stream) throws IOException {
- this(host != null ? new InetSocketAddress(host, port) : null,
- new InetSocketAddress(0), stream);
+ this(nonNullAddress(host), port, new InetSocketAddress(0), stream);
}
- private Socket(SocketAddress address, SocketAddress localAddr,
- boolean stream) throws IOException {
- setImpl();
-
+ private static InetAddress[] nonNullAddress(InetAddress address) {
// backward compatibility
if (address == null)
throw new NullPointerException();
- try {
- createImpl(stream);
- if (localAddr != null)
- bind(localAddr);
- if (address != null)
+ return new InetAddress[] { address };
+ }
+
+ // Android-changed: Socket ctor should try all addresses
+ // b/30007735
+ private Socket(InetAddress[] addresses, int port, SocketAddress localAddr,
+ boolean stream) throws IOException {
+ if (addresses == null || addresses.length == 0) {
+ throw new SocketException("Impossible: empty address list");
+ }
+
+ for (int i = 0; i < addresses.length; i++) {
+ setImpl();
+ try {
+ InetSocketAddress address = new InetSocketAddress(addresses[i], port);
+ createImpl(stream);
+ if (localAddr != null) {
+ bind(localAddr);
+ }
connect(address);
- } catch (IOException e) {
- // Do not call #close, classes that extend this class may do not expect a call
- // to #close coming from the superclass constructor.
- if (impl != null) {
- impl.close();
+ break;
+ } catch (IOException | IllegalArgumentException | SecurityException e) {
+ try {
+ // Android-changed:
+ // Do not call #close, classes that extend this class may do not expect a call
+ // to #close coming from the superclass constructor.
+ impl.close();
+ closed = true;
+ } catch (IOException ce) {
+ e.addSuppressed(ce);
+ }
+
+ // Only stop on the last address.
+ if (i == addresses.length - 1) {
+ throw e;
+ }
}
- closed = true;
- throw e;
+
+ // Discard the connection state and try again.
+ impl = null;
+ created = false;
+ bound = false;
+ closed = false;
}
}
diff --git a/ojluni/src/main/java/java/nio/charset/Charset.java b/ojluni/src/main/java/java/nio/charset/Charset.java
index f85b82b..17e1160 100755
--- a/ojluni/src/main/java/java/nio/charset/Charset.java
+++ b/ojluni/src/main/java/java/nio/charset/Charset.java
@@ -214,10 +214,7 @@
* input sequence are not omitted since the same code is used to represent
* <small>ZERO-WIDTH NON-BREAKING SPACE</small>.
*
- * <p> Every instance of the Java virtual machine has a default charset, which
- * may or may not be one of the standard charsets. The default charset is
- * determined during virtual-machine startup and typically depends upon the
- * locale and charset being used by the underlying operating system. </p>
+ * <p>Android note: The Android platform default is always UTF-8.
*
* <p>The {@link StandardCharsets} class defines constants for each of the
* standard charsets.
@@ -653,9 +650,7 @@
/**
* Returns the default charset of this Java virtual machine.
*
- * <p> The default charset is determined during virtual-machine startup and
- * typically depends upon the locale and charset of the underlying
- * operating system.
+ * <p>Android note: The Android platform default is always UTF-8.
*
* @return A charset object for the default charset
*
diff --git a/ojluni/src/main/java/java/util/Vector.java b/ojluni/src/main/java/java/util/Vector.java
index b2bc200..0f2dedd 100755
--- a/ojluni/src/main/java/java/util/Vector.java
+++ b/ojluni/src/main/java/java/util/Vector.java
@@ -1238,6 +1238,7 @@
checkForComodification();
Vector.this.add(i, e);
expectedModCount = modCount;
+ limit++;
}
cursor = i + 1;
lastRet = -1;
diff --git a/ojluni/src/main/java/java/util/zip/ZipFile.java b/ojluni/src/main/java/java/util/zip/ZipFile.java
index e6624af..9ff661a 100755
--- a/ojluni/src/main/java/java/util/zip/ZipFile.java
+++ b/ojluni/src/main/java/java/util/zip/ZipFile.java
@@ -771,6 +771,14 @@
return locsig;
}
+ /** @hide */
+ // @VisibleForTesting
+ public int getFileDescriptor() {
+ return getFileDescriptor(jzfile);
+ }
+
+ private static native int getFileDescriptor(long jzfile);
+
private static native long open(String name, int mode, long lastModified,
boolean usemmap) throws IOException;
private static native int getTotal(long jzfile);
diff --git a/ojluni/src/main/java/sun/security/util/DerIndefLenConverter.java b/ojluni/src/main/java/sun/security/util/DerIndefLenConverter.java
index 78d9e30..cbd5ecc 100755
--- a/ojluni/src/main/java/sun/security/util/DerIndefLenConverter.java
+++ b/ojluni/src/main/java/sun/security/util/DerIndefLenConverter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2012, 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
@@ -156,12 +156,18 @@
}
if (isLongForm(lenByte)) {
lenByte &= LEN_MASK;
- if (lenByte > 4)
+ if (lenByte > 4) {
throw new IOException("Too much data");
- if ((dataSize - dataPos) < (lenByte + 1))
+ }
+ if ((dataSize - dataPos) < (lenByte + 1)) {
throw new IOException("Too little data");
- for (int i = 0; i < lenByte; i++)
+ }
+ for (int i = 0; i < lenByte; i++) {
curLen = (curLen << 8) + (data[dataPos++] & 0xff);
+ }
+ if (curLen < 0) {
+ throw new IOException("Invalid length bytes");
+ }
} else {
curLen = (lenByte & LEN_MASK);
}
@@ -188,10 +194,15 @@
}
if (isLongForm(lenByte)) {
lenByte &= LEN_MASK;
- for (int i = 0; i < lenByte; i++)
+ for (int i = 0; i < lenByte; i++) {
curLen = (curLen << 8) + (data[dataPos++] & 0xff);
- } else
+ }
+ if (curLen < 0) {
+ throw new IOException("Invalid length bytes");
+ }
+ } else {
curLen = (lenByte & LEN_MASK);
+ }
writeLength(curLen);
writeValue(curLen);
}
diff --git a/ojluni/src/main/java/sun/security/util/DerInputStream.java b/ojluni/src/main/java/sun/security/util/DerInputStream.java
index 8f51439..dae8afd 100755
--- a/ojluni/src/main/java/sun/security/util/DerInputStream.java
+++ b/ojluni/src/main/java/sun/security/util/DerInputStream.java
@@ -604,6 +604,10 @@
value <<= 8;
value += 0x0ff & in.read();
}
+ if (value < 0) {
+ throw new IOException("DerInputStream.getLength(): "
+ + "Invalid length bytes");
+ }
}
return value;
}
diff --git a/ojluni/src/main/native/java_util_zip_ZipFile.c b/ojluni/src/main/native/java_util_zip_ZipFile.c
index 7280d97..5353ff4 100644
--- a/ojluni/src/main/native/java_util_zip_ZipFile.c
+++ b/ojluni/src/main/native/java_util_zip_ZipFile.c
@@ -156,6 +156,12 @@
ZIP_Close(jlong_to_ptr(zfile));
}
+JNIEXPORT jint JNICALL
+ZipFile_getFileDescriptor(JNIEnv *env, jclass cls, jlong zfile) {
+ jzfile *zip = jlong_to_ptr(zfile);
+ return zip->zfd;
+}
+
JNIEXPORT jlong JNICALL
ZipFile_getEntry(JNIEnv *env, jclass cls, jlong zfile,
jbyteArray name, jboolean addSlash)
@@ -390,6 +396,7 @@
}
static JNINativeMethod gMethods[] = {
+ NATIVE_METHOD(ZipFile, getFileDescriptor, "(J)I"),
NATIVE_METHOD(ZipFile, getEntry, "(J[BZ)J"),
NATIVE_METHOD(ZipFile, freeEntry, "(JJ)V"),
NATIVE_METHOD(ZipFile, getNextEntry, "(JI)J"),
diff --git a/ojluni/src/main/native/zip_util.c b/ojluni/src/main/native/zip_util.c
index 66ce7e5..5a2a0b8 100644
--- a/ojluni/src/main/native/zip_util.c
+++ b/ojluni/src/main/native/zip_util.c
@@ -47,8 +47,6 @@
#include "zip_util.h"
#include <zlib.h>
-#include "cutils/log.h"
-
#ifdef _ALLBSD_SOURCE
#define off64_t off_t
#define mmap64 mmap
@@ -144,7 +142,7 @@
}
static int
-ZFILE_read(ZFILE zfd, char *buf, jint nbytes) {
+ZFILE_read(ZFILE zfd, char *buf, jint nbytes, jlong offset) {
#ifdef WIN32
return (int) IO_Read(zfd, buf, nbytes);
#else
@@ -156,7 +154,7 @@
* JVM_IO_INTR is tricky and could cause undesired side effect. So we decided
* to simply call "read" on Solaris/Linux. See details in bug 6304463.
*/
- return read(zfd, buf, nbytes);
+ return pread(zfd, buf, nbytes, offset);
#endif
}
@@ -185,11 +183,11 @@
}
/*
- * Reads len bytes of data into buf.
+ * Reads len bytes of data from the specified offset into buf.
* Returns 0 if all bytes could be read, otherwise returns -1.
*/
static int
-readFully(ZFILE zfd, void *buf, jlong len) {
+readFullyAt(ZFILE zfd, void *buf, jlong len, jlong offset) {
char *bp = (char *) buf;
while (len > 0) {
@@ -197,9 +195,10 @@
jint count = (len < limit) ?
(jint) len :
(jint) limit;
- jint n = ZFILE_read(zfd, bp, count);
+ jint n = ZFILE_read(zfd, bp, count, offset);
if (n > 0) {
bp += n;
+ offset += n;
len -= n;
} else if (n == JVM_IO_ERR && errno == EINTR) {
/* Retry after EINTR (interrupted by signal).
@@ -212,19 +211,6 @@
return 0;
}
-/*
- * Reads len bytes of data from the specified offset into buf.
- * Returns 0 if all bytes could be read, otherwise returns -1.
- */
-static int
-readFullyAt(ZFILE zfd, void *buf, jlong len, jlong offset)
-{
- if (IO_Lseek(zfd, offset, SEEK_SET) == -1) {
- return -1; /* lseek failure. */
- }
-
- return readFully(zfd, buf, len);
-}
/*
* Allocates a new zip file object for the specified file name.
@@ -879,14 +865,17 @@
return NULL;
}
- // Assumption, zfd refers to start of file. Trivially, reuse errbuf.
- if (readFully(zfd, errbuf, 4) != -1) { // errors will be handled later
+ // Trivially, reuse errbuf.
+ if (readFullyAt(zfd, errbuf, 4, 0 /* offset */) != -1) { // errors will be handled later
if (GETSIG(errbuf) == LOCSIG)
zip->locsig = JNI_TRUE;
else
zip->locsig = JNI_FALSE;
}
+ // This lseek is safe because it happens during construction of the ZipFile
+ // object. We must take care not to perform any operations that change the
+ // offset after (see b/30407219).
len = zip->len = IO_Lseek(zfd, 0, SEEK_END);
if (len <= 0) {
if (len == 0) { /* zip file is empty */
@@ -986,7 +975,7 @@
censize = CENSIZE(cen);
if (censize <= bufsize) return cen;
if ((cen = realloc(cen, censize)) == NULL) goto Catch;
- if (readFully(zfd, cen+bufsize, censize-bufsize) == -1) goto Catch;
+ if (readFullyAt(zfd, cen+bufsize, censize-bufsize, cenpos + bufsize) == -1) goto Catch;
return cen;
Catch:
@@ -1287,40 +1276,6 @@
MUNLOCK(zip->lock);
}
-// Temporary debugging information for b/30529561. If we encounter
-// a zip file with a bad LOC header, we log :
-//
-// - the file data at the offset.
-// - the offset itself.
-// - the first PATH_MAX bytes of the path associated with the fd.
-//
-// TODO(narayan): Remove this once b/30529561 is resolved.
-void ZIP_PrintDebugInfo(jzfile *zip, jzentry *entry,
- const unsigned char *loc)
-{
- ALOGE("b/30529561: Unexpected LOC header: %x", *((unsigned int*) loc));
- ALOGE("b/30529561: Entry offset: %zd", (size_t) entry->pos);
-
- char path[64];
- snprintf(path, sizeof(path), "/proc/self/fd/%d", zip->zfd);
-
- char buf[PATH_MAX];
- ssize_t count = readlink(path, buf, PATH_MAX);
-
- if (count == -1) {
- ALOGE("b/30529561: readlink failed for %s (%s)", path, strerror(errno));
- return;
- } else if (count == PATH_MAX) {
- // Truncate the name, we're just using this for debugging purposes
- // anyway.
- --count;
- }
-
- buf[count] = '\0';
-
- ALOGE("b/30529561: Reading zip file %s", buf);
-}
-
/*
* Returns the offset of the entry data within the zip file.
* Returns -1 if an error occurred, in which case zip->msg will
@@ -1345,10 +1300,6 @@
return -1;
}
if (GETSIG(loc) != LOCSIG) {
- // TODO(narayan): Remove this debug message once b/30529561 is
- // sorted out satisfactorily.
- ZIP_PrintDebugInfo(zip, entry, loc);
-
zip->msg = "invalid LOC header (bad signature)";
return -1;
}