am 46fc50e8: Merge "Support wrapping app processes to inject debug instrumentation. Bug: 4437846"
* commit '46fc50e810eaab93f2787b355257ab1e837d8bf7':
Support wrapping app processes to inject debug instrumentation. Bug: 4437846
diff --git a/Android.mk b/Android.mk
index 4384c3e..69e46ec 100644
--- a/Android.mk
+++ b/Android.mk
@@ -54,5 +54,18 @@
ifeq ($(WITH_HOST_DALVIK),true)
.PHONY: dalvik-host
- dalvik-host: dalvik $(HOST_OUT)/bin/dalvikvm $(HOST_OUT)/bin/dexopt $(HOST_OUT)/lib/libsqlite.so cacerts-host $(HOST_OUT)/usr/share/zoneinfo/zoneinfo.dat $(HOST_OUT)/usr/share/zoneinfo/zoneinfo.idx $(HOST_OUT)/usr/share/zoneinfo/zoneinfo.version core-hostdex bouncycastle-hostdex apache-xml-hostdex $(call intermediates-dir-for,JAVA_LIBRARIES,core-tests,,COMMON)/classes.jar
+ dalvik-host: \
+ dalvik \
+ $(HOST_OUT)/bin/dalvikvm \
+ $(HOST_OUT)/bin/dexopt \
+ $(HOST_OUT)/lib/libsqlite.so \
+ cacerts-host \
+ $(HOST_OUT)/usr/share/zoneinfo/zoneinfo.dat \
+ $(HOST_OUT)/usr/share/zoneinfo/zoneinfo.idx \
+ $(HOST_OUT)/usr/share/zoneinfo/zoneinfo.version \
+ core-hostdex \
+ bouncycastle-hostdex \
+ apache-xml-hostdex \
+ apache-harmony-tests-hostdex \
+ $(call intermediates-dir-for,JAVA_LIBRARIES,core-tests,,COMMON)/classes.jar
endif
diff --git a/dalvik/src/main/java/dalvik/system/BlockGuard.java b/dalvik/src/main/java/dalvik/system/BlockGuard.java
index a0d9828..cf9789e 100644
--- a/dalvik/src/main/java/dalvik/system/BlockGuard.java
+++ b/dalvik/src/main/java/dalvik/system/BlockGuard.java
@@ -25,12 +25,10 @@
import java.net.InetAddress;
import java.net.SocketException;
import java.net.SocketImpl;
-
import libcore.io.ErrnoException;
import libcore.io.Libcore;
import libcore.io.StructLinger;
-import org.apache.harmony.luni.platform.INetworkSystem;
-
+import libcore.util.EmptyArray;
import static libcore.io.OsConstants.*;
/**
@@ -219,8 +217,8 @@
throws IOException {
if (!TAG_SOCKETS) return;
- final byte[] tagBytes = tag != null ? tag.getBytes() : new byte[0];
- final byte[] uidBytes = uid != -1 ? Integer.toString(uid).getBytes() : new byte[0];
+ final byte[] tagBytes = tag != null ? tag.getBytes() : EmptyArray.BYTE;
+ final byte[] uidBytes = uid != -1 ? Integer.toString(uid).getBytes() : EmptyArray.BYTE;
final ByteArrayOutputStream buffer = new ByteArrayOutputStream(
4 + tagBytes.length + uidBytes.length);
@@ -244,125 +242,4 @@
}
private BlockGuard() {}
-
- /**
- * A network wrapper that calls the policy check functions.
- */
- public static class WrappedNetworkSystem implements INetworkSystem {
- private final INetworkSystem mNetwork;
-
- public WrappedNetworkSystem(INetworkSystem network) {
- mNetwork = network;
- }
-
- public void accept(FileDescriptor serverFd, SocketImpl newSocket,
- FileDescriptor clientFd) throws IOException {
- BlockGuard.getThreadPolicy().onNetwork();
- mNetwork.accept(serverFd, newSocket, clientFd);
- tagSocketFd(clientFd);
- }
-
- public void bind(FileDescriptor aFD, InetAddress inetAddress, int port)
- throws SocketException {
- mNetwork.bind(aFD, inetAddress, port);
- }
-
- public int read(FileDescriptor aFD, byte[] data, int offset, int count) throws IOException {
- BlockGuard.getThreadPolicy().onNetwork();
- return mNetwork.read(aFD, data, offset, count);
- }
-
- public int readDirect(FileDescriptor aFD, int address, int count) throws IOException {
- BlockGuard.getThreadPolicy().onNetwork();
- return mNetwork.readDirect(aFD, address, count);
- }
-
- public int write(FileDescriptor fd, byte[] data, int offset, int count)
- throws IOException {
- BlockGuard.getThreadPolicy().onNetwork();
- return mNetwork.write(fd, data, offset, count);
- }
-
- public int writeDirect(FileDescriptor fd, int address, int offset, int count)
- throws IOException {
- BlockGuard.getThreadPolicy().onNetwork();
- return mNetwork.writeDirect(fd, address, offset, count);
- }
-
- public boolean connect(FileDescriptor fd, InetAddress inetAddress, int port) throws IOException {
- BlockGuard.getThreadPolicy().onNetwork();
- return mNetwork.connect(fd, inetAddress, port);
- }
-
- public boolean isConnected(FileDescriptor fd, int timeout) throws IOException {
- if (timeout != 0) {
- // Greater than 0 is a timeout, but zero means "poll and return immediately".
- BlockGuard.getThreadPolicy().onNetwork();
- }
- return mNetwork.isConnected(fd, timeout);
- }
-
- public int send(FileDescriptor fd, byte[] data, int offset, int length,
- int port, InetAddress inetAddress) throws IOException {
- // Note: no BlockGuard violation. We permit datagrams
- // without hostname lookups. (short, bounded amount of time)
- return mNetwork.send(fd, data, offset, length, port, inetAddress);
- }
-
- public int sendDirect(FileDescriptor fd, int address, int offset, int length,
- int port, InetAddress inetAddress) throws IOException {
- // Note: no BlockGuard violation. We permit datagrams
- // without hostname lookups. (short, bounded amount of time)
- return mNetwork.sendDirect(fd, address, offset, length, port, inetAddress);
- }
-
- public int recv(FileDescriptor fd, DatagramPacket packet, byte[] data, int offset,
- int length, boolean peek, boolean connected) throws IOException {
- BlockGuard.getThreadPolicy().onNetwork();
- return mNetwork.recv(fd, packet, data, offset, length, peek, connected);
- }
-
- public int recvDirect(FileDescriptor fd, DatagramPacket packet, int address, int offset,
- int length, boolean peek, boolean connected) throws IOException {
- BlockGuard.getThreadPolicy().onNetwork();
- return mNetwork.recvDirect(fd, packet, address, offset, length, peek, connected);
- }
-
- public void disconnectDatagram(FileDescriptor aFD) throws SocketException {
- mNetwork.disconnectDatagram(aFD);
- }
-
- public void sendUrgentData(FileDescriptor fd, byte value) {
- mNetwork.sendUrgentData(fd, value);
- }
-
- public boolean select(FileDescriptor[] readFDs, FileDescriptor[] writeFDs,
- int numReadable, int numWritable, long timeout, int[] flags)
- throws SocketException {
- BlockGuard.getThreadPolicy().onNetwork();
- return mNetwork.select(readFDs, writeFDs, numReadable, numWritable, timeout, flags);
- }
-
- public void close(FileDescriptor aFD) throws IOException {
- // We exclude sockets without SO_LINGER so that apps can close their network connections
- // in methods like onDestroy, which will run on the UI thread, without jumping through
- // extra hoops.
- if (isLingerSocket(aFD)) {
- BlockGuard.getThreadPolicy().onNetwork();
- }
- mNetwork.close(aFD);
- }
-
- private boolean isLingerSocket(FileDescriptor fd) {
- try {
- StructLinger linger = Libcore.os.getsockoptLinger(fd, SOL_SOCKET, SO_LINGER);
- return linger.isOn() && linger.l_linger > 0;
- } catch (Exception ignored) {
- // We're called via Socket.close (which doesn't ask for us to be called), so we
- // must not throw here, because Socket.close must not throw if asked to close an
- // already-closed socket.
- return false;
- }
- }
- }
}
diff --git a/expectations/icebox.txt b/expectations/icebox.txt
index 4d664a6..d85efc1 100644
--- a/expectations/icebox.txt
+++ b/expectations/icebox.txt
@@ -42,13 +42,13 @@
description: "Dalvik doesn't support XML Schemas, DTDs or validation",
bug: 3268630,
name: "libcore.xml.DeclarationTest#testGetXmlEncoding",
- substring: "This implementation doesn't parse the encoding from the XML declaration expected:<ISO-8859-1> but was:<null>"
+ substring: "This implementation doesn't parse the encoding from the XML declaration"
},
{
description: "Dalvik doesn't support XML Schemas, DTDs or validation",
bug: 3268630,
name: "libcore.xml.DeclarationTest#testGetXmlStandalone",
- substring: "This implementation doesn't parse standalone from the XML declaration expected:<true> but was:<false>"
+ substring: "This implementation doesn't parse standalone from the XML declaration"
},
{
description: "Dalvik doesn't support XML Schemas, DTDs or validation",
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index 3d2eb5f..f784394 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -11,6 +11,11 @@
bug: 3483365
},
{
+ description: "Expat uses an unbounded number of global references",
+ name: "libcore.xml.ExpatSaxParserTest#testGlobalReferenceTableOverflow",
+ bug: 2772628
+},
+{
description: "Test fails, Intermediate certificate lacks BasicConstraints",
name: "com.android.org.bouncycastle.jce.provider.PKIXCertPathValidatorSpiTest#testTrustAndRemoteCertificatesWithDifferentEncodings",
bug: 3474648
diff --git a/junit/src/main/java/junit/framework/ComparisonFailure.java b/junit/src/main/java/junit/framework/ComparisonFailure.java
index 0cb2cee..ccd476b 100644
--- a/junit/src/main/java/junit/framework/ComparisonFailure.java
+++ b/junit/src/main/java/junit/framework/ComparisonFailure.java
@@ -34,7 +34,7 @@
int end= Math.min(fExpected.length(), fActual.length());
int i= 0;
- for(; i < end; i++) {
+ for (; i < end; i++) {
if (fExpected.charAt(i) != fActual.charAt(i))
break;
}
diff --git a/luni/src/main/java/java/io/FileInputStream.java b/luni/src/main/java/java/io/FileInputStream.java
index 5b26792..6c2da1e 100644
--- a/luni/src/main/java/java/io/FileInputStream.java
+++ b/luni/src/main/java/java/io/FileInputStream.java
@@ -23,6 +23,7 @@
import java.nio.channels.FileChannel;
import java.util.Arrays;
import libcore.io.ErrnoException;
+import libcore.io.IoBridge;
import libcore.io.IoUtils;
import libcore.io.Libcore;
import libcore.io.Streams;
@@ -76,7 +77,7 @@
if (file == null) {
throw new NullPointerException("file == null");
}
- this.fd = IoUtils.open(file.getAbsolutePath(), O_RDONLY);
+ this.fd = IoBridge.open(file.getAbsolutePath(), O_RDONLY);
this.ownedFd = fd;
guard.open("close");
}
@@ -108,7 +109,7 @@
@Override
public int available() throws IOException {
- return IoUtils.available(fd);
+ return IoBridge.available(fd);
}
@Override
@@ -171,7 +172,7 @@
}
@Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
- return IoUtils.read(fd, buffer, byteOffset, byteCount);
+ return IoBridge.read(fd, buffer, byteOffset, byteCount);
}
@Override
diff --git a/luni/src/main/java/java/io/FileOutputStream.java b/luni/src/main/java/java/io/FileOutputStream.java
index 8c61281..cc57571 100644
--- a/luni/src/main/java/java/io/FileOutputStream.java
+++ b/luni/src/main/java/java/io/FileOutputStream.java
@@ -21,6 +21,7 @@
import java.nio.NioUtils;
import java.nio.channels.FileChannel;
import java.util.Arrays;
+import libcore.io.IoBridge;
import libcore.io.IoUtils;
import static libcore.io.OsConstants.*;
@@ -88,7 +89,7 @@
throw new NullPointerException("file == null");
}
this.mode = O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC);
- this.fd = IoUtils.open(file.getAbsolutePath(), mode);
+ this.fd = IoBridge.open(file.getAbsolutePath(), mode);
this.ownedFd = fd;
this.guard.open("close");
}
@@ -175,7 +176,7 @@
@Override
public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
- IoUtils.write(fd, buffer, byteOffset, byteCount);
+ IoBridge.write(fd, buffer, byteOffset, byteCount);
}
@Override
diff --git a/luni/src/main/java/java/io/InterruptedIOException.java b/luni/src/main/java/java/io/InterruptedIOException.java
index 8d79ba5..af307bd 100644
--- a/luni/src/main/java/java/io/InterruptedIOException.java
+++ b/luni/src/main/java/java/io/InterruptedIOException.java
@@ -32,20 +32,23 @@
public int bytesTransferred;
/**
- * Constructs a new {@code InterruptedIOException} with its stack trace
- * filled in.
+ * Constructs a new instance.
*/
public InterruptedIOException() {
}
/**
- * Constructs a new {@code InterruptedIOException} with its stack trace and
- * detail message filled in.
- *
- * @param detailMessage
- * the detail message for this exception.
+ * Constructs a new instance with the given detail message.
*/
public InterruptedIOException(String detailMessage) {
super(detailMessage);
}
+
+ /**
+ * Constructs a new instance with given detail message and cause.
+ * @hide internal use only
+ */
+ public InterruptedIOException(String detailMessage, Throwable cause) {
+ super(detailMessage, cause);
+ }
}
diff --git a/luni/src/main/java/java/io/ObjectStreamClass.java b/luni/src/main/java/java/io/ObjectStreamClass.java
index 386cfb0..e87fcd4 100644
--- a/luni/src/main/java/java/io/ObjectStreamClass.java
+++ b/luni/src/main/java/java/io/ObjectStreamClass.java
@@ -810,7 +810,7 @@
return loadFields == null ? fields().clone() : loadFields.clone();
}
- private volatile List<ObjectStreamClass> cachedHierarchy;
+ private transient volatile List<ObjectStreamClass> cachedHierarchy;
List<ObjectStreamClass> getHierarchy() {
List<ObjectStreamClass> result = cachedHierarchy;
diff --git a/luni/src/main/java/java/io/RandomAccessFile.java b/luni/src/main/java/java/io/RandomAccessFile.java
index 8517604..dde779e 100644
--- a/luni/src/main/java/java/io/RandomAccessFile.java
+++ b/luni/src/main/java/java/io/RandomAccessFile.java
@@ -24,6 +24,7 @@
import java.nio.charset.ModifiedUtf8;
import java.util.Arrays;
import libcore.io.ErrnoException;
+import libcore.io.IoBridge;
import libcore.io.IoUtils;
import libcore.io.Libcore;
import libcore.io.Memory;
@@ -114,7 +115,7 @@
throw new IllegalArgumentException("Invalid mode: " + mode);
}
this.mode = flags;
- this.fd = IoUtils.open(file.getAbsolutePath(), flags);
+ this.fd = IoBridge.open(file.getAbsolutePath(), flags);
// if we are in "rws" mode, attempt to sync file+metadata
if (syncMetadata) {
@@ -286,7 +287,7 @@
* if this file is closed or another I/O error occurs.
*/
public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
- return IoUtils.read(fd, buffer, byteOffset, byteCount);
+ return IoBridge.read(fd, buffer, byteOffset, byteCount);
}
/**
@@ -688,7 +689,7 @@
* if an I/O error occurs while writing to this file.
*/
public void write(byte[] buffer, int byteOffset, int byteCount) throws IOException {
- IoUtils.write(fd, buffer, byteOffset, byteCount);
+ IoBridge.write(fd, buffer, byteOffset, byteCount);
// if we are in "rws" mode, attempt to sync file+metadata
if (syncMetadata) {
fd.sync();
diff --git a/luni/src/main/java/java/lang/Boolean.java b/luni/src/main/java/java/lang/Boolean.java
index f95a38b..2c8ffc8 100644
--- a/luni/src/main/java/java/lang/Boolean.java
+++ b/luni/src/main/java/java/lang/Boolean.java
@@ -101,9 +101,9 @@
* {@code Boolean}; {@code false} otherwise.
*/
@Override
+ @FindBugsSuppressWarnings("RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN")
public boolean equals(Object o) {
- return (o == this)
- || ((o instanceof Boolean) && (value == ((Boolean) o).value));
+ return (o == this) || ((o instanceof Boolean) && (((Boolean) o).value == value));
}
/**
diff --git a/luni/src/main/java/java/lang/Byte.java b/luni/src/main/java/java/lang/Byte.java
index 7e0b293..bd1a1e4 100644
--- a/luni/src/main/java/java/lang/Byte.java
+++ b/luni/src/main/java/java/lang/Byte.java
@@ -22,6 +22,7 @@
*
* @since 1.1
*/
+@FindBugsSuppressWarnings("DM_NUMBER_CTOR")
public final class Byte extends Number implements Comparable<Byte> {
private static final long serialVersionUID = -7183698231559129828L;
@@ -155,9 +156,9 @@
* {@code Byte}; {@code false} otherwise.
*/
@Override
+ @FindBugsSuppressWarnings("RC_REF_COMPARISON")
public boolean equals(Object object) {
- return (object == this) || (object instanceof Byte)
- && (value == ((Byte) object).value);
+ return (object == this) || ((object instanceof Byte) && (((Byte) object).value == value));
}
@Override
diff --git a/luni/src/main/java/java/lang/Character.java b/luni/src/main/java/java/lang/Character.java
index 1e39428..67c5c71 100644
--- a/luni/src/main/java/java/lang/Character.java
+++ b/luni/src/main/java/java/lang/Character.java
@@ -96,6 +96,7 @@
*
* @since 1.0
*/
+@FindBugsSuppressWarnings("DM_NUMBER_CTOR")
public final class Character implements Serializable, Comparable<Character> {
private static final long serialVersionUID = 3786198910865385080L;
@@ -1641,7 +1642,7 @@
private static final Character[] SMALL_VALUES = new Character[128];
static {
- for(int i = 0; i < 128; i++) {
+ for (int i = 0; i < 128; i++) {
SMALL_VALUES[i] = new Character((char) i);
}
}
@@ -2405,7 +2406,7 @@
*/
@Override
public boolean equals(Object object) {
- return (object instanceof Character) && (value == ((Character) object).value);
+ return (object instanceof Character) && (((Character) object).value == value);
}
/**
diff --git a/luni/src/main/java/java/lang/ClassLoader.java b/luni/src/main/java/java/lang/ClassLoader.java
index 2ec066f..0cdc448 100644
--- a/luni/src/main/java/java/lang/ClassLoader.java
+++ b/luni/src/main/java/java/lang/ClassLoader.java
@@ -747,6 +747,7 @@
private static BootClassLoader instance;
+ @FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED")
public static synchronized BootClassLoader getInstance() {
if (instance == null) {
instance = new BootClassLoader();
diff --git a/luni/src/main/java/java/lang/Double.java b/luni/src/main/java/java/lang/Double.java
index 1748128..456529b 100644
--- a/luni/src/main/java/java/lang/Double.java
+++ b/luni/src/main/java/java/lang/Double.java
@@ -166,31 +166,18 @@
}
/**
- * Converts the specified double value to a binary representation conforming
- * to the IEEE 754 floating-point double precision bit layout. All
- * <em>Not-a-Number (NaN)</em> values are converted to a single NaN
- * representation ({@code 0x7ff8000000000000L}).
- *
- * @param value
- * the double value to convert.
- * @return the IEEE 754 floating-point double precision representation of
- * {@code value}.
- * @see #doubleToRawLongBits(double)
- * @see #longBitsToDouble(long)
+ * Returns an integer corresponding to the bits of the given
+ * <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> double precision
+ * {@code value}. All <em>Not-a-Number (NaN)</em> values are converted to a single NaN
+ * representation ({@code 0x7ff8000000000000L}) (compare to {@link #doubleToRawLongBits}).
*/
public static native long doubleToLongBits(double value);
/**
- * Converts the specified double value to a binary representation conforming
- * to the IEEE 754 floating-point double precision bit layout.
- * <em>Not-a-Number (NaN)</em> values are preserved.
- *
- * @param value
- * the double value to convert.
- * @return the IEEE 754 floating-point double precision representation of
- * {@code value}.
- * @see #doubleToLongBits(double)
- * @see #longBitsToDouble(long)
+ * Returns an integer corresponding to the bits of the given
+ * <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> double precision
+ * {@code value}. <em>Not-a-Number (NaN)</em> values are preserved (compare
+ * to {@link #doubleToLongBits}).
*/
public static native long doubleToRawLongBits(double value);
@@ -219,9 +206,8 @@
*/
@Override
public boolean equals(Object object) {
- return (object == this)
- || (object instanceof Double)
- && (doubleToLongBits(this.value) == doubleToLongBits(((Double) object).value));
+ return (object instanceof Double) &&
+ (doubleToLongBits(this.value) == doubleToLongBits(((Double) object).value));
}
@Override
@@ -286,15 +272,8 @@
}
/**
- * Converts the specified IEEE 754 floating-point double precision bit
- * pattern to a Java double value.
- *
- * @param bits
- * the IEEE 754 floating-point double precision representation of
- * a double value.
- * @return the double value converted from {@code bits}.
- * @see #doubleToLongBits(double)
- * @see #doubleToRawLongBits(double)
+ * Returns the <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a>
+ * double precision float corresponding to the given {@code bits}.
*/
public static native double longBitsToDouble(long bits);
@@ -313,7 +292,7 @@
* if {@code string} cannot be parsed as a double value.
*/
public static double parseDouble(String string) throws NumberFormatException {
- return org.apache.harmony.luni.util.FloatingPointParser.parseDouble(string);
+ return StringToReal.parseDouble(string);
}
@Override
@@ -421,7 +400,7 @@
*/
public static String toHexString(double d) {
/*
- * Reference: http://en.wikipedia.org/wiki/IEEE_754
+ * Reference: http://en.wikipedia.org/wiki/IEEE_754-1985
*/
if (d != d) {
return "NaN";
diff --git a/luni/src/main/java/java/lang/FinalizerThread.java b/luni/src/main/java/java/lang/FinalizerThread.java
index 5f478df..978332c 100644
--- a/luni/src/main/java/java/lang/FinalizerThread.java
+++ b/luni/src/main/java/java/lang/FinalizerThread.java
@@ -67,6 +67,7 @@
}
}
+ @FindBugsSuppressWarnings("FI_EXPLICIT_INVOCATION")
private void doFinalize(FinalizerReference<Object> reference) {
FinalizerReference.remove(reference);
Object obj = reference.get();
diff --git a/luni/src/main/java/java/lang/FindBugsSuppressWarnings.java b/luni/src/main/java/java/lang/FindBugsSuppressWarnings.java
new file mode 100644
index 0000000..ac753bd
--- /dev/null
+++ b/luni/src/main/java/java/lang/FindBugsSuppressWarnings.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.lang;
+
+import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.TYPE;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+import java.lang.annotation.Target;
+
+/**
+ * Suppress FindBugs warnings on the annotated element. FindBugs will recognize
+ * any annotation that has class retention and whose name ends with
+ * "SuppressWarnings".
+ *
+ * @hide
+ */
+@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
+@Retention(CLASS)
+public @interface FindBugsSuppressWarnings {
+
+ /**
+ * The <a href="http://findbugs.sourceforge.net/bugDescriptions.html">FindBugs
+ * Patterns</a> to suppress, such as {@code SE_TRANSIENT_FIELD_NOT_RESTORED}
+ * or {@code Se}. Full, upper case names are preferred.
+ */
+ String[] value();
+}
diff --git a/luni/src/main/java/java/lang/Float.java b/luni/src/main/java/java/lang/Float.java
index bbbb7f7..900b2a0 100644
--- a/luni/src/main/java/java/lang/Float.java
+++ b/luni/src/main/java/java/lang/Float.java
@@ -189,37 +189,23 @@
*/
@Override
public boolean equals(Object object) {
- return (object == this)
- || (object instanceof Float)
- && (floatToIntBits(this.value) == floatToIntBits(((Float) object).value));
+ return (object instanceof Float) &&
+ (floatToIntBits(this.value) == floatToIntBits(((Float) object).value));
}
/**
- * Converts the specified float value to a binary representation conforming
- * to the IEEE 754 floating-point single precision bit layout. All
- * <em>Not-a-Number (NaN)</em> values are converted to a single NaN
- * representation ({@code 0x7fc00000}).
- *
- * @param value
- * the float value to convert.
- * @return the IEEE 754 floating-point single precision representation of
- * {@code value}.
- * @see #floatToRawIntBits(float)
- * @see #intBitsToFloat(int)
+ * Returns an integer corresponding to the bits of the given
+ * <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> single precision
+ * float {@code value}. All <em>Not-a-Number (NaN)</em> values are converted to a single NaN
+ * representation ({@code 0x7fc00000}) (compare to {@link #floatToRawIntBits}).
*/
public static native int floatToIntBits(float value);
/**
- * Converts the specified float value to a binary representation conforming
- * to the IEEE 754 floating-point single precision bit layout.
- * <em>Not-a-Number (NaN)</em> values are preserved.
- *
- * @param value
- * the float value to convert.
- * @return the IEEE 754 floating-point single precision representation of
- * {@code value}.
- * @see #floatToIntBits(float)
- * @see #intBitsToFloat(int)
+ * Returns an integer corresponding to the bits of the given
+ * <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> single precision
+ * float {@code value}. <em>Not-a-Number (NaN)</em> values are preserved (compare
+ * to {@link #floatToIntBits}).
*/
public static native int floatToRawIntBits(float value);
@@ -239,15 +225,8 @@
}
/**
- * Converts the specified IEEE 754 floating-point single precision bit
- * pattern to a Java float value.
- *
- * @param bits
- * the IEEE 754 floating-point single precision representation of
- * a float value.
- * @return the float value converted from {@code bits}.
- * @see #floatToIntBits(float)
- * @see #floatToRawIntBits(float)
+ * Returns the <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a>
+ * single precision float corresponding to the given {@code bits}.
*/
public static native float intBitsToFloat(int bits);
@@ -318,8 +297,7 @@
* @since 1.2
*/
public static float parseFloat(String string) throws NumberFormatException {
- return org.apache.harmony.luni.util.FloatingPointParser
- .parseFloat(string);
+ return StringToReal.parseFloat(string);
}
@Override
@@ -429,7 +407,7 @@
*/
public static String toHexString(float f) {
/*
- * Reference: http://en.wikipedia.org/wiki/IEEE_754
+ * Reference: http://en.wikipedia.org/wiki/IEEE_754-1985
*/
if (f != f) {
return "NaN";
diff --git a/luni/src/main/java/org/apache/harmony/luni/util/HexStringParser.java b/luni/src/main/java/java/lang/HexStringParser.java
similarity index 99%
rename from luni/src/main/java/org/apache/harmony/luni/util/HexStringParser.java
rename to luni/src/main/java/java/lang/HexStringParser.java
index 9e20a16..8593b99 100644
--- a/luni/src/main/java/org/apache/harmony/luni/util/HexStringParser.java
+++ b/luni/src/main/java/java/lang/HexStringParser.java
@@ -15,13 +15,17 @@
* limitations under the License.
*/
-package org.apache.harmony.luni.util;
+package java.lang;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/*
* Parses hex string to a single or double precision floating point number.
+ *
+ * TODO: rewrite this!
+ *
+ * @hide
*/
final class HexStringParser {
diff --git a/luni/src/main/java/java/lang/Integer.java b/luni/src/main/java/java/lang/Integer.java
index 06d7f90..15511a8 100644
--- a/luni/src/main/java/java/lang/Integer.java
+++ b/luni/src/main/java/java/lang/Integer.java
@@ -29,6 +29,7 @@
* @see java.lang.Long
* @since 1.0
*/
+@FindBugsSuppressWarnings("DM_NUMBER_CTOR")
public final class Integer extends Number implements Comparable<Integer> {
private static final long serialVersionUID = 1360826667806852920L;
@@ -205,7 +206,7 @@
*/
@Override
public boolean equals(Object o) {
- return o instanceof Integer && ((Integer) o).value == value;
+ return (o instanceof Integer) && (((Integer) o).value == value);
}
@Override
@@ -712,7 +713,7 @@
private static final Integer[] SMALL_VALUES = new Integer[256];
static {
- for(int i = -128; i < 128; i++) {
+ for (int i = -128; i < 128; i++) {
SMALL_VALUES[i + 128] = new Integer(i);
}
}
diff --git a/luni/src/main/java/java/lang/IntegralToString.java b/luni/src/main/java/java/lang/IntegralToString.java
index c858a37..03a5359 100644
--- a/luni/src/main/java/java/lang/IntegralToString.java
+++ b/luni/src/main/java/java/lang/IntegralToString.java
@@ -462,6 +462,17 @@
return new String(0, 2, buf);
}
+ public static String bytesToHexString(byte[] bytes, boolean upperCase) {
+ char[] digits = upperCase ? UPPER_CASE_DIGITS : DIGITS;
+ char[] buf = new char[bytes.length * 2];
+ int c = 0;
+ for (byte b : bytes) {
+ buf[c++] = digits[(b >> 4) & 0xf];
+ buf[c++] = digits[b & 0xf];
+ }
+ return new String(buf);
+ }
+
public static String intToHexString(int i, boolean upperCase, int minWidth) {
int bufLen = 8; // Max number of hex digits in an int
char[] buf = new char[bufLen];
diff --git a/luni/src/main/java/java/lang/Long.java b/luni/src/main/java/java/lang/Long.java
index d6605e6..8b592e2 100644
--- a/luni/src/main/java/java/lang/Long.java
+++ b/luni/src/main/java/java/lang/Long.java
@@ -29,6 +29,7 @@
* @see java.lang.Integer
* @since 1.0
*/
+@FindBugsSuppressWarnings("DM_NUMBER_CTOR")
public final class Long extends Number implements Comparable<Long> {
private static final long serialVersionUID = 4290774380558885855L;
@@ -193,7 +194,7 @@
*/
@Override
public boolean equals(Object o) {
- return o instanceof Long && ((Long) o).value == value;
+ return (o instanceof Long) && (((Long) o).value == value);
}
@Override
@@ -724,8 +725,7 @@
* @since 1.5
*/
public static Long valueOf(long v) {
- return v >= 128 || v < -128 ? new Long(v)
- : SMALL_VALUES[((int) v) + 128];
+ return v >= 128 || v < -128 ? new Long(v) : SMALL_VALUES[((int) v) + 128];
}
/**
@@ -734,7 +734,7 @@
private static final Long[] SMALL_VALUES = new Long[256];
static {
- for(int i = -128; i < 128; i++) {
+ for (int i = -128; i < 128; i++) {
SMALL_VALUES[i + 128] = new Long(i);
}
}
diff --git a/luni/src/main/java/java/lang/Object.java b/luni/src/main/java/java/lang/Object.java
index f6c01fe..7f4b490 100644
--- a/luni/src/main/java/java/lang/Object.java
+++ b/luni/src/main/java/java/lang/Object.java
@@ -219,6 +219,7 @@
*
* See <i>Effective Java</i> Item 7, "Avoid finalizers" for more.
*/
+ @FindBugsSuppressWarnings("FI_EMPTY")
protected void finalize() throws Throwable {
}
diff --git a/luni/src/main/java/java/lang/ProcessManager.java b/luni/src/main/java/java/lang/ProcessManager.java
index cdcbb8a..f5afedb 100644
--- a/luni/src/main/java/java/lang/ProcessManager.java
+++ b/luni/src/main/java/java/lang/ProcessManager.java
@@ -31,31 +31,13 @@
import libcore.io.ErrnoException;
import libcore.io.IoUtils;
import libcore.io.Libcore;
+import libcore.util.MutableInt;
import static libcore.io.OsConstants.*;
/**
* Manages child processes.
*/
final class ProcessManager {
-
- /**
- * constant communicated from native code indicating that a
- * child died, but it was unable to determine the status
- */
- private static final int WAIT_STATUS_UNKNOWN = -1;
-
- /**
- * constant communicated from native code indicating that there
- * are currently no children to wait for
- */
- private static final int WAIT_STATUS_NO_CHILDREN = -2;
-
- /**
- * constant communicated from native code indicating that a wait()
- * call returned -1 and set an undocumented (and hence unexpected) errno
- */
- private static final int WAIT_STATUS_STRANGE_ERRNO = -3;
-
/**
* Map from pid to Process. We keep weak references to the Process objects
* and clean up the entries when no more external references are left. The
@@ -71,13 +53,13 @@
private ProcessManager() {
// Spawn a thread to listen for signals from child processes.
- Thread processThread = new Thread(ProcessManager.class.getName()) {
+ Thread reaperThread = new Thread(ProcessManager.class.getName()) {
@Override public void run() {
- watchChildren(ProcessManager.this);
+ watchChildren();
}
};
- processThread.setDaemon(true);
- processThread.start();
+ reaperThread.setDaemon(true);
+ reaperThread.start();
}
/**
@@ -94,10 +76,40 @@
}
/**
- * Listens for signals from processes and calls back to
- * {@link #onExit(int,int)}.
+ * Loops indefinitely and calls ProcessManager.onExit() when children exit.
*/
- private static native void watchChildren(ProcessManager manager);
+ private void watchChildren() {
+ MutableInt status = new MutableInt(-1);
+ while (true) {
+ try {
+ // Wait for children in our process group.
+ int pid = Libcore.os.waitpid(0, status, 0);
+
+ // Work out what onExit wants to hear.
+ int exitValue;
+ if (WIFEXITED(status.value)) {
+ exitValue = WEXITSTATUS(status.value);
+ } else if (WIFSIGNALED(status.value)) {
+ exitValue = WTERMSIG(status.value);
+ } else if (WIFSTOPPED(status.value)) {
+ exitValue = WSTOPSIG(status.value);
+ } else {
+ throw new AssertionError("unexpected status from waitpid: " + status.value);
+ }
+
+ onExit(pid, exitValue);
+ } catch (ErrnoException errnoException) {
+ if (errnoException.errno == ECHILD) {
+ // Expected errno: there are no children to wait for.
+ // onExit will sleep until it is informed of another child coming to life.
+ waitForMoreChildren();
+ continue;
+ } else {
+ throw errnoException;
+ }
+ }
+ }
+ }
/**
* Called by {@link #watchChildren()} when a child process exits.
@@ -107,38 +119,10 @@
*/
private void onExit(int pid, int exitValue) {
ProcessReference processReference = null;
-
synchronized (processReferences) {
cleanUp();
- if (pid >= 0) {
- processReference = processReferences.remove(pid);
- } else if (exitValue == WAIT_STATUS_NO_CHILDREN) {
- if (processReferences.isEmpty()) {
- /*
- * There are no eligible children; wait for one to be
- * added. The wait() will return due to the
- * notifyAll() call below.
- */
- try {
- processReferences.wait();
- } catch (InterruptedException ex) {
- // This should never happen.
- throw new AssertionError("unexpected interrupt");
- }
- } else {
- /*
- * A new child was spawned just before we entered
- * the synchronized block. We can just fall through
- * without doing anything special and land back in
- * the native wait().
- */
- }
- } else {
- // Something weird is happening; abort!
- throw new AssertionError("unexpected wait() behavior");
- }
+ processReference = processReferences.remove(pid);
}
-
if (processReference != null) {
ProcessImpl process = processReference.get();
if (process != null) {
@@ -147,6 +131,28 @@
}
}
+ private void waitForMoreChildren() {
+ synchronized (processReferences) {
+ if (processReferences.isEmpty()) {
+ // There are no eligible children; wait for one to be added.
+ // This wait will return because of the notifyAll call in exec.
+ try {
+ processReferences.wait();
+ } catch (InterruptedException ex) {
+ // This should never happen.
+ throw new AssertionError("unexpected interrupt");
+ }
+ } else {
+ /*
+ * A new child was spawned just before we entered
+ * the synchronized block. We can just fall through
+ * without doing anything special and land back in
+ * the native waitpid().
+ */
+ }
+ }
+ }
+
/**
* Executes a native process. Fills in in, out, and err and returns the
* new process ID upon success.
diff --git a/luni/src/main/java/java/lang/Short.java b/luni/src/main/java/java/lang/Short.java
index 02f7e65..1101805 100644
--- a/luni/src/main/java/java/lang/Short.java
+++ b/luni/src/main/java/java/lang/Short.java
@@ -23,6 +23,7 @@
* @see java.lang.Number
* @since 1.1
*/
+@FindBugsSuppressWarnings("DM_NUMBER_CTOR")
public final class Short extends Number implements Comparable<Short> {
private static final long serialVersionUID = 7515723908773894738L;
@@ -156,8 +157,7 @@
*/
@Override
public boolean equals(Object object) {
- return (object instanceof Short)
- && (value == ((Short) object).value);
+ return (object instanceof Short) && (((Short) object).value == value);
}
@Override
@@ -314,7 +314,7 @@
private static final Short[] SMALL_VALUES = new Short[256];
static {
- for(int i = -128; i < 128; i++) {
+ for (int i = -128; i < 128; i++) {
SMALL_VALUES[i + 128] = new Short((short) i);
}
}
diff --git a/luni/src/main/java/java/lang/String.java b/luni/src/main/java/java/lang/String.java
index 56b99f4..5991f7a 100644
--- a/luni/src/main/java/java/lang/String.java
+++ b/luni/src/main/java/java/lang/String.java
@@ -136,6 +136,7 @@
* Converts the byte array to a string using the system's
* {@link java.nio.charset.Charset#defaultCharset default charset}.
*/
+ @FindBugsSuppressWarnings("DM_DEFAULT_ENCODING")
public String(byte[] data) {
this(data, 0, data.length);
}
@@ -762,6 +763,7 @@
* @return {@code true} if the specified string is equal to this string,
* {@code false} otherwise.
*/
+ @FindBugsSuppressWarnings("ES_COMPARING_PARAMETER_STRING_WITH_EQ")
public boolean equalsIgnoreCase(String string) {
if (string == this) {
return true;
@@ -803,6 +805,7 @@
*/
@Deprecated
public void getBytes(int start, int end, byte[] data, int index) {
+ // Note: last character not copied!
if (start >= 0 && start <= end && end <= count) {
end += offset;
try {
@@ -812,8 +815,9 @@
} catch (ArrayIndexOutOfBoundsException ignored) {
throw failedBoundsCheck(data.length, index, end - start);
}
+ } else {
+ throw startEndAndLength(start, end);
}
- throw startEndAndLength(start, end);
}
/**
@@ -890,7 +894,7 @@
* index}
*/
public void getChars(int start, int end, char[] buffer, int index) {
- // NOTE last character not copied!
+ // Note: last character not copied!
if (start >= 0 && start <= end && end <= count) {
System.arraycopy(value, start + offset, buffer, index, end - start);
} else {
@@ -2020,6 +2024,7 @@
* where the needle is a constant string, may compute the values cache, md2
* and lastChar, and change the call to the following method.
*/
+ @FindBugsSuppressWarnings("UPM_UNCALLED_PRIVATE_METHOD")
@SuppressWarnings("unused")
private static int indexOf(String haystackString, String needleString,
int cache, int md2, char lastChar) {
diff --git a/luni/src/main/java/org/apache/harmony/luni/util/FloatingPointParser.java b/luni/src/main/java/java/lang/StringToReal.java
similarity index 98%
rename from luni/src/main/java/org/apache/harmony/luni/util/FloatingPointParser.java
rename to luni/src/main/java/java/lang/StringToReal.java
index e08e560..97f6d6b 100644
--- a/luni/src/main/java/org/apache/harmony/luni/util/FloatingPointParser.java
+++ b/luni/src/main/java/java/lang/StringToReal.java
@@ -15,14 +15,14 @@
* limitations under the License.
*/
-package org.apache.harmony.luni.util;
-
+package java.lang;
/**
* Used to parse a string and return either a single or double precision
* floating point number.
+ * @hide
*/
-public final class FloatingPointParser {
+final class StringToReal {
private static final class StringExponentPair {
String s;
diff --git a/luni/src/main/java/java/lang/System.java b/luni/src/main/java/java/lang/System.java
index 203e5e3..c65ebbb 100644
--- a/luni/src/main/java/java/lang/System.java
+++ b/luni/src/main/java/java/lang/System.java
@@ -272,9 +272,13 @@
p.put("java.boot.class.path", runtime.bootClassPath());
p.put("java.class.path", runtime.classPath());
- p.put("java.class.version", "46.0");
+ // None of these four are meaningful on Android, but these keys are guaranteed
+ // to be present for System.getProperty. For java.class.version, we use the maximum
+ // class file version that dx currently supports.
+ p.put("java.class.version", "50.0");
p.put("java.compiler", "");
p.put("java.ext.dirs", "");
+ p.put("java.version", "0");
p.put("java.home", getenv("JAVA_HOME", "/system"));
@@ -287,7 +291,6 @@
p.put("java.vendor", projectName);
p.put("java.vendor.url", projectUrl);
- p.put("java.version", "0");
p.put("java.vm.name", "Dalvik");
p.put("java.vm.specification.name", "Dalvik Virtual Machine Specification");
p.put("java.vm.specification.vendor", projectName);
@@ -358,7 +361,7 @@
* <tr><td>file.separator</td> <td>{@link java.io.File#separator}</td> <td>{@code /}</td></tr>
*
* <tr><td>java.class.path</td> <td>System class path</td> <td>{@code .}</td></tr>
- * <tr><td>java.class.version</td> <td>Maximum supported .class file version</td> <td>{@code 46.0}</td></tr>
+ * <tr><td>java.class.version</td> <td>(Not useful on Android)</td> <td>{@code 50.0}</td></tr>
* <tr><td>java.compiler</td> <td>(Not useful on Android)</td> <td>Empty</td></tr>
* <tr><td>java.ext.dirs</td> <td>(Not useful on Android)</td> <td>Empty</td></tr>
* <tr><td>java.home</td> <td>Location of the VM on the file system</td> <td>{@code /system}</td></tr>
diff --git a/luni/src/main/java/java/lang/ThreadGroup.java b/luni/src/main/java/java/lang/ThreadGroup.java
index d0e593f..e99e99f 100644
--- a/luni/src/main/java/java/lang/ThreadGroup.java
+++ b/luni/src/main/java/java/lang/ThreadGroup.java
@@ -75,7 +75,6 @@
* @param name the name
* @see Thread#currentThread
*/
-
public ThreadGroup(String name) {
this(Thread.currentThread().getThreadGroup(), name);
}
diff --git a/luni/src/main/java/java/lang/UnsafeByteSequence.java b/luni/src/main/java/java/lang/UnsafeByteSequence.java
index 66f904b..228bb01 100644
--- a/luni/src/main/java/java/lang/UnsafeByteSequence.java
+++ b/luni/src/main/java/java/lang/UnsafeByteSequence.java
@@ -67,6 +67,7 @@
bytes[count++] = (byte) b;
}
+ @FindBugsSuppressWarnings("EI_EXPOSE_REP")
public byte[] toByteArray() {
if (count == bytes.length) {
return bytes;
diff --git a/luni/src/main/java/java/lang/reflect/Constructor.java b/luni/src/main/java/java/lang/reflect/Constructor.java
index ceb54c4..b03e28b 100644
--- a/luni/src/main/java/java/lang/reflect/Constructor.java
+++ b/luni/src/main/java/java/lang/reflect/Constructor.java
@@ -344,7 +344,7 @@
StringBuilder result = new StringBuilder();
result.append('(');
- for(int i = 0; i < parameterTypes.length; i++) {
+ for (int i = 0; i < parameterTypes.length; i++) {
result.append(getSignature(parameterTypes[i]));
}
result.append(")V");
diff --git a/luni/src/main/java/java/lang/reflect/Method.java b/luni/src/main/java/java/lang/reflect/Method.java
index de1dcbd..8a9c0f1 100644
--- a/luni/src/main/java/java/lang/reflect/Method.java
+++ b/luni/src/main/java/java/lang/reflect/Method.java
@@ -571,7 +571,7 @@
StringBuilder result = new StringBuilder();
result.append('(');
- for(int i = 0; i < parameterTypes.length; i++) {
+ for (int i = 0; i < parameterTypes.length; i++) {
result.append(getSignature(parameterTypes[i]));
}
result.append(')');
diff --git a/luni/src/main/java/java/math/BitLevel.java b/luni/src/main/java/java/math/BitLevel.java
index 607358f..91f7a9b 100644
--- a/luni/src/main/java/java/math/BitLevel.java
+++ b/luni/src/main/java/java/math/BitLevel.java
@@ -113,7 +113,7 @@
static void shiftLeftOneBit(int[] result, int[] source, int srcLen) {
int carry = 0;
- for(int i = 0; i < srcLen; i++) {
+ for (int i = 0; i < srcLen; i++) {
int val = source[i];
result[i] = (val << 1) | carry;
carry = val >>> 31;
diff --git a/luni/src/main/java/java/math/Conversion.java b/luni/src/main/java/java/math/Conversion.java
index 731f972..585fff4 100644
--- a/luni/src/main/java/java/math/Conversion.java
+++ b/luni/src/main/java/java/math/Conversion.java
@@ -350,7 +350,7 @@
if (exponent >= 0) {
// special case 1
int insertPoint = currentChar + (int) exponent ;
- for(int j=resLengthInChars-1; j>=insertPoint; j--) {
+ for (int j=resLengthInChars-1; j>=insertPoint; j--) {
result[j+1] = result[j];
}
result[++insertPoint]='.';
diff --git a/luni/src/main/java/java/math/Logical.java b/luni/src/main/java/java/math/Logical.java
index 229a5e8..9de0924 100644
--- a/luni/src/main/java/java/math/Logical.java
+++ b/luni/src/main/java/java/math/Logical.java
@@ -207,7 +207,7 @@
resDigits[i] = longer.digits[i] | shorter.digits[i];
}
// shorter has only the remaining virtual sign bits
- for( ; i < longer.numberLength; i++){
+ for ( ; i < longer.numberLength; i++){
resDigits[i] = longer.digits[i];
}
@@ -533,7 +533,7 @@
i = iPos;
resDigits[i] = -positive.digits[i];
limit = Math.min(positive.numberLength, iNeg);
- for(i++; i < limit; i++ ) {
+ for (i++; i < limit; i++ ) {
resDigits[i] = ~positive.digits[i];
}
if (i != positive.numberLength) {
@@ -558,7 +558,7 @@
// resDigits[i] = ~(~negative.digits[i] | positive.digits[i] );
resDigits[i] = negative.digits[i] & ~positive.digits[i];
}
- for( ; i < negative.numberLength; i++) {
+ for ( ; i < negative.numberLength; i++) {
resDigits[i] = negative.digits[i];
}
@@ -611,7 +611,7 @@
for ( ; i < shorter.numberLength; i++) {
resDigits[i] = longer.digits[i] ^ shorter.digits[i];
}
- for( ; i < longer.numberLength; i++ ){
+ for ( ; i < longer.numberLength; i++ ){
resDigits[i] = longer.digits[i];
}
diff --git a/luni/src/main/java/java/math/MathContext.java b/luni/src/main/java/java/math/MathContext.java
index 24ccb29..6f3f1ed 100644
--- a/luni/src/main/java/java/math/MathContext.java
+++ b/luni/src/main/java/java/math/MathContext.java
@@ -30,21 +30,21 @@
private static final long serialVersionUID = 5579720004786848255L;
/**
- * A {@code MathContext} which corresponds to the IEEE 754r quadruple
+ * A {@code MathContext} which corresponds to the <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> quadruple
* decimal precision format: 34 digit precision and
* {@link RoundingMode#HALF_EVEN} rounding.
*/
public static final MathContext DECIMAL128 = new MathContext(34, RoundingMode.HALF_EVEN);
/**
- * A {@code MathContext} which corresponds to the IEEE 754r single decimal
+ * A {@code MathContext} which corresponds to the <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> single decimal
* precision format: 7 digit precision and {@link RoundingMode#HALF_EVEN}
* rounding.
*/
public static final MathContext DECIMAL32 = new MathContext(7, RoundingMode.HALF_EVEN);
/**
- * A {@code MathContext} which corresponds to the IEEE 754r double decimal
+ * A {@code MathContext} which corresponds to the <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> double decimal
* precision format: 16 digit precision and {@link RoundingMode#HALF_EVEN}
* rounding.
*/
diff --git a/luni/src/main/java/java/net/BindException.java b/luni/src/main/java/java/net/BindException.java
index 498ead6..4f035cc 100644
--- a/luni/src/main/java/java/net/BindException.java
+++ b/luni/src/main/java/java/net/BindException.java
@@ -26,15 +26,23 @@
private static final long serialVersionUID = -5945005768251722951L;
/**
- * Constructs a new instance with the current stack trace.
+ * Constructs a new instance.
*/
public BindException() {
}
/**
- * Constructs a new instance with the current stack trace and given detail message.
+ * Constructs a new instance with the given detail message.
*/
public BindException(String detailMessage) {
super(detailMessage);
}
+
+ /**
+ * Constructs a new instance with the given detail message and cause.
+ * @hide
+ */
+ public BindException(String detailMessage, Throwable cause) {
+ super(detailMessage, cause);
+ }
}
diff --git a/luni/src/main/java/java/net/ConnectException.java b/luni/src/main/java/java/net/ConnectException.java
index 554316f..45f60be 100644
--- a/luni/src/main/java/java/net/ConnectException.java
+++ b/luni/src/main/java/java/net/ConnectException.java
@@ -26,18 +26,23 @@
private static final long serialVersionUID = 3831404271622369215L;
/**
- * This implementation does nothing.
+ * Constructs a new instance.
*/
public ConnectException() {
}
/**
- * This implementation does nothing.
- *
- * @param detailMessage
- * detail message of the exception.
+ * Constructs a new instance with the given detail message.
*/
public ConnectException(String detailMessage) {
super(detailMessage);
}
+
+ /**
+ * Constructs a new instance with the given detail message and cause.
+ * @hide
+ */
+ public ConnectException(String detailMessage, Throwable cause) {
+ super(detailMessage, cause);
+ }
}
diff --git a/luni/src/main/java/java/net/DatagramSocket.java b/luni/src/main/java/java/net/DatagramSocket.java
index 47ac0ff..70e3db9 100644
--- a/luni/src/main/java/java/net/DatagramSocket.java
+++ b/luni/src/main/java/java/net/DatagramSocket.java
@@ -161,8 +161,7 @@
isConnected = false;
}
- synchronized void createSocket(int aPort, InetAddress addr)
- throws SocketException {
+ synchronized void createSocket(int aPort, InetAddress addr) throws SocketException {
impl = factory != null ? factory.createDatagramSocketImpl()
: new PlainDatagramSocketImpl();
impl.create();
diff --git a/luni/src/main/java/java/net/DatagramSocketImpl.java b/luni/src/main/java/java/net/DatagramSocketImpl.java
index 4485b18..6e706c3 100644
--- a/luni/src/main/java/java/net/DatagramSocketImpl.java
+++ b/luni/src/main/java/java/net/DatagramSocketImpl.java
@@ -19,7 +19,7 @@
import java.io.FileDescriptor;
import java.io.IOException;
-import libcore.io.IoUtils;
+import libcore.io.IoBridge;
/**
* The abstract superclass for datagram and multicast socket implementations.
@@ -85,7 +85,7 @@
* Returns the local address to which the socket is bound.
*/
InetAddress getLocalAddress() {
- return IoUtils.getSocketLocalAddress(fd);
+ return IoBridge.getSocketLocalAddress(fd);
}
/**
diff --git a/luni/src/main/java/java/net/HttpURLConnection.java b/luni/src/main/java/java/net/HttpURLConnection.java
index 8376220..a54e11a 100644
--- a/luni/src/main/java/java/net/HttpURLConnection.java
+++ b/luni/src/main/java/java/net/HttpURLConnection.java
@@ -251,7 +251,7 @@
/**
* The subset of HTTP methods that the user may select via {@link
- * libcore.net.http.HttpURLConnectionImpl#setRequestMethod(String)}.
+ * #setRequestMethod(String)}.
*/
private static final String[] PERMITTED_USER_METHODS = {
HttpEngine.OPTIONS,
@@ -472,7 +472,11 @@
public static final int HTTP_SERVER_ERROR = 500;
/**
- * Numeric status code, 305: Use proxy
+ * Numeric status code, 305: Use proxy.
+ *
+ * <p>Like Firefox and Chrome, this class doesn't honor this response code.
+ * Other implementations respond to this status code by retrying the request
+ * using the HTTP proxy named by the response's Location header field.
*/
public static final int HTTP_USE_PROXY = 305;
diff --git a/luni/src/main/java/java/net/Inet4Address.java b/luni/src/main/java/java/net/Inet4Address.java
index a12eacd..8f8f4f8 100644
--- a/luni/src/main/java/java/net/Inet4Address.java
+++ b/luni/src/main/java/java/net/Inet4Address.java
@@ -20,6 +20,7 @@
import java.io.ObjectStreamException;
import java.nio.ByteOrder;
import libcore.io.Memory;
+import static libcore.io.OsConstants.*;
/**
* An IPv4 address. See {@link InetAddress}.
@@ -28,8 +29,6 @@
private static final long serialVersionUID = 3286316764910316507L;
- private static final int AF_INET = 2;
-
/**
* @hide
*/
diff --git a/luni/src/main/java/java/net/Inet6Address.java b/luni/src/main/java/java/net/Inet6Address.java
index c253a50..71c8e4a 100644
--- a/luni/src/main/java/java/net/Inet6Address.java
+++ b/luni/src/main/java/java/net/Inet6Address.java
@@ -23,6 +23,7 @@
import java.io.ObjectStreamField;
import java.util.Arrays;
import java.util.Enumeration;
+import static libcore.io.OsConstants.*;
/**
* An IPv6 address. See {@link InetAddress}.
@@ -31,8 +32,6 @@
private static final long serialVersionUID = 6880410070516793377L;
- private static final int AF_INET6 = 10;
-
/**
* @hide
*/
@@ -46,11 +45,11 @@
new Inet6Address(new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
"localhost", 0);
- boolean scope_id_set;
- int scope_id;
+ private boolean scope_id_set;
+ private int scope_id;
- boolean scope_ifname_set;
- String ifname;
+ private boolean scope_ifname_set;
+ private String ifname;
/**
* Constructs an {@code InetAddress} representing the {@code address} and
@@ -269,7 +268,7 @@
*/
public NetworkInterface getScopedInterface() {
try {
- return scope_ifname_set ? NetworkInterface.getByName(ifname) : null;
+ return (scope_ifname_set && ifname != null) ? NetworkInterface.getByName(ifname) : null;
} catch (SocketException ex) {
return null;
}
diff --git a/luni/src/main/java/java/net/InetAddress.java b/luni/src/main/java/java/net/InetAddress.java
index 463bff4..a507f82 100644
--- a/luni/src/main/java/java/net/InetAddress.java
+++ b/luni/src/main/java/java/net/InetAddress.java
@@ -31,12 +31,12 @@
import java.util.Comparator;
import java.util.Enumeration;
import java.util.List;
+import libcore.io.ErrnoException;
import libcore.io.GaiException;
import libcore.io.Libcore;
-import libcore.io.IoUtils;
+import libcore.io.IoBridge;
import libcore.io.Memory;
import libcore.io.StructAddrinfo;
-import org.apache.harmony.luni.platform.Platform;
import static libcore.io.OsConstants.*;
/**
@@ -143,8 +143,6 @@
/** Our Java-side DNS cache. */
private static final AddressCache addressCache = new AddressCache();
- private static final String ERRMSG_CONNECTION_REFUSED = "Connection refused";
-
private static final long serialVersionUID = 3286316764910316507L;
private transient Object waitReachable = new Object();
@@ -160,16 +158,15 @@
String hostName;
/**
+ * Used by the DatagramSocket.disconnect implementation.
+ * @hide internal use only
+ */
+ public static final InetAddress UNSPECIFIED = new InetAddress(AF_UNSPEC, null, null);
+
+ /**
* Constructs an {@code InetAddress}.
*
- * Note: this constructor should not be used. Creating an InetAddress
- * without specifying whether it's an IPv4 or IPv6 address does not make
- * sense, because subsequent code cannot know which of of the subclasses'
- * methods need to be called to implement a given InetAddress method. The
- * proper way to create an InetAddress is to call new Inet4Address or
- * Inet6Address or to use one of the static methods that return
- * InetAddresses (e.g., getByAddress). That is why the API does not have
- * public constructors for any of these classes.
+ * Note: this constructor is for subclasses only.
*/
InetAddress(int family, byte[] ipaddress, String hostName) {
this.family = family;
@@ -810,53 +807,39 @@
}
private boolean isReachableByTCP(InetAddress destination, InetAddress source, int timeout) throws IOException {
- FileDescriptor fd = IoUtils.socket(true);
+ FileDescriptor fd = IoBridge.socket(true);
boolean reached = false;
try {
if (source != null) {
- Platform.NETWORK.bind(fd, source, 0);
+ IoBridge.bind(fd, source, 0);
}
- IoUtils.connect(fd, destination, 7, timeout);
+ IoBridge.connect(fd, destination, 7, timeout);
reached = true;
} catch (IOException e) {
- if (ERRMSG_CONNECTION_REFUSED.equals(e.getMessage())) {
- // Connection refused means the IP is reachable
- reached = true;
+ if (e.getCause() instanceof ErrnoException) {
+ // "Connection refused" means the IP address was reachable.
+ reached = (((ErrnoException) e.getCause()).errno == ECONNREFUSED);
}
}
- Platform.NETWORK.close(fd);
+ IoBridge.closeSocket(fd);
return reached;
}
/**
- * Equivalent to {@code getByAddress(null, ipAddress, 0)}. Handy for IPv4 addresses with
+ * Equivalent to {@code getByAddress(null, ipAddress)}. Handy for addresses with
* no associated hostname.
- *
- * <p>(Note that numeric addresses such as {@code "127.0.0.1"} are names for the
- * purposes of this API. Most callers probably want {@link #getAllByName} instead.)
*/
public static InetAddress getByAddress(byte[] ipAddress) throws UnknownHostException {
- return getByAddressInternal(null, ipAddress, 0);
- }
-
- /**
- * Equivalent to {@code getByAddress(hostName, ipAddress, 0)}. Handy for IPv4 addresses
- * with an associated hostname.
- *
- * <p>(Note that numeric addresses such as {@code "127.0.0.1"} are names for the
- * purposes of this API. Most callers probably want {@link #getAllByName} instead.)
- */
- public static InetAddress getByAddress(String hostName, byte[] ipAddress) throws UnknownHostException {
- return getByAddressInternal(hostName, ipAddress, 0);
+ return getByAddress(null, ipAddress, 0);
}
/**
* Returns an {@code InetAddress} corresponding to the given network-order
* bytes {@code ipAddress} and {@code scopeId}.
*
- * <p>For an IPv4 address, the byte array must be of length 4, and the scopeId is ignored.
+ * <p>For an IPv4 address, the byte array must be of length 4.
* For IPv6, the byte array must be of length 16. Any other length will cause an {@code
* UnknownHostException}.
*
@@ -868,8 +851,11 @@
*
* @throws UnknownHostException if {@code ipAddress} is null or the wrong length.
*/
- private static InetAddress getByAddressInternal(String hostName, byte[] ipAddress, int scopeId)
- throws UnknownHostException {
+ public static InetAddress getByAddress(String hostName, byte[] ipAddress) throws UnknownHostException {
+ return getByAddress(hostName, ipAddress, 0);
+ }
+
+ private static InetAddress getByAddress(String hostName, byte[] ipAddress, int scopeId) throws UnknownHostException {
if (ipAddress == null) {
throw new UnknownHostException("ipAddress == null");
}
@@ -913,7 +899,7 @@
private static byte[] ipv4MappedToIPv4(byte[] mappedAddress) {
byte[] ipv4Address = new byte[4];
- for(int i = 0; i < 4; i++) {
+ for (int i = 0; i < 4; i++) {
ipv4Address[i] = mappedAddress[12 + i];
}
return ipv4Address;
diff --git a/luni/src/main/java/java/net/InetSocketAddress.java b/luni/src/main/java/java/net/InetSocketAddress.java
index 7b33a44..01b5301 100644
--- a/luni/src/main/java/java/net/InetSocketAddress.java
+++ b/luni/src/main/java/java/net/InetSocketAddress.java
@@ -34,6 +34,16 @@
private final int port;
/**
+ * @hide internal use only
+ */
+ public InetSocketAddress() {
+ // These will be filled in the native implementation of recvfrom.
+ this.addr = null;
+ this.hostname = null;
+ this.port = -1;
+ }
+
+ /**
* Creates a socket endpoint with the given port number {@code port} and
* no specified address. The range for valid port numbers is between 0 and
* 65535 inclusive.
diff --git a/luni/src/main/java/java/net/MalformedURLException.java b/luni/src/main/java/java/net/MalformedURLException.java
index 77965e8..c6b82c4 100644
--- a/luni/src/main/java/java/net/MalformedURLException.java
+++ b/luni/src/main/java/java/net/MalformedURLException.java
@@ -30,15 +30,23 @@
private static final long serialVersionUID = -182787522200415866L;
/**
- * Constructs a new instance with the current stack trace.
+ * Constructs a new instance.
*/
public MalformedURLException() {
}
/**
- * Constructs a new instance with the current stack trace and given detail message.
+ * Constructs a new instance with the given detail message.
*/
public MalformedURLException(String detailMessage) {
super(detailMessage);
}
+
+ /**
+ * Constructs a new instance with given detail message and cause.
+ * @hide internal use only
+ */
+ public MalformedURLException(String detailMessage, Throwable cause) {
+ super(detailMessage, cause);
+ }
}
diff --git a/luni/src/main/java/java/net/NetworkInterface.java b/luni/src/main/java/java/net/NetworkInterface.java
index 9aa20a0..c6d9af4 100644
--- a/luni/src/main/java/java/net/NetworkInterface.java
+++ b/luni/src/main/java/java/net/NetworkInterface.java
@@ -184,6 +184,7 @@
}
}
+ @FindBugsSuppressWarnings("DMI_HARDCODED_ABSOLUTE_FILENAME")
private static boolean isValidInterfaceName(String interfaceName) {
// Don't just stat because a crafty user might have / or .. in the supposed interface name.
for (String validName : new File("/sys/class/net").list()) {
@@ -261,6 +262,7 @@
return Collections.enumeration(getNetworkInterfacesList());
}
+ @FindBugsSuppressWarnings("DMI_HARDCODED_ABSOLUTE_FILENAME")
private static List<NetworkInterface> getNetworkInterfacesList() throws SocketException {
String[] interfaceNames = new File("/sys/class/net").list();
NetworkInterface[] interfaces = new NetworkInterface[interfaceNames.length];
diff --git a/luni/src/main/java/java/net/NoRouteToHostException.java b/luni/src/main/java/java/net/NoRouteToHostException.java
index 3d46821..8d6d941 100644
--- a/luni/src/main/java/java/net/NoRouteToHostException.java
+++ b/luni/src/main/java/java/net/NoRouteToHostException.java
@@ -27,15 +27,23 @@
private static final long serialVersionUID = -1897550894873493790L;
/**
- * Constructs a new instance with the current stack trace.
+ * Constructs a new instance.
*/
public NoRouteToHostException() {
}
/**
- * Constructs a new instance with the current stack trace and given detail message.
+ * Constructs a new instance with the given detail message.
*/
public NoRouteToHostException(String detailMessage) {
super(detailMessage);
}
+
+ /**
+ * Constructs a new instance with given detail message and cause.
+ * @hide internal use only
+ */
+ public NoRouteToHostException(String detailMessage, Throwable cause) {
+ super(detailMessage, cause);
+ }
}
diff --git a/luni/src/main/java/java/net/PlainDatagramSocketImpl.java b/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
index 0eb413f..f27db949 100644
--- a/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
+++ b/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
@@ -28,10 +28,11 @@
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
-import libcore.io.IoUtils;
+import libcore.io.IoBridge;
+import libcore.io.Libcore;
import libcore.io.StructGroupReq;
import libcore.util.EmptyArray;
-import org.apache.harmony.luni.platform.Platform;
+import static libcore.io.OsConstants.*;
/**
* @hide used in java.nio.
@@ -62,15 +63,13 @@
fd = new FileDescriptor();
}
- @Override
- public void bind(int port, InetAddress addr) throws SocketException {
- Platform.NETWORK.bind(fd, addr, port);
+ @Override public void bind(int port, InetAddress address) throws SocketException {
+ IoBridge.bind(fd, address, port);
if (port != 0) {
localPort = port;
} else {
- localPort = IoUtils.getSocketLocalPort(fd);
+ localPort = IoBridge.getSocketLocalPort(fd);
}
-
try {
setOption(SocketOptions.SO_BROADCAST, Boolean.TRUE);
} catch (IOException ignored) {
@@ -81,14 +80,14 @@
public synchronized void close() {
guard.close();
try {
- Platform.NETWORK.close(fd);
+ IoBridge.closeSocket(fd);
} catch (IOException ignored) {
}
}
@Override
public void create() throws SocketException {
- this.fd = IoUtils.socket(false);
+ this.fd = IoBridge.socket(false);
}
@Override protected void finalize() throws Throwable {
@@ -103,12 +102,12 @@
}
@Override public Object getOption(int option) throws SocketException {
- return IoUtils.getSocketOption(fd, option);
+ return IoBridge.getSocketOption(fd, option);
}
@Override
public int getTimeToLive() throws IOException {
- return (Integer) getOption(IoUtils.JAVA_IP_MULTICAST_TTL);
+ return (Integer) getOption(IoBridge.JAVA_IP_MULTICAST_TTL);
}
@Override
@@ -123,27 +122,27 @@
@Override
public void join(InetAddress addr) throws IOException {
- setOption(IoUtils.JAVA_MCAST_JOIN_GROUP, makeGroupReq(addr, null));
+ setOption(IoBridge.JAVA_MCAST_JOIN_GROUP, makeGroupReq(addr, null));
}
@Override
public void joinGroup(SocketAddress addr, NetworkInterface netInterface) throws IOException {
if (addr instanceof InetSocketAddress) {
InetAddress groupAddr = ((InetSocketAddress) addr).getAddress();
- setOption(IoUtils.JAVA_MCAST_JOIN_GROUP, makeGroupReq(groupAddr, netInterface));
+ setOption(IoBridge.JAVA_MCAST_JOIN_GROUP, makeGroupReq(groupAddr, netInterface));
}
}
@Override
public void leave(InetAddress addr) throws IOException {
- setOption(IoUtils.JAVA_MCAST_LEAVE_GROUP, makeGroupReq(addr, null));
+ setOption(IoBridge.JAVA_MCAST_LEAVE_GROUP, makeGroupReq(addr, null));
}
@Override
public void leaveGroup(SocketAddress addr, NetworkInterface netInterface) throws IOException {
if (addr instanceof InetSocketAddress) {
InetAddress groupAddr = ((InetSocketAddress) addr).getAddress();
- setOption(IoUtils.JAVA_MCAST_LEAVE_GROUP, makeGroupReq(groupAddr, netInterface));
+ setOption(IoBridge.JAVA_MCAST_LEAVE_GROUP, makeGroupReq(groupAddr, netInterface));
}
}
@@ -152,14 +151,13 @@
// We don't actually want the data: we just want the DatagramPacket's filled-in address.
DatagramPacket packet = new DatagramPacket(EmptyArray.BYTE, 0);
int result = peekData(packet);
- // TODO: maybe recv should do this?
+ // Note: evil side-effect on InetAddress! This method should have returned InetSocketAddress!
sender.ipaddress = packet.getAddress().getAddress();
return result;
}
- private void doRecv(DatagramPacket pack, boolean peek) throws IOException {
- Platform.NETWORK.recv(fd, pack, pack.getData(), pack.getOffset(), pack.getLength(), peek,
- isNativeConnected);
+ private void doRecv(DatagramPacket pack, int flags) throws IOException {
+ IoBridge.recvfrom(false, fd, pack.getData(), pack.getOffset(), pack.getLength(), flags, pack, isNativeConnected);
if (isNativeConnected) {
updatePacketRecvAddress(pack);
}
@@ -167,12 +165,12 @@
@Override
public void receive(DatagramPacket pack) throws IOException {
- doRecv(pack, false);
+ doRecv(pack, 0);
}
@Override
public int peekData(DatagramPacket pack) throws IOException {
- doRecv(pack, true);
+ doRecv(pack, MSG_PEEK);
return pack.getPort();
}
@@ -180,17 +178,16 @@
public void send(DatagramPacket packet) throws IOException {
int port = isNativeConnected ? 0 : packet.getPort();
InetAddress address = isNativeConnected ? null : packet.getAddress();
- Platform.NETWORK.send(fd, packet.getData(), packet.getOffset(), packet.getLength(),
- port, address);
+ IoBridge.sendto(fd, packet.getData(), packet.getOffset(), packet.getLength(), 0, address, port);
}
public void setOption(int option, Object value) throws SocketException {
- IoUtils.setSocketOption(fd, option, value);
+ IoBridge.setSocketOption(fd, option, value);
}
@Override
public void setTimeToLive(int ttl) throws IOException {
- setOption(IoUtils.JAVA_IP_MULTICAST_TTL, Integer.valueOf(ttl));
+ setOption(IoBridge.JAVA_IP_MULTICAST_TTL, Integer.valueOf(ttl));
}
@Override
@@ -200,7 +197,7 @@
@Override
public void connect(InetAddress inetAddr, int port) throws SocketException {
- IoUtils.connect(fd, inetAddr, port); // Throws on failure.
+ IoBridge.connect(fd, inetAddr, port); // Throws on failure.
try {
connectedAddress = InetAddress.getByAddress(inetAddr.getAddress());
} catch (UnknownHostException e) {
@@ -214,10 +211,7 @@
@Override
public void disconnect() {
- try {
- Platform.NETWORK.disconnectDatagram(fd);
- } catch (Exception ignored) {
- }
+ Libcore.os.connect(fd, InetAddress.UNSPECIFIED, 0);
connectedPort = -1;
connectedAddress = null;
isNativeConnected = false;
diff --git a/luni/src/main/java/java/net/PlainServerSocketImpl.java b/luni/src/main/java/java/net/PlainServerSocketImpl.java
index cb45010..4fc1a9d 100644
--- a/luni/src/main/java/java/net/PlainServerSocketImpl.java
+++ b/luni/src/main/java/java/net/PlainServerSocketImpl.java
@@ -26,8 +26,8 @@
* @hide used in java.nio.
*/
public class PlainServerSocketImpl extends PlainSocketImpl {
-
- public PlainServerSocketImpl() {}
+ public PlainServerSocketImpl() {
+ }
public PlainServerSocketImpl(FileDescriptor fd) {
super(fd);
diff --git a/luni/src/main/java/java/net/PlainSocketImpl.java b/luni/src/main/java/java/net/PlainSocketImpl.java
index ccdbb5b..20dfcc2 100644
--- a/luni/src/main/java/java/net/PlainSocketImpl.java
+++ b/luni/src/main/java/java/net/PlainSocketImpl.java
@@ -34,11 +34,10 @@
import java.nio.ByteOrder;
import java.util.Arrays;
import libcore.io.ErrnoException;
-import libcore.io.IoUtils;
+import libcore.io.IoBridge;
import libcore.io.Libcore;
import libcore.io.Memory;
import libcore.io.Streams;
-import org.apache.harmony.luni.platform.Platform;
import static libcore.io.OsConstants.*;
/**
@@ -93,7 +92,28 @@
((PlainSocketImpl) newImpl).socksAccept();
return;
}
- Platform.NETWORK.accept(fd, newImpl, newImpl.getFileDescriptor());
+
+ try {
+ InetSocketAddress peerAddress = new InetSocketAddress();
+ FileDescriptor clientFd = Libcore.os.accept(fd, peerAddress);
+
+ // TODO: we can't just set newImpl.fd to clientFd because a nio SocketChannel may
+ // be sharing the FileDescriptor. http://b//4452981.
+ newImpl.fd.setInt$(clientFd.getInt$());
+
+ newImpl.address = peerAddress.getAddress();
+ newImpl.port = peerAddress.getPort();
+ } catch (ErrnoException errnoException) {
+ if (errnoException.errno == EAGAIN || errnoException.errno == EWOULDBLOCK) {
+ throw new SocketTimeoutException(errnoException);
+ }
+ throw errnoException.rethrowAsSocketException();
+ }
+
+ // Reset the client's inherited read timeout to the Java-specified default of 0.
+ newImpl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(0));
+
+ newImpl.localport = IoBridge.getSocketLocalPort(newImpl.fd);
}
private boolean usingSocks() {
@@ -123,24 +143,23 @@
if (shutdownInput) {
return 0;
}
- return IoUtils.available(fd);
+ return IoBridge.available(fd);
}
- @Override
- protected void bind(InetAddress address, int port) throws IOException {
- Platform.NETWORK.bind(fd, address, port);
+ @Override protected void bind(InetAddress address, int port) throws IOException {
+ IoBridge.bind(fd, address, port);
this.address = address;
if (port != 0) {
this.localport = port;
} else {
- this.localport = IoUtils.getSocketLocalPort(fd);
+ this.localport = IoBridge.getSocketLocalPort(fd);
}
}
@Override
protected synchronized void close() throws IOException {
guard.close();
- Platform.NETWORK.close(fd);
+ IoBridge.closeSocket(fd);
}
@Override
@@ -167,14 +186,10 @@
*/
private void connect(InetAddress anAddr, int aPort, int timeout) throws IOException {
InetAddress normalAddr = anAddr.isAnyLocalAddress() ? InetAddress.getLocalHost() : anAddr;
- try {
- if (streaming && usingSocks()) {
- socksConnect(anAddr, aPort, 0);
- } else {
- IoUtils.connect(fd, normalAddr, aPort, timeout);
- }
- } catch (ConnectException e) {
- throw new ConnectException(anAddr + " (port " + aPort + "): " + e.getMessage());
+ if (streaming && usingSocks()) {
+ socksConnect(anAddr, aPort, 0);
+ } else {
+ IoBridge.connect(fd, normalAddr, aPort, timeout);
}
super.address = normalAddr;
super.port = aPort;
@@ -183,7 +198,7 @@
@Override
protected void create(boolean streaming) throws IOException {
this.streaming = streaming;
- this.fd = IoUtils.socket(streaming);
+ this.fd = IoBridge.socket(streaming);
}
@Override protected void finalize() throws Throwable {
@@ -227,7 +242,7 @@
}
@Override public Object getOption(int option) throws SocketException {
- return IoUtils.getSocketOption(fd, option);
+ return IoBridge.getSocketOption(fd, option);
}
@Override protected synchronized OutputStream getOutputStream() throws IOException {
@@ -271,7 +286,7 @@
@Override
public void setOption(int option, Object value) throws SocketException {
- IoUtils.setSocketOption(fd, option, value);
+ IoBridge.setSocketOption(fd, option, value);
}
/**
@@ -306,7 +321,7 @@
*/
private void socksConnect(InetAddress applicationServerAddress, int applicationServerPort, int timeout) throws IOException {
try {
- IoUtils.connect(fd, socksGetServerAddress(), socksGetServerPort(), timeout);
+ IoBridge.connect(fd, socksGetServerAddress(), socksGetServerPort(), timeout);
} catch (Exception e) {
throw new SocketException("SOCKS connection failed", e);
}
@@ -371,7 +386,7 @@
*/
private void socksBind() throws IOException {
try {
- IoUtils.connect(fd, socksGetServerAddress(), socksGetServerPort());
+ IoBridge.connect(fd, socksGetServerAddress(), socksGetServerPort());
} catch (Exception e) {
throw new IOException("Unable to connect to SOCKS server", e);
}
@@ -451,7 +466,12 @@
@Override
protected void sendUrgentData(int value) throws IOException {
- Platform.NETWORK.sendUrgentData(fd, (byte) value);
+ try {
+ byte[] buffer = new byte[] { (byte) value };
+ Libcore.os.sendto(fd, buffer, 0, 1, MSG_OOB, null, 0);
+ } catch (ErrnoException errnoException) {
+ throw errnoException.rethrowAsSocketException();
+ }
}
/**
@@ -465,16 +485,16 @@
if (shutdownInput) {
return -1;
}
- int read = Platform.NETWORK.read(fd, buffer, offset, byteCount);
+ int readCount = IoBridge.recvfrom(true, fd, buffer, offset, byteCount, 0, null, false);
// Return of zero bytes for a blocking socket means a timeout occurred
- if (read == 0) {
+ if (readCount == 0) {
throw new SocketTimeoutException();
}
// Return of -1 indicates the peer was closed
- if (read == -1) {
+ if (readCount == -1) {
shutdownInput = true;
}
- return read;
+ return readCount;
}
/**
@@ -484,7 +504,7 @@
Arrays.checkOffsetAndCount(buffer.length, offset, byteCount);
if (streaming) {
while (byteCount > 0) {
- int bytesWritten = Platform.NETWORK.write(fd, buffer, offset, byteCount);
+ int bytesWritten = IoBridge.sendto(fd, buffer, offset, byteCount, 0, null, 0);
byteCount -= bytesWritten;
offset += bytesWritten;
}
@@ -492,7 +512,7 @@
// Unlike writes to a streaming socket, writes to a datagram
// socket are all-or-nothing, so we don't need a loop here.
// http://code.google.com/p/android/issues/detail?id=15304
- Platform.NETWORK.send(fd, buffer, offset, byteCount, port, address);
+ IoBridge.sendto(fd, buffer, offset, byteCount, 0, address, port);
}
}
}
diff --git a/luni/src/main/java/java/net/PortUnreachableException.java b/luni/src/main/java/java/net/PortUnreachableException.java
index b8e8fa9..0cb4114 100644
--- a/luni/src/main/java/java/net/PortUnreachableException.java
+++ b/luni/src/main/java/java/net/PortUnreachableException.java
@@ -26,15 +26,23 @@
private static final long serialVersionUID = 8462541992376507323L;
/**
- * Constructs a new instance with the current stack trace.
+ * Constructs a new instance.
*/
public PortUnreachableException() {
}
/**
- * Constructs a new instance with the current stack trace and given detail message.
+ * Constructs a new instance with the given detail message.
*/
public PortUnreachableException(String detailMessage) {
super(detailMessage);
}
+
+ /**
+ * Constructs a new instance with given detail message and cause.
+ * @hide internal use only
+ */
+ public PortUnreachableException(String detailMessage, Throwable cause) {
+ super(detailMessage, cause);
+ }
}
diff --git a/luni/src/main/java/java/net/ProtocolException.java b/luni/src/main/java/java/net/ProtocolException.java
index 979f7d9..e0bec50 100644
--- a/luni/src/main/java/java/net/ProtocolException.java
+++ b/luni/src/main/java/java/net/ProtocolException.java
@@ -27,15 +27,23 @@
private static final long serialVersionUID = -6098449442062388080L;
/**
- * Constructs a new instance with the current stack trace.
+ * Constructs a new instance.
*/
public ProtocolException() {
}
/**
- * Constructs a new instance with the current stack trace and given detail message.
+ * Constructs a new instance with the given detail message.
*/
public ProtocolException(String detailMessage) {
super(detailMessage);
}
+
+ /**
+ * Constructs a new instance with given detail message and cause.
+ * @hide internal use only
+ */
+ public ProtocolException(String detailMessage, Throwable cause) {
+ super(detailMessage, cause);
+ }
}
diff --git a/luni/src/main/java/java/net/ServerSocket.java b/luni/src/main/java/java/net/ServerSocket.java
index 663cddf..ba8d6269 100644
--- a/luni/src/main/java/java/net/ServerSocket.java
+++ b/luni/src/main/java/java/net/ServerSocket.java
@@ -27,99 +27,79 @@
* implemented by an internal {@code SocketImpl} instance.
*/
public class ServerSocket {
+ /**
+ * The RI specifies that where the caller doesn't give an explicit backlog,
+ * the default is 50. The OS disagrees, so we need to explicitly call listen(2).
+ */
+ private static final int DEFAULT_BACKLOG = 50;
- SocketImpl impl;
+ private final SocketImpl impl;
+
+ /**
+ * @hide internal use only
+ */
+ public SocketImpl getImpl$() {
+ return impl;
+ }
static SocketImplFactory factory;
- private volatile boolean isCreated;
-
private boolean isBound;
private boolean isClosed;
/**
- * Constructs a new {@code ServerSocket} instance which is not bound to any
- * port. The default number of pending connections may be backlogged.
+ * Constructs a new unbound {@code ServerSocket}.
*
- * @throws IOException
- * if an error occurs while creating the server socket.
+ * @throws IOException if an error occurs while creating the socket.
*/
public ServerSocket() throws IOException {
- impl = factory != null ? factory.createSocketImpl()
+ this.impl = factory != null ? factory.createSocketImpl()
: new PlainServerSocketImpl();
+ impl.create(true);
}
/**
- * Unspecified constructor needed by ServerSocketChannelImpl.ServerSocketAdapter.
+ * Constructs a new {@code ServerSocket} instance bound to the given {@code port}.
+ * The backlog is set to 50. If {@code port == 0}, a port will be assigned by the OS.
*
- * @hide
+ * @throws IOException if an error occurs while creating the socket.
*/
- protected ServerSocket(SocketImpl impl) {
- this.impl = impl;
+ public ServerSocket(int port) throws IOException {
+ this(port, DEFAULT_BACKLOG, Inet4Address.ANY);
}
/**
- * Constructs a new {@code ServerSocket} instance bound to the nominated
- * port on the localhost. The default number of pending connections may be
- * backlogged. If {@code aport} is 0 a free port is assigned to the socket.
+ * Constructs a new {@code ServerSocket} instance bound to the given {@code port}.
+ * The backlog is set to {@code backlog}.
+ * If {@code port == 0}, a port will be assigned by the OS.
*
- * @param aport
- * the port number to listen for connection requests on.
- * @throws IOException
- * if an error occurs while creating the server socket.
+ * @throws IOException if an error occurs while creating the socket.
*/
- public ServerSocket(int aport) throws IOException {
- this(aport, defaultBacklog(), Inet4Address.ANY);
+ public ServerSocket(int port, int backlog) throws IOException {
+ this(port, backlog, Inet4Address.ANY);
}
/**
- * Constructs a new {@code ServerSocket} instance bound to the nominated
- * port on the localhost. The number of pending connections that may be
- * backlogged is specified by {@code backlog}. If {@code aport} is 0 a free
- * port is assigned to the socket.
+ * Constructs a new {@code ServerSocket} instance bound to the given {@code localAddress}
+ * and {@code port}. The backlog is set to {@code backlog}.
+ * If {@code localAddress == null}, the ANY address is used.
+ * If {@code port == 0}, a port will be assigned by the OS.
*
- * @param aport
- * the port number to listen for connection requests on.
- * @param backlog
- * the number of pending connection requests, before requests
- * will be rejected.
- * @throws IOException
- * if an error occurs while creating the server socket.
+ * @throws IOException if an error occurs while creating the socket.
*/
- public ServerSocket(int aport, int backlog) throws IOException {
- this(aport, backlog, Inet4Address.ANY);
- }
-
- /**
- * Constructs a new {@code ServerSocket} instance bound to the nominated
- * local host address and port. The number of pending connections that may
- * be backlogged is specified by {@code backlog}. If {@code aport} is 0 a
- * free port is assigned to the socket.
- *
- * @param aport
- * the port number to listen for connection requests on.
- * @param localAddr
- * the local machine address to bind on.
- * @param backlog
- * the number of pending connection requests, before requests
- * will be rejected.
- * @throws IOException
- * if an error occurs while creating the server socket.
- */
- public ServerSocket(int aport, int backlog, InetAddress localAddr) throws IOException {
- checkListen(aport);
- impl = factory != null ? factory.createSocketImpl()
+ public ServerSocket(int port, int backlog, InetAddress localAddress) throws IOException {
+ checkListen(port);
+ this.impl = factory != null ? factory.createSocketImpl()
: new PlainServerSocketImpl();
- InetAddress addr = localAddr == null ? Inet4Address.ANY : localAddr;
+ InetAddress addr = (localAddress == null) ? Inet4Address.ANY : localAddress;
synchronized (this) {
impl.create(true);
- isCreated = true;
try {
- impl.bind(addr, aport);
+ impl.bind(addr, port);
isBound = true;
- impl.listen(backlog > 0 ? backlog : defaultBacklog());
+ impl.listen(backlog > 0 ? backlog : DEFAULT_BACKLOG);
} catch (IOException e) {
close();
throw e;
@@ -137,7 +117,7 @@
* if an error occurs while accepting a new connection.
*/
public Socket accept() throws IOException {
- checkClosedAndCreate(false);
+ checkOpen();
if (!isBound()) {
throw new SocketException("Socket is not bound");
}
@@ -171,17 +151,6 @@
}
/**
- * Returns the default number of pending connections on a server socket. If
- * the backlog value maximum is reached, any subsequent incoming request is
- * rejected.
- *
- * @return int the default number of pending connection requests
- */
- static int defaultBacklog() {
- return 50;
- }
-
- /**
* Gets the local IP address of this server socket or {@code null} if the
* socket is unbound. This is useful for multihomed hosts.
*
@@ -214,20 +183,7 @@
* if the option cannot be retrieved.
*/
public synchronized int getSoTimeout() throws IOException {
- if (!isCreated) {
- synchronized (this) {
- if (!isCreated) {
- try {
- impl.create(true);
- } catch (SocketException e) {
- throw e;
- } catch (IOException e) {
- throw new SocketException(e.toString());
- }
- isCreated = true;
- }
- }
- }
+ checkOpen();
return ((Integer) impl.getOption(SocketOptions.SO_TIMEOUT)).intValue();
}
@@ -259,8 +215,7 @@
* @throws IOException
* if the factory could not be set or is already set.
*/
- public static synchronized void setSocketFactory(SocketImplFactory aFactory)
- throws IOException {
+ public static synchronized void setSocketFactory(SocketImplFactory aFactory) throws IOException {
if (factory != null) {
throw new SocketException("Factory already set");
}
@@ -279,7 +234,7 @@
* if an error occurs while setting the option.
*/
public synchronized void setSoTimeout(int timeout) throws SocketException {
- checkClosedAndCreate(true);
+ checkOpen();
if (timeout < 0) {
throw new IllegalArgumentException("timeout < 0");
}
@@ -323,7 +278,7 @@
* binding.
*/
public void bind(SocketAddress localAddr) throws IOException {
- bind(localAddr, defaultBacklog());
+ bind(localAddr, DEFAULT_BACKLOG);
}
/**
@@ -340,7 +295,7 @@
* during binding.
*/
public void bind(SocketAddress localAddr, int backlog) throws IOException {
- checkClosedAndCreate(true);
+ checkOpen();
if (isBound()) {
throw new BindException("Socket is already bound");
}
@@ -362,7 +317,7 @@
try {
impl.bind(addr, port);
isBound = true;
- impl.listen(backlog > 0 ? backlog : defaultBacklog());
+ impl.listen(backlog > 0 ? backlog : DEFAULT_BACKLOG);
} catch (IOException e) {
close();
throw e;
@@ -402,31 +357,10 @@
return isClosed;
}
- /**
- * Checks whether the socket is closed, and throws an exception.
- */
- private void checkClosedAndCreate(boolean create) throws SocketException {
+ private void checkOpen() throws SocketException {
if (isClosed()) {
throw new SocketException("Socket is closed");
}
-
- if (!create || isCreated) {
- return;
- }
-
- synchronized (this) {
- if (isCreated) {
- return;
- }
- try {
- impl.create(true);
- } catch (SocketException e) {
- throw e;
- } catch (IOException e) {
- throw new SocketException(e.toString());
- }
- isCreated = true;
- }
}
/**
@@ -438,7 +372,7 @@
* if an error occurs while setting the option value.
*/
public void setReuseAddress(boolean reuse) throws SocketException {
- checkClosedAndCreate(true);
+ checkOpen();
impl.setOption(SocketOptions.SO_REUSEADDR, Boolean.valueOf(reuse));
}
@@ -450,16 +384,15 @@
* if an error occurs while reading the option value.
*/
public boolean getReuseAddress() throws SocketException {
- checkClosedAndCreate(true);
- return ((Boolean) impl.getOption(SocketOptions.SO_REUSEADDR))
- .booleanValue();
+ checkOpen();
+ return ((Boolean) impl.getOption(SocketOptions.SO_REUSEADDR)).booleanValue();
}
/**
* Sets this socket's {@link SocketOptions#SO_SNDBUF receive buffer size}.
*/
public void setReceiveBufferSize(int size) throws SocketException {
- checkClosedAndCreate(true);
+ checkOpen();
if (size < 1) {
throw new IllegalArgumentException("size < 1");
}
@@ -470,7 +403,7 @@
* Returns this socket's {@link SocketOptions#SO_RCVBUF receive buffer size}.
*/
public int getReceiveBufferSize() throws SocketException {
- checkClosedAndCreate(true);
+ checkOpen();
return ((Integer) impl.getOption(SocketOptions.SO_RCVBUF)).intValue();
}
@@ -498,8 +431,7 @@
* @param bandwidth
* the value representing the importance of high bandwidth.
*/
- public void setPerformancePreferences(int connectionTime, int latency,
- int bandwidth) {
+ public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
// Our socket implementation only provide one protocol: TCP/IP, so
// we do nothing for this method
}
diff --git a/luni/src/main/java/java/net/Socket.java b/luni/src/main/java/java/net/Socket.java
index 06e1ab3..f7788b5 100644
--- a/luni/src/main/java/java/net/Socket.java
+++ b/luni/src/main/java/java/net/Socket.java
@@ -22,7 +22,7 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.SocketChannel;
-import libcore.io.IoUtils;
+import libcore.io.IoBridge;
/**
* Provides a client-side TCP socket.
@@ -947,7 +947,7 @@
}
private void cacheLocalAddress() {
- this.localAddress = IoUtils.getSocketLocalAddress(impl.fd);
+ this.localAddress = IoBridge.getSocketLocalAddress(impl.fd);
}
/**
diff --git a/luni/src/main/java/java/net/SocketException.java b/luni/src/main/java/java/net/SocketException.java
index b7366b3..23e5c01 100644
--- a/luni/src/main/java/java/net/SocketException.java
+++ b/luni/src/main/java/java/net/SocketException.java
@@ -28,19 +28,19 @@
private static final long serialVersionUID = -5935874303556886934L;
/**
- * Constructs a new instance with the current stack trace.
+ * Constructs a new instance.
*/
public SocketException() {
}
/**
- * Constructs a new instance with the current stack trace and given detail message.
+ * Constructs a new instance with the given detail message.
*/
public SocketException(String detailMessage) {
super(detailMessage);
}
/**
- * Constructs a new instance with the current stack trace and given cause.
+ * Constructs a new instance with the given cause.
* @hide internal use only
*/
public SocketException(Throwable cause) {
@@ -48,7 +48,7 @@
}
/**
- * Constructs a new instance with the current stack trace, given detail message and given cause.
+ * Constructs a new instance with given detail message and cause.
* @hide internal use only
*/
public SocketException(String detailMessage, Throwable cause) {
diff --git a/luni/src/main/java/java/net/SocketImpl.java b/luni/src/main/java/java/net/SocketImpl.java
index 3d0507c..92de9cf 100644
--- a/luni/src/main/java/java/net/SocketImpl.java
+++ b/luni/src/main/java/java/net/SocketImpl.java
@@ -142,6 +142,13 @@
}
/**
+ * @hide used by java.nio
+ */
+ public FileDescriptor getFD$() {
+ return fd;
+ }
+
+ /**
* Gets the remote address this socket is connected to.
*
* @return the remote address of this socket.
diff --git a/luni/src/main/java/java/net/SocketTimeoutException.java b/luni/src/main/java/java/net/SocketTimeoutException.java
index 62e8731..8f8ef82 100644
--- a/luni/src/main/java/java/net/SocketTimeoutException.java
+++ b/luni/src/main/java/java/net/SocketTimeoutException.java
@@ -28,15 +28,31 @@
private static final long serialVersionUID = -8846654841826352300L;
/**
- * Constructs a new instance with the current stack trace.
+ * Constructs a new instance.
*/
public SocketTimeoutException() {
}
/**
- * Constructs a new instance with the current stack trace and given detail message.
+ * Constructs a new instance with the given detail message.
*/
public SocketTimeoutException(String detailMessage) {
super(detailMessage);
}
+
+ /**
+ * Constructs a new instance with given cause.
+ * @hide internal use only
+ */
+ public SocketTimeoutException(Throwable cause) {
+ super(null, cause);
+ }
+
+ /**
+ * Constructs a new instance with given detail message and cause.
+ * @hide internal use only
+ */
+ public SocketTimeoutException(String detailMessage, Throwable cause) {
+ super(detailMessage, cause);
+ }
}
diff --git a/luni/src/main/java/java/net/URL.java b/luni/src/main/java/java/net/URL.java
index 8779cca..4e68b39 100644
--- a/luni/src/main/java/java/net/URL.java
+++ b/luni/src/main/java/java/net/URL.java
@@ -19,9 +19,12 @@
import java.io.IOException;
import java.io.InputStream;
+import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
+import java.io.Serializable;
import java.util.Hashtable;
import java.util.Locale;
+import java.util.jar.JarFile;
import libcore.net.http.HttpHandler;
import libcore.net.http.HttpsHandler;
import libcore.net.url.FileHandler;
@@ -29,175 +32,126 @@
import libcore.net.url.JarHandler;
/**
- * A URL instance specifies the location of a resource on the internet as
- * specified by <a href="http://www.ietf.org/rfc/rfc1738.txt">RFC 1738</a>.
- * Such a resource can be a simple file or a service
- * which generates the output dynamically. A URL is divided in its parts
- * protocol, host name, port, path, file, user-info, query, reference and
- * authority. However, not each of this parts has to be defined.
+ * A Uniform Resource Locator that identifies the location of an Internet
+ * resource as specified by <a href="http://www.ietf.org/rfc/rfc1738.txt">RFC
+ * 1738</a>.
+ *
+ * <h3>Parts of a URL</h3>
+ * A URL is composed of many parts. This class can both parse URL strings into
+ * parts and compose URL strings from parts. For example, consider the parts of
+ * this URL:
+ * {@code http://username:password@host:8080/directory/file?query#ref}:
+ * <table>
+ * <tr><th>Component</th><th>Example value</th><th>Also known as</th></tr>
+ * <tr><td>{@link #getProtocol() Protocol}</td><td>{@code http}</td><td>scheme</td></tr>
+ * <tr><td>{@link #getAuthority() Authority}</td><td>{@code username:password@host:8080}</td><td></td></tr>
+ * <tr><td>{@link #getUserInfo() User Info}</td><td>{@code username:password}</td><td></td></tr>
+ * <tr><td>{@link #getHost() Host}</td><td>{@code host}</td><td></td></tr>
+ * <tr><td>{@link #getPort() Port}</td><td>{@code 8080}</td><td></td></tr>
+ * <tr><td>{@link #getFile() File}</td><td>{@code /directory/file?query}</td><td></td></tr>
+ * <tr><td>{@link #getPath() Path}</td><td>{@code /directory/file}</td><td></td></tr>
+ * <tr><td>{@link #getQuery() Query}</td><td>{@code query}</td><td></td></tr>
+ * <tr><td>{@link #getRef() Ref}</td><td>{@code ref}</td><td>fragment</td></tr>
+ * </table>
+ *
+ * <h3>Supported Protocols</h3>
+ * This class may be used to construct URLs with the following protocols:
+ * <ul>
+ * <li><strong>file</strong>: read files from the local filesystem.
+ * <li><strong>ftp</strong>: <a href="http://www.ietf.org/rfc/rfc959.txt">File
+ * Transfer Protocol</a>
+ * <li><strong>http</strong>: <a href="http://www.ietf.org/rfc/rfc2616.txt">Hypertext
+ * Transfer Protocol</a>
+ * <li><strong>https</strong>: <a href="http://www.ietf.org/rfc/rfc2818.txt">HTTP
+ * over TLS</a>
+ * <li><strong>jar</strong>: read {@link JarFile Jar files} from the
+ * filesystem</li>
+ * </ul>
+ * In general, attempts to create URLs with any other protocol will fail with a
+ * {@link MalformedURLException}. Applications may install handlers for other
+ * schemes using {@link #setURLStreamHandlerFactory} or with the {@code
+ * java.protocol.handler.pkgs} system property.
+ *
+ * <p>The {@link URI} class can be used to manipulate URLs of any protocol.
*/
-public final class URL implements java.io.Serializable {
+public final class URL implements Serializable {
private static final long serialVersionUID = -7627629688361524110L;
- private static final NetPermission specifyStreamHandlerPermission = new NetPermission(
- "specifyStreamHandler");
+ private static URLStreamHandlerFactory streamHandlerFactory;
- private int hashCode;
-
- /**
- * The receiver's filename.
- *
- * @serial the file of this URL
- *
- */
- private String file;
-
- /**
- * The receiver's protocol identifier.
- *
- * @serial the protocol of this URL (http, file)
- *
- */
- private String protocol = null;
-
- /**
- * The receiver's host name.
- *
- * @serial the host of this URL
- *
- */
- private String host;
-
- /**
- * The receiver's port number.
- *
- * @serial the port of this URL
- *
- */
- private int port = -1;
-
- /**
- * The receiver's authority.
- *
- * @serial the authority of this URL
- *
- */
- private String authority = null;
-
- /**
- * The receiver's userInfo.
- */
- private transient String userInfo = null;
-
- /**
- * The receiver's path.
- */
- private transient String path = null;
-
- /**
- * The receiver's query.
- */
- private transient String query = null;
-
- /**
- * The receiver's reference.
- *
- * @serial the reference of this URL
- *
- */
- private String ref = null;
-
- /**
- * Cache for storing protocol handler
- */
+ /** Cache of protocols to their handlers */
private static final Hashtable<String, URLStreamHandler> streamHandlers
= new Hashtable<String, URLStreamHandler>();
- /**
- * The URL Stream (protocol) Handler
- */
- transient URLStreamHandler strmHandler;
+ private String protocol;
+ private String authority;
+ private String host;
+ private int port = -1;
+ private String file;
+ private String ref;
+
+ private transient String userInfo;
+ private transient String path;
+ private transient String query;
+
+ transient URLStreamHandler streamHandler;
/**
- * The factory responsible for producing URL Stream (protocol) Handler
+ * The cached hash code, or 0 if it hasn't been computed yet. Unlike the RI,
+ * this implementation's hashCode is transient because the hash code is
+ * unspecified and may vary between VMs or versions.
*/
- private static URLStreamHandlerFactory streamHandlerFactory;
+ private transient int hashCode;
/**
- * Sets the {@code URLStreamHandlerFactory} which creates protocol specific
- * stream handlers. This method can be invoked only once during an
- * application's lifetime. If the {@code URLStreamHandlerFactory} is already
- * set an {@link Error} will be thrown.
- * <p>
- * A security check is performed to verify whether the current policy allows
- * to set the stream handler factory.
+ * Sets the stream handler factory for this VM.
*
- * @param streamFactory
- * the factory to be used for creating stream protocol handlers.
+ * @throws Error if a URLStreamHandlerFactory has already been installed
+ * for the current VM.
*/
- public static synchronized void setURLStreamHandlerFactory(
- URLStreamHandlerFactory streamFactory) {
+ public static synchronized void setURLStreamHandlerFactory(URLStreamHandlerFactory factory) {
if (streamHandlerFactory != null) {
throw new Error("Factory already set");
}
streamHandlers.clear();
- streamHandlerFactory = streamFactory;
+ streamHandlerFactory = factory;
}
/**
- * Creates a new URL instance by parsing the string {@code spec}.
+ * Creates a new URL instance by parsing {@code spec}.
*
- * @param spec
- * the URL string representation which has to be parsed.
- * @throws MalformedURLException
- * if the given string {@code spec} could not be parsed as a
- * URL.
+ * @throws MalformedURLException if {@code spec} could not be parsed as a
+ * URL.
*/
public URL(String spec) throws MalformedURLException {
- this((URL) null, spec, (URLStreamHandler) null);
+ this((URL) null, spec, null);
}
/**
- * Creates a new URL to the specified resource {@code spec}. This URL is
- * relative to the given {@code context}. If the protocol of the parsed URL
- * does not match with the protocol of the context URL, then the newly
- * created URL is absolute and bases only on the given URL represented by
- * {@code spec}. Otherwise the protocol is defined by the context URL.
+ * Creates a new URL by resolving {@code spec} relative to {@code context}.
*
- * @param context
- * the URL which is used as the context.
- * @param spec
- * the URL string representation which has to be parsed.
- * @throws MalformedURLException
- * if the given string {@code spec} could not be parsed as a URL
- * or an invalid protocol has been found.
+ * @param context the URL to which {@code spec} is relative, or null for
+ * no context in which case {@code spec} must be an absolute URL.
+ * @throws MalformedURLException if {@code spec} could not be parsed as a
+ * URL or has an unsupported protocol.
*/
public URL(URL context, String spec) throws MalformedURLException {
- this(context, spec, (URLStreamHandler) null);
+ this(context, spec, null);
}
/**
- * Creates a new URL to the specified resource {@code spec}. This URL is
- * relative to the given {@code context}. The {@code handler} will be used
- * to parse the URL string representation. If this argument is {@code null}
- * the default {@code URLStreamHandler} will be used. If the protocol of the
- * parsed URL does not match with the protocol of the context URL, then the
- * newly created URL is absolute and bases only on the given URL represented
- * by {@code spec}. Otherwise the protocol is defined by the context URL.
+ * Creates a new URL by resolving {@code spec} relative to {@code context}.
*
- * @param context
- * the URL which is used as the context.
- * @param spec
- * the URL string representation which has to be parsed.
- * @param handler
- * the specific stream handler to be used by this URL.
- * @throws MalformedURLException
- * if the given string {@code spec} could not be parsed as a URL
- * or an invalid protocol has been found.
+ * @param context the URL to which {@code spec} is relative, or null for
+ * no context in which case {@code spec} must be an absolute URL.
+ * @param handler the stream handler for this URL, or null for the
+ * protocol's default stream handler.
+ * @throws MalformedURLException if the given string {@code spec} could not
+ * be parsed as a URL or an invalid protocol has been found.
*/
public URL(URL context, String spec, URLStreamHandler handler) throws MalformedURLException {
if (handler != null) {
- strmHandler = handler;
+ streamHandler = handler;
}
if (spec == null) {
@@ -208,15 +162,10 @@
// The spec includes a protocol if it includes a colon character
// before the first occurrence of a slash character. Note that,
// "protocol" is the field which holds this URLs protocol.
- int index;
- try {
- index = spec.indexOf(':');
- } catch (NullPointerException e) {
- throw new MalformedURLException(e.toString());
- }
- int startIPv6Addr = spec.indexOf('[');
+ int index = spec.indexOf(':');
+ int startIpv6Address = spec.indexOf('[');
if (index >= 0) {
- if ((startIPv6Addr == -1) || (index < startIPv6Addr)) {
+ if ((startIpv6Address == -1) || (index < startIpv6Address)) {
protocol = spec.substring(0, index);
// According to RFC 2396 scheme part should match
// the following expression:
@@ -254,8 +203,8 @@
.getAuthority(), context.getUserInfo(), cPath,
context.getQuery(), null);
}
- if (strmHandler == null) {
- strmHandler = context.strmHandler;
+ if (streamHandler == null) {
+ streamHandler = context.streamHandler;
}
}
} else {
@@ -269,16 +218,16 @@
set(context.getProtocol(), context.getHost(), context.getPort(),
context.getAuthority(), context.getUserInfo(), context
.getPath(), context.getQuery(), null);
- if (strmHandler == null) {
- strmHandler = context.strmHandler;
+ if (streamHandler == null) {
+ streamHandler = context.streamHandler;
}
}
// If the stream handler has not been determined, set it
// to the default for the specified protocol.
- if (strmHandler == null) {
+ if (streamHandler == null) {
setupStreamHandler();
- if (strmHandler == null) {
+ if (streamHandler == null) {
throw new MalformedURLException("Unknown protocol: " + protocol);
}
}
@@ -292,7 +241,7 @@
// increment it to point at either character 0 or the character
// after the colon.
try {
- strmHandler.parseURL(this, spec, ++index, spec.length());
+ streamHandler.parseURL(this, spec, ++index, spec.length());
} catch (Exception e) {
throw new MalformedURLException(e.toString());
}
@@ -303,64 +252,41 @@
}
/**
- * Creates a new URL instance using the given arguments. The URL uses the
- * default port for the specified protocol.
+ * Creates a new URL of the given component parts. The URL uses the
+ * protocol's default port.
*
- * @param protocol
- * the protocol of the new URL.
- * @param host
- * the host name or IP address of the new URL.
- * @param file
- * the name of the resource.
- * @throws MalformedURLException
- * if the combination of all arguments do not represent a valid
- * URL or the protocol is invalid.
+ * @throws MalformedURLException if the combination of all arguments do not
+ * represent a valid URL or if the protocol is invalid.
*/
- public URL(String protocol, String host, String file)
- throws MalformedURLException {
- this(protocol, host, -1, file, (URLStreamHandler) null);
+ public URL(String protocol, String host, String file) throws MalformedURLException {
+ this(protocol, host, -1, file, null);
}
/**
- * Creates a new URL instance using the given arguments. The URL uses the
- * specified port instead of the default port for the given protocol.
+ * Creates a new URL of the given component parts. The URL uses the
+ * protocol's default port.
*
- * @param protocol
- * the protocol of the new URL.
- * @param host
- * the host name or IP address of the new URL.
- * @param port
- * the specific port number of the URL. {@code -1} represents the
- * default port of the protocol.
- * @param file
- * the name of the resource.
- * @throws MalformedURLException
- * if the combination of all arguments do not represent a valid
- * URL or the protocol is invalid.
+ * @param host the host name or IP address of the new URL.
+ * @param port the port, or {@code -1} for the protocol's default port.
+ * @param file the name of the resource.
+ * @throws MalformedURLException if the combination of all arguments do not
+ * represent a valid URL or if the protocol is invalid.
*/
- public URL(String protocol, String host, int port, String file)
- throws MalformedURLException {
- this(protocol, host, port, file, (URLStreamHandler) null);
+ public URL(String protocol, String host, int port, String file) throws MalformedURLException {
+ this(protocol, host, port, file, null);
}
/**
- * Creates a new URL instance using the given arguments. The URL uses the
- * specified port instead of the default port for the given protocol.
+ * Creates a new URL of the given component parts. The URL uses the
+ * protocol's default port.
*
- * @param protocol
- * the protocol of the new URL.
- * @param host
- * the host name or IP address of the new URL.
- * @param port
- * the specific port number of the URL. {@code -1} represents the
- * default port of the protocol.
- * @param file
- * the name of the resource.
- * @param handler
- * the stream handler to be used by this URL.
- * @throws MalformedURLException
- * if the combination of all arguments do not represent a valid
- * URL or the protocol is invalid.
+ * @param host the host name or IP address of the new URL.
+ * @param port the port, or {@code -1} for the protocol's default port.
+ * @param file the name of the resource.
+ * @param handler the stream handler for this URL, or null for the
+ * protocol's default stream handler.
+ * @throws MalformedURLException if the combination of all arguments do not
+ * represent a valid URL or if the protocol is invalid.
*/
public URL(String protocol, String host, int port, String file,
URLStreamHandler handler) throws MalformedURLException {
@@ -368,7 +294,7 @@
throw new MalformedURLException("Port out of range: " + port);
}
- if (host != null && host.indexOf(":") != -1 && host.charAt(0) != '[') {
+ if (host != null && host.contains(":") && host.charAt(0) != '[') {
host = "[" + host + "]";
}
@@ -397,11 +323,11 @@
// receiver's protocol if the handler was null.
if (handler == null) {
setupStreamHandler();
- if (strmHandler == null) {
+ if (streamHandler == null) {
throw new MalformedURLException("Unknown protocol: " + protocol);
}
} else {
- strmHandler = handler;
+ streamHandler = handler;
}
}
@@ -434,20 +360,8 @@
* Sets the properties of this URL using the provided arguments. Only a
* {@code URLStreamHandler} can use this method to set fields of the
* existing URL instance. A URL is generally constant.
- *
- * @param protocol
- * the protocol to be set.
- * @param host
- * the host name to be set.
- * @param port
- * the port number to be set.
- * @param file
- * the file to be set.
- * @param ref
- * the reference to be set.
*/
- protected void set(String protocol, String host, int port, String file,
- String ref) {
+ protected void set(String protocol, String host, int port, String file, String ref) {
if (this.protocol == null) {
this.protocol = protocol;
}
@@ -497,59 +411,48 @@
if (this.getClass() != o.getClass()) {
return false;
}
- return strmHandler.equals(this, (URL) o);
+ return streamHandler.equals(this, (URL) o);
}
/**
- * Returns whether this URL refers to the same resource as the given
- * argument {@code otherURL}. All URL components except the reference field
- * are compared.
- *
- * @param otherURL
- * the URL to compare against.
- * @return {@code true} if both instances refer to the same resource,
- * {@code false} otherwise.
+ * Returns true if this URL refers to the same resource as {@code otherURL}.
+ * All URL components except the reference field are compared.
*/
public boolean sameFile(URL otherURL) {
- return strmHandler.sameFile(this, otherURL);
+ return streamHandler.sameFile(this, otherURL);
}
- /**
- * Gets the hashcode value of this URL instance.
- *
- * @return the appropriate hashcode value.
- */
- @Override
- public int hashCode() {
+ @Override public int hashCode() {
if (hashCode == 0) {
- hashCode = strmHandler.hashCode(this);
+ hashCode = streamHandler.hashCode(this);
}
return hashCode;
}
/**
* Sets the receiver's stream handler to one which is appropriate for its
- * protocol. Throws a MalformedURLException if no reasonable handler is
- * available.
- * <p>
- * Note that this will overwrite any existing stream handler with the new
- * one. Senders must check if the strmHandler is null before calling the
+ * protocol.
+ *
+ * <p>Note that this will overwrite any existing stream handler with the new
+ * one. Senders must check if the streamHandler is null before calling the
* method if they do not want this behavior (a speed optimization).
+ *
+ * @throws MalformedURLException if no reasonable handler is available.
*/
void setupStreamHandler() {
// Check for a cached (previously looked up) handler for
// the requested protocol.
- strmHandler = streamHandlers.get(protocol);
- if (strmHandler != null) {
+ streamHandler = streamHandlers.get(protocol);
+ if (streamHandler != null) {
return;
}
// If there is a stream handler factory, then attempt to
// use it to create the handler.
if (streamHandlerFactory != null) {
- strmHandler = streamHandlerFactory.createURLStreamHandler(protocol);
- if (strmHandler != null) {
- streamHandlers.put(protocol, strmHandler);
+ streamHandler = streamHandlerFactory.createURLStreamHandler(protocol);
+ if (streamHandler != null) {
+ streamHandlers.put(protocol, streamHandler);
return;
}
}
@@ -561,10 +464,10 @@
for (String packageName : packageList.split("\\|")) {
String className = packageName + "." + protocol + ".Handler";
try {
- Class<?> klass = Class.forName(className, true, ClassLoader.getSystemClassLoader());
- strmHandler = (URLStreamHandler) klass.newInstance();
- if (strmHandler != null) {
- streamHandlers.put(protocol, strmHandler);
+ Class<?> c = Class.forName(className, true, ClassLoader.getSystemClassLoader());
+ streamHandler = (URLStreamHandler) c.newInstance();
+ if (streamHandler != null) {
+ streamHandlers.put(protocol, streamHandler);
}
return;
} catch (IllegalAccessException ignored) {
@@ -576,87 +479,75 @@
// Fall back to a built-in stream handler if the user didn't supply one
if (protocol.equals("file")) {
- strmHandler = new FileHandler();
+ streamHandler = new FileHandler();
} else if (protocol.equals("ftp")) {
- strmHandler = new FtpHandler();
+ streamHandler = new FtpHandler();
} else if (protocol.equals("http")) {
- strmHandler = new HttpHandler();
+ streamHandler = new HttpHandler();
} else if (protocol.equals("https")) {
- strmHandler = new HttpsHandler();
+ streamHandler = new HttpsHandler();
} else if (protocol.equals("jar")) {
- strmHandler = new JarHandler();
+ streamHandler = new JarHandler();
}
- if (strmHandler != null) {
- streamHandlers.put(protocol, strmHandler);
+ if (streamHandler != null) {
+ streamHandlers.put(protocol, streamHandler);
}
}
/**
- * Gets the content of the resource which is referred by this URL. By
- * default one of the following object types will be returned:
- * <p>
- * <li>Image for pictures</li>
- * <li>AudioClip for audio sequences</li>
- * <li>{@link InputStream} for all other data</li>
- *
- * @return the content of the referred resource.
- * @throws IOException
- * if an error occurs obtaining the content.
+ * Returns the content of the resource which is referred by this URL. By
+ * default, this returns an {@code InputStream} or null if the content type
+ * of the response is unknown.
*/
public final Object getContent() throws IOException {
return openConnection().getContent();
}
/**
- * Gets the content of the resource which is referred by this URL. The
- * argument {@code types} is an array of allowed or expected object types.
- * {@code null} will be returned if the obtained object type does not match
- * with one from this list. Otherwise the first type that matches will be
- * used.
- *
- * @param types
- * the list of allowed or expected object types.
- * @return the object representing the resource referred by this URL,
- * {@code null} if the content does not match to a specified content
- * type.
- * @throws IOException
- * if an error occurs obtaining the content.
+ * Equivalent to {@code openConnection().getContent(types)}.
*/
- // Param not generic in spec
- @SuppressWarnings("unchecked")
+ @SuppressWarnings("unchecked") // Param not generic in spec
public final Object getContent(Class[] types) throws IOException {
return openConnection().getContent(types);
}
/**
- * Opens an InputStream to read the resource referred by this URL.
- *
- * @return the stream which allows to read the resource.
- * @throws IOException
- * if an error occurs while opening the InputStream.
+ * Equivalent to {@code openConnection().getInputStream(types)}.
*/
- public final InputStream openStream() throws java.io.IOException {
+ public final InputStream openStream() throws IOException {
return openConnection().getInputStream();
}
/**
- * Opens a connection to the remote resource specified by this URL. This
- * connection allows bidirectional data transfer.
+ * Returns a new connection to the resource referred to by this URL.
*
- * @return the connection to this URL.
- * @throws IOException
- * if an error occurs while opening the connection.
+ * @throws IOException if an error occurs while opening the connection.
*/
public URLConnection openConnection() throws IOException {
- return strmHandler.openConnection(this);
+ return streamHandler.openConnection(this);
}
/**
- * Converts this URL instance into an equivalent URI object.
+ * Returns a new connection to the resource referred to by this URL.
*
- * @return the URI instance that represents this URL.
- * @throws URISyntaxException
- * if this URL cannot be converted into a URI.
+ * @param proxy the proxy through which the connection will be established.
+ * @throws IOException if an I/O error occurs while opening the connection.
+ * @throws IllegalArgumentException if the argument proxy is null or of is
+ * an invalid type.
+ * @throws UnsupportedOperationException if the protocol handler does not
+ * support opening connections through proxies.
+ */
+ public URLConnection openConnection(Proxy proxy) throws IOException {
+ if (proxy == null) {
+ throw new IllegalArgumentException("proxy == null");
+ }
+ return streamHandler.openConnection(this, proxy);
+ }
+
+ /**
+ * Returns the URI equivalent to this URL.
+ *
+ * @throws URISyntaxException if this URL cannot be converted into a URI.
*/
public URI toURI() throws URISyntaxException {
return new URI(toExternalForm());
@@ -669,74 +560,33 @@
* @hide
*/
public URI toURILenient() throws URISyntaxException {
- if (strmHandler == null) {
+ if (streamHandler == null) {
throw new IllegalStateException(protocol);
}
- return new URI(strmHandler.toExternalForm(this, true));
- }
-
- /**
- * Opens a connection to the remote resource specified by this URL. The
- * connection will be established through the given proxy and allows
- * bidirectional data transfer.
- *
- * @param proxy
- * the proxy through which the connection will be established.
- * @return the appropriate URLConnection instance representing the
- * connection to this URL.
- * @throws IOException
- * if an I/O error occurs while opening the connection.
- * @throws IllegalArgumentException
- * if the argument proxy is {@code null} or is an invalid type.
- * @throws UnsupportedOperationException
- * if the protocol handler does not support opening connections
- * through proxies.
- */
- public URLConnection openConnection(Proxy proxy) throws IOException {
- if (proxy == null) {
- throw new IllegalArgumentException("proxy == null");
- }
- return strmHandler.openConnection(this, proxy);
+ return new URI(streamHandler.toExternalForm(this, true));
}
/**
* Returns a string containing a concise, human-readable representation of
* this URL. The returned string is the same as the result of the method
* {@code toExternalForm()}.
- *
- * @return the string representation of this URL.
*/
- @Override
- public String toString() {
+ @Override public String toString() {
return toExternalForm();
}
/**
* Returns a string containing a concise, human-readable representation of
* this URL.
- *
- * @return the string representation of this URL.
*/
public String toExternalForm() {
- if (strmHandler == null) {
+ if (streamHandler == null) {
return "unknown protocol(" + protocol + ")://" + host + file;
}
- return strmHandler.toExternalForm(this);
+ return streamHandler.toExternalForm(this);
}
- /**
- * This method is called to restore the state of a URL object that has been
- * serialized. The stream handler is determined from the URL's protocol.
- *
- * @param stream
- * the stream to read from.
- *
- * @throws IOException
- * if an IO Exception occurs while reading the stream or the
- * handler can not be found.
- */
- private void readObject(java.io.ObjectInputStream stream)
- throws java.io.IOException {
+ private void readObject(ObjectInputStream stream) throws IOException {
try {
stream.defaultReadObject();
if (host != null && authority == null) {
@@ -754,142 +604,111 @@
}
}
setupStreamHandler();
- if (strmHandler == null) {
+ if (streamHandler == null) {
throw new IOException("Unknown protocol: " + protocol);
}
+ hashCode = 0; // necessary until http://b/4471249 is fixed
} catch (ClassNotFoundException e) {
- throw new IOException(e.toString());
+ throw new IOException(e);
}
}
- /**
- * This method is called to write any non-transient, non-static variables
- * into the output stream.
- * <p>
- * Note that, we really only need the readObject method but the spec that
- * says readObject will be ignored if no writeObject is present.
- *
- * @param s
- * the stream to write on.
- * @throws IOException
- * if an IO Exception occurs during the write.
- */
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
}
- /**
- * Gets the value of the file part of this URL.
- *
- * @return the file name this URL refers to or an empty string if the file
- * part is not set.
- */
- public String getFile() {
- return file;
- }
-
- /**
- * Gets the value of the host part of this URL.
- *
- * @return the host name or IP address of this URL.
- */
- public String getHost() {
- return host;
- }
-
- /**
- * Gets the port number of this URL or {@code -1} if the port is not set.
- *
- * @return the port number of this URL.
- */
- public int getPort() {
- return port;
- }
-
/** @hide */
public int getEffectivePort() {
return URI.getEffectivePort(protocol, port);
}
/**
- * Gets the protocol of this URL.
- *
- * @return the protocol type of this URL.
+ * Returns the protocol of this URL like "http" or "file". This is also
+ * known as the scheme. The returned string is lower case.
*/
public String getProtocol() {
return protocol;
}
/**
- * Gets the value of the reference part of this URL.
- *
- * @return the reference part of this URL.
- */
- public String getRef() {
- return ref;
- }
-
- /**
- * Gets the value of the query part of this URL.
- *
- * @return the query part of this URL.
- */
- public String getQuery() {
- return query;
- }
-
- /**
- * Gets the value of the path part of this URL.
- *
- * @return the path part of this URL.
- */
- public String getPath() {
- return path;
- }
-
- /**
- * Gets the value of the user-info part of this URL.
- *
- * @return the user-info part of this URL.
- */
- public String getUserInfo() {
- return userInfo;
- }
-
- /**
- * Gets the value of the authority part of this URL.
- *
- * @return the authority part of this URL.
+ * Returns the authority part of this URL.
*/
public String getAuthority() {
return authority;
}
/**
+ * Returns the user-info part of this URL.
+ */
+ public String getUserInfo() {
+ return userInfo;
+ }
+
+ /**
+ * Returns the host name or IP address of this URL.
+ */
+ public String getHost() {
+ return host;
+ }
+
+ /**
+ * Returns the port number of this URL, or {@code -1} if this URL has no
+ * explicit port.
+ *
+ * <p>If this URL has no explicit port, connections opened using this URL
+ * will use its {@link #getDefaultPort() default port}.
+ */
+ public int getPort() {
+ return port;
+ }
+
+ /**
+ * Returns the default port number of the protocol used by this URL. If no
+ * default port is defined by the protocol or the {@code URLStreamHandler},
+ * {@code -1} will be returned.
+ *
+ * @see URLStreamHandler#getDefaultPort
+ */
+ public int getDefaultPort() {
+ return streamHandler.getDefaultPort();
+ }
+
+ /**
+ * Returns the file of this URL, or an empty string if the file is not set.
+ */
+ public String getFile() {
+ return file;
+ }
+
+ /**
+ * Returns the path part of this URL.
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * Returns the query part of this URL.
+ */
+ public String getQuery() {
+ return query;
+ }
+
+ /**
+ * Returns the value of the reference part of this URL. This is also known
+ * as the fragment.
+ */
+ public String getRef() {
+ return ref;
+ }
+
+ /**
* Sets the properties of this URL using the provided arguments. Only a
* {@code URLStreamHandler} can use this method to set fields of the
* existing URL instance. A URL is generally constant.
- *
- * @param protocol
- * the protocol to be set.
- * @param host
- * the host name to be set.
- * @param port
- * the port number to be set.
- * @param authority
- * the authority to be set.
- * @param userInfo
- * the user-info to be set.
- * @param path
- * the path to be set.
- * @param query
- * the query to be set.
- * @param ref
- * the reference to be set.
*/
- protected void set(String protocol, String host, int port,
- String authority, String userInfo, String path, String query,
- String ref) {
+ protected void set(String protocol, String host, int port, String authority, String userInfo,
+ String path, String query, String ref) {
String filePart = path;
if (query != null && !query.isEmpty()) {
if (filePart != null) {
@@ -904,16 +723,4 @@
this.path = path;
this.query = query;
}
-
- /**
- * Gets the default port number of the protocol used by this URL. If no
- * default port is defined by the protocol or the {@code URLStreamHandler},
- * {@code -1} will be returned.
- *
- * @return the default port number according to the protocol of this URL.
- * @see URLStreamHandler#getDefaultPort
- */
- public int getDefaultPort() {
- return strmHandler.getDefaultPort();
- }
}
diff --git a/luni/src/main/java/java/net/URLClassLoader.java b/luni/src/main/java/java/net/URLClassLoader.java
index 735793c..1c8bc43 100644
--- a/luni/src/main/java/java/net/URLClassLoader.java
+++ b/luni/src/main/java/java/net/URLClassLoader.java
@@ -51,6 +51,7 @@
* loaded by this {@code URLClassLoader} are granted permission to access the
* URLs contained in the URL search list.
*/
+@FindBugsSuppressWarnings({ "DMI_COLLECTION_OF_URLS", "DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED" })
public class URLClassLoader extends SecureClassLoader {
ArrayList<URL> originalUrls;
@@ -174,13 +175,11 @@
if (is == null) {
return null;
}
- byte[] clBuf = null;
+ byte[] clBuf;
try {
- clBuf = getBytes(is);
+ clBuf = Streams.readFully(is);
} catch (IOException e) {
return null;
- } finally {
- IoUtils.closeQuietly(is);
}
if (packageName != null) {
String packageDotName = packageName.replace('/', '.');
@@ -329,15 +328,12 @@
}
private Class<?> createClass(JarEntry entry, Manifest manifest, String packageName, String origName) {
- InputStream is = null;
- byte[] clBuf = null;
+ byte[] clBuf;
try {
- is = jf.getInputStream(entry);
- clBuf = getBytes(is);
+ InputStream is = jf.getInputStream(entry);
+ clBuf = Streams.readFully(is);
} catch (IOException e) {
return null;
- } finally {
- IoUtils.closeQuietly(is);
}
if (packageName != null) {
String packageDotName = packageName.replace('/', '.');
@@ -600,17 +596,6 @@
}
/**
- * Converts an input stream into a byte array.
- *
- * @param is
- * the input stream
- * @return byte[] the byte array
- */
- private static byte[] getBytes(InputStream is) throws IOException {
- return Streams.readFully(is);
- }
-
- /**
* Gets all permissions for the specified {@code codesource}. First, this
* method retrieves the permissions from the system policy. If the protocol
* is "file:/" then a new permission, {@code FilePermission}, granting the
diff --git a/luni/src/main/java/java/net/URLConnection.java b/luni/src/main/java/java/net/URLConnection.java
index 26b805f..a8d967c 100644
--- a/luni/src/main/java/java/net/URLConnection.java
+++ b/luni/src/main/java/java/net/URLConnection.java
@@ -353,13 +353,8 @@
}
/**
- * Returns the default value for the specified request {@code field} or {@code
- * null} if the field could not be found. The base implementation of this
- * method returns always {@code null}.
+ * Returns null.
*
- * @param field
- * the request field whose default value shall be returned.
- * @return the default value for the given field.
* @deprecated Use {@link #getRequestProperty}
*/
@Deprecated
@@ -856,16 +851,9 @@
}
/**
- * Sets the default value of the specified request header field. This value
- * will be used for the specific field of every newly created connection.
- * The base implementation of this method does nothing.
+ * Does nothing.
*
- * @param field
- * the request header field to be set.
- * @param value
- * the default value to be used.
- * @deprecated Use {@link #setRequestProperty} of an existing {@code
- * URLConnection} instance.
+ * @deprecated Use {@link URLConnection#setRequestProperty(String, String)}.
*/
@Deprecated
public static void setDefaultRequestProperty(String field, String value) {
diff --git a/luni/src/main/java/java/net/URLStreamHandler.java b/luni/src/main/java/java/net/URLStreamHandler.java
index 7a86644..dc1c680 100644
--- a/luni/src/main/java/java/net/URLStreamHandler.java
+++ b/luni/src/main/java/java/net/URLStreamHandler.java
@@ -90,7 +90,7 @@
throw new StringIndexOutOfBoundsException(end - 2 - start);
}
if (end < start) {
- if (this != u.strmHandler) {
+ if (this != u.streamHandler) {
throw new SecurityException();
}
return;
@@ -251,7 +251,7 @@
@Deprecated
protected void setURL(URL u, String protocol, String host, int port,
String file, String ref) {
- if (this != u.strmHandler) {
+ if (this != u.streamHandler) {
throw new SecurityException();
}
u.set(protocol, host, port, file, ref);
@@ -283,7 +283,7 @@
protected void setURL(URL u, String protocol, String host, int port,
String authority, String userInfo, String file, String query,
String ref) {
- if (this != u.strmHandler) {
+ if (this != u.streamHandler) {
throw new SecurityException();
}
u.set(protocol, host, port, authority, userInfo, file, query, ref);
diff --git a/luni/src/main/java/java/net/UnknownServiceException.java b/luni/src/main/java/java/net/UnknownServiceException.java
index a8afa41..bdcf6a5 100644
--- a/luni/src/main/java/java/net/UnknownServiceException.java
+++ b/luni/src/main/java/java/net/UnknownServiceException.java
@@ -30,15 +30,23 @@
private static final long serialVersionUID = -4169033248853639508L;
/**
- * Constructs a new instance with the current stack trace.
+ * Constructs a new instance.
*/
public UnknownServiceException() {
}
/**
- * Constructs a new instance with the current stack trace and given detail message.
+ * Constructs a new instance with the given detail message.
*/
public UnknownServiceException(String detailMessage) {
super(detailMessage);
}
+
+ /**
+ * Constructs a new instance with given detail message and cause.
+ * @hide internal use only
+ */
+ public UnknownServiceException(String detailMessage, Throwable cause) {
+ super(detailMessage, cause);
+ }
}
diff --git a/luni/src/main/java/java/nio/Buffer.java b/luni/src/main/java/java/nio/Buffer.java
index c0ba368..bc152ef 100644
--- a/luni/src/main/java/java/nio/Buffer.java
+++ b/luni/src/main/java/java/nio/Buffer.java
@@ -303,7 +303,7 @@
*/
void limitImpl(int newLimit) {
if (newLimit < 0 || newLimit > capacity) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("Bad limit (capacity " + capacity + "): " + newLimit);
}
limit = newLimit;
@@ -355,7 +355,7 @@
void positionImpl(int newPosition) {
if (newPosition < 0 || newPosition > limit) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("Bad position (limit " + limit + "): " + newPosition);
}
position = newPosition;
@@ -383,7 +383,7 @@
*/
public final Buffer reset() {
if (mark == UNSET_MARK) {
- throw new InvalidMarkException();
+ throw new InvalidMarkException("Mark not set");
}
position = mark;
return this;
diff --git a/luni/src/main/java/java/nio/CharBuffer.java b/luni/src/main/java/java/nio/CharBuffer.java
index cb041f5..03bac04 100644
--- a/luni/src/main/java/java/nio/CharBuffer.java
+++ b/luni/src/main/java/java/nio/CharBuffer.java
@@ -630,8 +630,6 @@
/**
* Returns a string representing the current remaining chars of this buffer.
- *
- * @return a string representing the current remaining chars of this buffer.
*/
@Override
public String toString() {
diff --git a/luni/src/main/java/java/nio/DatagramChannelImpl.java b/luni/src/main/java/java/nio/DatagramChannelImpl.java
index 970c116..a626857 100644
--- a/luni/src/main/java/java/nio/DatagramChannelImpl.java
+++ b/luni/src/main/java/java/nio/DatagramChannelImpl.java
@@ -36,9 +36,11 @@
import java.nio.channels.NotYetConnectedException;
import java.nio.channels.spi.SelectorProvider;
import java.util.Arrays;
+import libcore.io.ErrnoException;
+import libcore.io.IoBridge;
import libcore.io.IoUtils;
+import libcore.io.Libcore;
import libcore.util.EmptyArray;
-import org.apache.harmony.luni.platform.Platform;
/*
* The default implementation class of java.nio.channels.DatagramChannel.
@@ -70,7 +72,7 @@
*/
protected DatagramChannelImpl(SelectorProvider selectorProvider) throws IOException {
super(selectorProvider);
- fd = IoUtils.socket(false);
+ fd = IoBridge.socket(false);
}
/*
@@ -99,7 +101,7 @@
* Returns the local address to which the socket is bound.
*/
InetAddress getLocalAddress() {
- return IoUtils.getSocketLocalAddress(fd);
+ return IoBridge.getSocketLocalAddress(fd);
}
/**
@@ -126,7 +128,7 @@
InetSocketAddress inetSocketAddress = SocketChannelImpl.validateAddress(address);
try {
begin();
- IoUtils.connect(fd, inetSocketAddress.getAddress(), inetSocketAddress.getPort());
+ IoBridge.connect(fd, inetSocketAddress.getAddress(), inetSocketAddress.getPort());
} catch (ConnectException e) {
// ConnectException means connect fail, not exception
} finally {
@@ -150,7 +152,11 @@
}
connected = false;
connectAddress = null;
- Platform.NETWORK.disconnectDatagram(fd);
+ try {
+ Libcore.os.connect(fd, InetAddress.UNSPECIFIED, 0);
+ } catch (ErrnoException errnoException) {
+ throw errnoException.rethrowAsIOException();
+ }
if (socket != null) {
socket.disconnect();
}
@@ -193,21 +199,15 @@
DatagramPacket receivePacket;
int oldposition = target.position();
int received = 0;
+ // TODO: disallow mapped buffers and lose this conditional?
if (target.hasArray()) {
- receivePacket = new DatagramPacket(target.array(), target
- .position()
- + target.arrayOffset(), target.remaining());
+ receivePacket = new DatagramPacket(target.array(), target.position() + target.arrayOffset(), target.remaining());
} else {
- receivePacket = new DatagramPacket(new byte[target.remaining()],
- target.remaining());
+ receivePacket = new DatagramPacket(new byte[target.remaining()], target.remaining());
}
do {
- received = Platform.NETWORK.recv(fd, receivePacket,
- receivePacket.getData(), receivePacket.getOffset(), receivePacket.getLength(),
- false, isConnected());
-
+ received = IoBridge.recvfrom(false, fd, receivePacket.getData(), receivePacket.getOffset(), receivePacket.getLength(), 0, receivePacket, isConnected());
if (receivePacket != null && receivePacket.getAddress() != null) {
-
if (received > 0) {
if (target.hasArray()) {
target.position(oldposition + received);
@@ -229,10 +229,7 @@
int oldposition = target.position();
int received = 0;
do {
- int address = NioUtils.getDirectBufferAddress(target);
- received = Platform.NETWORK.recvDirect(fd, receivePacket, address,
- target.position(), target.remaining(), false, isConnected());
-
+ received = IoBridge.recvfrom(false, fd, target, 0, receivePacket, isConnected());
if (receivePacket != null && receivePacket.getAddress() != null) {
// copy the data of received packet
if (received > 0) {
@@ -245,18 +242,11 @@
return retAddr;
}
- /**
- * @see java.nio.channels.DatagramChannel#send(java.nio.ByteBuffer,
- * java.net.SocketAddress)
- */
@Override
public int send(ByteBuffer source, SocketAddress socketAddress) throws IOException {
- // must not null
checkNotNull(source);
- // must open
checkOpen();
- // transfer socketAddress
InetSocketAddress isa = (InetSocketAddress) socketAddress;
if (isa.getAddress() == null) {
throw new IOException();
@@ -266,38 +256,19 @@
throw new IllegalArgumentException();
}
- // the return value.
- int sendCount = 0;
- try {
- begin();
- byte[] array = null;
- int length = source.remaining();
- int oldposition = source.position();
- int start = oldposition;
- if (source.isDirect()) {
- synchronized (writeLock) {
- int address = NioUtils.getDirectBufferAddress(source);
- sendCount = Platform.NETWORK.sendDirect(fd, address, start, length,
- isa.getPort(), isa.getAddress());
+ synchronized (writeLock) {
+ int sendCount = 0;
+ try {
+ begin();
+ int oldPosition = source.position();
+ sendCount = IoBridge.sendto(fd, source, 0, isa.getAddress(), isa.getPort());
+ if (sendCount > 0) {
+ source.position(oldPosition + sendCount);
}
- } else {
- if (source.hasArray()) {
- array = source.array();
- start += source.arrayOffset();
- } else {
- array = new byte[length];
- source.get(array);
- start = 0;
- }
- synchronized (writeLock) {
- sendCount = Platform.NETWORK.send(fd, array, start, length,
- isa.getPort(), isa.getAddress());
- }
+ } finally {
+ end(sendCount >= 0);
}
- source.position(oldposition + sendCount);
return sendCount;
- } finally {
- end(sendCount >= 0);
}
}
@@ -360,64 +331,34 @@
/*
* read from channel, and store the result in the target.
*/
- private int readImpl(ByteBuffer readBuffer) throws IOException {
+ private int readImpl(ByteBuffer dst) throws IOException {
synchronized (readLock) {
int readCount = 0;
try {
begin();
- int start = readBuffer.position();
- int length = readBuffer.remaining();
- if (readBuffer.isDirect()) {
- int address = NioUtils.getDirectBufferAddress(readBuffer);
- readCount = Platform.NETWORK.recvDirect(fd, null, address, start, length,
- false, isConnected());
- } else {
- // the target is assured to have array.
- byte[] target = readBuffer.array();
- start += readBuffer.arrayOffset();
- readCount = Platform.NETWORK.recv(fd, null, target, start, length, false,
- isConnected());
- }
- return readCount;
+ readCount = IoBridge.recvfrom(false, fd, dst, 0, null, isConnected());
} catch (InterruptedIOException e) {
// InterruptedIOException will be thrown when timeout.
return 0;
} finally {
end(readCount > 0);
}
+ return readCount;
}
}
- /**
- * @see java.nio.channels.DatagramChannel#write(java.nio.ByteBuffer)
- */
- @Override
- public int write(ByteBuffer source) throws IOException {
- // source buffer must be not null
- checkNotNull(source);
- // status must be open and connected
+ @Override public int write(ByteBuffer src) throws IOException {
+ checkNotNull(src);
checkOpenConnected();
- // return immediately if source is full
- if (!source.hasRemaining()) {
+ if (!src.hasRemaining()) {
return 0;
}
- ByteBuffer writeBuffer = null;
- byte[] writeArray = null;
- int oldposition = source.position();
- int result;
- if (source.isDirect() || source.hasArray()) {
- writeBuffer = source;
- } else {
- writeArray = new byte[source.remaining()];
- source.get(writeArray);
- writeBuffer = ByteBuffer.wrap(writeArray);
+ int writeCount = writeImpl(src);
+ if (writeCount > 0) {
+ src.position(src.position() + writeCount);
}
- result = writeImpl(writeBuffer);
- if (result > 0) {
- source.position(oldposition + result);
- }
- return result;
+ return writeCount;
}
/**
@@ -455,50 +396,32 @@
return written;
}
- /*
- * Write the source. Return the count of bytes written.
- */
private int writeImpl(ByteBuffer buf) throws IOException {
synchronized (writeLock) {
int result = 0;
try {
begin();
- int length = buf.remaining();
- int start = buf.position();
-
- if (buf.isDirect()) {
- int address = NioUtils.getDirectBufferAddress(buf);
- result = Platform.NETWORK.sendDirect(fd, address, start, length, 0, null);
- } else {
- // buf is assured to have array.
- start += buf.arrayOffset();
- result = Platform.NETWORK.send(fd, buf.array(), start, length, 0, null);
- }
- return result;
+ result = IoBridge.sendto(fd, buf, 0, null, 0);
} finally {
end(result > 0);
}
+ return result;
}
}
- /*
- * Do really closing action here.
- */
- @Override
- protected synchronized void implCloseSelectableChannel() throws IOException {
+ @Override protected synchronized void implCloseSelectableChannel() throws IOException {
connected = false;
if (socket != null && !socket.isClosed()) {
socket.close();
} else {
- Platform.NETWORK.close(fd);
+ IoBridge.closeSocket(fd);
}
}
- @Override
- protected void implConfigureBlocking(boolean blockingMode) throws IOException {
- // Do nothing here. For real read/write operation in nonblocking mode,
- // it uses select system call. Whether a channel is blocking can be
- // decided by isBlocking() method.
+ @Override protected void implConfigureBlocking(boolean blocking) throws IOException {
+ synchronized (blockingLock()) {
+ IoUtils.setBlocking(fd, blocking);
+ }
}
/*
diff --git a/luni/src/main/java/java/nio/FileChannelImpl.java b/luni/src/main/java/java/nio/FileChannelImpl.java
index acc3725..379c99f 100644
--- a/luni/src/main/java/java/nio/FileChannelImpl.java
+++ b/luni/src/main/java/java/nio/FileChannelImpl.java
@@ -238,9 +238,6 @@
public long position() throws IOException {
checkOpen();
- if ((mode & O_APPEND) != 0) {
- return size();
- }
try {
return Libcore.os.lseek(fd, 0L, SEEK_CUR);
} catch (ErrnoException errnoException) {
@@ -388,12 +385,12 @@
}
}
- public long transferTo(long position, long count, WritableByteChannel target)
- throws IOException {
+ public long transferTo(long position, long count, WritableByteChannel target) throws IOException {
checkOpen();
if (!target.isOpen()) {
throw new ClosedChannelException();
}
+ checkReadable();
if (target instanceof FileChannelImpl) {
((FileChannelImpl) target).checkWritable();
}
@@ -482,9 +479,6 @@
public int write(ByteBuffer buffer) throws IOException {
checkOpen();
checkWritable();
- if ((mode & O_APPEND) != 0) {
- position(size());
- }
return writeImpl(buffer);
}
diff --git a/luni/src/main/java/java/nio/InvalidMarkException.java b/luni/src/main/java/java/nio/InvalidMarkException.java
index cb1050e..349cf3a 100644
--- a/luni/src/main/java/java/nio/InvalidMarkException.java
+++ b/luni/src/main/java/java/nio/InvalidMarkException.java
@@ -29,4 +29,12 @@
*/
public InvalidMarkException() {
}
+
+ /**
+ * Constructs an {@code InvalidMarkException} with the given detail message.
+ * @hide
+ */
+ public InvalidMarkException(String detailMessage) {
+ super(detailMessage);
+ }
}
diff --git a/luni/src/main/java/java/nio/IoVec.java b/luni/src/main/java/java/nio/IoVec.java
index 77bddb6..948e658 100644
--- a/luni/src/main/java/java/nio/IoVec.java
+++ b/luni/src/main/java/java/nio/IoVec.java
@@ -22,7 +22,7 @@
import libcore.io.ErrnoException;
/**
- * Used to implement java.nio read(ByteBuffer[])/write(ByteBuffer[]) operations to POSIX readv(2)
+ * Used to implement java.nio read(ByteBuffer[])/write(ByteBuffer[]) operations as POSIX readv(2)
* and writev(2) calls.
*/
final class IoVec {
diff --git a/luni/src/main/java/java/nio/SelectorImpl.java b/luni/src/main/java/java/nio/SelectorImpl.java
index 925c1e2..dfd3270 100644
--- a/luni/src/main/java/java/nio/SelectorImpl.java
+++ b/luni/src/main/java/java/nio/SelectorImpl.java
@@ -34,36 +34,20 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
+import java.util.UnsafeArrayList;
import libcore.io.ErrnoException;
+import libcore.io.IoBridge;
import libcore.io.IoUtils;
import libcore.io.Libcore;
+import libcore.io.StructPollfd;
import libcore.util.EmptyArray;
-import org.apache.harmony.luni.platform.Platform;
+import static libcore.io.OsConstants.*;
/*
* Default implementation of java.nio.channels.Selector
*/
final class SelectorImpl extends AbstractSelector {
- static final FileDescriptor[] EMPTY_FILE_DESCRIPTORS_ARRAY = new FileDescriptor[0];
-
- private static final SelectionKeyImpl[] EMPTY_SELECTION_KEY_IMPLS_ARRAY
- = new SelectionKeyImpl[0];
-
- private static final int CONNECT_OR_WRITE = OP_CONNECT | OP_WRITE;
-
- private static final int ACCEPT_OR_READ = OP_ACCEPT | OP_READ;
-
- private static final int NA = 0;
-
- private static final int READABLE = 1;
-
- private static final int WRITABLE = 2;
-
- private static final int SELECT_BLOCK = -1;
-
- private static final int SELECT_NOW = 0;
-
/**
* Used to synchronize when a key's interest ops change.
*/
@@ -94,33 +78,7 @@
private final FileDescriptor wakeupIn;
private final FileDescriptor wakeupOut;
- /**
- * File descriptors we're interested in reading from. When actively
- * selecting, the first element is always the wakeup channel's file
- * descriptor, and the other elements are user-specified file descriptors.
- * Otherwise, all elements are null.
- */
- private FileDescriptor[] readableFDs = EMPTY_FILE_DESCRIPTORS_ARRAY;
-
- /**
- * File descriptors we're interested in writing from. May be empty. When not
- * actively selecting, all elements are null.
- */
- private FileDescriptor[] writableFDs = EMPTY_FILE_DESCRIPTORS_ARRAY;
-
- /**
- * Selection keys that correspond to the concatenation of readableFDs and
- * writableFDs. This is used to interpret the results returned by select().
- * When not actively selecting, all elements are null.
- */
- private SelectionKeyImpl[] readyKeys = EMPTY_SELECTION_KEY_IMPLS_ARRAY;
-
- /**
- * Selection flags that define the ready ops on the ready keys. When not
- * actively selecting, all elements are 0. Corresponds to the ready keys
- * set.
- */
- private int[] flags = EmptyArray.INT;
+ private final UnsafeArrayList<StructPollfd> pollFds = new UnsafeArrayList<StructPollfd>(StructPollfd.class, 8);
public SelectorImpl(SelectorProvider selectorProvider) throws IOException {
super(selectorProvider);
@@ -135,6 +93,8 @@
wakeupIn = pipeFds[0];
wakeupOut = pipeFds[1];
IoUtils.setBlocking(wakeupIn, false);
+ pollFds.add(new StructPollfd());
+ setPollFd(0, wakeupIn, POLLIN, null);
} catch (ErrnoException errnoException) {
throw errnoException.rethrowAsIOException();
}
@@ -163,9 +123,10 @@
}
synchronized (this) {
synchronized (unmodifiableKeys) {
- SelectionKeyImpl selectionKey = new SelectionKeyImpl(
- channel, operations, attachment, this);
+ SelectionKeyImpl selectionKey = new SelectionKeyImpl(channel, operations,
+ attachment, this);
mutableKeys.add(selectionKey);
+ ensurePollFdsCapacity();
return selectionKey;
}
}
@@ -183,18 +144,20 @@
}
@Override public int select() throws IOException {
- return selectInternal(SELECT_BLOCK);
+ // Blocks until some fd is ready.
+ return selectInternal(-1);
}
@Override public int select(long timeout) throws IOException {
if (timeout < 0) {
throw new IllegalArgumentException();
}
- return selectInternal((timeout == 0) ? SELECT_BLOCK : timeout);
+ // Our timeout is interpreted differently to Unix's --- 0 means block. See selectNow.
+ return selectInternal((timeout == 0) ? -1 : timeout);
}
@Override public int selectNow() throws IOException {
- return selectInternal(SELECT_NOW);
+ return selectInternal(0);
}
private int selectInternal(long timeout) throws IOException {
@@ -203,146 +166,113 @@
synchronized (unmodifiableKeys) {
synchronized (selectedKeys) {
doCancel();
- boolean isBlock = (SELECT_NOW != timeout);
- int readableKeysCount = 1; // first is always the wakeup channel
- int writableKeysCount = 0;
+ boolean isBlock = (timeout != 0);
synchronized (keysLock) {
- for (SelectionKeyImpl key : mutableKeys) {
- int ops = key.interestOpsNoCheck();
- if ((ACCEPT_OR_READ & ops) != 0) {
- readableKeysCount++;
- }
- if ((CONNECT_OR_WRITE & ops) != 0) {
- writableKeysCount++;
- }
- }
- prepareChannels(readableKeysCount, writableKeysCount);
+ preparePollFds();
}
- boolean success;
+ int rc;
try {
if (isBlock) {
begin();
}
- success = Platform.NETWORK.select(readableFDs, writableFDs,
- readableKeysCount, writableKeysCount, timeout, flags);
+ rc = Libcore.os.poll(pollFds.array(), (int) timeout);
} finally {
if (isBlock) {
end();
}
}
- int selected = success ? processSelectResult() : 0;
-
- Arrays.fill(readableFDs, null);
- Arrays.fill(writableFDs, null);
- Arrays.fill(readyKeys, null);
- Arrays.fill(flags, 0);
-
- selected -= doCancel();
-
- return selected;
+ int readyCount = (rc > 0) ? processPollFds() : 0;
+ readyCount -= doCancel();
+ return readyCount;
}
}
}
}
- private int getReadyOps(SelectionKeyImpl key) {
- SelectableChannel channel = key.channel();
- return ((channel instanceof SocketChannel) && !((SocketChannel) channel).isConnectionPending()) ?
- OP_WRITE : CONNECT_OR_WRITE;
+ private void setPollFd(int i, FileDescriptor fd, int events, Object object) {
+ StructPollfd pollFd = pollFds.get(i);
+ pollFd.fd = fd;
+ pollFd.events = (short) events;
+ pollFd.userData = object;
}
- /**
- * Prepare the readableFDs, writableFDs, readyKeys and flags arrays in
- * preparation for a call to {@code INetworkSystem#select()}. After they're
- * used, the array elements must be cleared.
- */
- private void prepareChannels(int numReadable, int numWritable) {
- // grow each array to sufficient capacity. Always grow to at least 1.5x
- // to avoid growing too frequently
- if (readableFDs.length < numReadable) {
- int newSize = Math.max((int) (readableFDs.length * 1.5f), numReadable);
- readableFDs = new FileDescriptor[newSize];
- }
- if (writableFDs.length < numWritable) {
- int newSize = Math.max((int) (writableFDs.length * 1.5f), numWritable);
- writableFDs = new FileDescriptor[newSize];
- }
- int total = numReadable + numWritable;
- if (readyKeys.length < total) {
- int newSize = Math.max((int) (readyKeys.length * 1.5f), total);
- readyKeys = new SelectionKeyImpl[newSize];
- flags = new int[newSize];
- }
-
- // populate the FDs, including the wakeup channel
- readableFDs[0] = wakeupIn;
- int r = 1;
- int w = 0;
+ private void preparePollFds() {
+ int i = 1; // Our wakeup pipe comes before all the user's fds.
for (SelectionKeyImpl key : mutableKeys) {
int interestOps = key.interestOpsNoCheck();
- if ((ACCEPT_OR_READ & interestOps) != 0) {
- readableFDs[r] = ((FileDescriptorChannel) key.channel()).getFD();
- readyKeys[r] = key;
- r++;
+ short eventMask = 0;
+ if (((OP_ACCEPT | OP_READ) & interestOps) != 0) {
+ eventMask |= POLLIN;
}
- if ((getReadyOps(key) & interestOps) != 0) {
- writableFDs[w] = ((FileDescriptorChannel) key.channel()).getFD();
- readyKeys[w + numReadable] = key;
- w++;
+ if (((OP_CONNECT | OP_WRITE) & interestOps) != 0) {
+ eventMask |= POLLOUT;
+ }
+ if (eventMask != 0) {
+ setPollFd(i++, ((FileDescriptorChannel) key.channel()).getFD(), eventMask, key);
}
}
}
+ private void ensurePollFdsCapacity() {
+ // We need one slot for each element of mutableKeys, plus one for the wakeup pipe.
+ while (pollFds.size() < mutableKeys.size() + 1) {
+ pollFds.add(new StructPollfd());
+ }
+ }
+
/**
- * Updates the key ready ops and selected key set with data from the flags
- * array.
+ * Updates the key ready ops and selected key set.
*/
- private int processSelectResult() throws IOException {
- if (flags[0] == READABLE) {
+ private int processPollFds() throws IOException {
+ if (pollFds.get(0).revents == POLLIN) {
// Read bytes from the wakeup pipe until the pipe is empty.
byte[] buffer = new byte[8];
- while (IoUtils.read(wakeupIn, buffer, 0, 1) > 0) {
+ while (IoBridge.read(wakeupIn, buffer, 0, 1) > 0) {
}
}
- int selected = 0;
- for (int i = 1; i < flags.length; i++) {
- if (flags[i] == NA) {
+ int readyKeyCount = 0;
+ for (int i = 1; i < pollFds.size(); ++i) {
+ StructPollfd pollFd = pollFds.get(i);
+ if (pollFd.revents == 0) {
continue;
}
+ if (pollFd.fd == null) {
+ break;
+ }
- SelectionKeyImpl key = readyKeys[i];
+ SelectionKeyImpl key = (SelectionKeyImpl) pollFd.userData;
+
+ pollFd.fd = null;
+ pollFd.userData = null;
+
int ops = key.interestOpsNoCheck();
int selectedOp = 0;
-
- switch (flags[i]) {
- case READABLE:
- selectedOp = ACCEPT_OR_READ & ops;
- break;
- case WRITABLE:
- if (key.isConnected()) {
- selectedOp = OP_WRITE & ops;
- } else {
- selectedOp = OP_CONNECT & ops;
- }
- break;
+ if ((pollFd.revents & POLLIN) != 0) {
+ selectedOp = ops & (OP_ACCEPT | OP_READ);
+ } else if ((pollFd.revents & POLLOUT) != 0) {
+ if (key.isConnected()) {
+ selectedOp = ops & OP_WRITE;
+ } else {
+ selectedOp = ops & OP_CONNECT;
+ }
}
if (selectedOp != 0) {
boolean wasSelected = mutableSelectedKeys.contains(key);
if (wasSelected && key.readyOps() != selectedOp) {
key.setReadyOps(key.readyOps() | selectedOp);
- selected++;
+ ++readyKeyCount;
} else if (!wasSelected) {
key.setReadyOps(selectedOp);
mutableSelectedKeys.add(key);
- selected++;
+ ++readyKeyCount;
}
}
}
- return selected;
+ return readyKeyCount;
}
@Override public synchronized Set<SelectionKey> selectedKeys() {
diff --git a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
index e3103b3..5331158 100644
--- a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
+++ b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
@@ -31,16 +31,15 @@
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
-import org.apache.harmony.luni.platform.Platform;
+import libcore.io.IoUtils;
/**
* The default ServerSocketChannel.
*/
final class ServerSocketChannelImpl extends ServerSocketChannel implements FileDescriptorChannel {
- private final FileDescriptor fd = new FileDescriptor();
- private final SocketImpl impl = new PlainServerSocketImpl(fd);
- private final ServerSocketAdapter socket = new ServerSocketAdapter(impl, this);
+ private final ServerSocketAdapter socket;
+ private final SocketImpl impl;
private boolean isBound = false;
@@ -48,12 +47,8 @@
public ServerSocketChannelImpl(SelectorProvider sp) throws IOException {
super(sp);
- }
-
- // for native call
- @SuppressWarnings("unused")
- private ServerSocketChannelImpl() throws IOException {
- this(SelectorProvider.provider());
+ this.socket = new ServerSocketAdapter(this);
+ this.impl = socket.getImpl$();
}
@Override public ServerSocket socket() {
@@ -68,47 +63,35 @@
throw new NotYetBoundException();
}
- // TODO: pass in the SelectorProvider used to create this ServerSocketChannelImpl?
// Create an empty socket channel. This will be populated by ServerSocketAdapter.accept.
- SocketChannelImpl result = new SocketChannelImpl(SelectorProvider.provider(), false);
- Socket resultSocket = result.socket();
-
+ SocketChannelImpl result = new SocketChannelImpl(provider(), false);
+ boolean connected = false;
try {
begin();
synchronized (acceptLock) {
synchronized (blockingLock()) {
- boolean isBlocking = isBlocking();
- if (!isBlocking) {
- int[] tryResult = new int[1];
- boolean success = Platform.NETWORK.select(new FileDescriptor[] { fd },
- SelectorImpl.EMPTY_FILE_DESCRIPTORS_ARRAY, 1, 0, 0, tryResult);
- if (!success || tryResult[0] == 0) {
- // no pending connections, returns immediately.
- return null;
- }
- }
- // do accept.
do {
try {
- socket.accept(resultSocket, result);
+ socket.implAccept(result);
// select successfully, break out immediately.
break;
} catch (SocketTimeoutException e) {
// continue to accept if the channel is in blocking mode.
+ // TODO: does this make sense? why does blocking imply no timeouts?
}
- } while (isBlocking);
+ } while (isBlocking());
}
}
} finally {
- end(resultSocket.isConnected());
+ end(result.socket().isConnected());
}
- return result;
+ return result.socket().isConnected() ? result : null;
}
- protected void implConfigureBlocking(boolean blockingMode) throws IOException {
- // Do nothing here. For real accept() operation in non-blocking mode,
- // it uses INetworkSystem.select. Whether a channel is blocking can be
- // decided by isBlocking() method.
+ @Override protected void implConfigureBlocking(boolean blocking) throws IOException {
+ synchronized (blockingLock()) {
+ IoUtils.setBlocking(impl.getFD$(), blocking);
+ }
}
synchronized protected void implCloseSelectableChannel() throws IOException {
@@ -118,14 +101,13 @@
}
public FileDescriptor getFD() {
- return fd;
+ return impl.getFD$();
}
private static class ServerSocketAdapter extends ServerSocket {
private final ServerSocketChannelImpl channelImpl;
- ServerSocketAdapter(SocketImpl impl, ServerSocketChannelImpl aChannelImpl) {
- super(impl);
+ ServerSocketAdapter(ServerSocketChannelImpl aChannelImpl) throws IOException {
this.channelImpl = aChannelImpl;
}
@@ -145,22 +127,23 @@
return sc.socket();
}
- private Socket accept(Socket socket, SocketChannelImpl sockChannel) throws IOException {
+ public Socket implAccept(SocketChannelImpl clientSocketChannel) throws IOException {
+ Socket clientSocket = clientSocketChannel.socket();
boolean connectOK = false;
try {
synchronized (this) {
- super.implAccept(socket);
- sockChannel.setConnected();
- sockChannel.setBound(true);
- sockChannel.finishAccept();
+ super.implAccept(clientSocket);
+ clientSocketChannel.setConnected();
+ clientSocketChannel.setBound(true);
+ clientSocketChannel.finishAccept();
}
connectOK = true;
} finally {
if (!connectOK) {
- socket.close();
+ clientSocket.close();
}
}
- return socket;
+ return clientSocket;
}
@Override public ServerSocketChannel getChannel() {
diff --git a/luni/src/main/java/java/nio/SocketChannelImpl.java b/luni/src/main/java/java/nio/SocketChannelImpl.java
index 51cda16..9f8c690 100644
--- a/luni/src/main/java/java/nio/SocketChannelImpl.java
+++ b/luni/src/main/java/java/nio/SocketChannelImpl.java
@@ -44,52 +44,43 @@
import java.util.Arrays;
import libcore.io.ErrnoException;
import libcore.io.Libcore;
+import libcore.io.IoBridge;
import libcore.io.IoUtils;
-import org.apache.harmony.luni.platform.Platform;
import static libcore.io.OsConstants.*;
/*
* The default implementation class of java.nio.channels.SocketChannel.
*/
class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
-
- private static final int EOF = -1;
-
- // Status un-init, not initialized.
- static final int SOCKET_STATUS_UNINIT = EOF;
+ private static final int SOCKET_STATUS_UNINITIALIZED = -1;
// Status before connect.
- static final int SOCKET_STATUS_UNCONNECTED = 0;
+ private static final int SOCKET_STATUS_UNCONNECTED = 0;
// Status connection pending.
- static final int SOCKET_STATUS_PENDING = 1;
+ private static final int SOCKET_STATUS_PENDING = 1;
// Status after connection success.
- static final int SOCKET_STATUS_CONNECTED = 2;
+ private static final int SOCKET_STATUS_CONNECTED = 2;
// Status closed.
- static final int SOCKET_STATUS_CLOSED = 3;
+ private static final int SOCKET_STATUS_CLOSED = 3;
- // The descriptor to interact with native code.
- final FileDescriptor fd;
+ private final FileDescriptor fd;
// Our internal Socket.
private SocketAdapter socket = null;
// The address to be connected.
- InetSocketAddress connectAddress = null;
+ private InetSocketAddress connectAddress = null;
- // Local address of the this socket (package private for adapter).
- InetAddress localAddress = null;
+ private InetAddress localAddress = null;
+ private int localPort;
- // Local port number.
- int localPort;
-
- // At first, uninitialized.
- int status = SOCKET_STATUS_UNINIT;
+ private int status = SOCKET_STATUS_UNINITIALIZED;
// Whether the socket is bound.
- volatile boolean isBound = false;
+ private volatile boolean isBound = false;
private final Object readLock = new Object();
@@ -108,7 +99,7 @@
public SocketChannelImpl(SelectorProvider selectorProvider, boolean connect) throws IOException {
super(selectorProvider);
status = SOCKET_STATUS_UNCONNECTED;
- fd = (connect ? IoUtils.socket(true) : new FileDescriptor());
+ fd = (connect ? IoBridge.socket(true) : new FileDescriptor());
}
/*
@@ -174,7 +165,7 @@
if (isBlocking()) {
begin();
}
- finished = IoUtils.connect(fd, normalAddr, port);
+ finished = IoBridge.connect(fd, normalAddr, port);
isBound = finished;
} catch (IOException e) {
if (e instanceof ConnectException && !isBlocking()) {
@@ -236,7 +227,9 @@
boolean finished = false;
try {
begin();
- finished = Platform.NETWORK.isConnected(fd, 0);
+ InetAddress inetAddress = connectAddress.getAddress();
+ int port = connectAddress.getPort();
+ finished = IoBridge.isConnected(fd, inetAddress, port, 0, 0); // Return immediately.
isBound = finished;
} catch (ConnectException e) {
if (isOpen()) {
@@ -260,30 +253,13 @@
}
@Override
- public int read(ByteBuffer target) throws IOException {
- FileChannelImpl.checkWritable(target);
+ public int read(ByteBuffer dst) throws IOException {
+ FileChannelImpl.checkWritable(dst);
checkOpenConnected();
- if (!target.hasRemaining()) {
+ if (!dst.hasRemaining()) {
return 0;
}
-
- int readCount;
- if (target.isDirect() || target.hasArray()) {
- readCount = readImpl(target);
- if (readCount > 0) {
- target.position(target.position() + readCount);
- }
- } else {
- ByteBuffer readBuffer = null;
- byte[] readArray = null;
- readArray = new byte[target.remaining()];
- readBuffer = ByteBuffer.wrap(readArray);
- readCount = readImpl(readBuffer);
- if (readCount > 0) {
- target.put(readArray, 0, readCount);
- }
- }
- return readCount;
+ return readImpl(dst);
}
@Override
@@ -297,9 +273,9 @@
byte[] readArray = new byte[totalCount];
ByteBuffer readBuffer = ByteBuffer.wrap(readArray);
int readCount;
- // read data to readBuffer, and then transfer data from readBuffer to
- // targets.
+ // read data to readBuffer, and then transfer data from readBuffer to targets.
readCount = readImpl(readBuffer);
+ readBuffer.flip();
if (readCount > 0) {
int left = readCount;
int index = offset;
@@ -314,49 +290,36 @@
return readCount;
}
- /**
- * Read from channel, and store the result in the target.
- *
- * @param target
- * output parameter
- */
- private int readImpl(ByteBuffer target) throws IOException {
+ private int readImpl(ByteBuffer dst) throws IOException {
synchronized (readLock) {
int readCount = 0;
try {
if (isBlocking()) {
begin();
}
- int offset = target.position();
- int length = target.remaining();
- if (target.isDirect()) {
- int address = NioUtils.getDirectBufferAddress(target);
- readCount = Platform.NETWORK.readDirect(fd, address + offset, length);
- } else {
- // target is assured to have array.
- byte[] array = target.array();
- offset += target.arrayOffset();
- readCount = Platform.NETWORK.read(fd, array, offset, length);
+ readCount = IoBridge.recvfrom(true, fd, dst, 0, null, false);
+ if (readCount > 0) {
+ dst.position(dst.position() + readCount);
}
- return readCount;
} finally {
if (isBlocking()) {
end(readCount > 0);
}
}
+ return readCount;
}
}
@Override
- public int write(ByteBuffer source) throws IOException {
- if (source == null) {
+ public int write(ByteBuffer src) throws IOException {
+ if (src == null) {
throw new NullPointerException();
}
checkOpenConnected();
- if (!source.hasRemaining()) {
+ if (!src.hasRemaining()) {
return 0;
}
- return writeImpl(source);
+ return writeImpl(src);
}
@Override
@@ -388,33 +351,20 @@
return written;
}
- /*
- * Write the source. return the count of bytes written.
- */
- private int writeImpl(ByteBuffer source) throws IOException {
+ private int writeImpl(ByteBuffer src) throws IOException {
synchronized (writeLock) {
- if (!source.hasRemaining()) {
+ if (!src.hasRemaining()) {
return 0;
}
int writeCount = 0;
try {
- int pos = source.position();
- int length = source.remaining();
if (isBlocking()) {
begin();
}
- if (source.isDirect()) {
- int address = NioUtils.getDirectBufferAddress(source);
- writeCount = Platform.NETWORK.writeDirect(fd, address, pos, length);
- } else if (source.hasArray()) {
- pos += source.arrayOffset();
- writeCount = Platform.NETWORK.write(fd, source.array(), pos, length);
- } else {
- byte[] array = new byte[length];
- source.get(array);
- writeCount = Platform.NETWORK.write(fd, array, 0, length);
+ writeCount = IoBridge.sendto(fd, src, 0, null, 0);
+ if (writeCount > 0) {
+ src.position(src.position() + writeCount);
}
- source.position(pos + writeCount);
} finally {
if (isBlocking()) {
end(writeCount >= 0);
@@ -486,15 +436,14 @@
if (socket != null && !socket.isClosed()) {
socket.close();
} else {
- Platform.NETWORK.close(fd);
+ IoBridge.closeSocket(fd);
}
}
}
- @Override
- protected void implConfigureBlocking(boolean blockMode) throws IOException {
+ @Override protected void implConfigureBlocking(boolean blocking) throws IOException {
synchronized (blockingLock()) {
- IoUtils.setBlocking(fd, blockMode);
+ IoUtils.setBlocking(fd, blocking);
}
}
diff --git a/luni/src/main/java/java/nio/channels/spi/AbstractSelectableChannel.java b/luni/src/main/java/java/nio/channels/spi/AbstractSelectableChannel.java
index 8e98a02..ab76e8e 100644
--- a/luni/src/main/java/java/nio/channels/spi/AbstractSelectableChannel.java
+++ b/luni/src/main/java/java/nio/channels/spi/AbstractSelectableChannel.java
@@ -250,15 +250,13 @@
}
/**
- * Implements the setting of the blocking mode.
+ * Implements the configuration of blocking/non-blocking mode.
*
- * @param blockingMode
- * {@code true} for setting this channel's mode to blocking,
- * {@code false} to set it to non-blocking.
+ * @param blocking true for blocking, false for non-blocking.
* @throws IOException
* if an I/O error occurs.
*/
- protected abstract void implConfigureBlocking(boolean blockingMode) throws IOException;
+ protected abstract void implConfigureBlocking(boolean blocking) throws IOException;
/*
* package private for deregister method in AbstractSelector.
diff --git a/luni/src/main/java/java/security/KeyStore.java b/luni/src/main/java/java/security/KeyStore.java
index 40f7f9d..c233a5b 100644
--- a/luni/src/main/java/java/security/KeyStore.java
+++ b/luni/src/main/java/java/security/KeyStore.java
@@ -1226,8 +1226,8 @@
// clone chain - this.chain = (Certificate[])chain.clone();
boolean isAllX509Certificates = true;
// assert chain length > 0
- for(Certificate cert: chain){
- if(!(cert instanceof X509Certificate)){
+ for (Certificate cert: chain) {
+ if (!(cert instanceof X509Certificate)) {
isAllX509Certificates = false;
break;
}
diff --git a/luni/src/main/java/java/sql/Date.java b/luni/src/main/java/java/sql/Date.java
index adc35aa..2434fbd 100644
--- a/luni/src/main/java/java/sql/Date.java
+++ b/luni/src/main/java/java/sql/Date.java
@@ -32,6 +32,7 @@
* java.sql.Date} class are "normalized" to the time 00:00:00.000 GMT on the
* date implied by the time value.
*/
+@FindBugsSuppressWarnings("NM_SAME_SIMPLE_NAME_AS_SUPERCLASS")
public class Date extends java.util.Date {
private static final long serialVersionUID = 1511598038487230103L;
diff --git a/luni/src/main/java/java/util/AbstractList.java b/luni/src/main/java/java/util/AbstractList.java
index f6e5903..3b522c0 100644
--- a/luni/src/main/java/java/util/AbstractList.java
+++ b/luni/src/main/java/java/util/AbstractList.java
@@ -27,8 +27,7 @@
*
* @since 1.2
*/
-public abstract class AbstractList<E> extends AbstractCollection<E> implements
- List<E> {
+public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
/**
* A counter for changes to the list.
diff --git a/luni/src/main/java/java/util/EventObject.java b/luni/src/main/java/java/util/EventObject.java
index e8a169f..058e7e8 100644
--- a/luni/src/main/java/java/util/EventObject.java
+++ b/luni/src/main/java/java/util/EventObject.java
@@ -30,9 +30,7 @@
private static final long serialVersionUID = 5516075349620653480L;
- /**
- * The event source.
- */
+ @FindBugsSuppressWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED")
protected transient Object source;
/**
@@ -42,17 +40,14 @@
* the object which fired the event.
*/
public EventObject(Object source) {
- if (source != null) {
- this.source = source;
- } else {
+ if (source == null) {
throw new IllegalArgumentException();
}
+ this.source = source;
}
/**
- * Returns the event source.
- *
- * @return the object which fired the event.
+ * Returns the object which fired the event.
*/
public Object getSource() {
return source;
@@ -60,11 +55,8 @@
/**
* Returns the string representation of this {@code EventObject}.
- *
- * @return the string representation of this {@code EventObject}.
*/
- @Override
- public String toString() {
+ @Override public String toString() {
return getClass().getName() + "[source=" + source + ']';
}
}
diff --git a/luni/src/main/java/java/util/ResourceBundle.java b/luni/src/main/java/java/util/ResourceBundle.java
index a34b34a..ff38b5b 100644
--- a/luni/src/main/java/java/util/ResourceBundle.java
+++ b/luni/src/main/java/java/util/ResourceBundle.java
@@ -24,6 +24,8 @@
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
+import java.nio.charset.Charsets;
+import static java.nio.charset.Charsets.UTF_8;
import libcore.io.IoUtils;
/**
@@ -514,7 +516,7 @@
: ClassLoader.getSystemResourceAsStream(fileName);
if (stream != null) {
try {
- bundle = new PropertyResourceBundle(new InputStreamReader(stream));
+ bundle = new PropertyResourceBundle(new InputStreamReader(stream, UTF_8));
bundle.setLocale(locale);
} catch (IOException ignored) {
} finally {
diff --git a/luni/src/main/java/java/util/TreeMap.java b/luni/src/main/java/java/util/TreeMap.java
index 8d878cd..7809110 100644
--- a/luni/src/main/java/java/util/TreeMap.java
+++ b/luni/src/main/java/java/util/TreeMap.java
@@ -1094,11 +1094,11 @@
* A map with optional limits on its range.
*/
final class BoundedMap extends AbstractMap<K, V> implements NavigableMap<K, V>, Serializable {
- private final boolean ascending;
- private final K from;
- private final Bound fromBound;
- private final K to;
- private final Bound toBound;
+ private final transient boolean ascending;
+ private final transient K from;
+ private final transient Bound fromBound;
+ private final transient K to;
+ private final transient Bound toBound;
BoundedMap(boolean ascending, K from, Bound fromBound, K to, Bound toBound) {
/*
@@ -1375,8 +1375,8 @@
* View factory methods.
*/
- private BoundedEntrySet entrySet;
- private BoundedKeySet keySet;
+ private transient BoundedEntrySet entrySet;
+ private transient BoundedKeySet keySet;
@Override public Set<Entry<K, V>> entrySet() {
BoundedEntrySet result = entrySet;
diff --git a/luni/src/main/java/java/util/TreeSet.java b/luni/src/main/java/java/util/TreeSet.java
index 63a51c9..502329e 100644
--- a/luni/src/main/java/java/util/TreeSet.java
+++ b/luni/src/main/java/java/util/TreeSet.java
@@ -490,7 +490,7 @@
(Comparator<? super E>) stream.readObject());
int size = stream.readInt();
if (size > 0) {
- for(int i=0; i<size; i++) {
+ for (int i=0; i<size; i++) {
E elem = (E)stream.readObject();
map.put(elem, Boolean.TRUE);
}
diff --git a/luni/src/main/java/java/util/UnsafeArrayList.java b/luni/src/main/java/java/util/UnsafeArrayList.java
new file mode 100644
index 0000000..106e73e
--- /dev/null
+++ b/luni/src/main/java/java/util/UnsafeArrayList.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.util;
+
+import java.lang.reflect.Array;
+
+/**
+ * An array-backed list that exposes its array.
+ *
+ * @hide
+ */
+public class UnsafeArrayList<T> extends AbstractList<T> {
+ private final Class<T> elementType;
+ private T[] array;
+ private int size;
+
+ public UnsafeArrayList(Class<T> elementType, int initialCapacity) {
+ this.array = (T[]) Array.newInstance(elementType, initialCapacity);
+ this.elementType = elementType;
+ }
+
+ @Override public boolean add(T element) {
+ if (size == array.length) {
+ T[] newArray = (T[]) Array.newInstance(elementType, size * 2);
+ System.arraycopy(array, 0, newArray, 0, size);
+ array = newArray;
+ }
+ array[size++] = element;
+ ++modCount;
+ return true;
+ }
+
+ public T[] array() {
+ return array;
+ }
+
+ public T get(int i) {
+ return array[i];
+ }
+
+ public int size() {
+ return size;
+ }
+}
diff --git a/luni/src/main/java/java/util/jar/JarFile.java b/luni/src/main/java/java/util/jar/JarFile.java
index a26a602..e129e82 100644
--- a/luni/src/main/java/java/util/jar/JarFile.java
+++ b/luni/src/main/java/java/util/jar/JarFile.java
@@ -289,11 +289,7 @@
try {
InputStream is = super.getInputStream(manifestEntry);
if (verifier != null) {
- try {
- verifier.addMetaEntry(manifestEntry.getName(), Streams.readFully(is));
- } finally {
- is.close();
- }
+ verifier.addMetaEntry(manifestEntry.getName(), Streams.readFully(is));
is = super.getInputStream(manifestEntry);
}
try {
@@ -344,11 +340,7 @@
|| endsWithIgnoreCase(entryName, ".RSA"))) {
signed = true;
InputStream is = super.getInputStream(entry);
- try {
- verifier.addMetaEntry(entryName, Streams.readFully(is));
- } finally {
- is.close();
- }
+ verifier.addMetaEntry(entryName, Streams.readFully(is));
}
}
}
diff --git a/luni/src/main/java/java/util/jar/JarVerifier.java b/luni/src/main/java/java/util/jar/JarVerifier.java
index 81bbe77..ec0e088 100644
--- a/luni/src/main/java/java/util/jar/JarVerifier.java
+++ b/luni/src/main/java/java/util/jar/JarVerifier.java
@@ -33,7 +33,7 @@
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Vector;
-import org.apache.harmony.luni.util.Base64;
+import libcore.io.Base64;
import org.apache.harmony.security.utils.JarUtils;
/**
diff --git a/luni/src/main/java/java/util/prefs/AbstractPreferences.java b/luni/src/main/java/java/util/prefs/AbstractPreferences.java
index 06bef54..d86d789 100644
--- a/luni/src/main/java/java/util/prefs/AbstractPreferences.java
+++ b/luni/src/main/java/java/util/prefs/AbstractPreferences.java
@@ -24,12 +24,11 @@
import java.util.EventListener;
import java.util.EventObject;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
-import org.apache.harmony.luni.util.Base64;
+import libcore.io.Base64;
import libcore.util.EmptyArray;
/**
@@ -193,8 +192,8 @@
checkState();
AbstractPreferences result = null;
String[] childrenNames = childrenNames();
- for (int i = 0; i < childrenNames.length; i++) {
- if (childrenNames[i].equals(name)) {
+ for (String childrenName : childrenNames) {
+ if (childrenName.equals(name)) {
result = childSpi(name);
break;
}
@@ -364,9 +363,8 @@
@Override
public void clear() throws BackingStoreException {
synchronized (lock) {
- String[] keyList = keys();
- for (int i = 0; i < keyList.length; i++) {
- remove(keyList[i]);
+ for (String key : keys()) {
+ remove(key);
}
}
}
@@ -562,20 +560,17 @@
throws BackingStoreException {
String[] names = path.split("/");
AbstractPreferences currentNode = this;
- AbstractPreferences temp = null;
- if (currentNode != null) {
- for (int i = 0; i < names.length; i++) {
- String name = names[i];
- synchronized (currentNode.lock) {
- temp = currentNode.cachedNode.get(name);
- if (temp == null) {
- temp = getNodeFromBackend(createNew, currentNode, name);
- }
+ AbstractPreferences temp;
+ for (String name : names) {
+ synchronized (currentNode.lock) {
+ temp = currentNode.cachedNode.get(name);
+ if (temp == null) {
+ temp = getNodeFromBackend(createNew, currentNode, name);
}
- currentNode = temp;
- if (currentNode == null) {
- break;
- }
+ }
+ currentNode = temp;
+ if (currentNode == null) {
+ break;
}
}
return currentNode;
@@ -660,37 +655,32 @@
@Override
public void putBoolean(String key, boolean value) {
- String sval = String.valueOf(value);
- put(key, sval);
+ put(key, String.valueOf(value));
}
@Override
public void putByteArray(String key, byte[] value) {
- put(key, Base64.encode(value, Charsets.US_ASCII));
+ put(key, Base64.encode(value));
}
@Override
public void putDouble(String key, double value) {
- String sval = Double.toString(value);
- put(key, sval);
+ put(key, Double.toString(value));
}
@Override
public void putFloat(String key, float value) {
- String sval = Float.toString(value);
- put(key, sval);
+ put(key, Float.toString(value));
}
@Override
public void putInt(String key, int value) {
- String sval = Integer.toString(value);
- put(key, sval);
+ put(key, Integer.toString(value));
}
@Override
public void putLong(String key, long value) {
- String sval = Long.toString(value);
- put(key, sval);
+ put(key, Long.toString(value));
}
@Override
@@ -716,10 +706,10 @@
synchronized (lock) {
checkState();
String[] childrenNames = childrenNamesSpi();
- for (int i = 0; i < childrenNames.length; i++) {
- if (cachedNode.get(childrenNames[i]) == null) {
- AbstractPreferences child = childSpi(childrenNames[i]);
- cachedNode.put(childrenNames[i], child);
+ for (String childrenName : childrenNames) {
+ if (cachedNode.get(childrenName) == null) {
+ AbstractPreferences child = childSpi(childrenName);
+ cachedNode.put(childrenName, child);
}
}
@@ -831,15 +821,14 @@
@Override
public void run() {
while (true) {
- EventObject event = null;
+ EventObject event;
try {
event = getEventObject();
} catch (InterruptedException e) {
e.printStackTrace();
continue;
}
- AbstractPreferences pref = (AbstractPreferences) event
- .getSource();
+ AbstractPreferences pref = (AbstractPreferences) event.getSource();
if (event instanceof NodeAddEvent) {
dispatchNodeAdd((NodeChangeEvent) event,
pref.nodeChangeListeners);
@@ -867,11 +856,8 @@
private void dispatchPrefChange(PreferenceChangeEvent event,
List<EventListener> preferenceChangeListeners) {
synchronized (preferenceChangeListeners) {
- Iterator<EventListener> i = preferenceChangeListeners.iterator();
- while (i.hasNext()) {
- PreferenceChangeListener pcl = (PreferenceChangeListener) i
- .next();
- pcl.preferenceChange(event);
+ for (EventListener preferenceChangeListener : preferenceChangeListeners) {
+ ((PreferenceChangeListener) preferenceChangeListener).preferenceChange(event);
}
}
}
@@ -879,10 +865,8 @@
private void dispatchNodeRemove(NodeChangeEvent event,
List<EventListener> nodeChangeListeners) {
synchronized (nodeChangeListeners) {
- Iterator<EventListener> i = nodeChangeListeners.iterator();
- while (i.hasNext()) {
- NodeChangeListener ncl = (NodeChangeListener) i.next();
- ncl.childRemoved(event);
+ for (EventListener nodeChangeListener : nodeChangeListeners) {
+ ((NodeChangeListener) nodeChangeListener).childRemoved(event);
}
}
}
@@ -890,9 +874,8 @@
private void dispatchNodeAdd(NodeChangeEvent event,
List<EventListener> nodeChangeListeners) {
synchronized (nodeChangeListeners) {
- Iterator<EventListener> i = nodeChangeListeners.iterator();
- while (i.hasNext()) {
- NodeChangeListener ncl = (NodeChangeListener) i.next();
+ for (EventListener nodeChangeListener : nodeChangeListeners) {
+ NodeChangeListener ncl = (NodeChangeListener) nodeChangeListener;
ncl.childAdded(event);
}
}
diff --git a/luni/src/main/java/java/util/prefs/XMLParser.java b/luni/src/main/java/java/util/prefs/XMLParser.java
index 6546b76..6e25c7d 100644
--- a/luni/src/main/java/java/util/prefs/XMLParser.java
+++ b/luni/src/main/java/java/util/prefs/XMLParser.java
@@ -405,7 +405,7 @@
NodeList childNodes = documentElement.getChildNodes();
if(path[0].equals("entry") || path[0].equals("node")) {
- for(int i = 0; i < childNodes.getLength(); i++) {
+ for (int i = 0; i < childNodes.getLength(); i++) {
Object next = childNodes.item(i);
if(next instanceof Element) {
if(((Element) next).getNodeName().equals(path[0])) {
@@ -414,12 +414,12 @@
}
}
} else if(path[0].equals("map") && path[1].equals("entry")) {
- for(int i = 0; i < childNodes.getLength(); i++) {
+ for (int i = 0; i < childNodes.getLength(); i++) {
Object next = childNodes.item(i);
if(next instanceof Element) {
if(((Element) next).getNodeName().equals(path[0])) {
NodeList nextChildNodes = ((Node)next).getChildNodes();
- for(int j = 0; j < nextChildNodes.getLength(); j++) {
+ for (int j = 0; j < nextChildNodes.getLength(); j++) {
Object subnext = nextChildNodes.item(j);
if(subnext instanceof Element) {
if(((Element)subnext).getNodeName().equals(path[1])) {
diff --git a/luni/src/main/java/libcore/io/AsynchronousCloseMonitor.java b/luni/src/main/java/libcore/io/AsynchronousCloseMonitor.java
new file mode 100644
index 0000000..62eec24
--- /dev/null
+++ b/luni/src/main/java/libcore/io/AsynchronousCloseMonitor.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.io;
+
+import java.io.FileDescriptor;
+
+public final class AsynchronousCloseMonitor {
+ private AsynchronousCloseMonitor() {
+ }
+
+ public static native void signalBlockedThreads(FileDescriptor fd);
+}
diff --git a/luni/src/main/java/org/apache/harmony/luni/util/Base64.java b/luni/src/main/java/libcore/io/Base64.java
similarity index 87%
rename from luni/src/main/java/org/apache/harmony/luni/util/Base64.java
rename to luni/src/main/java/libcore/io/Base64.java
index 4c9b509..e4643b5 100644
--- a/luni/src/main/java/org/apache/harmony/luni/util/Base64.java
+++ b/luni/src/main/java/libcore/io/Base64.java
@@ -19,16 +19,16 @@
* @author Alexander Y. Kleymenov
*/
-package org.apache.harmony.luni.util;
+package libcore.io;
-import java.nio.charset.Charset;
+import java.nio.charset.Charsets;
import libcore.util.EmptyArray;
/**
- * This class implements Base64 encoding/decoding functionality
- * as specified in RFC 2045 (http://www.ietf.org/rfc/rfc2045.txt).
+ * <a href="http://www.ietf.org/rfc/rfc2045.txt">Base64</a> encoder/decoder.
+ * In violation of the RFC, this encoder doesn't wrap lines at 76 columns.
*/
-public class Base64 {
+public final class Base64 {
private Base64() {
}
@@ -132,22 +132,15 @@
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/'};
- public static String encode(byte[] in, Charset charset) {
- int length = in.length * 4 / 3;
- length += length / 76 + 3; // for crlr
+ public static String encode(byte[] in) {
+ int length = (in.length + 2) * 4 / 3;
byte[] out = new byte[length];
- int index = 0, i, crlr = 0, end = in.length - in.length%3;
- for (i=0; i<end; i+=3) {
+ int index = 0, end = in.length - in.length % 3;
+ for (int i = 0; i < end; i += 3) {
out[index++] = map[(in[i] & 0xff) >> 2];
out[index++] = map[((in[i] & 0x03) << 4) | ((in[i+1] & 0xff) >> 4)];
out[index++] = map[((in[i+1] & 0x0f) << 2) | ((in[i+2] & 0xff) >> 6)];
out[index++] = map[(in[i+2] & 0x3f)];
- if (((index - crlr)%76 == 0) && (index != 0)) {
- out[index++] = '\n';
- crlr++;
- //out[index++] = '\r';
- //crlr++;
- }
}
switch (in.length % 3) {
case 1:
@@ -163,6 +156,6 @@
out[index++] = '=';
break;
}
- return new String(out, 0, index, charset);
+ return new String(out, 0, index, Charsets.US_ASCII);
}
}
diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java
index cda6a58..e77e0bb 100644
--- a/luni/src/main/java/libcore/io/BlockGuardOs.java
+++ b/luni/src/main/java/libcore/io/BlockGuardOs.java
@@ -18,6 +18,8 @@
import dalvik.system.BlockGuard;
import java.io.FileDescriptor;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
import java.net.SocketException;
import java.nio.ByteBuffer;
import static libcore.io.OsConstants.*;
@@ -30,26 +32,65 @@
super(os);
}
- @Override
- public void fdatasync(FileDescriptor fd) throws ErrnoException {
+ private FileDescriptor tagSocket(FileDescriptor fd) {
+ try {
+ BlockGuard.tagSocketFd(fd);
+ return fd;
+ } catch (SocketException e) {
+ throw new ErrnoException("socket", EINVAL, e);
+ }
+ }
+
+ @Override public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException {
+ BlockGuard.getThreadPolicy().onNetwork();
+ return tagSocket(os.accept(fd, peerAddress));
+ }
+
+ @Override public void close(FileDescriptor fd) throws ErrnoException {
+ // TODO: is there a way to avoid calling getsockopt(2) on non-socket fds?
+ if (isLingerSocket(fd)) {
+ // If the fd is a socket with SO_LINGER set, we might block indefinitely.
+ // We allow non-linger sockets so that apps can close their network connections in
+ // methods like onDestroy which will run on the UI thread.
+ BlockGuard.getThreadPolicy().onNetwork();
+ }
+ os.close(fd);
+ }
+
+ private static boolean isLingerSocket(FileDescriptor fd) {
+ try {
+ StructLinger linger = Libcore.os.getsockoptLinger(fd, SOL_SOCKET, SO_LINGER);
+ return linger.isOn() && linger.l_linger > 0;
+ } catch (ErrnoException ignored) {
+ // We're called via Socket.close (which doesn't ask for us to be called), so we
+ // must not throw here, because Socket.close must not throw if asked to close an
+ // already-closed socket. Also, the passed-in FileDescriptor isn't necessarily
+ // a socket at all.
+ return false;
+ }
+ }
+
+ @Override public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException {
+ BlockGuard.getThreadPolicy().onNetwork();
+ os.connect(fd, address, port);
+ }
+
+ @Override public void fdatasync(FileDescriptor fd) throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
os.fdatasync(fd);
}
- @Override
- public void fsync(FileDescriptor fd) throws ErrnoException {
+ @Override public void fsync(FileDescriptor fd) throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
os.fsync(fd);
}
- @Override
- public void ftruncate(FileDescriptor fd, long length) throws ErrnoException {
+ @Override public void ftruncate(FileDescriptor fd, long length) throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
os.ftruncate(fd, length);
}
- @Override
- public FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
+ @Override public FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
BlockGuard.getThreadPolicy().onReadFromDisk();
if ((mode & O_ACCMODE) != O_RDONLY) {
BlockGuard.getThreadPolicy().onWriteToDisk();
@@ -57,49 +98,68 @@
return os.open(path, flags, mode);
}
- @Override
- public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
+ @Override public int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException {
+ // Greater than 0 is a timeout in milliseconds and -1 means "block forever",
+ // but 0 means "poll and return immediately", which shouldn't be subject to BlockGuard.
+ if (timeoutMs != 0) {
+ BlockGuard.getThreadPolicy().onNetwork();
+ }
+ return os.poll(fds, timeoutMs);
+ }
+
+ @Override public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
BlockGuard.getThreadPolicy().onReadFromDisk();
return os.read(fd, buffer);
}
- @Override
- public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
+ @Override public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
BlockGuard.getThreadPolicy().onReadFromDisk();
return os.read(fd, bytes, byteOffset, byteCount);
}
- @Override
- public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException {
+ @Override public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException {
BlockGuard.getThreadPolicy().onReadFromDisk();
return os.readv(fd, buffers, offsets, byteCounts);
}
- @Override
- public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException {
- final FileDescriptor fd = os.socket(domain, type, protocol);
- try {
- BlockGuard.tagSocketFd(fd);
- } catch (SocketException e) {
- throw new ErrnoException("socket", EINVAL, e);
- }
- return fd;
+ @Override public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException {
+ BlockGuard.getThreadPolicy().onNetwork();
+ return os.recvfrom(fd, buffer, flags, srcAddress);
}
- @Override
- public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
+ @Override public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException {
+ BlockGuard.getThreadPolicy().onNetwork();
+ return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress);
+ }
+
+ @Override public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException {
+ BlockGuard.getThreadPolicy().onNetwork();
+ return os.sendto(fd, buffer, flags, inetAddress, port);
+ }
+
+ @Override public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException {
+ // We permit datagrams without hostname lookups.
+ if (inetAddress != null) {
+ BlockGuard.getThreadPolicy().onNetwork();
+ }
+ return os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port);
+ }
+
+ @Override public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException {
+ return tagSocket(os.socket(domain, type, protocol));
+ }
+
+ @Override public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
return os.write(fd, buffer);
}
- @Override
- public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
+ @Override public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
return os.write(fd, bytes, byteOffset, byteCount);
}
- @Override
- public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException {
+ @Override public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException {
BlockGuard.getThreadPolicy().onWriteToDisk();
return os.writev(fd, buffers, offsets, byteCounts);
}
diff --git a/luni/src/main/java/libcore/io/DiskLruCache.java b/luni/src/main/java/libcore/io/DiskLruCache.java
new file mode 100644
index 0000000..c887541
--- /dev/null
+++ b/luni/src/main/java/libcore/io/DiskLruCache.java
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.io;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedWriter;
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.Charsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A cache that uses a bounded amount of space on a filesystem. Each cache
+ * entry has a string key and a fixed number of values. Values are byte
+ * sequences, accessible as streams or files. Each value must be between {@code
+ * 0} and {@code Integer.MAX_VALUE} bytes in length.
+ *
+ * <p>The cache stores its data in a directory on the filesystem. This
+ * directory must be exclusive to the cache; the cache may delete or overwrite
+ * files from its directory. It is an error for multiple processes to use the
+ * same cache directory at the same time.
+ *
+ * <p>This cache limits the number of bytes that it will store on the
+ * filesystem. When the number of stored bytes exceeds the limit, the cache will
+ * remove entries in the background until the limit is satisfied. The limit is
+ * not strict: the cache may temporarily exceed it while waiting for files to be
+ * deleted. The limit does not include filesystem overhead or the cache
+ * journal so space-sensitive applications should set a conservative limit.
+ *
+ * <p>Clients call {@link #edit} to create or update the values of an entry. An
+ * entry may have only one editor at one time; if a value is not available to be
+ * edited then {@link #edit} will return null.
+ * <ul>
+ * <li>When an entry is being <strong>created</strong> it is necessary to
+ * supply a full set of values; the empty value should be used as a
+ * placeholder if necessary.
+ * <li>When an entry is being <strong>created</strong>, it is not necessary
+ * to supply data for every value; values default to their previous
+ * value.
+ * </ul>
+ * Every {@link #edit} call must be matched by a call to {@link Editor#commit}
+ * or {@link Editor#abort}. Committing is atomic: a read observes the full set
+ * of values as they were before or after the commit, but never a mix of values.
+ *
+ * <p>Clients call {@link #get} to read a snapshot of an entry. The read will
+ * observe the value at the time that {@link #get} was called. Updates and
+ * removals after the call do not impact ongoing reads.
+ *
+ * <p>This class is tolerant of some I/O errors. If files are missing from the
+ * filesystem, the corresponding entries will be dropped from the cache. If
+ * an error occurs while writing a cache value, the edit will fail silently.
+ * Callers should handle other problems by catching {@code IOException} and
+ * responding appropriately.
+ */
+public final class DiskLruCache implements Closeable {
+ static final String JOURNAL_FILE = "journal";
+ static final String JOURNAL_FILE_TMP = "journal.tmp";
+ static final String MAGIC = "libcore.io.DiskLruCache";
+ static final String VERSION_1 = "1";
+ private static final String CLEAN = "CLEAN";
+ private static final String DIRTY = "DIRTY";
+ private static final String REMOVE = "REMOVE";
+ private static final String READ = "READ";
+
+ /*
+ * This cache uses a journal file named "journal". A typical journal file
+ * looks like this:
+ * libcore.io.DiskLruCache
+ * 1
+ * 100
+ * 2
+ *
+ * CLEAN 3400330d1dfc7f3f7f4b8d4d803dfcf6 832 21054
+ * DIRTY 335c4c6028171cfddfbaae1a9c313c52
+ * CLEAN 335c4c6028171cfddfbaae1a9c313c52 3934 2342
+ * REMOVE 335c4c6028171cfddfbaae1a9c313c52
+ * DIRTY 1ab96a171faeeee38496d8b330771a7a
+ * CLEAN 1ab96a171faeeee38496d8b330771a7a 1600 234
+ * READ 335c4c6028171cfddfbaae1a9c313c52
+ * READ 3400330d1dfc7f3f7f4b8d4d803dfcf6
+ *
+ * The first five lines of the journal form its header. They are the
+ * constant string "libcore.io.DiskLruCache", the disk cache's version,
+ * the application's version, the value count, and a blank line.
+ *
+ * Each of the subsequent lines in the file is a record of the state of a
+ * cache entry. Each line contains space-separated values: a state, a key,
+ * and optional state-specific values.
+ * o DIRTY lines track that an entry is actively being created or updated.
+ * Every successful DIRTY action should be followed by a CLEAN or REMOVE
+ * action. DIRTY lines without a matching CLEAN or REMOVE indicate that
+ * temporary files may need to be deleted.
+ * o CLEAN lines track a cache entry that has been successfully published
+ * and may be read. A publish line is followed by the lengths of each of
+ * its values.
+ * o READ lines track accesses for LRU.
+ * o REMOVE lines track entries that have been deleted.
+ *
+ * The journal file is appended to as cache operations occur. The journal may
+ * occasionally be compacted by dropping redundant lines. A temporary file named
+ * "journal.tmp" will be used during compaction; that file should be deleted if
+ * it exists when the cache is opened.
+ */
+
+ private final File directory;
+ private final File journalFile;
+ private final File journalFileTmp;
+ private final int appVersion;
+ private final long maxSize;
+ private final int valueCount;
+ private long size = 0;
+ private Writer journalWriter;
+ private final LinkedHashMap<String, Entry> lruEntries
+ = new LinkedHashMap<String, Entry>(0, 0.75f, true);
+ private int redundantOpCount;
+
+ /** This cache uses a single background thread to evict entries. */
+ private final ExecutorService executorService = new ThreadPoolExecutor(0, 1,
+ 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
+ private final Callable<Void> cleanupCallable = new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ synchronized (DiskLruCache.this) {
+ if (journalWriter == null) {
+ return null; // closed
+ }
+ trimToSize();
+ if (journalRebuildRequired()) {
+ rebuildJournal();
+ redundantOpCount = 0;
+ }
+ }
+ return null;
+ }
+ };
+
+ private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) {
+ this.directory = directory;
+ this.appVersion = appVersion;
+ this.journalFile = new File(directory, JOURNAL_FILE);
+ this.journalFileTmp = new File(directory, JOURNAL_FILE_TMP);
+ this.valueCount = valueCount;
+ this.maxSize = maxSize;
+ }
+
+ /**
+ * Opens the cache in {@code directory}, creating a cache if none exists
+ * there.
+ *
+ * @param directory a writable directory
+ * @param appVersion
+ * @param valueCount the number of values per cache entry. Must be positive.
+ * @param maxSize the maximum number of bytes this cache should use to store
+ * @throws IOException if reading or writing the cache directory fails
+ */
+ public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
+ throws IOException {
+ if (maxSize <= 0) {
+ throw new IllegalArgumentException("maxSize <= 0");
+ }
+ if (valueCount <= 0) {
+ throw new IllegalArgumentException("valueCount <= 0");
+ }
+
+ // prefer to pick up where we left off
+ DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
+ if (cache.journalFile.exists()) {
+ try {
+ cache.readJournal();
+ cache.processJournal();
+ cache.journalWriter = new BufferedWriter(new FileWriter(cache.journalFile, true));
+ return cache;
+ } catch (IOException journalIsCorrupt) {
+ System.logW("DiskLruCache " + directory + " is corrupt: "
+ + journalIsCorrupt.getMessage() + ", removing");
+ cache.delete();
+ }
+ }
+
+ // create a new empty cache
+ directory.mkdirs();
+ cache = new DiskLruCache(directory, appVersion, valueCount, maxSize);
+ cache.rebuildJournal();
+ return cache;
+ }
+
+ private void readJournal() throws IOException {
+ InputStream in = new BufferedInputStream(new FileInputStream(journalFile));
+ try {
+ String magic = Streams.readAsciiLine(in);
+ String version = Streams.readAsciiLine(in);
+ String appVersionString = Streams.readAsciiLine(in);
+ String valueCountString = Streams.readAsciiLine(in);
+ String blank = Streams.readAsciiLine(in);
+ if (!MAGIC.equals(magic)
+ || !VERSION_1.equals(version)
+ || !Integer.toString(appVersion).equals(appVersionString)
+ || !Integer.toString(valueCount).equals(valueCountString)
+ || !"".equals(blank)) {
+ throw new IOException("unexpected journal header: ["
+ + magic + ", " + version + ", " + valueCountString + ", " + blank + "]");
+ }
+
+ while (true) {
+ try {
+ readJournalLine(Streams.readAsciiLine(in));
+ } catch (EOFException endOfJournal) {
+ break;
+ }
+ }
+ } finally {
+ IoUtils.closeQuietly(in);
+ }
+ }
+
+ private void readJournalLine(String line) throws IOException {
+ String[] parts = line.split(" ");
+ if (parts.length < 2) {
+ throw new IOException("unexpected journal line: " + line);
+ }
+
+ String key = parts[1];
+ if (parts[0].equals(REMOVE) && parts.length == 2) {
+ lruEntries.remove(key);
+ return;
+ }
+
+ Entry entry = lruEntries.get(key);
+ if (entry == null) {
+ entry = new Entry(key);
+ lruEntries.put(key, entry);
+ }
+
+ if (parts[0].equals(CLEAN) && parts.length == 2 + valueCount) {
+ entry.readable = true;
+ entry.currentEditor = null;
+ entry.setLengths(Arrays.copyOfRange(parts, 2, parts.length));
+ } else if (parts[0].equals(DIRTY) && parts.length == 2) {
+ entry.currentEditor = new Editor(entry);
+ } else if (parts[0].equals(READ) && parts.length == 2) {
+ // this work was already done by calling lruEntries.get()
+ } else {
+ throw new IOException("unexpected journal line: " + line);
+ }
+ }
+
+ /**
+ * Computes the initial size and collects garbage as a part of opening the
+ * cache. Dirty entries are assumed to be inconsistent and will be deleted.
+ */
+ private void processJournal() throws IOException {
+ deleteIfExists(journalFileTmp);
+ for (Iterator<Entry> i = lruEntries.values().iterator(); i.hasNext(); ) {
+ Entry entry = i.next();
+ if (entry.currentEditor == null) {
+ for (int t = 0; t < valueCount; t++) {
+ size += entry.lengths[t];
+ }
+ } else {
+ entry.currentEditor = null;
+ for (int t = 0; t < valueCount; t++) {
+ deleteIfExists(entry.getCleanFile(t));
+ deleteIfExists(entry.getDirtyFile(t));
+ }
+ i.remove();
+ }
+ }
+ }
+
+ /**
+ * Creates a new journal that omits redundant information. This replaces the
+ * current journal if it exists.
+ */
+ private synchronized void rebuildJournal() throws IOException {
+ if (journalWriter != null) {
+ journalWriter.close();
+ }
+
+ Writer writer = new BufferedWriter(new FileWriter(journalFileTmp));
+ writer.write(MAGIC);
+ writer.write("\n");
+ writer.write(VERSION_1);
+ writer.write("\n");
+ writer.write(Integer.toString(appVersion));
+ writer.write("\n");
+ writer.write(Integer.toString(valueCount));
+ writer.write("\n");
+ writer.write("\n");
+
+ for (Entry entry : lruEntries.values()) {
+ if (entry.currentEditor != null) {
+ writer.write(DIRTY + ' ' + entry.key + '\n');
+ } else {
+ writer.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
+ }
+ }
+
+ writer.close();
+ journalFileTmp.renameTo(journalFile);
+ journalWriter = new BufferedWriter(new FileWriter(journalFile, true));
+ }
+
+ private static void deleteIfExists(File file) throws IOException {
+ try {
+ Libcore.os.remove(file.getPath());
+ } catch (ErrnoException e) {
+ if (e.errno != OsConstants.ENOENT) {
+ throw e;
+ }
+ }
+ }
+
+ /**
+ * Returns a snapshot of the entry named {@code key}, or null if it doesn't
+ * exist is not currently readable. If a value is returned, it is moved to
+ * the head of the LRU queue.
+ */
+ public synchronized Snapshot get(String key) throws IOException {
+ checkNotClosed();
+ validateKey(key);
+ Entry entry = lruEntries.get(key);
+ if (entry == null) {
+ return null;
+ }
+
+ if (!entry.readable) {
+ return null;
+ }
+
+ /*
+ * Open all streams eagerly to guarantee that we see a single published
+ * snapshot. If we opened streams lazily then the streams could come
+ * from different edits.
+ */
+ InputStream[] ins = new InputStream[valueCount];
+ try {
+ for (int i = 0; i < valueCount; i++) {
+ ins[i] = new FileInputStream(entry.getCleanFile(i));
+ }
+ } catch (FileNotFoundException e) {
+ // a file must have been deleted manually!
+ return null;
+ }
+
+ redundantOpCount++;
+ journalWriter.append(READ + ' ' + key + '\n');
+ if (journalRebuildRequired()) {
+ executorService.submit(cleanupCallable);
+ }
+
+ return new Snapshot(ins);
+ }
+
+ /**
+ * Returns an editor for the entry named {@code key}, or null if it cannot
+ * currently be edited.
+ */
+ public synchronized Editor edit(String key) throws IOException {
+ checkNotClosed();
+ validateKey(key);
+ Entry entry = lruEntries.get(key);
+ if (entry == null) {
+ entry = new Entry(key);
+ lruEntries.put(key, entry);
+ } else if (entry.currentEditor != null) {
+ return null;
+ }
+
+ Editor editor = new Editor(entry);
+ entry.currentEditor = editor;
+
+ // flush the journal before creating files to prevent file leaks
+ journalWriter.write(DIRTY + ' ' + key + '\n');
+ journalWriter.flush();
+ return editor;
+ }
+
+ /**
+ * Returns the directory where this cache stores its data.
+ */
+ public File getDirectory() {
+ return directory;
+ }
+
+ /**
+ * Returns the maximum number of bytes that this cache should use to store
+ * its data.
+ */
+ public long maxSize() {
+ return maxSize;
+ }
+
+ /**
+ * Returns the number of bytes currently being used to store the values in
+ * this cache. This may be greater than the max size if a background
+ * deletion is pending.
+ */
+ public synchronized long size() {
+ return size;
+ }
+
+ private synchronized void completeEdit(Editor editor, boolean success) throws IOException {
+ Entry entry = editor.entry;
+ if (entry.currentEditor != editor) {
+ throw new IllegalStateException();
+ }
+
+ // if this edit is creating the entry for the first time, every index must have a value
+ if (success && !entry.readable) {
+ for (int i = 0; i < valueCount; i++) {
+ if (!entry.getDirtyFile(i).exists()) {
+ editor.abort();
+ throw new IllegalStateException("edit didn't create file " + i);
+ }
+ }
+ }
+
+ for (int i = 0; i < valueCount; i++) {
+ File dirty = entry.getDirtyFile(i);
+ if (success) {
+ if (dirty.exists()) {
+ File clean = entry.getCleanFile(i);
+ dirty.renameTo(clean);
+ long oldLength = entry.lengths[i];
+ long newLength = clean.length();
+ entry.lengths[i] = newLength;
+ size = size - oldLength + newLength;
+ }
+ } else {
+ deleteIfExists(dirty);
+ }
+ }
+
+ redundantOpCount++;
+ entry.currentEditor = null;
+ if (entry.readable | success) {
+ entry.readable = true;
+ journalWriter.write(CLEAN + ' ' + entry.key + entry.getLengths() + '\n');
+ } else {
+ lruEntries.remove(entry.key);
+ journalWriter.write(REMOVE + ' ' + entry.key + '\n');
+ }
+
+ if (size > maxSize || journalRebuildRequired()) {
+ executorService.submit(cleanupCallable);
+ }
+ }
+
+ /**
+ * We only rebuild the journal when it will halve the size of the journal
+ * and eliminate at least 2000 ops.
+ */
+ private boolean journalRebuildRequired() {
+ final int REDUNDANT_OP_COMPACT_THRESHOLD = 2000;
+ return redundantOpCount >= REDUNDANT_OP_COMPACT_THRESHOLD
+ && redundantOpCount >= lruEntries.size();
+ }
+
+ /**
+ * Drops the entry for {@code key} if it exists and can be removed. Entries
+ * actively being edited cannot be removed.
+ *
+ * @return true if an entry was removed.
+ */
+ public synchronized boolean remove(String key) throws IOException {
+ checkNotClosed();
+ validateKey(key);
+ Entry entry = lruEntries.get(key);
+ if (entry == null || entry.currentEditor != null) {
+ return false;
+ }
+
+ for (int i = 0; i < valueCount; i++) {
+ File file = entry.getCleanFile(i);
+ if (!file.delete()) {
+ throw new IOException("failed to delete " + file);
+ }
+ size -= entry.lengths[i];
+ entry.lengths[i] = 0;
+ }
+
+ redundantOpCount++;
+ journalWriter.append(REMOVE + ' ' + key + '\n');
+ lruEntries.remove(key);
+
+ if (journalRebuildRequired()) {
+ executorService.submit(cleanupCallable);
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns true if this cache has been closed.
+ */
+ public boolean isClosed() {
+ return journalWriter == null;
+ }
+
+ private void checkNotClosed() {
+ if (journalWriter == null) {
+ throw new IllegalStateException("cache is closed");
+ }
+ }
+
+ /**
+ * Force buffered operations to the filesystem.
+ */
+ public synchronized void flush() throws IOException {
+ checkNotClosed();
+ trimToSize();
+ journalWriter.flush();
+ }
+
+ /**
+ * Closes this cache. Stored values will remain on the filesystem.
+ */
+ public synchronized void close() throws IOException {
+ if (journalWriter == null) {
+ return; // already closed
+ }
+ for (Entry entry : new ArrayList<Entry>(lruEntries.values())) {
+ if (entry.currentEditor != null) {
+ entry.currentEditor.abort();
+ }
+ }
+ trimToSize();
+ journalWriter.close();
+ journalWriter = null;
+ }
+
+ private void trimToSize() throws IOException {
+ while (size > maxSize) {
+ Map.Entry<String, Entry> toEvict = lruEntries.eldest();
+ remove(toEvict.getKey());
+ }
+ }
+
+ /**
+ * Closes the cache and deletes all of its stored values. This will delete
+ * all files in the cache directory including files that weren't created by
+ * the cache.
+ */
+ public void delete() throws IOException {
+ close();
+ IoUtils.deleteContents(directory);
+ }
+
+ private void validateKey(String key) {
+ if (key.contains(" ") || key.contains("\n") || key.contains("\r")) {
+ throw new IllegalArgumentException(
+ "keys must not contain spaces or newlines: \"" + key + "\"");
+ }
+ }
+
+ private static String inputStreamToString(InputStream in) throws IOException {
+ return Streams.readFully(new InputStreamReader(in, Charsets.UTF_8));
+ }
+
+ /**
+ * A snapshot of the values for an entry.
+ */
+ public static final class Snapshot implements Closeable {
+ private final InputStream[] ins;
+
+ private Snapshot(InputStream[] ins) {
+ this.ins = ins;
+ }
+
+ /**
+ * Returns the unbuffered stream with the value for {@code index}.
+ */
+ public InputStream getInputStream(int index) {
+ return ins[index];
+ }
+
+ /**
+ * Returns the string value for {@code index}.
+ */
+ public String getString(int index) throws IOException {
+ return inputStreamToString(getInputStream(index));
+ }
+
+ @Override public void close() {
+ for (InputStream in : ins) {
+ IoUtils.closeQuietly(in);
+ }
+ }
+ }
+
+ /**
+ * Edits the values for an entry.
+ */
+ public final class Editor {
+ private final Entry entry;
+ private boolean hasErrors;
+
+ private Editor(Entry entry) {
+ this.entry = entry;
+ }
+
+ /**
+ * Returns an unbuffered input stream to read the last committed value,
+ * or null if no value has been committed.
+ */
+ public InputStream newInputStream(int index) throws IOException {
+ synchronized (DiskLruCache.this) {
+ if (entry.currentEditor != this) {
+ throw new IllegalStateException();
+ }
+ if (!entry.readable) {
+ return null;
+ }
+ return new FileInputStream(entry.getCleanFile(index));
+ }
+ }
+
+ /**
+ * Returns the last committed value as a string, or null if no value
+ * has been committed.
+ */
+ public String getString(int index) throws IOException {
+ InputStream in = newInputStream(index);
+ return in != null ? inputStreamToString(in) : null;
+ }
+
+ /**
+ * Returns a new unbuffered output stream to write the value at
+ * {@code index}. If the underlying output stream encounters errors
+ * when writing to the filesystem, this edit will be aborted when
+ * {@link #commit} is called. The returned output stream does not throw
+ * IOExceptions.
+ */
+ public OutputStream newOutputStream(int index) throws IOException {
+ synchronized (DiskLruCache.this) {
+ if (entry.currentEditor != this) {
+ throw new IllegalStateException();
+ }
+ return new FaultHidingOutputStream(new FileOutputStream(entry.getDirtyFile(index)));
+ }
+ }
+
+ /**
+ * Sets the value at {@code index} to {@code value}.
+ */
+ public void set(int index, String value) throws IOException {
+ Writer writer = null;
+ try {
+ writer = new OutputStreamWriter(newOutputStream(index), Charsets.UTF_8);
+ writer.write(value);
+ } finally {
+ IoUtils.closeQuietly(writer);
+ }
+ }
+
+ /**
+ * Commits this edit so it is visible to readers. This releases the
+ * edit lock so another edit may be started on the same key.
+ */
+ public void commit() throws IOException {
+ if (hasErrors) {
+ completeEdit(this, false);
+ remove(entry.key); // the previous entry is stale
+ } else {
+ completeEdit(this, true);
+ }
+ }
+
+ /**
+ * Aborts this edit. This releases the edit lock so another edit may be
+ * started on the same key.
+ */
+ public void abort() throws IOException {
+ completeEdit(this, false);
+ }
+
+ private class FaultHidingOutputStream extends FilterOutputStream {
+ private FaultHidingOutputStream(OutputStream out) {
+ super(out);
+ }
+
+ @Override public void write(int oneByte) {
+ try {
+ out.write(oneByte);
+ } catch (IOException e) {
+ hasErrors = true;
+ }
+ }
+
+ @Override public void write(byte[] buffer, int offset, int length) {
+ try {
+ out.write(buffer, offset, length);
+ } catch (IOException e) {
+ hasErrors = true;
+ }
+ }
+
+ @Override public void close() {
+ try {
+ out.close();
+ } catch (IOException e) {
+ hasErrors = true;
+ }
+ }
+
+ @Override public void flush() {
+ try {
+ out.flush();
+ } catch (IOException e) {
+ hasErrors = true;
+ }
+ }
+ }
+ }
+
+ private final class Entry {
+ private final String key;
+
+ /** Lengths of this entry's files. */
+ private final long[] lengths;
+
+ /** True if this entry has ever been published */
+ private boolean readable;
+
+ /** The ongoing edit or null if this entry is not being edited. */
+ private Editor currentEditor;
+
+ private Entry(String key) {
+ this.key = key;
+ this.lengths = new long[valueCount];
+ }
+
+ public String getLengths() throws IOException {
+ StringBuilder result = new StringBuilder();
+ for (long size : lengths) {
+ result.append(' ').append(size);
+ }
+ return result.toString();
+ }
+
+ /**
+ * Set lengths using decimal numbers like "10123".
+ */
+ private void setLengths(String[] strings) throws IOException {
+ if (strings.length != valueCount) {
+ throw invalidLengths(strings);
+ }
+
+ try {
+ for (int i = 0; i < strings.length; i++) {
+ lengths[i] = Long.parseLong(strings[i]);
+ }
+ } catch (NumberFormatException e) {
+ throw invalidLengths(strings);
+ }
+ }
+
+ private IOException invalidLengths(String[] strings) throws IOException {
+ throw new IOException("unexpected journal line: " + Arrays.toString(strings));
+ }
+
+ public File getCleanFile(int i) {
+ return new File(directory, key + "." + i);
+ }
+
+ public File getDirtyFile(int i) {
+ return new File(directory, key + "." + i + ".tmp");
+ }
+ }
+}
\ No newline at end of file
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
index 9e2b2c9..f205994 100644
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -18,6 +18,7 @@
import java.io.FileDescriptor;
import java.net.InetAddress;
+import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import libcore.util.MutableInt;
@@ -33,9 +34,14 @@
this.os = os;
}
+ public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException { return os.accept(fd, peerAddress); }
public boolean access(String path, int mode) throws ErrnoException { return os.access(path, mode); }
+ public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException { os.bind(fd, address, port); }
public void chmod(String path, int mode) throws ErrnoException { os.chmod(path, mode); }
public void close(FileDescriptor fd) throws ErrnoException { os.close(fd); }
+ public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException { os.connect(fd, address, port); }
+ public FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException { return os.dup(oldFd); }
+ public FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException { return os.dup2(oldFd, newFd); }
public String[] environ() { return os.environ(); }
public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException { return os.fcntlVoid(fd, cmd); }
public int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException { return os.fcntlLong(fd, cmd, arg); }
@@ -47,14 +53,22 @@
public void ftruncate(FileDescriptor fd, long length) throws ErrnoException { os.ftruncate(fd, length); }
public String gai_strerror(int error) { return os.gai_strerror(error); }
public InetAddress[] getaddrinfo(String node, StructAddrinfo hints) throws GaiException { return os.getaddrinfo(node, hints); }
+ public int getegid() { return os.getegid(); }
+ public int geteuid() { return os.geteuid(); }
+ public int getgid() { return os.getgid(); }
public String getenv(String name) { return os.getenv(name); }
public String getnameinfo(InetAddress address, int flags) throws GaiException { return os.getnameinfo(address, flags); }
+ public int getpid() { return os.getpid(); }
+ public int getppid() { return os.getppid(); }
+ public StructPasswd getpwnam(String name) throws ErrnoException { return os.getpwnam(name); }
+ public StructPasswd getpwuid(int uid) throws ErrnoException { return os.getpwuid(uid); }
public SocketAddress getsockname(FileDescriptor fd) throws ErrnoException { return os.getsockname(fd); }
public int getsockoptByte(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptByte(fd, level, option); }
public InetAddress getsockoptInAddr(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptInAddr(fd, level, option); }
public int getsockoptInt(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptInt(fd, level, option); }
public StructLinger getsockoptLinger(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptLinger(fd, level, option); }
public StructTimeval getsockoptTimeval(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptTimeval(fd, level, option); }
+ public int getuid() { return os.getuid(); }
public String if_indextoname(int index) { return os.if_indextoname(index); }
public InetAddress inet_aton(String address) { return os.inet_aton(address); }
public InetAddress ioctlInetAddress(FileDescriptor fd, int cmd, String interfaceName) throws ErrnoException { return os.ioctlInetAddress(fd, cmd, interfaceName); }
@@ -73,12 +87,20 @@
public void munmap(long address, long byteCount) throws ErrnoException { os.munmap(address, byteCount); }
public FileDescriptor open(String path, int flags, int mode) throws ErrnoException { return os.open(path, flags, mode); }
public FileDescriptor[] pipe() throws ErrnoException { return os.pipe(); }
+ public int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException { return os.poll(fds, timeoutMs); }
public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { return os.read(fd, buffer); }
public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException { return os.read(fd, bytes, byteOffset, byteCount); }
public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException { return os.readv(fd, buffers, offsets, byteCounts); }
+ public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException { return os.recvfrom(fd, buffer, flags, srcAddress); }
+ public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException { return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); }
public void remove(String path) throws ErrnoException { os.remove(path); }
public void rename(String oldPath, String newPath) throws ErrnoException { os.rename(oldPath, newPath); }
public long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException { return os.sendfile(outFd, inFd, inOffset, byteCount); }
+ public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException { return os.sendto(fd, buffer, flags, inetAddress, port); }
+ public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException { return os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); }
+ public void setegid(int egid) throws ErrnoException { os.setegid(egid); }
+ public void seteuid(int euid) throws ErrnoException { os.seteuid(euid); }
+ public void setgid(int gid) throws ErrnoException { os.setgid(gid); }
public void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException { os.setsockoptByte(fd, level, option, value); }
public void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException { os.setsockoptIfreq(fd, level, option, value); }
public void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException { os.setsockoptInt(fd, level, option, value); }
@@ -86,6 +108,7 @@
public void setsockoptGroupReq(FileDescriptor fd, int level, int option, StructGroupReq value) throws ErrnoException { os.setsockoptGroupReq(fd, level, option, value); }
public void setsockoptLinger(FileDescriptor fd, int level, int option, StructLinger value) throws ErrnoException { os.setsockoptLinger(fd, level, option, value); }
public void setsockoptTimeval(FileDescriptor fd, int level, int option, StructTimeval value) throws ErrnoException { os.setsockoptTimeval(fd, level, option, value); }
+ public void setuid(int uid) throws ErrnoException { os.setuid(uid); }
public void shutdown(FileDescriptor fd, int how) throws ErrnoException { os.shutdown(fd, how); }
public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException { return os.socket(domain, type, protocol); }
public StructStat stat(String path) throws ErrnoException { return os.stat(path); }
@@ -94,6 +117,7 @@
public void symlink(String oldPath, String newPath) throws ErrnoException { os.symlink(oldPath, newPath); }
public long sysconf(int name) { return os.sysconf(name); }
public StructUtsname uname() throws ErrnoException { return os.uname(); }
+ public int waitpid(int pid, MutableInt status, int options) throws ErrnoException { return os.waitpid(pid, status, options); }
public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { return os.write(fd, buffer); }
public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException { return os.write(fd, bytes, byteOffset, byteCount); }
public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException { return os.writev(fd, buffers, offsets, byteCounts); }
diff --git a/luni/src/main/java/libcore/io/IoBridge.java b/luni/src/main/java/libcore/io/IoBridge.java
new file mode 100644
index 0000000..c26b565
--- /dev/null
+++ b/luni/src/main/java/libcore/io/IoBridge.java
@@ -0,0 +1,593 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.io;
+
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.BindException;
+import java.net.ConnectException;
+import java.net.DatagramPacket;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.PortUnreachableException;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.SocketOptions;
+import java.net.SocketTimeoutException;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+import libcore.util.MutableInt;
+import static libcore.io.OsConstants.*;
+
+/**
+ * Implements java.io/java.net/java.nio semantics in terms of the underlying POSIX system calls.
+ */
+public final class IoBridge {
+
+ private IoBridge() {
+ }
+
+ public static int available(FileDescriptor fd) throws IOException {
+ try {
+ MutableInt available = new MutableInt(0);
+ int rc = Libcore.os.ioctlInt(fd, FIONREAD, available);
+ if (available.value < 0) {
+ // If the fd refers to a regular file, the result is the difference between
+ // the file size and the file position. This may be negative if the position
+ // is past the end of the file. If the fd refers to a special file masquerading
+ // as a regular file, the result may be negative because the special file
+ // may appear to have zero size and yet a previous read call may have
+ // read some amount of data and caused the file position to be advanced.
+ available.value = 0;
+ }
+ return available.value;
+ } catch (ErrnoException errnoException) {
+ if (errnoException.errno == ENOTTY) {
+ // The fd is unwilling to opine about its read buffer.
+ return 0;
+ }
+ throw errnoException.rethrowAsIOException();
+ }
+ }
+
+
+ public static void bind(FileDescriptor fd, InetAddress address, int port) throws SocketException {
+ if (address instanceof Inet6Address && ((Inet6Address) address).getScopeId() == 0) {
+ // Linux won't let you bind a link-local address without a scope id. Find one.
+ NetworkInterface nif = NetworkInterface.getByInetAddress(address);
+ if (nif == null) {
+ throw new SocketException("Can't bind to a link-local address without a scope id: " + address);
+ }
+ try {
+ address = Inet6Address.getByAddress(address.getHostName(), address.getAddress(), nif.getIndex());
+ } catch (UnknownHostException ex) {
+ throw new AssertionError(ex); // Can't happen.
+ }
+ }
+ try {
+ Libcore.os.bind(fd, address, port);
+ } catch (ErrnoException errnoException) {
+ throw new BindException(errnoException.getMessage(), errnoException);
+ }
+ }
+
+
+ /**
+ * Connects socket 'fd' to 'inetAddress' on 'port', with no timeout. The lack of a timeout
+ * means this method won't throw SocketTimeoutException.
+ */
+ public static boolean connect(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException {
+ try {
+ return IoBridge.connect(fd, inetAddress, port, 0);
+ } catch (SocketTimeoutException ex) {
+ throw new AssertionError(ex); // Can't happen for a connect without a timeout.
+ }
+ }
+
+ /**
+ * Connects socket 'fd' to 'inetAddress' on 'port', with a the given 'timeoutMs'.
+ * Use timeoutMs == 0 for a blocking connect with no timeout.
+ */
+ public static boolean connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws SocketException, SocketTimeoutException {
+ try {
+ return connectErrno(fd, inetAddress, port, timeoutMs);
+ } catch (ErrnoException errnoException) {
+ throw new ConnectException(connectDetail(inetAddress, port, timeoutMs, errnoException), errnoException);
+ } catch (SocketException ex) {
+ throw ex; // We don't want to doubly wrap these.
+ } catch (SocketTimeoutException ex) {
+ throw ex; // We don't want to doubly wrap these.
+ } catch (IOException ex) {
+ throw new SocketException(ex);
+ }
+ }
+
+ private static boolean connectErrno(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws IOException {
+ // With no timeout, just call connect(2) directly.
+ if (timeoutMs == 0) {
+ Libcore.os.connect(fd, inetAddress, port);
+ return true;
+ }
+
+ // With a timeout, we set the socket to non-blocking, connect(2), and then loop
+ // using poll(2) to decide whether we're connected, whether we should keep waiting,
+ // or whether we've seen a permanent failure and should give up.
+ long finishTimeMs = System.currentTimeMillis() + timeoutMs;
+ IoUtils.setBlocking(fd, false);
+ try {
+ try {
+ Libcore.os.connect(fd, inetAddress, port);
+ return true; // We connected immediately.
+ } catch (ErrnoException errnoException) {
+ if (errnoException.errno != EINPROGRESS) {
+ throw errnoException;
+ }
+ // EINPROGRESS means we should keep trying...
+ }
+ int remainingTimeoutMs;
+ do {
+ remainingTimeoutMs = (int) (finishTimeMs - System.currentTimeMillis());
+ if (remainingTimeoutMs <= 0) {
+ throw new SocketTimeoutException(connectDetail(inetAddress, port, timeoutMs, null));
+ }
+ } while (!IoBridge.isConnected(fd, inetAddress, port, timeoutMs, remainingTimeoutMs));
+ return true; // Or we'd have thrown.
+ } finally {
+ IoUtils.setBlocking(fd, true);
+ }
+ }
+
+ private static String connectDetail(InetAddress inetAddress, int port, int timeoutMs, ErrnoException cause) {
+ String detail = "failed to connect to " + inetAddress + " (port " + port + ")";
+ if (timeoutMs > 0) {
+ detail += " after " + timeoutMs + "ms";
+ }
+ if (cause != null) {
+ detail += ": " + cause.getMessage();
+ }
+ return detail;
+ }
+
+ public static void closeSocket(FileDescriptor fd) throws IOException {
+ if (!fd.valid()) {
+ // Socket.close doesn't throw if you try to close an already-closed socket.
+ return;
+ }
+ int intFd = fd.getInt$();
+ fd.setInt$(-1);
+ FileDescriptor oldFd = new FileDescriptor();
+ oldFd.setInt$(intFd);
+ AsynchronousCloseMonitor.signalBlockedThreads(oldFd);
+ try {
+ Libcore.os.close(oldFd);
+ } catch (ErrnoException errnoException) {
+ // TODO: are there any cases in which we should throw?
+ }
+ }
+
+ public static boolean isConnected(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs, int remainingTimeoutMs) throws IOException {
+ ErrnoException cause = null;
+ try {
+ StructPollfd[] pollFds = new StructPollfd[] { new StructPollfd() };
+ pollFds[0].fd = fd;
+ pollFds[0].events = (short) POLLOUT;
+ int rc = Libcore.os.poll(pollFds, remainingTimeoutMs);
+ if (rc == 0) {
+ return false; // Timeout.
+ }
+ int connectError = Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_ERROR);
+ if (connectError == 0) {
+ return true; // Success!
+ }
+ throw new ErrnoException("isConnected", connectError); // The connect(2) failed.
+ } catch (ErrnoException errnoException) {
+ if (errnoException.errno == EINTR) {
+ return false; // Punt and ask the caller to try again.
+ } else {
+ cause = errnoException;
+ }
+ }
+ // TODO: is it really helpful/necessary to throw so many different exceptions?
+ String detail = connectDetail(inetAddress, port, timeoutMs, cause);
+ if (cause.errno == ECONNRESET || cause.errno == ECONNREFUSED ||
+ cause.errno == EADDRNOTAVAIL || cause.errno == EADDRINUSE ||
+ cause.errno == ENETUNREACH) {
+ throw new ConnectException(detail, cause);
+ } else if (cause.errno == EACCES) {
+ throw new SecurityException(detail, cause);
+ } else if (cause.errno == ETIMEDOUT) {
+ throw new SocketTimeoutException(detail, cause);
+ }
+ throw new SocketException(detail, cause);
+ }
+
+ // Socket options used by java.net but not exposed in SocketOptions.
+ public static final int JAVA_MCAST_JOIN_GROUP = 19;
+ public static final int JAVA_MCAST_LEAVE_GROUP = 20;
+ public static final int JAVA_IP_MULTICAST_TTL = 17;
+
+ /**
+ * java.net has its own socket options similar to the underlying Unix ones. We paper over the
+ * differences here.
+ */
+ public static Object getSocketOption(FileDescriptor fd, int option) throws SocketException {
+ try {
+ return getSocketOptionErrno(fd, option);
+ } catch (ErrnoException errnoException) {
+ throw errnoException.rethrowAsSocketException();
+ }
+ }
+
+ private static Object getSocketOptionErrno(FileDescriptor fd, int option) throws SocketException {
+ switch (option) {
+ case SocketOptions.IP_MULTICAST_IF:
+ // This is IPv4-only.
+ return Libcore.os.getsockoptInAddr(fd, IPPROTO_IP, IP_MULTICAST_IF);
+ case SocketOptions.IP_MULTICAST_IF2:
+ // This is IPv6-only.
+ return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF);
+ case SocketOptions.IP_MULTICAST_LOOP:
+ // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
+ // it doesn't matter which we return.
+ return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP));
+ case IoBridge.JAVA_IP_MULTICAST_TTL:
+ // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
+ // it doesn't matter which we return.
+ return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS);
+ case SocketOptions.IP_TOS:
+ // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
+ // it doesn't matter which we return.
+ return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS);
+ case SocketOptions.SO_BROADCAST:
+ return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_BROADCAST));
+ case SocketOptions.SO_KEEPALIVE:
+ return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE));
+ case SocketOptions.SO_LINGER:
+ StructLinger linger = Libcore.os.getsockoptLinger(fd, SOL_SOCKET, SO_LINGER);
+ if (!linger.isOn()) {
+ return false;
+ }
+ return linger.l_linger;
+ case SocketOptions.SO_OOBINLINE:
+ return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE));
+ case SocketOptions.SO_RCVBUF:
+ return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_SNDBUF);
+ case SocketOptions.SO_REUSEADDR:
+ return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR));
+ case SocketOptions.SO_SNDBUF:
+ return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_SNDBUF);
+ case SocketOptions.SO_TIMEOUT:
+ return (int) Libcore.os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO).toMillis();
+ case SocketOptions.TCP_NODELAY:
+ return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY));
+ default:
+ throw new SocketException("Unknown socket option: " + option);
+ }
+ }
+
+ private static boolean booleanFromInt(int i) {
+ return (i != 0);
+ }
+
+ private static int booleanToInt(boolean b) {
+ return b ? 1 : 0;
+ }
+
+ /**
+ * java.net has its own socket options similar to the underlying Unix ones. We paper over the
+ * differences here.
+ */
+ public static void setSocketOption(FileDescriptor fd, int option, Object value) throws SocketException {
+ try {
+ setSocketOptionErrno(fd, option, value);
+ } catch (ErrnoException errnoException) {
+ throw errnoException.rethrowAsSocketException();
+ }
+ }
+
+ private static void setSocketOptionErrno(FileDescriptor fd, int option, Object value) throws SocketException {
+ switch (option) {
+ case SocketOptions.IP_MULTICAST_IF:
+ throw new UnsupportedOperationException("Use IP_MULTICAST_IF2 on Android");
+ case SocketOptions.IP_MULTICAST_IF2:
+ // Although IPv6 was cleaned up to use int, IPv4 uses an ip_mreqn containing an int.
+ Libcore.os.setsockoptIpMreqn(fd, IPPROTO_IP, IP_MULTICAST_IF, (Integer) value);
+ Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (Integer) value);
+ return;
+ case SocketOptions.IP_MULTICAST_LOOP:
+ // Although IPv6 was cleaned up to use int, IPv4 multicast loopback uses a byte.
+ Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_LOOP, booleanToInt((Boolean) value));
+ Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, booleanToInt((Boolean) value));
+ return;
+ case IoBridge.JAVA_IP_MULTICAST_TTL:
+ // Although IPv6 was cleaned up to use int, and IPv4 non-multicast TTL uses int,
+ // IPv4 multicast TTL uses a byte.
+ Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_TTL, (Integer) value);
+ Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (Integer) value);
+ return;
+ case SocketOptions.IP_TOS:
+ Libcore.os.setsockoptInt(fd, IPPROTO_IP, IP_TOS, (Integer) value);
+ Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS, (Integer) value);
+ return;
+ case SocketOptions.SO_BROADCAST:
+ Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_BROADCAST, booleanToInt((Boolean) value));
+ return;
+ case SocketOptions.SO_KEEPALIVE:
+ Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE, booleanToInt((Boolean) value));
+ return;
+ case SocketOptions.SO_LINGER:
+ boolean on = false;
+ int seconds = 0;
+ if (value instanceof Integer) {
+ on = true;
+ seconds = Math.min((Integer) value, 65535);
+ }
+ StructLinger linger = new StructLinger(booleanToInt(on), seconds);
+ Libcore.os.setsockoptLinger(fd, SOL_SOCKET, SO_LINGER, linger);
+ return;
+ case SocketOptions.SO_OOBINLINE:
+ Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE, booleanToInt((Boolean) value));
+ return;
+ case SocketOptions.SO_RCVBUF:
+ Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, (Integer) value);
+ return;
+ case SocketOptions.SO_REUSEADDR:
+ Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR, booleanToInt((Boolean) value));
+ return;
+ case SocketOptions.SO_SNDBUF:
+ Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_SNDBUF, (Integer) value);
+ return;
+ case SocketOptions.SO_TIMEOUT:
+ int millis = (Integer) value;
+ StructTimeval tv = StructTimeval.fromMillis(millis);
+ Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, tv);
+ return;
+ case SocketOptions.TCP_NODELAY:
+ Libcore.os.setsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY, booleanToInt((Boolean) value));
+ return;
+ case IoBridge.JAVA_MCAST_JOIN_GROUP:
+ case IoBridge.JAVA_MCAST_LEAVE_GROUP:
+ StructGroupReq groupReq = (StructGroupReq) value;
+ int level = (groupReq.gr_group instanceof Inet4Address) ? IPPROTO_IP : IPPROTO_IPV6;
+ int op = (option == JAVA_MCAST_JOIN_GROUP) ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP;
+ Libcore.os.setsockoptGroupReq(fd, level, op, groupReq);
+ return;
+ default:
+ throw new SocketException("Unknown socket option: " + option);
+ }
+ }
+
+ /**
+ * java.io only throws FileNotFoundException when opening files, regardless of what actually
+ * went wrong. Additionally, java.io is more restrictive than POSIX when it comes to opening
+ * directories: POSIX says read-only is okay, but java.io doesn't even allow that. We also
+ * have an Android-specific hack to alter the default permissions.
+ */
+ public static FileDescriptor open(String path, int flags) throws FileNotFoundException {
+ FileDescriptor fd = null;
+ try {
+ // On Android, we don't want default permissions to allow global access.
+ int mode = ((flags & O_ACCMODE) == O_RDONLY) ? 0 : 0600;
+ fd = Libcore.os.open(path, flags, mode);
+ if (fd.valid()) {
+ // Posix open(2) fails with EISDIR only if you ask for write permission.
+ // Java disallows reading directories too.
+ boolean isDirectory = false;
+ if (S_ISDIR(Libcore.os.fstat(fd).st_mode)) {
+ throw new ErrnoException("open", EISDIR);
+ }
+ }
+ return fd;
+ } catch (ErrnoException errnoException) {
+ try {
+ if (fd != null) {
+ IoUtils.close(fd);
+ }
+ } catch (IOException ignored) {
+ }
+ FileNotFoundException ex = new FileNotFoundException(path + ": " + errnoException.getMessage());
+ ex.initCause(errnoException);
+ throw ex;
+ }
+ }
+
+ /**
+ * java.io thinks that a read at EOF is an error and should return -1, contrary to traditional
+ * Unix practice where you'd read until you got 0 bytes (and any future read would return -1).
+ */
+ public static int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
+ Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount);
+ if (byteCount == 0) {
+ return 0;
+ }
+ try {
+ int readCount = Libcore.os.read(fd, bytes, byteOffset, byteCount);
+ if (readCount == 0) {
+ return -1;
+ }
+ return readCount;
+ } catch (ErrnoException errnoException) {
+ if (errnoException.errno == EAGAIN) {
+ // We return 0 rather than throw if we try to read from an empty non-blocking pipe.
+ return 0;
+ }
+ throw errnoException.rethrowAsIOException();
+ }
+ }
+
+ /**
+ * java.io always writes every byte it's asked to, or fails with an error. (That is, unlike
+ * Unix it never just writes as many bytes as happens to be convenient.)
+ */
+ public static void write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
+ Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount);
+ if (byteCount == 0) {
+ return;
+ }
+ try {
+ while (byteCount > 0) {
+ int bytesWritten = Libcore.os.write(fd, bytes, byteOffset, byteCount);
+ byteCount -= bytesWritten;
+ byteOffset += bytesWritten;
+ }
+ } catch (ErrnoException errnoException) {
+ throw errnoException.rethrowAsIOException();
+ }
+ }
+
+ public static int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws IOException {
+ boolean isDatagram = (inetAddress != null);
+ if (!isDatagram && byteCount <= 0) {
+ return 0;
+ }
+ int result;
+ try {
+ result = Libcore.os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port);
+ } catch (ErrnoException errnoException) {
+ result = maybeThrowAfterSendto(isDatagram, errnoException);
+ }
+ return result;
+ }
+
+ public static int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws IOException {
+ boolean isDatagram = (inetAddress != null);
+ if (!isDatagram && buffer.remaining() == 0) {
+ return 0;
+ }
+ int result;
+ try {
+ result = Libcore.os.sendto(fd, buffer, flags, inetAddress, port);
+ } catch (ErrnoException errnoException) {
+ result = maybeThrowAfterSendto(isDatagram, errnoException);
+ }
+ return result;
+ }
+
+ private static int maybeThrowAfterSendto(boolean isDatagram, ErrnoException errnoException) throws SocketException {
+ if (isDatagram) {
+ if (errnoException.errno == ECONNRESET || errnoException.errno == ECONNREFUSED) {
+ return 0;
+ }
+ } else {
+ if (errnoException.errno == EAGAIN || errnoException.errno == EWOULDBLOCK) {
+ // We were asked to write to a non-blocking socket, but were told
+ // it would block, so report "no bytes written".
+ return 0;
+ }
+ }
+ throw errnoException.rethrowAsSocketException();
+ }
+
+ public static int recvfrom(boolean isRead, FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, DatagramPacket packet, boolean isConnected) throws IOException {
+ int result;
+ try {
+ InetSocketAddress srcAddress = (packet != null && !isConnected) ? new InetSocketAddress() : null;
+ result = Libcore.os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress);
+ result = postRecvfrom(isRead, packet, isConnected, srcAddress, result);
+ } catch (ErrnoException errnoException) {
+ result = maybeThrowAfterRecvfrom(isRead, isConnected, errnoException);
+ }
+ return result;
+ }
+
+ public static int recvfrom(boolean isRead, FileDescriptor fd, ByteBuffer buffer, int flags, DatagramPacket packet, boolean isConnected) throws IOException {
+ int result;
+ try {
+ InetSocketAddress srcAddress = (packet != null && !isConnected) ? new InetSocketAddress() : null;
+ result = Libcore.os.recvfrom(fd, buffer, flags, srcAddress);
+ result = postRecvfrom(isRead, packet, isConnected, srcAddress, result);
+ } catch (ErrnoException errnoException) {
+ result = maybeThrowAfterRecvfrom(isRead, isConnected, errnoException);
+ }
+ return result;
+ }
+
+ private static int postRecvfrom(boolean isRead, DatagramPacket packet, boolean isConnected, InetSocketAddress srcAddress, int byteCount) {
+ if (isRead && byteCount == 0) {
+ return -1;
+ }
+ if (packet != null) {
+ packet.setLength(byteCount);
+ if (!isConnected) {
+ packet.setAddress(srcAddress.getAddress());
+ packet.setPort(srcAddress.getPort());
+ }
+ }
+ return byteCount;
+ }
+
+ private static int maybeThrowAfterRecvfrom(boolean isRead, boolean isConnected, ErrnoException errnoException) throws SocketException, SocketTimeoutException {
+ if (isRead) {
+ if (errnoException.errno == EAGAIN || errnoException.errno == EWOULDBLOCK) {
+ return 0;
+ } else {
+ throw errnoException.rethrowAsSocketException();
+ }
+ } else {
+ if (isConnected && errnoException.errno == ECONNREFUSED) {
+ throw new PortUnreachableException("", errnoException);
+ } else if (errnoException.errno == EAGAIN || errnoException.errno == EWOULDBLOCK) {
+ throw new SocketTimeoutException(errnoException);
+ } else {
+ throw errnoException.rethrowAsSocketException();
+ }
+ }
+ }
+
+ public static FileDescriptor socket(boolean stream) throws SocketException {
+ FileDescriptor fd;
+ try {
+ fd = Libcore.os.socket(AF_INET6, stream ? SOCK_STREAM : SOCK_DGRAM, 0);
+
+ // The RFC (http://www.ietf.org/rfc/rfc3493.txt) says that IPV6_MULTICAST_HOPS defaults
+ // to 1. The Linux kernel (at least up to 2.6.38) accidentally defaults to 64 (which
+ // would be correct for the *unicast* hop limit).
+ // See http://www.spinics.net/lists/netdev/msg129022.html, though no patch appears to
+ // have been applied as a result of that discussion. If that bug is ever fixed, we can
+ // remove this code. Until then, we manually set the hop limit on IPv6 datagram sockets.
+ // (IPv4 is already correct.)
+ if (!stream) {
+ Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 1);
+ }
+
+ return fd;
+ } catch (ErrnoException errnoException) {
+ throw errnoException.rethrowAsSocketException();
+ }
+ }
+
+ public static InetAddress getSocketLocalAddress(FileDescriptor fd) {
+ SocketAddress sa = Libcore.os.getsockname(fd);
+ InetSocketAddress isa = (InetSocketAddress) sa;
+ return isa.getAddress();
+ }
+
+ public static int getSocketLocalPort(FileDescriptor fd) {
+ SocketAddress sa = Libcore.os.getsockname(fd);
+ InetSocketAddress isa = (InetSocketAddress) sa;
+ return isa.getPort();
+ }
+
+}
diff --git a/luni/src/main/java/libcore/io/IoUtils.java b/luni/src/main/java/libcore/io/IoUtils.java
index eeb8d99..6680882 100644
--- a/luni/src/main/java/libcore/io/IoUtils.java
+++ b/luni/src/main/java/libcore/io/IoUtils.java
@@ -17,137 +17,19 @@
package libcore.io;
import java.io.Closeable;
+import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
-import java.net.InetAddress;
-import java.net.Inet4Address;
-import java.net.InetSocketAddress;
import java.net.Socket;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.net.SocketOptions;
-import java.net.SocketTimeoutException;
import java.nio.charset.Charsets;
-import java.util.Arrays;
-import libcore.io.ErrnoException;
-import libcore.io.Libcore;
-import libcore.util.MutableInt;
import static libcore.io.OsConstants.*;
-// TODO: kill this!
-import org.apache.harmony.luni.platform.Platform;
-
public final class IoUtils {
private IoUtils() {
}
/**
- * Implements java.io/java.net "available" semantics.
- */
- public static int available(FileDescriptor fd) throws IOException {
- try {
- MutableInt available = new MutableInt(0);
- int rc = Libcore.os.ioctlInt(fd, FIONREAD, available);
- if (available.value < 0) {
- // If the fd refers to a regular file, the result is the difference between
- // the file size and the file position. This may be negative if the position
- // is past the end of the file. If the fd refers to a special file masquerading
- // as a regular file, the result may be negative because the special file
- // may appear to have zero size and yet a previous read call may have
- // read some amount of data and caused the file position to be advanced.
- available.value = 0;
- }
- return available.value;
- } catch (ErrnoException errnoException) {
- if (errnoException.errno == ENOTTY) {
- // The fd is unwilling to opine about its read buffer.
- return 0;
- }
- throw errnoException.rethrowAsIOException();
- }
- }
-
- /**
- * java.io only throws FileNotFoundException when opening files, regardless of what actually
- * went wrong. Additionally, java.io is more restrictive than POSIX when it comes to opening
- * directories: POSIX says read-only is okay, but java.io doesn't even allow that. We also
- * have an Android-specific hack to alter the default permissions.
- */
- public static FileDescriptor open(String path, int flags) throws FileNotFoundException {
- FileDescriptor fd = null;
- try {
- // On Android, we don't want default permissions to allow global access.
- int mode = ((flags & O_ACCMODE) == O_RDONLY) ? 0 : 0600;
- fd = Libcore.os.open(path, flags, mode);
- if (fd.valid()) {
- // Posix open(2) fails with EISDIR only if you ask for write permission.
- // Java disallows reading directories too.
- boolean isDirectory = false;
- if (S_ISDIR(Libcore.os.fstat(fd).st_mode)) {
- throw new ErrnoException("open", EISDIR);
- }
- }
- return fd;
- } catch (ErrnoException errnoException) {
- try {
- if (fd != null) {
- close(fd);
- }
- } catch (IOException ignored) {
- }
- FileNotFoundException ex = new FileNotFoundException(path + ": " + errnoException.getMessage());
- ex.initCause(errnoException);
- throw ex;
- }
- }
-
- /**
- * java.io thinks that a read at EOF is an error and should return -1, contrary to traditional
- * Unix practice where you'd read until you got 0 bytes (and any future read would return -1).
- */
- public static int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
- Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount);
- if (byteCount == 0) {
- return 0;
- }
- try {
- int readCount = Libcore.os.read(fd, bytes, byteOffset, byteCount);
- if (readCount == 0) {
- return -1;
- }
- return readCount;
- } catch (ErrnoException errnoException) {
- if (errnoException.errno == EAGAIN) {
- // We return 0 rather than throw if we try to read from an empty non-blocking pipe.
- return 0;
- }
- throw errnoException.rethrowAsIOException();
- }
- }
-
- /**
- * java.io always writes every byte it's asked to, or fails with an error. (That is, unlike
- * Unix it never just writes as many bytes as happens to be convenient.)
- */
- public static void write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws IOException {
- Arrays.checkOffsetAndCount(bytes.length, byteOffset, byteCount);
- if (byteCount == 0) {
- return;
- }
- try {
- while (byteCount > 0) {
- int bytesWritten = Libcore.os.write(fd, bytes, byteOffset, byteCount);
- byteCount -= bytesWritten;
- byteOffset += bytesWritten;
- }
- } catch (ErrnoException errnoException) {
- throw errnoException.rethrowAsIOException();
- }
- }
-
- /**
* Calls close(2) on 'fd'. Also resets the internal int to -1. Does nothing if 'fd' is null
* or invalid.
*/
@@ -196,67 +78,6 @@
}
/**
- * Connects socket 'fd' to 'inetAddress' on 'port', with no timeout. The lack of a timeout
- * means this method won't throw SocketTimeoutException.
- */
- public static boolean connect(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException {
- try {
- return IoUtils.connect(fd, inetAddress, port, 0);
- } catch (SocketTimeoutException ex) {
- throw new AssertionError(ex); // Can't happen for a connect without a timeout.
- }
- }
-
- /**
- * Connects socket 'fd' to 'inetAddress' on 'port', with a the given 'timeoutMs'.
- * Use timeoutMs == 0 for a blocking connect with no timeout.
- */
- public static boolean connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws SocketException, SocketTimeoutException {
- try {
- return connectErrno(fd, inetAddress, port, timeoutMs);
- } catch (SocketException ex) {
- throw ex; // We don't want to doubly wrap these.
- } catch (SocketTimeoutException ex) {
- throw ex; // We don't want to doubly wrap these.
- } catch (IOException ex) {
- throw new SocketException(ex);
- }
- }
-
- // TODO: this is the wrong name now, but when this gets rewritten without Platform.NETWORK...
- private static boolean connectErrno(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws IOException {
- // With no timeout, just call connect(2) directly.
- if (timeoutMs == 0) {
- return Platform.NETWORK.connect(fd, inetAddress, port);
- }
-
- // With a timeout, we set the socket to non-blocking, connect(2), and then loop
- // using select(2) to decide whether we're connected, whether we should keep waiting,
- // or whether we've seen a permanent failure and should give up.
- long finishTimeMs = System.currentTimeMillis() + timeoutMs;
- IoUtils.setBlocking(fd, false);
- try {
- if (Platform.NETWORK.connect(fd, inetAddress, port)) {
- return true;
- }
- int remainingTimeoutMs;
- do {
- remainingTimeoutMs = (int) (finishTimeMs - System.currentTimeMillis());
- if (remainingTimeoutMs <= 0) {
- String detail = "failed to connect to " + inetAddress + " (port " + port + ")";
- if (timeoutMs > 0) {
- detail += " after " + timeoutMs + "ms";
- }
- throw new SocketTimeoutException(detail);
- }
- } while (!Platform.NETWORK.isConnected(fd, remainingTimeoutMs));
- return true; // Or we'd have thrown.
- } finally {
- IoUtils.setBlocking(fd, true);
- }
- }
-
- /**
* Sets 'fd' to be blocking or non-blocking, according to the state of 'blocking'.
*/
public static void setBlocking(FileDescriptor fd, boolean blocking) throws IOException {
@@ -273,196 +94,6 @@
}
}
- public static FileDescriptor socket(boolean stream) throws SocketException {
- FileDescriptor fd;
- try {
- fd = Libcore.os.socket(AF_INET6, stream ? SOCK_STREAM : SOCK_DGRAM, 0);
-
- // The RFC (http://www.ietf.org/rfc/rfc3493.txt) says that IPV6_MULTICAST_HOPS defaults
- // to 1. The Linux kernel (at least up to 2.6.38) accidentally defaults to 64 (which
- // would be correct for the *unicast* hop limit).
- // See http://www.spinics.net/lists/netdev/msg129022.html, though no patch appears to
- // have been applied as a result of that discussion. If that bug is ever fixed, we can
- // remove this code. Until then, we manually set the hop limit on IPv6 datagram sockets.
- // (IPv4 is already correct.)
- if (!stream) {
- Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 1);
- }
-
- return fd;
- } catch (ErrnoException errnoException) {
- throw errnoException.rethrowAsSocketException();
- }
- }
-
- // Socket options used by java.net but not exposed in SocketOptions.
- public static final int JAVA_MCAST_JOIN_GROUP = 19;
- public static final int JAVA_MCAST_LEAVE_GROUP = 20;
- public static final int JAVA_IP_MULTICAST_TTL = 17;
-
- /**
- * java.net has its own socket options similar to the underlying Unix ones. We paper over the
- * differences here.
- */
- public static Object getSocketOption(FileDescriptor fd, int option) throws SocketException {
- try {
- return getSocketOptionErrno(fd, option);
- } catch (ErrnoException errnoException) {
- throw errnoException.rethrowAsSocketException();
- }
- }
-
- private static Object getSocketOptionErrno(FileDescriptor fd, int option) throws SocketException {
- switch (option) {
- case SocketOptions.IP_MULTICAST_IF:
- // This is IPv4-only.
- return Libcore.os.getsockoptInAddr(fd, IPPROTO_IP, IP_MULTICAST_IF);
- case SocketOptions.IP_MULTICAST_IF2:
- // This is IPv6-only.
- return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF);
- case SocketOptions.IP_MULTICAST_LOOP:
- // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
- // it doesn't matter which we return.
- return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP));
- case IoUtils.JAVA_IP_MULTICAST_TTL:
- // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
- // it doesn't matter which we return.
- return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS);
- case SocketOptions.IP_TOS:
- // Since setting this from java.net always sets IPv4 and IPv6 to the same value,
- // it doesn't matter which we return.
- return Libcore.os.getsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS);
- case SocketOptions.SO_BROADCAST:
- return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_BROADCAST));
- case SocketOptions.SO_KEEPALIVE:
- return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE));
- case SocketOptions.SO_LINGER:
- StructLinger linger = Libcore.os.getsockoptLinger(fd, SOL_SOCKET, SO_LINGER);
- if (!linger.isOn()) {
- return false;
- }
- return linger.l_linger;
- case SocketOptions.SO_OOBINLINE:
- return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE));
- case SocketOptions.SO_RCVBUF:
- return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_SNDBUF);
- case SocketOptions.SO_REUSEADDR:
- return booleanFromInt(Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR));
- case SocketOptions.SO_SNDBUF:
- return Libcore.os.getsockoptInt(fd, SOL_SOCKET, SO_SNDBUF);
- case SocketOptions.SO_TIMEOUT:
- return (int) Libcore.os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO).toMillis();
- case SocketOptions.TCP_NODELAY:
- return booleanFromInt(Libcore.os.getsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY));
- default:
- throw new SocketException("Unknown socket option: " + option);
- }
- }
-
- private static boolean booleanFromInt(int i) {
- return (i != 0);
- }
-
- private static int booleanToInt(boolean b) {
- return b ? 1 : 0;
- }
-
- /**
- * java.net has its own socket options similar to the underlying Unix ones. We paper over the
- * differences here.
- */
- public static void setSocketOption(FileDescriptor fd, int option, Object value) throws SocketException {
- try {
- setSocketOptionErrno(fd, option, value);
- } catch (ErrnoException errnoException) {
- throw errnoException.rethrowAsSocketException();
- }
- }
-
- private static void setSocketOptionErrno(FileDescriptor fd, int option, Object value) throws SocketException {
- switch (option) {
- case SocketOptions.IP_MULTICAST_IF:
- throw new UnsupportedOperationException("Use IP_MULTICAST_IF2 on Android");
- case SocketOptions.IP_MULTICAST_IF2:
- // Although IPv6 was cleaned up to use int, IPv4 uses an ip_mreqn containing an int.
- Libcore.os.setsockoptIpMreqn(fd, IPPROTO_IP, IP_MULTICAST_IF, (Integer) value);
- Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (Integer) value);
- return;
- case SocketOptions.IP_MULTICAST_LOOP:
- // Although IPv6 was cleaned up to use int, IPv4 multicast loopback uses a byte.
- Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_LOOP, booleanToInt((Boolean) value));
- Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, booleanToInt((Boolean) value));
- return;
- case IoUtils.JAVA_IP_MULTICAST_TTL:
- // Although IPv6 was cleaned up to use int, and IPv4 non-multicast TTL uses int,
- // IPv4 multicast TTL uses a byte.
- Libcore.os.setsockoptByte(fd, IPPROTO_IP, IP_MULTICAST_TTL, (Integer) value);
- Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (Integer) value);
- return;
- case SocketOptions.IP_TOS:
- Libcore.os.setsockoptInt(fd, IPPROTO_IP, IP_TOS, (Integer) value);
- Libcore.os.setsockoptInt(fd, IPPROTO_IPV6, IPV6_TCLASS, (Integer) value);
- return;
- case SocketOptions.SO_BROADCAST:
- Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_BROADCAST, booleanToInt((Boolean) value));
- return;
- case SocketOptions.SO_KEEPALIVE:
- Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_KEEPALIVE, booleanToInt((Boolean) value));
- return;
- case SocketOptions.SO_LINGER:
- boolean on = false;
- int seconds = 0;
- if (value instanceof Integer) {
- on = true;
- seconds = Math.min((Integer) value, 65535);
- }
- StructLinger linger = new StructLinger(booleanToInt(on), seconds);
- Libcore.os.setsockoptLinger(fd, SOL_SOCKET, SO_LINGER, linger);
- return;
- case SocketOptions.SO_OOBINLINE:
- Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_OOBINLINE, booleanToInt((Boolean) value));
- return;
- case SocketOptions.SO_RCVBUF:
- Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, (Integer) value);
- return;
- case SocketOptions.SO_REUSEADDR:
- Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_REUSEADDR, booleanToInt((Boolean) value));
- return;
- case SocketOptions.SO_SNDBUF:
- Libcore.os.setsockoptInt(fd, SOL_SOCKET, SO_SNDBUF, (Integer) value);
- return;
- case SocketOptions.SO_TIMEOUT:
- int millis = (Integer) value;
- StructTimeval tv = StructTimeval.fromMillis(millis);
- Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, tv);
- return;
- case SocketOptions.TCP_NODELAY:
- Libcore.os.setsockoptInt(fd, IPPROTO_TCP, TCP_NODELAY, booleanToInt((Boolean) value));
- return;
- case IoUtils.JAVA_MCAST_JOIN_GROUP:
- case IoUtils.JAVA_MCAST_LEAVE_GROUP:
- StructGroupReq groupReq = (StructGroupReq) value;
- int level = (groupReq.gr_group instanceof Inet4Address) ? IPPROTO_IP : IPPROTO_IPV6;
- int op = (option == JAVA_MCAST_JOIN_GROUP) ? MCAST_JOIN_GROUP : MCAST_LEAVE_GROUP;
- Libcore.os.setsockoptGroupReq(fd, level, op, groupReq);
- return;
- default:
- throw new SocketException("Unknown socket option: " + option);
- }
- }
-
- public static InetAddress getSocketLocalAddress(FileDescriptor fd) {
- SocketAddress sa = Libcore.os.getsockname(fd);
- InetSocketAddress isa = (InetSocketAddress) sa;
- return isa.getAddress();
- }
-
- public static int getSocketLocalPort(FileDescriptor fd) {
- SocketAddress sa = Libcore.os.getsockname(fd);
- InetSocketAddress isa = (InetSocketAddress) sa;
- return isa.getPort();
- }
-
/**
* Returns the contents of 'path' as a byte array.
*/
@@ -494,4 +125,23 @@
IoUtils.closeQuietly(f);
}
}
+
+ /**
+ * Recursively delete everything in {@code dir}.
+ */
+ // TODO: this should specify paths as Strings rather than as Files
+ public static void deleteContents(File dir) throws IOException {
+ File[] files = dir.listFiles();
+ if (files == null) {
+ throw new IllegalArgumentException("not a directory: " + dir);
+ }
+ for (File file : files) {
+ if (file.isDirectory()) {
+ deleteContents(file);
+ }
+ if (!file.delete()) {
+ throw new IOException("failed to delete file: " + file);
+ }
+ }
+ }
}
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
index 7fceedc..bd50998 100644
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -18,15 +18,21 @@
import java.io.FileDescriptor;
import java.net.InetAddress;
+import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import libcore.util.MutableInt;
import libcore.util.MutableLong;
public interface Os {
+ public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException;
public boolean access(String path, int mode) throws ErrnoException;
+ public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException;
public void chmod(String path, int mode) throws ErrnoException;
public void close(FileDescriptor fd) throws ErrnoException;
+ public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException;
+ public FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException;
+ public FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException;
public String[] environ();
public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException;
public int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException;
@@ -38,15 +44,23 @@
public void ftruncate(FileDescriptor fd, long length) throws ErrnoException;
public String gai_strerror(int error);
public InetAddress[] getaddrinfo(String node, StructAddrinfo hints) throws GaiException;
+ public int getegid();
+ public int geteuid();
+ public int getgid();
public String getenv(String name);
/* TODO: break into getnameinfoHost and getnameinfoService? */
public String getnameinfo(InetAddress address, int flags) throws GaiException;
+ public int getpid();
+ public int getppid();
+ public StructPasswd getpwnam(String name) throws ErrnoException;
+ public StructPasswd getpwuid(int uid) throws ErrnoException;
public SocketAddress getsockname(FileDescriptor fd) throws ErrnoException;
public int getsockoptByte(FileDescriptor fd, int level, int option) throws ErrnoException;
public InetAddress getsockoptInAddr(FileDescriptor fd, int level, int option) throws ErrnoException;
public int getsockoptInt(FileDescriptor fd, int level, int option) throws ErrnoException;
public StructLinger getsockoptLinger(FileDescriptor fd, int level, int option) throws ErrnoException;
public StructTimeval getsockoptTimeval(FileDescriptor fd, int level, int option) throws ErrnoException;
+ public int getuid();
public String if_indextoname(int index);
public InetAddress inet_aton(String address);
public InetAddress ioctlInetAddress(FileDescriptor fd, int cmd, String interfaceName) throws ErrnoException;
@@ -64,13 +78,22 @@
public void munmap(long address, long byteCount) throws ErrnoException;
public FileDescriptor open(String path, int flags, int mode) throws ErrnoException;
public FileDescriptor[] pipe() throws ErrnoException;
+ /* TODO: if we used the non-standard ppoll(2) behind the scenes, we could take a long timeout. */
+ public int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException;
public StructStat lstat(String path) throws ErrnoException;
public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException;
public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException;
public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
+ public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException;
+ public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException;
public void remove(String path) throws ErrnoException;
public void rename(String oldPath, String newPath) throws ErrnoException;
+ public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException;
+ public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException;
public long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException;
+ public void setegid(int egid) throws ErrnoException;
+ public void seteuid(int euid) throws ErrnoException;
+ public void setgid(int gid) throws ErrnoException;
public void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
public void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException;
public void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
@@ -78,6 +101,7 @@
public void setsockoptGroupReq(FileDescriptor fd, int level, int option, StructGroupReq value) throws ErrnoException;
public void setsockoptLinger(FileDescriptor fd, int level, int option, StructLinger value) throws ErrnoException;
public void setsockoptTimeval(FileDescriptor fd, int level, int option, StructTimeval value) throws ErrnoException;
+ public void setuid(int uid) throws ErrnoException;
public void shutdown(FileDescriptor fd, int how) throws ErrnoException;
public FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException;
public StructStat stat(String path) throws ErrnoException;
@@ -87,6 +111,7 @@
public void symlink(String oldPath, String newPath) throws ErrnoException;
public long sysconf(int name);
public StructUtsname uname() throws ErrnoException;
+ public int waitpid(int pid, MutableInt status, int options) throws ErrnoException;
public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException;
public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException;
public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
diff --git a/luni/src/main/java/libcore/io/OsConstants.java b/luni/src/main/java/libcore/io/OsConstants.java
index 34a931c..68a165c 100644
--- a/luni/src/main/java/libcore/io/OsConstants.java
+++ b/luni/src/main/java/libcore/io/OsConstants.java
@@ -230,6 +230,16 @@
public static final int O_SYNC = placeholder();
public static final int O_TRUNC = placeholder();
public static final int O_WRONLY = placeholder();
+ public static final int POLLERR = placeholder();
+ public static final int POLLHUP = placeholder();
+ public static final int POLLIN = placeholder();
+ public static final int POLLNVAL = placeholder();
+ public static final int POLLOUT = placeholder();
+ public static final int POLLPRI = placeholder();
+ public static final int POLLRDBAND = placeholder();
+ public static final int POLLRDNORM = placeholder();
+ public static final int POLLWRBAND = placeholder();
+ public static final int POLLWRNORM = placeholder();
public static final int PROT_EXEC = placeholder();
public static final int PROT_NONE = placeholder();
public static final int PROT_READ = placeholder();
diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java
index 4e23dc0..f976209 100644
--- a/luni/src/main/java/libcore/io/Posix.java
+++ b/luni/src/main/java/libcore/io/Posix.java
@@ -18,6 +18,7 @@
import java.io.FileDescriptor;
import java.net.InetAddress;
+import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.NioUtils;
@@ -27,9 +28,14 @@
public final class Posix implements Os {
Posix() { }
+ public native FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException;
public native boolean access(String path, int mode) throws ErrnoException;
+ public native void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException;
public native void chmod(String path, int mode) throws ErrnoException;
public native void close(FileDescriptor fd) throws ErrnoException;
+ public native void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException;
+ public native FileDescriptor dup(FileDescriptor oldFd) throws ErrnoException;
+ public native FileDescriptor dup2(FileDescriptor oldFd, int newFd) throws ErrnoException;
public native String[] environ();
public native int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException;
public native int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException;
@@ -41,16 +47,24 @@
public native void ftruncate(FileDescriptor fd, long length) throws ErrnoException;
public native String gai_strerror(int error);
public native InetAddress[] getaddrinfo(String node, StructAddrinfo hints) throws GaiException;
+ public native int getegid();
+ public native int geteuid();
+ public native int getgid();
public native String getenv(String name);
public native String getnameinfo(InetAddress address, int flags) throws GaiException;
+ public native int getpid();
+ public native int getppid();
+ public native StructPasswd getpwnam(String name) throws ErrnoException;
+ public native StructPasswd getpwuid(int uid) throws ErrnoException;
public native SocketAddress getsockname(FileDescriptor fd) throws ErrnoException;
public native int getsockoptByte(FileDescriptor fd, int level, int option) throws ErrnoException;
public native InetAddress getsockoptInAddr(FileDescriptor fd, int level, int option) throws ErrnoException;
public native int getsockoptInt(FileDescriptor fd, int level, int option) throws ErrnoException;
public native StructLinger getsockoptLinger(FileDescriptor fd, int level, int option) throws ErrnoException;
public native StructTimeval getsockoptTimeval(FileDescriptor fd, int level, int option) throws ErrnoException;
- public native InetAddress inet_aton(String address);
+ public native int getuid();
public native String if_indextoname(int index);
+ public native InetAddress inet_aton(String address);
public native InetAddress ioctlInetAddress(FileDescriptor fd, int cmd, String interfaceName) throws ErrnoException;
public native int ioctlInt(FileDescriptor fd, int cmd, MutableInt arg) throws ErrnoException;
public native boolean isatty(FileDescriptor fd);
@@ -66,6 +80,7 @@
public native void munmap(long address, long byteCount) throws ErrnoException;
public native FileDescriptor open(String path, int flags, int mode) throws ErrnoException;
public native FileDescriptor[] pipe() throws ErrnoException;
+ public native int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException;
public native StructStat lstat(String path) throws ErrnoException;
public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
if (buffer.isDirect()) {
@@ -80,9 +95,36 @@
}
private native int readBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException;
public native int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
+ public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException {
+ if (buffer.isDirect()) {
+ return recvfromBytes(fd, buffer, buffer.position(), buffer.remaining(), flags, srcAddress);
+ } else {
+ return recvfromBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining(), flags, srcAddress);
+ }
+ }
+ public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException {
+ // This indirection isn't strictly necessary, but ensures that our public interface is type safe.
+ return recvfromBytes(fd, bytes, byteOffset, byteCount, flags, srcAddress);
+ }
+ private native int recvfromBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException;
public native void remove(String path) throws ErrnoException;
public native void rename(String oldPath, String newPath) throws ErrnoException;
public native long sendfile(FileDescriptor outFd, FileDescriptor inFd, MutableLong inOffset, long byteCount) throws ErrnoException;
+ public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException {
+ if (buffer.isDirect()) {
+ return sendtoBytes(fd, buffer, buffer.position(), buffer.remaining(), flags, inetAddress, port);
+ } else {
+ return sendtoBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining(), flags, inetAddress, port);
+ }
+ }
+ public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException {
+ // This indirection isn't strictly necessary, but ensures that our public interface is type safe.
+ return sendtoBytes(fd, bytes, byteOffset, byteCount, flags, inetAddress, port);
+ }
+ private native int sendtoBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException;
+ public native void setegid(int egid) throws ErrnoException;
+ public native void seteuid(int euid) throws ErrnoException;
+ public native void setgid(int gid) throws ErrnoException;
public native void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
public native void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException;
public native void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException;
@@ -90,6 +132,7 @@
public native void setsockoptGroupReq(FileDescriptor fd, int level, int option, StructGroupReq value) throws ErrnoException;
public native void setsockoptLinger(FileDescriptor fd, int level, int option, StructLinger value) throws ErrnoException;
public native void setsockoptTimeval(FileDescriptor fd, int level, int option, StructTimeval value) throws ErrnoException;
+ public native void setuid(int uid) throws ErrnoException;
public native void shutdown(FileDescriptor fd, int how) throws ErrnoException;
public native FileDescriptor socket(int domain, int type, int protocol) throws ErrnoException;
public native StructStat stat(String path) throws ErrnoException;
@@ -98,6 +141,7 @@
public native void symlink(String oldPath, String newPath) throws ErrnoException;
public native long sysconf(int name);
public native StructUtsname uname() throws ErrnoException;
+ public native int waitpid(int pid, MutableInt status, int options) throws ErrnoException;
public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
if (buffer.isDirect()) {
return writeBytes(fd, buffer, buffer.position(), buffer.remaining());
diff --git a/luni/src/main/java/libcore/io/Streams.java b/luni/src/main/java/libcore/io/Streams.java
index 2ece93c..ab8795c 100644
--- a/luni/src/main/java/libcore/io/Streams.java
+++ b/luni/src/main/java/libcore/io/Streams.java
@@ -21,6 +21,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.Reader;
+import java.io.StringWriter;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;
@@ -86,18 +88,36 @@
}
/**
- * Returns a new byte[] containing the entire contents of the given InputStream.
- * Useful when you don't know in advance how much data there is to be read.
+ * Returns a byte[] containing the remainder of 'in', closing it when done.
*/
public static byte[] readFully(InputStream in) throws IOException {
- byte[] buffer = new byte[1024];
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- while (true) {
- int byteCount = in.read(buffer);
- if (byteCount == -1) {
- return bytes.toByteArray();
+ try {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ byte[] buffer = new byte[1024];
+ int count;
+ while ((count = in.read(buffer)) != -1) {
+ bytes.write(buffer, 0, count);
}
- bytes.write(buffer, 0, byteCount);
+ return bytes.toByteArray();
+ } finally {
+ in.close();
+ }
+ }
+
+ /**
+ * Returns the remainder of 'reader' as a string, closing it when done.
+ */
+ public static String readFully(Reader reader) throws IOException {
+ try {
+ StringWriter writer = new StringWriter();
+ char[] buffer = new char[1024];
+ int count;
+ while ((count = reader.read(buffer)) != -1) {
+ writer.write(buffer, 0, count);
+ }
+ return writer.toString();
+ } finally {
+ reader.close();
}
}
@@ -158,4 +178,32 @@
}
return total;
}
+
+ /**
+ * Returns the ASCII characters up to but not including the next "\r\n", or
+ * "\n".
+ *
+ * @throws java.io.EOFException if the stream is exhausted before the next newline
+ * character.
+ */
+ public static String readAsciiLine(InputStream in) throws IOException {
+ // TODO: support UTF-8 here instead
+
+ StringBuilder result = new StringBuilder(80);
+ while (true) {
+ int c = in.read();
+ if (c == -1) {
+ throw new EOFException();
+ } else if (c == '\n') {
+ break;
+ }
+
+ result.append((char) c);
+ }
+ int length = result.length();
+ if (length > 0 && result.charAt(length - 1) == '\r') {
+ result.setLength(length - 1);
+ }
+ return result.toString();
+ }
}
diff --git a/luni/src/main/java/libcore/io/StructPasswd.java b/luni/src/main/java/libcore/io/StructPasswd.java
new file mode 100644
index 0000000..6f5e058
--- /dev/null
+++ b/luni/src/main/java/libcore/io/StructPasswd.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.io;
+
+/**
+ * Information returned by getpwnam(3) and getpwuid(3). Corresponds to C's
+ * {@code struct passwd} from
+ * <a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/pwd.h.html"><pwd.h></a>
+ */
+public final class StructPasswd {
+ public String pw_name;
+ public int pw_uid; /* uid_t */
+ public int pw_gid; /* gid_t */
+ public String pw_dir;
+ public String pw_shell;
+
+ public StructPasswd(String pw_name, int pw_uid, int pw_gid, String pw_dir, String pw_shell) {
+ this.pw_name = pw_name;
+ this.pw_uid = pw_uid;
+ this.pw_gid = pw_gid;
+ this.pw_dir = pw_dir;
+ this.pw_shell = pw_shell;
+ }
+}
diff --git a/luni/src/main/java/libcore/io/StructPollfd.java b/luni/src/main/java/libcore/io/StructPollfd.java
new file mode 100644
index 0000000..c659d6e
--- /dev/null
+++ b/luni/src/main/java/libcore/io/StructPollfd.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.io;
+
+import java.io.FileDescriptor;
+
+/**
+ * Corresponds to C's {@code struct pollfd} from
+ * <a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/poll.h.html"><poll.h></a>
+ */
+public final class StructPollfd {
+ /** The file descriptor to poll. */
+ public FileDescriptor fd;
+
+ /**
+ * The events we're interested in. POLLIN corresponds to being in select(2)'s read fd set,
+ * POLLOUT to the write fd set.
+ */
+ public short events;
+
+ /** The events that actually happened. */
+ public short revents;
+
+ /**
+ * A non-standard extension that lets callers conveniently map back to the object
+ * their fd belongs to. This is used by Selector, for example, to associate each
+ * FileDescriptor with the corresponding SelectionKey.
+ */
+ public Object userData;
+
+ @Override public String toString() {
+ return "StructPollfd[fd=" + fd + ",events=" + events + ",revents=" + revents + "]";
+ }
+}
diff --git a/luni/src/main/java/libcore/net/RawSocket.java b/luni/src/main/java/libcore/net/RawSocket.java
index 255b0b9c..bb29fb0 100644
--- a/luni/src/main/java/libcore/net/RawSocket.java
+++ b/luni/src/main/java/libcore/net/RawSocket.java
@@ -22,7 +22,7 @@
import java.io.IOException;
import java.net.SocketException;
import java.util.Arrays;
-import org.apache.harmony.luni.platform.Platform;
+import libcore.io.IoBridge;
/**
* This class allows raw L2 packets to be sent and received via the
@@ -111,7 +111,7 @@
*/
public void close() throws IOException {
guard.close();
- Platform.NETWORK.close(fd);
+ IoBridge.closeSocket(fd);
}
@Override protected void finalize() throws Throwable {
diff --git a/luni/src/main/java/libcore/net/http/AbstractHttpInputStream.java b/luni/src/main/java/libcore/net/http/AbstractHttpInputStream.java
index 755bc96..70f76b7 100644
--- a/luni/src/main/java/libcore/net/http/AbstractHttpInputStream.java
+++ b/luni/src/main/java/libcore/net/http/AbstractHttpInputStream.java
@@ -83,7 +83,7 @@
if (cacheRequest != null) {
cacheBody.close();
}
- httpEngine.releaseSocket(reuseSocket);
+ httpEngine.release(reuseSocket);
}
/**
@@ -102,6 +102,6 @@
if (cacheRequest != null) {
cacheRequest.abort();
}
- httpEngine.releaseSocket(false);
+ httpEngine.release(false);
}
}
diff --git a/luni/src/main/java/libcore/net/http/ChunkedInputStream.java b/luni/src/main/java/libcore/net/http/ChunkedInputStream.java
index 274f03c..380ed02 100644
--- a/luni/src/main/java/libcore/net/http/ChunkedInputStream.java
+++ b/luni/src/main/java/libcore/net/http/ChunkedInputStream.java
@@ -20,6 +20,7 @@
import java.io.InputStream;
import java.net.CacheRequest;
import java.util.Arrays;
+import libcore.io.Streams;
/**
* An HTTP body with alternating chunk sizes and chunk bodies.
@@ -73,9 +74,9 @@
private void readChunkSize() throws IOException {
// read the suffix of the previous chunk
if (bytesRemainingInChunk != NO_CHUNK_YET) {
- HttpEngine.readLine(in);
+ Streams.readAsciiLine(in);
}
- String chunkSizeString = HttpEngine.readLine(in);
+ String chunkSizeString = Streams.readAsciiLine(in);
int index = chunkSizeString.indexOf(";");
if (index != -1) {
chunkSizeString = chunkSizeString.substring(0, index);
diff --git a/luni/src/main/java/libcore/net/http/HttpEngine.java b/luni/src/main/java/libcore/net/http/HttpEngine.java
index d023cbc..d95e21c 100644
--- a/luni/src/main/java/libcore/net/http/HttpEngine.java
+++ b/luni/src/main/java/libcore/net/http/HttpEngine.java
@@ -26,7 +26,6 @@
import java.net.CacheResponse;
import java.net.CookieHandler;
import java.net.HttpURLConnection;
-import java.net.ProtocolException;
import java.net.Proxy;
import java.net.ResponseCache;
import java.net.URI;
@@ -34,6 +33,7 @@
import java.net.URL;
import java.nio.charset.Charsets;
import java.util.Collections;
+import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -61,16 +61,15 @@
* network, or by both in the event of a conditional GET.
*
* <p>This class may hold a socket connection that needs to be released or
- * recycled. By default, this socket connection is released to the pool when the
- * last byte of the response is consumed. To prevent the connection from being
- * released to the pool, use {@link #dontReleaseSocketToPool()}.
+ * recycled. By default, this socket connection is held when the last byte of
+ * the response is consumed. To release the connection when it is no longer
+ * required, use {@link #automaticallyReleaseConnectionToPool()}.
*/
public class HttpEngine {
private static final CacheResponse BAD_GATEWAY_RESPONSE = new CacheResponse() {
@Override public Map<String, List<String>> getHeaders() throws IOException {
Map<String, List<String>> result = new HashMap<String, List<String>>();
result.put(null, Collections.singletonList("HTTP/1.1 502 Bad Gateway"));
- // TODO: other required fields?
return result;
}
@Override public InputStream getBody() throws IOException {
@@ -151,10 +150,10 @@
private final URI uri;
- private final RawHeaders rawRequestHeaders;
+ private final RequestHeaders requestHeaders;
/** Null until a response is received from the network or the cache */
- private RawHeaders rawResponseHeaders;
+ private ResponseHeaders responseHeaders;
/*
* The cache response currently being validated on a conditional get. Null
@@ -162,16 +161,21 @@
* conditional get succeeds, these will be used for the response headers and
* body. If it fails, these be closed and set to null.
*/
- private ResponseHeaders responseHeadersToValidate;
- private InputStream responseBodyToValidate;
+ private ResponseHeaders cachedResponseHeaders;
+ private InputStream cachedResponseBody;
/**
* True if the socket connection should be released to the connection pool
* when the response has been fully read.
*/
- private boolean dontReleaseSocketToPool;
+ private boolean automaticallyReleaseConnectionToPool;
+
+ /** True if the socket connection is no longer needed by this engine. */
+ private boolean connectionReleased;
/**
+ * @param requestHeaders the client's supplied request headers. This class
+ * creates a private copy that it can mutate.
* @param connection the connection used for an intermediate response
* immediately prior to this request/response pair, such as a same-host
* redirect. This engine assumes ownership of the connection and must
@@ -179,19 +183,8 @@
*/
public HttpEngine(HttpURLConnectionImpl policy, String method, RawHeaders requestHeaders,
HttpConnection connection, RetryableOutputStream requestBodyOut) throws IOException {
- if (policy.getDoOutput()) {
- if (method == GET) {
- // they are requesting a stream to write to. This implies a POST method
- method = POST;
- } else if (method != PUT && method != POST) {
- // If the request method is neither PUT or POST, then you're not writing
- throw new ProtocolException(method + " does not support writing");
- }
- }
-
this.policy = policy;
this.method = method;
- this.rawRequestHeaders = new RawHeaders(requestHeaders);
this.connection = connection;
this.requestBodyOut = requestBodyOut;
@@ -200,10 +193,8 @@
} catch (URISyntaxException e) {
throw new IOException(e);
}
- }
- public final void dontReleaseSocketToPool() {
- this.dontReleaseSocketToPool = true;
+ this.requestHeaders = new RequestHeaders(uri, new RawHeaders(requestHeaders));
}
/**
@@ -217,30 +208,31 @@
}
prepareRawRequestHeaders();
- RequestHeaders cacheRequestHeaders = new RequestHeaders(uri, rawRequestHeaders);
- initResponseSource(cacheRequestHeaders);
+ initResponseSource();
+ if (responseCache instanceof HttpResponseCache) {
+ ((HttpResponseCache) responseCache).trackResponse(responseSource);
+ }
/*
* The raw response source may require the network, but the request
* headers may forbid network use. In that case, dispose of the network
* response and use a BAD_GATEWAY response instead.
*/
- if (cacheRequestHeaders.onlyIfCached && responseSource.requiresConnection()) {
+ if (requestHeaders.onlyIfCached && responseSource.requiresConnection()) {
if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
- this.responseHeadersToValidate = null;
- IoUtils.closeQuietly(responseBodyToValidate);
- this.responseBodyToValidate = null;
+ IoUtils.closeQuietly(cachedResponseBody);
}
this.responseSource = ResponseSource.CACHE;
this.cacheResponse = BAD_GATEWAY_RESPONSE;
- setResponse(RawHeaders.fromMultimap(cacheResponse.getHeaders()),
- cacheResponse.getBody());
+ RawHeaders rawResponseHeaders = RawHeaders.fromMultimap(cacheResponse.getHeaders());
+ setResponse(new ResponseHeaders(uri, rawResponseHeaders), cacheResponse.getBody());
}
if (responseSource.requiresConnection()) {
sendSocketRequest();
- } else {
- // TODO: release 'connection' if it is non-null
+ } else if (connection != null) {
+ HttpConnectionPool.INSTANCE.recycle(connection);
+ connection = null;
}
}
@@ -248,38 +240,38 @@
* Initialize the source for this response. It may be corrected later if the
* request headers forbids network use.
*/
- private void initResponseSource(RequestHeaders cacheRequestHeaders) throws IOException {
+ private void initResponseSource() throws IOException {
responseSource = ResponseSource.NETWORK;
if (!policy.getUseCaches() || responseCache == null) {
return;
}
- CacheResponse candidate = responseCache.get(uri, method, rawRequestHeaders.toMultimap());
- if (candidate == null || !acceptCacheResponseType(candidate)) {
- return;
- }
- Map<String, List<String>> responseHeaders = candidate.getHeaders();
- if (responseHeaders == null) {
- return;
- }
- InputStream cacheBodyIn = candidate.getBody(); // must be closed
- if (cacheBodyIn == null) {
+ CacheResponse candidate = responseCache.get(uri, method,
+ requestHeaders.headers.toMultimap());
+ if (candidate == null) {
return;
}
- RawHeaders headers = RawHeaders.fromMultimap(responseHeaders);
- ResponseHeaders cacheResponseHeaders = new ResponseHeaders(uri, headers);
+ Map<String, List<String>> responseHeadersMap = candidate.getHeaders();
+ cachedResponseBody = candidate.getBody();
+ if (!acceptCacheResponseType(candidate)
+ || responseHeadersMap == null
+ || cachedResponseBody == null) {
+ IoUtils.closeQuietly(cachedResponseBody);
+ return;
+ }
+
+ RawHeaders rawResponseHeaders = RawHeaders.fromMultimap(responseHeadersMap);
+ cachedResponseHeaders = new ResponseHeaders(uri, rawResponseHeaders);
long now = System.currentTimeMillis();
- this.responseSource = cacheResponseHeaders.chooseResponseSource(now, cacheRequestHeaders);
+ this.responseSource = cachedResponseHeaders.chooseResponseSource(now, requestHeaders);
if (responseSource == ResponseSource.CACHE) {
this.cacheResponse = candidate;
- setResponse(headers, cacheBodyIn);
+ setResponse(cachedResponseHeaders, cachedResponseBody);
} else if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
this.cacheResponse = candidate;
- this.responseHeadersToValidate = cacheResponseHeaders;
- this.responseBodyToValidate = cacheBodyIn;
} else if (responseSource == ResponseSource.NETWORK) {
- IoUtils.closeQuietly(cacheBodyIn);
+ IoUtils.closeQuietly(cachedResponseBody);
} else {
throw new AssertionError();
}
@@ -298,7 +290,7 @@
requestOut = socketOut;
socketIn = connection.getInputStream();
- if (policy.getDoOutput() && method != CONNECT) {
+ if (hasRequestBody()) {
initRequestBodyOut();
}
}
@@ -324,17 +316,9 @@
}
protected void initRequestBodyOut() throws IOException {
- int contentLength = -1;
- String contentLengthString = rawRequestHeaders.get("Content-Length");
- if (contentLengthString != null) {
- contentLength = Integer.parseInt(contentLengthString);
- }
-
- String encoding = rawRequestHeaders.get("Transfer-Encoding");
int chunkLength = policy.getChunkLength();
- if (chunkLength > 0 || "chunked".equalsIgnoreCase(encoding)) {
+ if (chunkLength > 0 || requestHeaders.isChunked()) {
sendChunked = true;
- contentLength = -1;
if (chunkLength == -1) {
chunkLength = DEFAULT_CHUNK_LENGTH;
}
@@ -357,9 +341,9 @@
} else if (sendChunked) {
writeRequestHeaders(-1);
requestBodyOut = new ChunkedOutputStream(requestOut, chunkLength);
- } else if (contentLength != -1) {
- writeRequestHeaders(contentLength);
- requestBodyOut = new RetryableOutputStream(contentLength);
+ } else if (requestHeaders.contentLength != -1) {
+ writeRequestHeaders(requestHeaders.contentLength);
+ requestBodyOut = new RetryableOutputStream(requestHeaders.contentLength);
} else {
requestBodyOut = new RetryableOutputStream();
}
@@ -369,17 +353,21 @@
* @param body the response body, or null if it doesn't exist or isn't
* available.
*/
- private void setResponse(RawHeaders headers, InputStream body) throws IOException {
+ private void setResponse(ResponseHeaders headers, InputStream body) throws IOException {
if (this.responseBodyIn != null) {
throw new IllegalStateException();
}
- this.rawResponseHeaders = headers;
- this.httpMinorVersion = rawResponseHeaders.getHttpMinorVersion();
+ this.responseHeaders = headers;
+ this.httpMinorVersion = responseHeaders.headers.getHttpMinorVersion();
if (body != null) {
initContentStream(body);
}
}
+ private boolean hasRequestBody() {
+ return method == POST || method == PUT;
+ }
+
/**
* Returns the request body or null if this request doesn't have a body.
*/
@@ -391,29 +379,36 @@
}
public final boolean hasResponse() {
- return rawResponseHeaders != null;
+ return responseHeaders != null;
}
- public final RawHeaders getRequestHeaders() {
- return rawRequestHeaders;
+ public final RequestHeaders getRequestHeaders() {
+ return requestHeaders;
}
- public final RawHeaders getResponseHeaders() {
- if (rawResponseHeaders == null) {
+ public final ResponseHeaders getResponseHeaders() {
+ if (responseHeaders == null) {
throw new IllegalStateException();
}
- return rawResponseHeaders;
+ return responseHeaders;
+ }
+
+ public final int getResponseCode() {
+ if (responseHeaders == null) {
+ throw new IllegalStateException();
+ }
+ return responseHeaders.headers.getResponseCode();
}
public final InputStream getResponseBody() {
- if (rawResponseHeaders == null) {
+ if (responseHeaders == null) {
throw new IllegalStateException();
}
return responseBodyIn;
}
public final CacheResponse getCacheResponse() {
- if (rawResponseHeaders == null) {
+ if (responseHeaders == null) {
throw new IllegalStateException();
}
return cacheResponse;
@@ -439,9 +434,7 @@
}
// Should we cache this response for this request?
- RequestHeaders requestCacheHeaders = new RequestHeaders(uri, rawRequestHeaders);
- ResponseHeaders responseCacheHeaders = new ResponseHeaders(uri, rawResponseHeaders);
- if (!responseCacheHeaders.isCacheable(requestCacheHeaders)) {
+ if (!responseHeaders.isCacheable(requestHeaders)) {
return;
}
@@ -454,67 +447,71 @@
}
/**
- * Releases this connection so that it may be either reused or closed.
+ * Cause the socket connection to be released to the connection pool when
+ * it is no longer needed. If it is already unneeded, it will be pooled
+ * immediately.
*/
- public final void releaseSocket(boolean reuseSocket) {
- // we cannot recycle sockets that have incomplete output.
- if (requestBodyOut != null && !requestBodyOut.closed) {
- reuseSocket = false;
- }
-
- // if the headers specify that the connection shouldn't be reused, don't reuse it
- if (hasConnectionCloseHeaders()) {
- reuseSocket = false;
- }
-
- /*
- * Don't return the socket to the connection pool if this is an
- * intermediate response; we're going to use it again right away.
- */
- if (dontReleaseSocketToPool && reuseSocket) {
- return;
- }
-
- if (connection != null) {
- if (reuseSocket) {
- HttpConnectionPool.INSTANCE.recycle(connection);
- } else {
- connection.closeSocketAndStreams();
- }
+ public final void automaticallyReleaseConnectionToPool() {
+ automaticallyReleaseConnectionToPool = true;
+ if (connection != null && connectionReleased) {
+ HttpConnectionPool.INSTANCE.recycle(connection);
connection = null;
}
-
- /*
- * Ensure that no further I/O attempts from this instance make their way
- * to the underlying connection (which may get recycled).
- */
- socketOut = null;
- socketIn = null;
- requestOut = null;
}
/**
- * Consume the response body so the socket connection can be used for new
- * message pairs.
+ * Releases this engine so that its resources may be either reused or
+ * closed.
*/
- public final void discardResponseBody() throws IOException {
- if (responseBodyIn != null) {
- if (!(responseBodyIn instanceof UnknownLengthHttpInputStream)) {
- // skip the response so that the connection may be reused for the retry
- Streams.skipAll(responseBodyIn);
+ public final void release(boolean reusable) {
+ // If the response body comes from the cache, close it.
+ if (responseBodyIn == cachedResponseBody) {
+ IoUtils.closeQuietly(responseBodyIn);
+ }
+
+ if (!connectionReleased && connection != null) {
+ connectionReleased = true;
+
+ // We cannot reuse sockets that have incomplete output.
+ if (requestBodyOut != null && !requestBodyOut.closed) {
+ reusable = false;
}
- responseBodyIn.close();
+
+ // If the headers specify that the connection shouldn't be reused, don't reuse it.
+ if (hasConnectionCloseHeader()) {
+ reusable = false;
+ }
+
+ if (responseBodyIn instanceof UnknownLengthHttpInputStream) {
+ reusable = false;
+ }
+
+ if (reusable && responseBodyIn != null) {
+ // We must discard the response body before the connection can be reused.
+ try {
+ Streams.skipAll(responseBodyIn);
+ } catch (IOException e) {
+ reusable = false;
+ }
+ }
+
+ if (!reusable) {
+ connection.closeSocketAndStreams();
+ connection = null;
+ } else if (automaticallyReleaseConnectionToPool) {
+ HttpConnectionPool.INSTANCE.recycle(connection);
+ connection = null;
+ }
}
}
private void initContentStream(InputStream transferStream) throws IOException {
- if (transparentGzip
- && "gzip".equalsIgnoreCase(rawResponseHeaders.get("Content-Encoding"))) {
+ if (transparentGzip && responseHeaders.isContentEncodingGzip()) {
/*
* If the response was transparently gzipped, remove the gzip header field
* so clients don't double decompress. http://b/3009828
*/
- rawResponseHeaders.removeAll("Content-Encoding");
+ responseHeaders.stripContentEncoding();
responseBodyIn = new GZIPInputStream(transferStream);
} else {
responseBodyIn = transferStream;
@@ -526,17 +523,13 @@
return new FixedLengthInputStream(socketIn, cacheRequest, this, 0);
}
- if ("chunked".equalsIgnoreCase(rawResponseHeaders.get("Transfer-Encoding"))) {
+ if (responseHeaders.isChunked()) {
return new ChunkedInputStream(socketIn, cacheRequest, this);
}
- String contentLength = rawResponseHeaders.get("Content-Length");
- if (contentLength != null) {
- try {
- int length = Integer.parseInt(contentLength);
- return new FixedLengthInputStream(socketIn, cacheRequest, this, length);
- } catch (NumberFormatException ignored) {
- }
+ if (responseHeaders.contentLength != -1) {
+ return new FixedLengthInputStream(socketIn, cacheRequest, this,
+ responseHeaders.contentLength);
}
/*
@@ -547,35 +540,14 @@
return new UnknownLengthHttpInputStream(socketIn, cacheRequest, this);
}
- /**
- * Returns the characters up to but not including the next "\r\n", "\n", or
- * the end of the stream, consuming the end of line delimiter.
- */
- static String readLine(InputStream is) throws IOException {
- StringBuilder result = new StringBuilder(80);
- while (true) {
- int c = is.read();
- if (c == -1 || c == '\n') {
- break;
- }
-
- result.append((char) c);
- }
- int length = result.length();
- if (length > 0 && result.charAt(length - 1) == '\r') {
- result.setLength(length - 1);
- }
- return result.toString();
- }
-
private void readResponseHeaders() throws IOException {
RawHeaders headers;
do {
headers = new RawHeaders();
- headers.setStatusLine(readLine(socketIn).trim());
+ headers.setStatusLine(Streams.readAsciiLine(socketIn));
readHeaders(headers);
- setResponse(headers, null);
} while (headers.getResponseCode() == HTTP_CONTINUE);
+ setResponse(new ResponseHeaders(uri, headers), null);
}
/**
@@ -583,7 +555,7 @@
* See RFC 2616 section 4.3.
*/
public final boolean hasResponseBody() {
- int responseCode = rawResponseHeaders.getResponseCode();
+ int responseCode = responseHeaders.headers.getResponseCode();
if (method != HEAD
&& method != CONNECT
&& (responseCode < HTTP_CONTINUE || responseCode >= 200)
@@ -597,11 +569,7 @@
* response code, the response is malformed. For best compatibility, we
* honor the headers.
*/
- String contentLength = rawResponseHeaders.get("Content-Length");
- if (contentLength != null && Integer.parseInt(contentLength) > 0) {
- return true;
- }
- if ("chunked".equalsIgnoreCase(rawResponseHeaders.get("Transfer-Encoding"))) {
+ if (responseHeaders.contentLength != -1 || responseHeaders.isChunked()) {
return true;
}
@@ -613,20 +581,14 @@
* with chunked encoding.
*/
final void readTrailers() throws IOException {
- readHeaders(rawResponseHeaders);
+ readHeaders(responseHeaders.headers);
}
private void readHeaders(RawHeaders headers) throws IOException {
// parse the result headers until the first blank line
String line;
- while ((line = readLine(socketIn)).length() > 1) {
- // Header parsing
- int index = line.indexOf(":");
- if (index == -1) {
- headers.add("", line);
- } else {
- headers.add(line.substring(0, index), line.substring(index + 1));
- }
+ while (!(line = Streams.readAsciiLine(socketIn)).isEmpty()) {
+ headers.addLine(line);
}
CookieHandler cookieHandler = CookieHandler.getDefault();
@@ -676,23 +638,19 @@
* the connection is using a proxy.
*/
protected RawHeaders getNetworkRequestHeaders() throws IOException {
- rawRequestHeaders.setStatusLine(getRequestLine());
+ requestHeaders.headers.setStatusLine(getRequestLine());
int fixedContentLength = policy.getFixedContentLength();
if (fixedContentLength != -1) {
- rawRequestHeaders.addIfAbsent("Content-Length", Integer.toString(fixedContentLength));
+ requestHeaders.setContentLength(fixedContentLength);
} else if (sendChunked) {
- rawRequestHeaders.addIfAbsent("Transfer-Encoding", "chunked");
+ requestHeaders.setChunked();
} else if (requestBodyOut instanceof RetryableOutputStream) {
- int size = ((RetryableOutputStream) requestBodyOut).contentLength();
- rawRequestHeaders.addIfAbsent("Content-Length", Integer.toString(size));
+ int contentLength = ((RetryableOutputStream) requestBodyOut).contentLength();
+ requestHeaders.setContentLength(contentLength);
}
- if (requestBodyOut != null) {
- rawRequestHeaders.addIfAbsent("Content-Type", "application/x-www-form-urlencoded");
- }
-
- return rawRequestHeaders;
+ return requestHeaders.headers;
}
/**
@@ -702,35 +660,37 @@
* doesn't know what content types the application is interested in.
*/
private void prepareRawRequestHeaders() throws IOException {
- rawRequestHeaders.setStatusLine(getRequestLine());
+ requestHeaders.headers.setStatusLine(getRequestLine());
- if (rawRequestHeaders.get("User-Agent") == null) {
- rawRequestHeaders.add("User-Agent", getDefaultUserAgent());
+ if (requestHeaders.userAgent == null) {
+ requestHeaders.setUserAgent(getDefaultUserAgent());
}
- if (rawRequestHeaders.get("Host") == null) {
- rawRequestHeaders.add("Host", getOriginAddress(policy.getURL()));
+ if (requestHeaders.host == null) {
+ requestHeaders.setHost(getOriginAddress(policy.getURL()));
}
- if (httpMinorVersion > 0) {
- rawRequestHeaders.addIfAbsent("Connection", "Keep-Alive");
+ if (httpMinorVersion > 0 && requestHeaders.connection == null) {
+ requestHeaders.setConnection("Keep-Alive");
}
- if (rawRequestHeaders.get("Accept-Encoding") == null) {
+ if (requestHeaders.acceptEncoding == null) {
transparentGzip = true;
- rawRequestHeaders.set("Accept-Encoding", "gzip");
+ requestHeaders.setAcceptEncoding("gzip");
+ }
+
+ if (hasRequestBody() && requestHeaders.contentType == null) {
+ requestHeaders.setContentType("application/x-www-form-urlencoded");
+ }
+
+ long ifModifiedSince = policy.getIfModifiedSince();
+ if (ifModifiedSince != 0) {
+ requestHeaders.setIfModifiedSince(new Date(ifModifiedSince));
}
CookieHandler cookieHandler = CookieHandler.getDefault();
if (cookieHandler != null) {
- Map<String, List<String>> allCookieHeaders
- = cookieHandler.get(uri, rawRequestHeaders.toMultimap());
- for (Map.Entry<String, List<String>> entry : allCookieHeaders.entrySet()) {
- String key = entry.getKey();
- if ("Cookie".equalsIgnoreCase(key) || "Cookie2".equalsIgnoreCase(key)) {
- rawRequestHeaders.addAll(key, entry.getValue());
- }
- }
+ requestHeaders.addCookies(cookieHandler.get(uri, requestHeaders.headers.toMultimap()));
}
}
@@ -769,10 +729,9 @@
return agent != null ? agent : ("Java" + System.getProperty("java.version"));
}
- public final boolean hasConnectionCloseHeaders() {
- return (rawResponseHeaders != null
- && "close".equalsIgnoreCase(rawResponseHeaders.get("Connection")))
- || ("close".equalsIgnoreCase(rawRequestHeaders.get("Connection")));
+ private boolean hasConnectionCloseHeader() {
+ return (responseHeaders != null && responseHeaders.hasConnectionClose())
+ || requestHeaders.hasConnectionClose();
}
protected final String getOriginAddress(URL url) {
@@ -823,23 +782,19 @@
requestOut = socketOut;
readResponseHeaders();
- rawResponseHeaders.add(ResponseHeaders.SENT_MILLIS, Long.toString(sentRequestMillis));
- rawResponseHeaders.add(ResponseHeaders.RECEIVED_MILLIS,
- Long.toString(System.currentTimeMillis()));
+ responseHeaders.setLocalTimestamps(sentRequestMillis, System.currentTimeMillis());
if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
- if (responseHeadersToValidate.validate(new ResponseHeaders(uri, rawResponseHeaders))) {
- // discard the network response
- discardResponseBody();
-
- // use the cache response
- setResponse(responseHeadersToValidate.headers, responseBodyToValidate);
- responseBodyToValidate = null;
+ if (cachedResponseHeaders.validate(responseHeaders)) {
+ if (responseCache instanceof HttpResponseCache) {
+ ((HttpResponseCache) responseCache).trackConditionalCacheHit();
+ }
+ // Discard the network response body. Combine the headers.
+ release(true);
+ setResponse(cachedResponseHeaders.combine(responseHeaders), cachedResponseBody);
return;
} else {
- IoUtils.closeQuietly(responseBodyToValidate);
- responseBodyToValidate = null;
- responseHeadersToValidate = null;
+ IoUtils.closeQuietly(cachedResponseBody);
}
}
diff --git a/luni/src/main/java/libcore/net/http/HttpResponseCache.java b/luni/src/main/java/libcore/net/http/HttpResponseCache.java
new file mode 100644
index 0000000..50ad5e2
--- /dev/null
+++ b/luni/src/main/java/libcore/net/http/HttpResponseCache.java
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.net.http;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedWriter;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FilterInputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.CacheRequest;
+import java.net.CacheResponse;
+import java.net.HttpURLConnection;
+import java.net.ResponseCache;
+import java.net.SecureCacheResponse;
+import java.net.URI;
+import java.net.URLConnection;
+import java.nio.charset.Charsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import libcore.io.Base64;
+import libcore.io.DiskLruCache;
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+/**
+ * Cache responses in a directory on the file system. Most clients should use
+ * {@code android.net.HttpResponseCache}, the stable, documented front end for
+ * this.
+ */
+public final class HttpResponseCache extends ResponseCache {
+ // TODO: add APIs to iterate the cache?
+ private static final int VERSION = 201105;
+ private static final int ENTRY_METADATA = 0;
+ private static final int ENTRY_BODY = 1;
+ private static final int ENTRY_COUNT = 2;
+
+ private final DiskLruCache cache;
+
+ /* read and write statistics, all guarded by 'this' */
+ private int writeSuccessCount;
+ private int writeAbortCount;
+ private int networkCount;
+ private int hitCount;
+ private int requestCount;
+
+ public HttpResponseCache(File directory, long maxSize) throws IOException {
+ cache = DiskLruCache.open(directory, VERSION, ENTRY_COUNT, maxSize);
+ }
+
+ private String uriToKey(URI uri) {
+ try {
+ MessageDigest messageDigest = MessageDigest.getInstance("MD5");
+ byte[] md5bytes = messageDigest.digest(uri.toString().getBytes(Charsets.UTF_8));
+ return IntegralToString.bytesToHexString(md5bytes, false);
+ } catch (NoSuchAlgorithmException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ @Override public CacheResponse get(URI uri, String requestMethod,
+ Map<String, List<String>> requestHeaders) {
+ String key = uriToKey(uri);
+ DiskLruCache.Snapshot snapshot;
+ Entry entry;
+ try {
+ snapshot = cache.get(key);
+ if (snapshot == null) {
+ return null;
+ }
+ entry = new Entry(new BufferedInputStream(snapshot.getInputStream(ENTRY_METADATA)));
+ } catch (IOException e) {
+ // Give up because the cache cannot be read.
+ return null;
+ }
+
+ if (!entry.matches(uri, requestMethod, requestHeaders)) {
+ snapshot.close();
+ return null;
+ }
+
+ InputStream body = newBodyInputStream(snapshot);
+ return entry.isHttps()
+ ? entry.newSecureCacheResponse(body)
+ : entry.newCacheResponse(body);
+ }
+
+ /**
+ * Returns an input stream that reads the body of a snapshot, closing the
+ * snapshot when the stream is closed.
+ */
+ private InputStream newBodyInputStream(final DiskLruCache.Snapshot snapshot) {
+ return new FilterInputStream(snapshot.getInputStream(ENTRY_BODY)) {
+ @Override public void close() throws IOException {
+ snapshot.close();
+ super.close();
+ }
+ };
+ }
+
+ @Override public CacheRequest put(URI uri, URLConnection urlConnection) throws IOException {
+ if (!(urlConnection instanceof HttpURLConnection)) {
+ return null;
+ }
+
+ HttpURLConnection httpConnection = (HttpURLConnection) urlConnection;
+ String requestMethod = httpConnection.getRequestMethod();
+ String key = uriToKey(uri);
+
+ if (requestMethod.equals(HttpEngine.POST)
+ || requestMethod.equals(HttpEngine.PUT)
+ || requestMethod.equals(HttpEngine.DELETE)) {
+ try {
+ cache.remove(key);
+ } catch (IOException ignored) {
+ // The cache cannot be written.
+ }
+ return null;
+ } else if (!requestMethod.equals(HttpEngine.GET)) {
+ /*
+ * Don't cache non-GET responses. We're technically allowed to cache
+ * HEAD requests and some POST requests, but the complexity of doing
+ * so is high and the benefit is low.
+ */
+ return null;
+ }
+
+ HttpEngine httpEngine = getHttpEngine(httpConnection);
+ if (httpEngine == null) {
+ // Don't cache unless the HTTP implementation is ours.
+ return null;
+ }
+
+ ResponseHeaders response = httpEngine.getResponseHeaders();
+ if (response.hasVaryAll()) {
+ return null;
+ }
+
+ RawHeaders varyHeaders = httpEngine.getRequestHeaders().headers.getAll(response.varyFields);
+ Entry entry = new Entry(uri, varyHeaders, httpConnection);
+ DiskLruCache.Editor editor = null;
+ try {
+ editor = cache.edit(key);
+ if (editor == null) {
+ return null;
+ }
+ entry.writeTo(editor);
+ return new CacheRequestImpl(editor);
+ } catch (IOException e) {
+ // Give up because the cache cannot be written.
+ try {
+ if (editor != null) {
+ editor.abort();
+ }
+ } catch (IOException ignored) {
+ }
+ return null;
+ }
+ }
+
+ private HttpEngine getHttpEngine(HttpURLConnection httpConnection) {
+ if (httpConnection instanceof HttpURLConnectionImpl) {
+ return ((HttpURLConnectionImpl) httpConnection).getHttpEngine();
+ } else if (httpConnection instanceof HttpsURLConnectionImpl) {
+ return ((HttpsURLConnectionImpl) httpConnection).getHttpEngine();
+ } else {
+ return null;
+ }
+ }
+
+ public DiskLruCache getCache() {
+ return cache;
+ }
+
+ synchronized int getWriteAbortCount() {
+ return writeAbortCount;
+ }
+
+ synchronized int getWriteSuccessCount() {
+ return writeSuccessCount;
+ }
+
+ synchronized void trackResponse(ResponseSource source) {
+ requestCount++;
+
+ switch (source) {
+ case CACHE:
+ hitCount++;
+ break;
+ case CONDITIONAL_CACHE:
+ case NETWORK:
+ networkCount++;
+ break;
+ }
+ }
+
+ synchronized void trackConditionalCacheHit() {
+ hitCount++;
+ }
+
+ public synchronized int getNetworkCount() {
+ return networkCount;
+ }
+
+ public synchronized int getHitCount() {
+ return hitCount;
+ }
+
+ public synchronized int getRequestCount() {
+ return requestCount;
+ }
+
+ private final class CacheRequestImpl extends CacheRequest {
+ private final DiskLruCache.Editor editor;
+ private OutputStream cacheOut;
+ private boolean done;
+ private OutputStream body;
+
+ public CacheRequestImpl(final DiskLruCache.Editor editor) throws IOException {
+ this.editor = editor;
+ this.cacheOut = editor.newOutputStream(ENTRY_BODY);
+ this.body = new FilterOutputStream(cacheOut) {
+ @Override public void close() throws IOException {
+ synchronized (HttpResponseCache.this) {
+ if (done) {
+ return;
+ }
+ done = true;
+ writeSuccessCount++;
+ }
+ super.close();
+ editor.commit();
+ }
+ };
+ }
+
+ @Override public void abort() {
+ synchronized (HttpResponseCache.this) {
+ if (done) {
+ return;
+ }
+ done = true;
+ writeAbortCount++;
+ }
+ IoUtils.closeQuietly(cacheOut);
+ try {
+ editor.abort();
+ } catch (IOException ignored) {
+ }
+ }
+
+ @Override public OutputStream getBody() throws IOException {
+ return body;
+ }
+ }
+
+ private static final class Entry {
+ private final String uri;
+ private final RawHeaders varyHeaders;
+ private final String requestMethod;
+ private final RawHeaders responseHeaders;
+ private final String cipherSuite;
+ private final Certificate[] peerCertificates;
+ private final Certificate[] localCertificates;
+
+ /*
+ * Reads an entry from an input stream. A typical entry looks like this:
+ * http://google.com/foo
+ * GET
+ * 2
+ * Accept-Language: fr-CA
+ * Accept-Charset: UTF-8
+ * HTTP/1.1 200 OK
+ * 3
+ * Content-Type: image/png
+ * Content-Length: 100
+ * Cache-Control: max-age=600
+ *
+ * A typical HTTPS file looks like this:
+ * https://google.com/foo
+ * GET
+ * 2
+ * Accept-Language: fr-CA
+ * Accept-Charset: UTF-8
+ * HTTP/1.1 200 OK
+ * 3
+ * Content-Type: image/png
+ * Content-Length: 100
+ * Cache-Control: max-age=600
+ *
+ * AES_256_WITH_MD5
+ * 2
+ * base64-encoded peerCertificate[0]
+ * base64-encoded peerCertificate[1]
+ * -1
+ *
+ * The file is newline separated. The first two lines are the URL and
+ * the request method. Next is the number of HTTP Vary request header
+ * lines, followed by those lines.
+ *
+ * Next is the response status line, followed by the number of HTTP
+ * response header lines, followed by those lines.
+ *
+ * HTTPS responses also contain SSL session information. This begins
+ * with a blank line, and then a line containing the cipher suite. Next
+ * is the length of the peer certificate chain. These certificates are
+ * base64-encoded and appear each on their own line. The next line
+ * contains the length of the local certificate chain. These
+ * certificates are also base64-encoded and appear each on their own
+ * line. A length of -1 is used to encode a null array.
+ */
+ public Entry(InputStream in) throws IOException {
+ try {
+ uri = Streams.readAsciiLine(in);
+ requestMethod = Streams.readAsciiLine(in);
+ varyHeaders = new RawHeaders();
+ int varyRequestHeaderLineCount = readInt(in);
+ for (int i = 0; i < varyRequestHeaderLineCount; i++) {
+ varyHeaders.addLine(Streams.readAsciiLine(in));
+ }
+
+ responseHeaders = new RawHeaders();
+ responseHeaders.setStatusLine(Streams.readAsciiLine(in));
+ int responseHeaderLineCount = readInt(in);
+ for (int i = 0; i < responseHeaderLineCount; i++) {
+ responseHeaders.addLine(Streams.readAsciiLine(in));
+ }
+
+ if (isHttps()) {
+ String blank = Streams.readAsciiLine(in);
+ if (!blank.isEmpty()) {
+ throw new IOException("expected \"\" but was \"" + blank + "\"");
+ }
+ cipherSuite = Streams.readAsciiLine(in);
+ peerCertificates = readCertArray(in);
+ localCertificates = readCertArray(in);
+ } else {
+ cipherSuite = null;
+ peerCertificates = null;
+ localCertificates = null;
+ }
+ } finally {
+ in.close();
+ }
+ }
+
+ public Entry(URI uri, RawHeaders varyHeaders, HttpURLConnection httpConnection) {
+ this.uri = uri.toString();
+ this.varyHeaders = varyHeaders;
+ this.requestMethod = httpConnection.getRequestMethod();
+ this.responseHeaders = RawHeaders.fromMultimap(httpConnection.getHeaderFields());
+
+ if (isHttps()) {
+ HttpsURLConnection httpsConnection = (HttpsURLConnection) httpConnection;
+ cipherSuite = httpsConnection.getCipherSuite();
+ Certificate[] peerCertificatesNonFinal = null;
+ try {
+ peerCertificatesNonFinal = httpsConnection.getServerCertificates();
+ } catch (SSLPeerUnverifiedException ignored) {
+ }
+ peerCertificates = peerCertificatesNonFinal;
+ localCertificates = httpsConnection.getLocalCertificates();
+ } else {
+ cipherSuite = null;
+ peerCertificates = null;
+ localCertificates = null;
+ }
+ }
+
+ public void writeTo(DiskLruCache.Editor editor) throws IOException {
+ OutputStream out = editor.newOutputStream(0);
+ Writer writer = new BufferedWriter(new OutputStreamWriter(out, Charsets.UTF_8));
+
+ writer.write(uri + '\n');
+ writer.write(requestMethod + '\n');
+ writer.write(Integer.toString(varyHeaders.length()) + '\n');
+ for (int i = 0; i < varyHeaders.length(); i++) {
+ writer.write(varyHeaders.getFieldName(i) + ": "
+ + varyHeaders.getValue(i) + '\n');
+ }
+
+ writer.write(responseHeaders.getStatusLine() + '\n');
+ writer.write(Integer.toString(responseHeaders.length()) + '\n');
+ for (int i = 0; i < responseHeaders.length(); i++) {
+ writer.write(responseHeaders.getFieldName(i) + ": "
+ + responseHeaders.getValue(i) + '\n');
+ }
+
+ if (isHttps()) {
+ writer.write('\n');
+ writer.write(cipherSuite + '\n');
+ writeCertArray(writer, peerCertificates);
+ writeCertArray(writer, localCertificates);
+ }
+ writer.close();
+ }
+
+ private boolean isHttps() {
+ return uri.startsWith("https://");
+ }
+
+ private int readInt(InputStream in) throws IOException {
+ String intString = Streams.readAsciiLine(in);
+ try {
+ return Integer.parseInt(intString);
+ } catch (NumberFormatException e) {
+ throw new IOException("expected an int but was \"" + intString + "\"");
+ }
+ }
+
+ private Certificate[] readCertArray(InputStream in) throws IOException {
+ int length = readInt(in);
+ if (length == -1) {
+ return null;
+ }
+ try {
+ CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
+ Certificate[] result = new Certificate[length];
+ for (int i = 0; i < result.length; i++) {
+ String line = Streams.readAsciiLine(in);
+ byte[] bytes = Base64.decode(line.getBytes(Charsets.US_ASCII));
+ result[i] = certificateFactory.generateCertificate(
+ new ByteArrayInputStream(bytes));
+ }
+ return result;
+ } catch (CertificateException e) {
+ throw new IOException(e);
+ }
+ }
+
+ private void writeCertArray(Writer writer, Certificate[] certificates) throws IOException {
+ if (certificates == null) {
+ writer.write("-1\n");
+ return;
+ }
+ try {
+ writer.write(Integer.toString(certificates.length) + '\n');
+ for (Certificate certificate : certificates) {
+ byte[] bytes = certificate.getEncoded();
+ String line = Base64.encode(bytes);
+ writer.write(line + '\n');
+ }
+ } catch (CertificateEncodingException e) {
+ throw new IOException(e);
+ }
+ }
+
+ public boolean matches(URI uri, String requestMethod,
+ Map<String, List<String>> requestHeaders) {
+ return this.uri.equals(uri.toString())
+ && this.requestMethod.equals(requestMethod)
+ && new ResponseHeaders(uri, responseHeaders)
+ .varyMatches(varyHeaders.toMultimap(), requestHeaders);
+ }
+
+ public CacheResponse newCacheResponse(final InputStream in) {
+ return new CacheResponse() {
+ @Override public Map<String, List<String>> getHeaders() {
+ return responseHeaders.toMultimap();
+ }
+
+ @Override public InputStream getBody() {
+ return in;
+ }
+ };
+ }
+
+ public SecureCacheResponse newSecureCacheResponse(final InputStream in) {
+ return new SecureCacheResponse() {
+ @Override public Map<String, List<String>> getHeaders() {
+ return responseHeaders.toMultimap();
+ }
+
+ @Override public InputStream getBody() {
+ return in;
+ }
+
+ @Override public String getCipherSuite() {
+ return cipherSuite;
+ }
+
+ @Override public List<Certificate> getServerCertificateChain()
+ throws SSLPeerUnverifiedException {
+ if (peerCertificates == null || peerCertificates.length == 0) {
+ throw new SSLPeerUnverifiedException(null);
+ }
+ return Arrays.asList(peerCertificates.clone());
+ }
+
+ @Override public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
+ if (peerCertificates == null || peerCertificates.length == 0) {
+ throw new SSLPeerUnverifiedException(null);
+ }
+ return ((X509Certificate) peerCertificates[0]).getSubjectX500Principal();
+ }
+
+ @Override public List<Certificate> getLocalCertificateChain() {
+ if (localCertificates == null || localCertificates.length == 0) {
+ return null;
+ }
+ return Arrays.asList(localCertificates.clone());
+ }
+
+ @Override public Principal getLocalPrincipal() {
+ if (localCertificates == null || localCertificates.length == 0) {
+ return null;
+ }
+ return ((X509Certificate) localCertificates[0]).getSubjectX500Principal();
+ }
+ };
+ }
+ }
+}
diff --git a/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java b/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java
index 95b37fd..be4c132 100644
--- a/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java
+++ b/luni/src/main/java/libcore/net/http/HttpURLConnectionImpl.java
@@ -33,10 +33,9 @@
import java.net.URL;
import java.nio.charset.Charsets;
import java.security.Permission;
-import java.util.Date;
import java.util.List;
import java.util.Map;
-import org.apache.harmony.luni.util.Base64;
+import libcore.io.Base64;
/**
* This implementation uses HttpEngine to send requests and receive responses.
@@ -52,16 +51,13 @@
* connection} field on this class for null/non-null to determine of an instance
* is currently connected to a server.
*/
-public class HttpURLConnectionImpl extends HttpURLConnection {
+class HttpURLConnectionImpl extends HttpURLConnection {
private final int defaultPort;
private Proxy proxy;
- // TODO: should these be set by URLConnection.setDefaultRequestProperty ?
- private static RawHeaders defaultRequestHeaders = new RawHeaders();
-
- protected RawHeaders rawRequestHeaders = new RawHeaders(defaultRequestHeaders);
+ private final RawHeaders rawRequestHeaders = new RawHeaders();
private int redirectionCount;
@@ -88,13 +84,10 @@
}
}
- /**
- * Close the socket connection to the remote origin server or proxy.
- */
@Override public final void disconnect() {
- // TODO: what happens if they call disconnect() before connect?
+ // Calling disconnect() before a connection exists should have no effect.
if (httpEngine != null) {
- httpEngine.releaseSocket(false);
+ httpEngine.release(false);
}
}
@@ -106,7 +99,7 @@
try {
HttpEngine response = getResponse();
if (response.hasResponseBody()
- && response.getResponseHeaders().getResponseCode() >= HTTP_BAD_REQUEST) {
+ && response.getResponseCode() >= HTTP_BAD_REQUEST) {
return response.getResponseBody();
}
return null;
@@ -121,7 +114,7 @@
*/
@Override public final String getHeaderField(int position) {
try {
- return getResponse().getResponseHeaders().getValue(position);
+ return getResponse().getResponseHeaders().headers.getValue(position);
} catch (IOException e) {
return null;
}
@@ -134,10 +127,10 @@
*/
@Override public final String getHeaderField(String fieldName) {
try {
- RawHeaders responseHeaders = getResponse().getResponseHeaders();
+ RawHeaders rawHeaders = getResponse().getResponseHeaders().headers;
return fieldName == null
- ? responseHeaders.getStatusLine()
- : responseHeaders.get(fieldName);
+ ? rawHeaders.getStatusLine()
+ : rawHeaders.get(fieldName);
} catch (IOException e) {
return null;
}
@@ -145,7 +138,7 @@
@Override public final String getHeaderFieldKey(int position) {
try {
- return getResponse().getResponseHeaders().getFieldName(position);
+ return getResponse().getResponseHeaders().headers.getFieldName(position);
} catch (IOException e) {
return null;
}
@@ -153,7 +146,7 @@
@Override public final Map<String, List<String>> getHeaderFields() {
try {
- return getResponse().getResponseHeaders().toMultimap();
+ return getResponse().getResponseHeaders().headers.toMultimap();
} catch (IOException e) {
return null;
}
@@ -193,7 +186,15 @@
@Override public final OutputStream getOutputStream() throws IOException {
connect();
- return httpEngine.getRequestBody();
+
+ OutputStream result = httpEngine.getRequestBody();
+ if (result == null) {
+ throw new ProtocolException("method does not support a request body: " + method);
+ } else if (httpEngine.hasResponse()) {
+ throw new ProtocolException("cannot write request body after response has been read");
+ }
+
+ return result;
}
@Override public final Permission getPermission() throws IOException {
@@ -230,6 +231,15 @@
connected = true;
try {
+ if (doOutput) {
+ if (method == HttpEngine.GET) {
+ // they are requesting a stream to write to. This implies a POST method
+ method = HttpEngine.POST;
+ } else if (method != HttpEngine.POST && method != HttpEngine.PUT) {
+ // If the request method is neither POST nor PUT, then you're not writing
+ throw new ProtocolException(method + " does not support writing");
+ }
+ }
httpEngine = newHttpEngine(method, rawRequestHeaders, null, null);
} catch (IOException e) {
httpEngineFailure = e;
@@ -265,34 +275,41 @@
Retry retry = processResponseHeaders();
if (retry == Retry.NONE) {
+ httpEngine.automaticallyReleaseConnectionToPool();
break;
}
/*
* The first request was insufficient. Prepare for another...
*/
+ String retryMethod = method;
OutputStream requestBody = httpEngine.getRequestBody();
+
+ /*
+ * Although RFC 2616 10.3.2 specifies that a HTTP_MOVED_PERM
+ * redirect should keep the same method, Chrome, Firefox and the
+ * RI all issue GETs when following any redirect.
+ */
+ int responseCode = getResponseCode();
+ if (responseCode == HTTP_MULT_CHOICE || responseCode == HTTP_MOVED_PERM
+ || responseCode == HTTP_MOVED_TEMP || responseCode == HTTP_SEE_OTHER) {
+ retryMethod = HttpEngine.GET;
+ requestBody = null;
+ }
+
if (requestBody != null && !(requestBody instanceof RetryableOutputStream)) {
throw new HttpRetryException("Cannot retry streamed HTTP body",
- httpEngine.getResponseHeaders().getResponseCode());
+ httpEngine.getResponseCode());
}
- if (retry == Retry.SAME_CONNECTION && httpEngine.hasConnectionCloseHeaders()) {
- retry = Retry.NEW_CONNECTION;
+ if (retry == Retry.DIFFERENT_CONNECTION) {
+ httpEngine.automaticallyReleaseConnectionToPool();
}
- HttpConnection connection = null;
- if (retry == Retry.NEW_CONNECTION) {
- httpEngine.discardResponseBody();
- httpEngine.releaseSocket(true);
- } else {
- httpEngine.dontReleaseSocketToPool();
- httpEngine.discardResponseBody();
- connection = httpEngine.getConnection();
- }
+ httpEngine.release(true);
- httpEngine = newHttpEngine(method, rawRequestHeaders, connection,
- (RetryableOutputStream) requestBody);
+ httpEngine = newHttpEngine(retryMethod, rawRequestHeaders,
+ httpEngine.getConnection(), (RetryableOutputStream) requestBody);
}
return httpEngine;
} catch (IOException e) {
@@ -301,10 +318,14 @@
}
}
+ HttpEngine getHttpEngine() {
+ return httpEngine;
+ }
+
enum Retry {
NONE,
SAME_CONNECTION,
- NEW_CONNECTION
+ DIFFERENT_CONNECTION
}
/**
@@ -313,49 +334,32 @@
* prepare for a follow up request.
*/
private Retry processResponseHeaders() throws IOException {
- RawHeaders responseHeaders = httpEngine.getResponseHeaders();
- int responseCode = responseHeaders.getResponseCode();
- switch (responseCode) {
- case HTTP_PROXY_AUTH: // proxy authorization failed ?
+ switch (getResponseCode()) {
+ case HTTP_PROXY_AUTH:
if (!usingProxy()) {
throw new IOException(
"Received HTTP_PROXY_AUTH (407) code while not using proxy");
}
- return processAuthHeader("Proxy-Authenticate", "Proxy-Authorization");
-
- case HTTP_UNAUTHORIZED: // HTTP authorization failed ?
- return processAuthHeader("WWW-Authenticate", "Authorization");
+ // fall-through
+ case HTTP_UNAUTHORIZED:
+ boolean credentialsFound = processAuthHeader(getResponseCode(),
+ httpEngine.getResponseHeaders(), rawRequestHeaders);
+ return credentialsFound ? Retry.SAME_CONNECTION : Retry.NONE;
case HTTP_MULT_CHOICE:
case HTTP_MOVED_PERM:
case HTTP_MOVED_TEMP:
case HTTP_SEE_OTHER:
- case HTTP_USE_PROXY:
if (!getInstanceFollowRedirects()) {
return Retry.NONE;
}
- if (httpEngine.getRequestBody() != null) {
- // TODO: follow redirects for retryable output streams...
- return Retry.NONE;
- }
if (++redirectionCount > HttpEngine.MAX_REDIRECTS) {
throw new ProtocolException("Too many redirects");
}
- String location = responseHeaders.get("Location");
+ String location = getHeaderField("Location");
if (location == null) {
return Retry.NONE;
}
- if (responseCode == HTTP_USE_PROXY) {
- int start = 0;
- if (location.startsWith(url.getProtocol() + ':')) {
- start = url.getProtocol().length() + 1;
- }
- if (location.startsWith("//", start)) {
- start += 2;
- }
- setProxy(location.substring(start));
- return Retry.NEW_CONNECTION;
- }
URL previousUrl = url;
url = new URL(previousUrl, location);
if (!previousUrl.getProtocol().equals(url.getProtocol())) {
@@ -365,9 +369,7 @@
&& previousUrl.getEffectivePort() == url.getEffectivePort()) {
return Retry.SAME_CONNECTION;
} else {
- // TODO: strip cookies?
- rawRequestHeaders.removeAll("Host");
- return Retry.NEW_CONNECTION;
+ return Retry.DIFFERENT_CONNECTION;
}
default:
@@ -375,37 +377,36 @@
}
}
- private void setProxy(String proxy) {
- // TODO: convert IllegalArgumentException etc. to ProtocolException?
- int colon = proxy.indexOf(':');
- String host;
- int port;
- if (colon != -1) {
- host = proxy.substring(0, colon);
- port = Integer.parseInt(proxy.substring(colon + 1));
- } else {
- host = proxy;
- port = getDefaultPort();
- }
- this.proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port));
- }
-
/**
* React to a failed authorization response by looking up new credentials.
+ *
+ * @return true if credentials have been added to successorRequestHeaders
+ * and another request should be attempted.
*/
- private Retry processAuthHeader(String fieldName, String value) throws IOException {
+ final boolean processAuthHeader(int responseCode, ResponseHeaders response,
+ RawHeaders successorRequestHeaders) throws IOException {
+ if (responseCode != HTTP_PROXY_AUTH && responseCode != HTTP_UNAUTHORIZED) {
+ throw new IllegalArgumentException();
+ }
+
// keep asking for username/password until authorized
- String challenge = httpEngine.getResponseHeaders().get(fieldName);
+ String challenge = responseCode == HTTP_PROXY_AUTH
+ ? response.proxyAuthenticate
+ : response.wwwAuthenticate;
if (challenge == null) {
throw new IOException("Received authentication challenge is null");
}
String credentials = getAuthorizationCredentials(challenge);
if (credentials == null) {
- return Retry.NONE; // could not find credentials, end request cycle
+ return false; // could not find credentials, end request cycle
}
+
// add authorization credentials, bypassing the already-connected check
- rawRequestHeaders.set(value, credentials);
- return Retry.SAME_CONNECTION;
+ String fieldName = responseCode == HTTP_PROXY_AUTH
+ ? "Proxy-Authorization"
+ : "Authorization";
+ successorRequestHeaders.set(fieldName, credentials);
+ return true;
}
/**
@@ -434,7 +435,7 @@
// base64 encode the username and password
String usernameAndPassword = pa.getUserName() + ":" + new String(pa.getPassword());
byte[] bytes = usernameAndPassword.getBytes(Charsets.ISO_8859_1);
- String encoded = Base64.encode(bytes, Charsets.ISO_8859_1);
+ String encoded = Base64.encode(bytes);
return scheme + " " + encoded;
}
@@ -466,23 +467,16 @@
this.proxy = proxy;
}
-
@Override public final boolean usingProxy() {
return (proxy != null && proxy.type() != Proxy.Type.DIRECT);
}
@Override public String getResponseMessage() throws IOException {
- return getResponse().getResponseHeaders().getResponseMessage();
+ return getResponse().getResponseHeaders().headers.getResponseMessage();
}
@Override public final int getResponseCode() throws IOException {
- return getResponse().getResponseHeaders().getResponseCode();
- }
-
- @Override public final void setIfModifiedSince(long newValue) {
- // TODO: set this lazily in prepareRequestHeaders()
- super.setIfModifiedSince(newValue);
- rawRequestHeaders.add("If-Modified-Since", HttpDate.format(new Date(newValue)));
+ return getResponse().getResponseCode();
}
@Override public final void setRequestProperty(String field, String newValue) {
diff --git a/luni/src/main/java/libcore/net/http/HttpsURLConnectionImpl.java b/luni/src/main/java/libcore/net/http/HttpsURLConnectionImpl.java
index 46f74f4..e385c9e 100644
--- a/luni/src/main/java/libcore/net/http/HttpsURLConnectionImpl.java
+++ b/luni/src/main/java/libcore/net/http/HttpsURLConnectionImpl.java
@@ -57,6 +57,10 @@
}
}
+ HttpEngine getHttpEngine() {
+ return delegate.getHttpEngine();
+ }
+
@Override
public String getCipherSuite() {
SecureCacheResponse cacheResponse = delegate.getCacheResponse();
@@ -382,7 +386,8 @@
}
public SecureCacheResponse getCacheResponse() {
- return (SecureCacheResponse) httpEngine.getCacheResponse();
+ HttpsEngine engine = (HttpsEngine) httpEngine;
+ return engine != null ? (SecureCacheResponse) engine.getCacheResponse() : null;
}
public SSLSocket getSSLSocket() {
@@ -432,7 +437,7 @@
&& e.getCause() instanceof CertificateException) {
throw e;
}
- releaseSocket(false);
+ release(false);
connectionReused = makeSslConnection(false);
}
@@ -473,11 +478,36 @@
return false;
}
+ /**
+ * To make an HTTPS connection over an HTTP proxy, send an unencrypted
+ * CONNECT request to create the proxy connection. This may need to be
+ * retried if the proxy requires authorization.
+ */
private void makeTunnel(HttpURLConnectionImpl policy, HttpConnection connection,
- RawHeaders requestHeaders) throws IOException {
- HttpEngine connect = new ProxyConnectEngine(policy, requestHeaders, connection);
- connect.sendRequest();
- connect.readResponse();
+ RequestHeaders requestHeaders) throws IOException {
+ RawHeaders rawRequestHeaders = requestHeaders.headers;
+ while (true) {
+ HttpEngine connect = new ProxyConnectEngine(policy, rawRequestHeaders, connection);
+ connect.sendRequest();
+ connect.readResponse();
+
+ int responseCode = connect.getResponseCode();
+ switch (connect.getResponseCode()) {
+ case HTTP_OK:
+ return;
+ case HTTP_PROXY_AUTH:
+ rawRequestHeaders = new RawHeaders(rawRequestHeaders);
+ boolean credentialsFound = policy.processAuthHeader(HTTP_PROXY_AUTH,
+ connect.getResponseHeaders(), rawRequestHeaders);
+ if (credentialsFound) {
+ continue;
+ } else {
+ throw new IOException("Failed to authenticate with proxy");
+ }
+ default:
+ throw new IOException("Unexpected response code for CONNECT: " + responseCode);
+ }
+ }
}
@Override protected boolean acceptCacheResponseType(CacheResponse cacheResponse) {
@@ -497,8 +527,7 @@
private static class ProxyConnectEngine extends HttpEngine {
public ProxyConnectEngine(HttpURLConnectionImpl policy, RawHeaders requestHeaders,
HttpConnection connection) throws IOException {
- super(policy, HttpEngine.CONNECT, requestHeaders, null, null);
- this.connection = connection;
+ super(policy, HttpEngine.CONNECT, requestHeaders, connection, null);
}
/**
@@ -507,7 +536,7 @@
* sensitive data like HTTP cookies to the proxy unencrypted.
*/
@Override protected RawHeaders getNetworkRequestHeaders() throws IOException {
- RawHeaders privateHeaders = getRequestHeaders();
+ RequestHeaders privateHeaders = getRequestHeaders();
URL url = policy.getURL();
RawHeaders result = new RawHeaders();
@@ -515,20 +544,20 @@
+ " HTTP/1.1");
// Always set Host and User-Agent.
- String host = privateHeaders.get("Host");
+ String host = privateHeaders.host;
if (host == null) {
host = getOriginAddress(url);
}
result.set("Host", host);
- String userAgent = privateHeaders.get("User-Agent");
+ String userAgent = privateHeaders.userAgent;
if (userAgent == null) {
userAgent = getDefaultUserAgent();
}
result.set("User-Agent", userAgent);
// Copy over the Proxy-Authorization header if it exists.
- String proxyAuthorization = privateHeaders.get("Proxy-Authorization");
+ String proxyAuthorization = privateHeaders.proxyAuthorization;
if (proxyAuthorization != null) {
result.set("Proxy-Authorization", proxyAuthorization);
}
diff --git a/luni/src/main/java/libcore/net/http/RawHeaders.java b/luni/src/main/java/libcore/net/http/RawHeaders.java
index a6ec3c3..75a1392a 100644
--- a/luni/src/main/java/libcore/net/http/RawHeaders.java
+++ b/luni/src/main/java/libcore/net/http/RawHeaders.java
@@ -23,6 +23,7 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import java.util.TreeMap;
/**
@@ -41,9 +42,9 @@
* <p>This class trims whitespace from values. It never returns values with
* leading or trailing whitespace.
*/
-final class RawHeaders implements Cloneable {
-
+final class RawHeaders {
private static final Comparator<String> FIELD_NAME_COMPARATOR = new Comparator<String>() {
+ @FindBugsSuppressWarnings("ES_COMPARING_PARAMETER_STRING_WITH_EQ")
@Override public int compare(String a, String b) {
if (a == b) {
return 0;
@@ -78,6 +79,7 @@
* (like "GET / HTTP/1.1").
*/
public void setStatusLine(String statusLine) {
+ statusLine = statusLine.trim();
this.statusLine = statusLine;
if (statusLine == null || !statusLine.startsWith("HTTP/")) {
@@ -128,6 +130,19 @@
}
/**
+ * Add an HTTP header line containing a field name, a literal colon, and a
+ * value.
+ */
+ public void addLine(String line) {
+ int index = line.indexOf(":");
+ if (index == -1) {
+ add("", line);
+ } else {
+ add(line.substring(0, index), line.substring(index + 1));
+ }
+ }
+
+ /**
* Add a field with the specified value.
*/
public void add(String fieldName, String value) {
@@ -162,12 +177,6 @@
}
}
- public void addIfAbsent(String fieldName, String value) {
- if (get(fieldName) == null) {
- add(fieldName, value);
- }
- }
-
/**
* Set a field with the specified value. If the field is not found, it is
* added. If the field is found, the existing values are replaced.
@@ -218,6 +227,20 @@
return null;
}
+ /**
+ * @param fieldNames a case-insensitive set of HTTP header field names.
+ */
+ public RawHeaders getAll(Set<String> fieldNames) {
+ RawHeaders result = new RawHeaders();
+ for (int i = 0; i < namesAndValues.size(); i += 2) {
+ String fieldName = namesAndValues.get(i);
+ if (fieldNames.contains(fieldName)) {
+ result.add(fieldName, namesAndValues.get(i + 1));
+ }
+ }
+ return result;
+ }
+
public String toHeaderString() {
StringBuilder result = new StringBuilder(256);
result.append(statusLine).append("\r\n");
diff --git a/luni/src/main/java/libcore/net/http/RequestHeaders.java b/luni/src/main/java/libcore/net/http/RequestHeaders.java
index ce9354b..b143f81 100644
--- a/luni/src/main/java/libcore/net/http/RequestHeaders.java
+++ b/luni/src/main/java/libcore/net/http/RequestHeaders.java
@@ -17,6 +17,9 @@
package libcore.net.http;
import java.net.URI;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
/**
* Parsed HTTP request headers.
@@ -39,14 +42,6 @@
* header is set.
*/
boolean onlyIfCached;
- String noCacheField;
-
- /**
- * True if the request contains conditions that save the server from sending
- * a response that the client has locally. When the caller adds conditions,
- * this cache won't participate in the request.
- */
- boolean hasConditions;
/**
* True if the request contains an authorization field. Although this isn't
@@ -55,6 +50,17 @@
*/
boolean hasAuthorization;
+ int contentLength = -1;
+ String transferEncoding;
+ String userAgent;
+ String host;
+ String connection;
+ String acceptEncoding;
+ String contentType;
+ String ifModifiedSince;
+ String ifNoneMatch;
+ String proxyAuthorization;
+
public RequestHeaders(URI uri, RawHeaders headers) {
this.uri = uri;
this.headers = headers;
@@ -63,7 +69,6 @@
@Override public void handle(String directive, String parameter) {
if (directive.equalsIgnoreCase("no-cache")) {
noCache = true;
- noCacheField = parameter;
} else if (directive.equalsIgnoreCase("max-age")) {
maxAgeSeconds = HeaderParser.parseSeconds(parameter);
} else if (directive.equalsIgnoreCase("max-stale")) {
@@ -86,11 +91,129 @@
noCache = true;
}
} else if ("If-None-Match".equalsIgnoreCase(fieldName)) {
- hasConditions = true;
+ ifNoneMatch = value;
} else if ("If-Modified-Since".equalsIgnoreCase(fieldName)) {
- hasConditions = true;
+ ifModifiedSince = value;
} else if ("Authorization".equalsIgnoreCase(fieldName)) {
hasAuthorization = true;
+ } else if ("Content-Length".equalsIgnoreCase(fieldName)) {
+ try {
+ contentLength = Integer.parseInt(value);
+ } catch (NumberFormatException ignored) {
+ }
+ } else if ("Transfer-Encoding".equalsIgnoreCase(fieldName)) {
+ transferEncoding = value;
+ } else if ("User-Agent".equalsIgnoreCase(fieldName)) {
+ userAgent = value;
+ } else if ("Host".equalsIgnoreCase(fieldName)) {
+ host = value;
+ } else if ("Connection".equalsIgnoreCase(fieldName)) {
+ connection = value;
+ } else if ("Accept-Encoding".equalsIgnoreCase(fieldName)) {
+ acceptEncoding = value;
+ } else if ("Content-Type".equalsIgnoreCase(fieldName)) {
+ contentType = value;
+ } else if ("Proxy-Authorization".equalsIgnoreCase(fieldName)) {
+ proxyAuthorization = value;
+ }
+ }
+ }
+
+ public boolean isChunked() {
+ return "chunked".equalsIgnoreCase(transferEncoding);
+ }
+
+ public boolean hasConnectionClose() {
+ return "close".equalsIgnoreCase(connection);
+ }
+
+ public void setChunked() {
+ if (this.transferEncoding != null) {
+ headers.removeAll("Transfer-Encoding");
+ }
+ headers.add("Transfer-Encoding", "chunked");
+ this.transferEncoding = "chunked";
+ }
+
+ public void setContentLength(int contentLength) {
+ if (this.contentLength != -1) {
+ headers.removeAll("Content-Length");
+ }
+ headers.add("Content-Length", Integer.toString(contentLength));
+ this.contentLength = contentLength;
+ }
+
+ public void setUserAgent(String userAgent) {
+ if (this.userAgent != null) {
+ headers.removeAll("User-Agent");
+ }
+ headers.add("User-Agent", userAgent);
+ this.userAgent = userAgent;
+ }
+
+ public void setHost(String host) {
+ if (this.host != null) {
+ headers.removeAll("Host");
+ }
+ headers.add("Host", host);
+ this.host = host;
+ }
+
+ public void setConnection(String connection) {
+ if (this.connection != null) {
+ headers.removeAll("Connection");
+ }
+ headers.add("Connection", connection);
+ this.connection = connection;
+ }
+
+ public void setAcceptEncoding(String acceptEncoding) {
+ if (this.acceptEncoding != null) {
+ headers.removeAll("Accept-Encoding");
+ }
+ headers.add("Accept-Encoding", acceptEncoding);
+ this.acceptEncoding = acceptEncoding;
+ }
+
+ public void setContentType(String contentType) {
+ if (this.contentType != null) {
+ headers.removeAll("Content-Type");
+ }
+ headers.add("Content-Type", contentType);
+ this.contentType = contentType;
+ }
+
+ public void setIfModifiedSince(Date date) {
+ if (ifModifiedSince != null) {
+ headers.removeAll("If-Modified-Since");
+ }
+ String formattedDate = HttpDate.format(date);
+ headers.add("If-Modified-Since", formattedDate);
+ ifModifiedSince = formattedDate;
+ }
+
+ public void setIfNoneMatch(String ifNoneMatch) {
+ if (this.ifNoneMatch != null) {
+ headers.removeAll("If-None-Match");
+ }
+ headers.add("If-None-Match", ifNoneMatch);
+ this.ifNoneMatch = ifNoneMatch;
+ }
+
+ /**
+ * Returns true if the request contains conditions that save the server from
+ * sending a response that the client has locally. When the caller adds
+ * conditions, this cache won't participate in the request.
+ */
+ public boolean hasConditions() {
+ return ifModifiedSince != null || ifNoneMatch != null;
+ }
+
+ public void addCookies(Map<String, List<String>> allCookieHeaders) {
+ for (Map.Entry<String, List<String>> entry : allCookieHeaders.entrySet()) {
+ String key = entry.getKey();
+ if ("Cookie".equalsIgnoreCase(key) || "Cookie2".equalsIgnoreCase(key)) {
+ headers.addAll(key, entry.getValue());
}
}
}
diff --git a/luni/src/main/java/libcore/net/http/ResponseHeaders.java b/luni/src/main/java/libcore/net/http/ResponseHeaders.java
index 2ee5a58..0764a53 100644
--- a/luni/src/main/java/libcore/net/http/ResponseHeaders.java
+++ b/luni/src/main/java/libcore/net/http/ResponseHeaders.java
@@ -18,8 +18,14 @@
import java.net.HttpURLConnection;
import java.net.URI;
+import java.util.Collections;
import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
+import libcore.util.Objects;
/**
* Parsed HTTP response headers.
@@ -27,10 +33,10 @@
final class ResponseHeaders {
/** HTTP header name for the local time when the request was sent. */
- public static final String SENT_MILLIS = "X-Android-Sent-Millis";
+ private static final String SENT_MILLIS = "X-Android-Sent-Millis";
/** HTTP header name for the local time when the response was received. */
- public static final String RECEIVED_MILLIS = "X-Android-Received-Millis";
+ private static final String RECEIVED_MILLIS = "X-Android-Received-Millis";
final URI uri;
final RawHeaders headers;
@@ -91,11 +97,20 @@
* not permitted if this header is set.
*/
boolean isPublic;
- String privateField;
boolean mustRevalidate;
String etag;
int ageSeconds = -1;
+ /** Case-insensitive set of field names. */
+ Set<String> varyFields = Collections.emptySet();
+
+ String contentEncoding;
+ String transferEncoding;
+ int contentLength = -1;
+ String connection;
+ String proxyAuthenticate;
+ String wwwAuthenticate;
+
public ResponseHeaders(URI uri, RawHeaders headers) {
this.uri = uri;
this.headers = headers;
@@ -112,8 +127,6 @@
sMaxAgeSeconds = HeaderParser.parseSeconds(parameter);
} else if (directive.equalsIgnoreCase("public")) {
isPublic = true;
- } else if (directive.equalsIgnoreCase("private")) {
- privateField = parameter;
} else if (directive.equalsIgnoreCase("must-revalidate")) {
mustRevalidate = true;
}
@@ -139,6 +152,29 @@
}
} else if ("Age".equalsIgnoreCase(fieldName)) {
ageSeconds = HeaderParser.parseSeconds(value);
+ } else if ("Vary".equalsIgnoreCase(fieldName)) {
+ // Replace the immutable empty set with something we can mutate.
+ if (varyFields.isEmpty()) {
+ varyFields = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
+ }
+ for (String varyField : value.split(",")) {
+ varyFields.add(varyField.trim());
+ }
+ } else if ("Content-Encoding".equalsIgnoreCase(fieldName)) {
+ contentEncoding = value;
+ } else if ("Transfer-Encoding".equalsIgnoreCase(fieldName)) {
+ transferEncoding = value;
+ } else if ("Content-Length".equalsIgnoreCase(fieldName)) {
+ try {
+ contentLength = Integer.parseInt(value);
+ } catch (NumberFormatException ignored) {
+ }
+ } else if ("Connection".equalsIgnoreCase(fieldName)) {
+ connection = value;
+ } else if ("Proxy-Authenticate".equalsIgnoreCase(fieldName)) {
+ proxyAuthenticate = value;
+ } else if ("WWW-Authenticate".equalsIgnoreCase(fieldName)) {
+ wwwAuthenticate = value;
} else if (SENT_MILLIS.equalsIgnoreCase(fieldName)) {
sentRequestMillis = Long.parseLong(value);
} else if (RECEIVED_MILLIS.equalsIgnoreCase(fieldName)) {
@@ -147,6 +183,30 @@
}
}
+ public boolean isContentEncodingGzip() {
+ return "gzip".equalsIgnoreCase(contentEncoding);
+ }
+
+ public void stripContentEncoding() {
+ contentEncoding = null;
+ headers.removeAll("Content-Encoding");
+ }
+
+ public boolean isChunked() {
+ return "chunked".equalsIgnoreCase(transferEncoding);
+ }
+
+ public boolean hasConnectionClose() {
+ return "close".equalsIgnoreCase(connection);
+ }
+
+ public void setLocalTimestamps(long sentRequestMillis, long receivedResponseMillis) {
+ this.sentRequestMillis = sentRequestMillis;
+ headers.add(SENT_MILLIS, Long.toString(sentRequestMillis));
+ this.receivedResponseMillis = receivedResponseMillis;
+ headers.add(RECEIVED_MILLIS, Long.toString(receivedResponseMillis));
+ }
+
/**
* Returns the current age of the response, in milliseconds. The calculation
* is specified by RFC 2616, 13.2.3 Age Calculations.
@@ -234,6 +294,28 @@
}
/**
+ * Returns true if a Vary header contains an asterisk. Such responses cannot
+ * be cached.
+ */
+ public boolean hasVaryAll() {
+ return varyFields.contains("*");
+ }
+
+ /**
+ * Returns true if none of the Vary headers on this response have changed
+ * between {@code cachedRequest} and {@code newRequest}.
+ */
+ public boolean varyMatches(Map<String, List<String>> cachedRequest,
+ Map<String, List<String>> newRequest) {
+ for (String field : varyFields) {
+ if (!Objects.equal(cachedRequest.get(field), newRequest.get(field))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* Returns the source to satisfy {@code request} given this cached response.
*/
public ResponseSource chooseResponseSource(long nowMillis, RequestHeaders request) {
@@ -246,7 +328,7 @@
return ResponseSource.NETWORK;
}
- if (request.noCache || request.hasConditions) {
+ if (request.noCache || request.hasConditions()) {
return ResponseSource.NETWORK;
}
@@ -264,36 +346,31 @@
}
long maxStaleMillis = 0;
- if (request.maxStaleSeconds != -1) {
+ if (!mustRevalidate && request.maxStaleSeconds != -1) {
maxStaleMillis = TimeUnit.SECONDS.toMillis(request.maxStaleSeconds);
}
if (!noCache && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
if (ageMillis + minFreshMillis >= freshMillis) {
- // TODO: this should be RESPONSE headers
- request.headers.add("Warning", "110 HttpURLConnection \"Response is stale\"");
+ headers.add("Warning", "110 HttpURLConnection \"Response is stale\"");
}
if (ageMillis > TimeUnit.HOURS.toMillis(24) && isFreshnessLifetimeHeuristic()) {
- // TODO: this should be RESPONSE headers
- request.headers.add("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
+ headers.add("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
}
return ResponseSource.CACHE;
}
if (lastModified != null) {
- request.headers.add("If-Modified-Since", HttpDate.format(lastModified));
- request.hasConditions = true;
+ request.setIfModifiedSince(lastModified);
} else if (servedDate != null) {
- request.headers.add("If-Modified-Since", HttpDate.format(servedDate));
- request.hasConditions = true;
+ request.setIfModifiedSince(servedDate);
}
if (etag != null) {
- request.headers.add("If-None-Match", etag);
- request.hasConditions = true;
+ request.setIfNoneMatch(etag);
}
- return request.hasConditions
+ return request.hasConditions()
? ResponseSource.CONDITIONAL_CACHE
: ResponseSource.NETWORK;
}
@@ -320,4 +397,47 @@
return false;
}
+
+ /**
+ * Combines this cached header with a network header as defined by RFC 2616,
+ * 13.5.3.
+ */
+ public ResponseHeaders combine(ResponseHeaders network) {
+ RawHeaders result = new RawHeaders();
+
+ for (int i = 0; i < headers.length(); i++) {
+ String fieldName = headers.getFieldName(i);
+ String value = headers.getValue(i);
+ if (fieldName.equals("Warning") && value.startsWith("1")) {
+ continue; // drop 100-level freshness warnings
+ }
+ if (!isEndToEnd(fieldName) || network.headers.get(fieldName) == null) {
+ result.add(fieldName, value);
+ }
+ }
+
+ for (int i = 0; i < network.headers.length(); i++) {
+ String fieldName = network.headers.getFieldName(i);
+ if (isEndToEnd(fieldName)) {
+ result.add(fieldName, network.headers.getValue(i));
+ }
+ }
+
+ return new ResponseHeaders(uri, result);
+ }
+
+ /**
+ * Returns true if {@code fieldName} is an end-to-end HTTP header, as
+ * defined by RFC 2616, 13.5.1.
+ */
+ private static boolean isEndToEnd(String fieldName) {
+ return !fieldName.equalsIgnoreCase("Connection")
+ && !fieldName.equalsIgnoreCase("Keep-Alive")
+ && !fieldName.equalsIgnoreCase("Proxy-Authenticate")
+ && !fieldName.equalsIgnoreCase("Proxy-Authorization")
+ && !fieldName.equalsIgnoreCase("TE")
+ && !fieldName.equalsIgnoreCase("Trailers")
+ && !fieldName.equalsIgnoreCase("Transfer-Encoding")
+ && !fieldName.equalsIgnoreCase("Upgrade");
+ }
}
diff --git a/luni/src/main/java/org/apache/harmony/lang/annotation/AnnotationFactory.java b/luni/src/main/java/org/apache/harmony/lang/annotation/AnnotationFactory.java
index 4667416..3fd13f5 100644
--- a/luni/src/main/java/org/apache/harmony/lang/annotation/AnnotationFactory.java
+++ b/luni/src/main/java/org/apache/harmony/lang/annotation/AnnotationFactory.java
@@ -68,7 +68,7 @@
Method[] m = annotationType.getDeclaredMethods();
desc = new AnnotationMember[m.length];
int idx = 0;
- for(Method element : m) {
+ for (Method element : m) {
String name = element.getName();
Class<?> type = element.getReturnType();
try {
@@ -260,7 +260,7 @@
result.append('@');
result.append(klazz.getName());
result.append('(');
- for(int i = 0; i < elements.length; ++i) {
+ for (int i = 0; i < elements.length; ++i) {
if (i != 0) {
result.append(", ");
}
diff --git a/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java b/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java
deleted file mode 100644
index 485d722..0000000
--- a/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.harmony.luni.platform;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.net.DatagramPacket;
-import java.net.InetAddress;
-import java.net.SocketException;
-import java.net.SocketImpl;
-
-/*
- * The interface for network methods.
- */
-public interface INetworkSystem {
- public void accept(FileDescriptor serverFd, SocketImpl newSocket, FileDescriptor clientFd)
- throws IOException;
-
- public void bind(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException;
-
- public int read(FileDescriptor fd, byte[] data, int offset, int count) throws IOException;
-
- public int readDirect(FileDescriptor fd, int address, int count) throws IOException;
-
- public int write(FileDescriptor fd, byte[] data, int offset, int count) throws IOException;
-
- public int writeDirect(FileDescriptor fd, int address, int offset, int count) throws IOException;
-
- public boolean connect(FileDescriptor fd, InetAddress inetAddress, int port) throws IOException;
- public boolean isConnected(FileDescriptor fd, int timeout) throws IOException;
-
- public int send(FileDescriptor fd, byte[] data, int offset, int length,
- int port, InetAddress inetAddress) throws IOException;
- public int sendDirect(FileDescriptor fd, int address, int offset, int length,
- int port, InetAddress inetAddress) throws IOException;
-
- public int recv(FileDescriptor fd, DatagramPacket packet, byte[] data, int offset,
- int length, boolean peek, boolean connected) throws IOException;
- public int recvDirect(FileDescriptor fd, DatagramPacket packet, int address, int offset,
- int length, boolean peek, boolean connected) throws IOException;
-
- public void disconnectDatagram(FileDescriptor fd) throws SocketException;
-
- public void sendUrgentData(FileDescriptor fd, byte value);
-
- /**
- * Select the given file descriptors for read and write operations.
- *
- * <p>The first {@code numReadable} file descriptors of {@code readFDs} will
- * be selected for read-ready operations. The first {@code numWritable} file
- * descriptors in {@code writeFDs} will be selected for write-ready
- * operations. A file descriptor can appear in either or both and must not
- * be null. If the file descriptor is closed during the select the behavior
- * depends upon the underlying OS.
- *
- * @param readFDs
- * all sockets interested in read and accept
- * @param writeFDs
- * all sockets interested in write and connect
- * @param numReadable
- * the size of the subset of readFDs to read or accept.
- * @param numWritable
- * the size of the subset of writeFDs to write or connect
- * @param timeout
- * timeout in milliseconds
- * @param flags
- * for output. Length must be at least {@code numReadable
- * + numWritable}. Upon returning, each element describes the
- * state of the descriptor in the corresponding read or write
- * array. See {@code SelectorImpl.READABLE} and {@code
- * SelectorImpl.WRITEABLE}
- * @return true
- * unless selection timed out or was interrupted
- * @throws SocketException
- */
- public boolean select(FileDescriptor[] readFDs, FileDescriptor[] writeFDs,
- int numReadable, int numWritable, long timeout, int[] flags)
- throws SocketException;
-
- /**
- * It is an error to close the same file descriptor from multiple threads
- * concurrently.
- */
- public void close(FileDescriptor fd) throws IOException;
-}
diff --git a/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java b/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java
deleted file mode 100644
index e98b41cd..0000000
--- a/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.harmony.luni.platform;
-
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.net.DatagramPacket;
-import java.net.InetAddress;
-import java.net.SocketException;
-import java.net.SocketImpl;
-
-/**
- * This wraps native code that implements the INetworkSystem interface.
- * Address length was changed from long to int for performance reasons.
- */
-final class OSNetworkSystem implements INetworkSystem {
- private static final OSNetworkSystem singleton = new OSNetworkSystem();
-
- public static OSNetworkSystem getOSNetworkSystem() {
- return singleton;
- }
-
- private OSNetworkSystem() {
- }
-
- public native void accept(FileDescriptor serverFd, SocketImpl newSocket,
- FileDescriptor clientFd) throws IOException;
-
- public native void bind(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException;
-
- public native boolean connect(FileDescriptor fd, InetAddress inetAddress, int port) throws IOException;
- public native boolean isConnected(FileDescriptor fd, int timeout) throws IOException;
-
- public native void disconnectDatagram(FileDescriptor fd) throws SocketException;
-
- public native int read(FileDescriptor fd, byte[] data, int offset, int count)
- throws IOException;
-
- public native int readDirect(FileDescriptor fd, int address, int count) throws IOException;
-
- public native int recv(FileDescriptor fd, DatagramPacket packet,
- byte[] data, int offset, int length,
- boolean peek, boolean connected) throws IOException;
-
- public native int recvDirect(FileDescriptor fd, DatagramPacket packet,
- int address, int offset, int length,
- boolean peek, boolean connected) throws IOException;
-
- public boolean select(FileDescriptor[] readFDs, FileDescriptor[] writeFDs,
- int numReadable, int numWritable, long timeout, int[] flags)
- throws SocketException {
- if (numReadable < 0 || numWritable < 0) {
- throw new IllegalArgumentException();
- }
-
- int total = numReadable + numWritable;
- if (total == 0) {
- return true;
- }
-
- return selectImpl(readFDs, writeFDs, numReadable, numWritable, flags, timeout);
- }
-
- static native boolean selectImpl(FileDescriptor[] readfd,
- FileDescriptor[] writefd, int cread, int cwirte, int[] flags,
- long timeout);
-
- public native int send(FileDescriptor fd, byte[] data, int offset, int length,
- int port, InetAddress inetAddress) throws IOException;
- public native int sendDirect(FileDescriptor fd, int address, int offset, int length,
- int port, InetAddress inetAddress) throws IOException;
-
- public native void sendUrgentData(FileDescriptor fd, byte value);
-
- public native void close(FileDescriptor fd) throws IOException;
-
- public native int write(FileDescriptor fd, byte[] data, int offset, int count)
- throws IOException;
-
- public native int writeDirect(FileDescriptor fd, int address, int offset, int count)
- throws IOException;
-}
diff --git a/luni/src/main/java/org/apache/harmony/luni/platform/Platform.java b/luni/src/main/java/org/apache/harmony/luni/platform/Platform.java
deleted file mode 100644
index db79a59..0000000
--- a/luni/src/main/java/org/apache/harmony/luni/platform/Platform.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.harmony.luni.platform;
-
-import dalvik.system.BlockGuard;
-import dalvik.system.VMStack;
-
-/**
- * The Platform class gives access to the low-level underlying capabilities of
- * the operating system.
- *
- * The platform is structured into operations on the process heap memory,
- * network subsystem, and file system through different OS components.
- *
- * OS components are 'dangerous' in that they pass through the calls and
- * arguments to the OS with very little checking, and as such may cause fatal
- * exceptions in the runtime. Access to the OS components is restricted to
- * trusted code running on the system classpath.
- *
- * @see INetworkSystem
- */
-public class Platform {
- // BlockGuard-policy-free threads should have no extra overhead, but for
- // now they do because ThreadLocal lookups will be done on most operations, which
- // should be relatively less than the speed of the operation.
- // TODO: measure & fix if needed.
- public static final INetworkSystem NETWORK =
- new BlockGuard.WrappedNetworkSystem(OSNetworkSystem.getOSNetworkSystem());
-}
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertFactoryImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertFactoryImpl.java
index ac76a89..7207002 100644
--- a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertFactoryImpl.java
+++ b/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertFactoryImpl.java
@@ -36,8 +36,8 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
+import libcore.io.Base64;
import libcore.io.Streams;
-import org.apache.harmony.luni.util.Base64;
import org.apache.harmony.security.asn1.ASN1Constants;
import org.apache.harmony.security.asn1.BerInputStream;
import org.apache.harmony.security.pkcs7.ContentInfo;
diff --git a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertImpl.java b/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertImpl.java
index a144727..35bcb39 100644
--- a/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertImpl.java
+++ b/luni/src/main/java/org/apache/harmony/security/provider/cert/X509CertImpl.java
@@ -90,10 +90,6 @@
// encoding of the certificate
private volatile byte[] encoding;
- //
- // ---------------------- Constructors -------------------------------
- //
-
/**
* Constructs the instance on the base of ASN.1 encoded
* form of X.509 certificate provided via stream parameter.
@@ -133,10 +129,6 @@
this((Certificate) Certificate.ASN1.decode(encoding));
}
- //
- // ----------------- Public methods implementations ------------------
- //
-
public void checkValidity()
throws CertificateExpiredException, CertificateNotYetValidException {
checkValidity(System.currentTimeMillis());
@@ -351,11 +343,7 @@
}
}
- //
- // ----- java.security.cert.Certificate methods implementations ------
- //
-
- public byte[] getEncoded() throws CertificateEncodingException {
+ @Override public byte[] getEncoded() throws CertificateEncodingException {
return getEncodedInternal().clone();
}
private byte[] getEncodedInternal() throws CertificateEncodingException {
@@ -366,7 +354,7 @@
return result;
}
- public PublicKey getPublicKey() {
+ @Override public PublicKey getPublicKey() {
PublicKey result = publicKey;
if (result == null) {
publicKey = result = tbsCert.getSubjectPublicKeyInfo().getPublicKey();
@@ -374,15 +362,14 @@
return result;
}
- public String toString() {
+ @Override public String toString() {
return certificate.toString();
}
- public void verify(PublicKey key)
- throws CertificateException, NoSuchAlgorithmException,
- InvalidKeyException, NoSuchProviderException,
- SignatureException {
- if (getSigAlgName().endsWith("withRSA")) {
+ @Override public void verify(PublicKey key)
+ throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
+ NoSuchProviderException, SignatureException {
+ if (getSigAlgName().endsWith("withRSA") || getSigAlgName().endsWith("WithRSAEncryption")) {
fastVerify(key);
return;
}
@@ -398,11 +385,11 @@
}
}
- public void verify(PublicKey key, String sigProvider)
- throws CertificateException, NoSuchAlgorithmException,
- InvalidKeyException, NoSuchProviderException,
- SignatureException {
- if (getSigAlgName().endsWith("withRSA") && sigProvider == null) {
+ @Override public void verify(PublicKey key, String sigProvider)
+ throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
+ NoSuchProviderException, SignatureException {
+ if ((getSigAlgName().endsWith("withRSA") || getSigAlgName().endsWith("WithRSAEncryption"))
+ && sigProvider == null) {
fastVerify(key);
return;
}
@@ -457,11 +444,7 @@
}
}
- //
- // ----- java.security.cert.X509Extension methods implementations ----
- //
-
- public Set<String> getNonCriticalExtensionOIDs() {
+ @Override public Set<String> getNonCriticalExtensionOIDs() {
if (extensions == null) {
return null;
}
@@ -469,7 +452,7 @@
return extensions.getNonCriticalExtensions();
}
- public Set<String> getCriticalExtensionOIDs() {
+ @Override public Set<String> getCriticalExtensionOIDs() {
if (extensions == null) {
return null;
}
@@ -477,7 +460,7 @@
return extensions.getCriticalExtensions();
}
- public byte[] getExtensionValue(String oid) {
+ @Override public byte[] getExtensionValue(String oid) {
if (extensions == null) {
return null;
}
@@ -486,7 +469,7 @@
return (ext == null) ? null : ext.getRawExtnValue();
}
- public boolean hasUnsupportedCriticalExtension() {
+ @Override public boolean hasUnsupportedCriticalExtension() {
if (extensions == null) {
return false;
}
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java
index 42d7f0e..28aeef5 100644
--- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java
+++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSessionImpl.java
@@ -313,9 +313,6 @@
/**
* Returns a string identifier of the crypto tools used in the actual SSL
* session. For example AES_256_WITH_MD5.
- *
- * @return an identifier for all the cryptographic algorithms used in the
- * actual SSL session.
*/
public String getCipherSuite() {
if (cipherSuite == null) {
@@ -331,10 +328,6 @@
/**
* Returns the standard version name of the SSL protocol used in all
* connections pertaining to this SSL session.
- *
- * @return the standard version name of the SSL protocol used in all
- * connections pertaining to this SSL session.
- *
*/
public String getProtocol() {
if (protocol == null) {
@@ -346,10 +339,6 @@
/**
* Returns the compression method name used in all connections
* pertaining to this SSL session.
- *
- * @return the compresison method used in all connections
- * pertaining to this SSL session.
- *
*/
public String getCompressionMethod() {
if (compressionMethod == null) {
diff --git a/luni/src/main/native/JniConstants.cpp b/luni/src/main/native/JniConstants.cpp
index 2681b47..ebdad0c 100644
--- a/luni/src/main/native/JniConstants.cpp
+++ b/luni/src/main/native/JniConstants.cpp
@@ -25,7 +25,6 @@
jclass JniConstants::byteClass;
jclass JniConstants::charsetICUClass;
jclass JniConstants::constructorClass;
-jclass JniConstants::datagramPacketClass;
jclass JniConstants::deflaterClass;
jclass JniConstants::doubleClass;
jclass JniConstants::errnoExceptionClass;
@@ -33,6 +32,7 @@
jclass JniConstants::fieldPositionIteratorClass;
jclass JniConstants::fileDescriptorClass;
jclass JniConstants::gaiExceptionClass;
+jclass JniConstants::inet6AddressClass;
jclass JniConstants::inetAddressClass;
jclass JniConstants::inetSocketAddressClass;
jclass JniConstants::inflaterClass;
@@ -53,6 +53,8 @@
jclass JniConstants::structFlockClass;
jclass JniConstants::structGroupReqClass;
jclass JniConstants::structLingerClass;
+jclass JniConstants::structPasswdClass;
+jclass JniConstants::structPollfdClass;
jclass JniConstants::structStatClass;
jclass JniConstants::structStatFsClass;
jclass JniConstants::structTimevalClass;
@@ -75,7 +77,6 @@
byteArrayClass = findClass(env, "[B");
charsetICUClass = findClass(env, "java/nio/charset/CharsetICU");
constructorClass = findClass(env, "java/lang/reflect/Constructor");
- datagramPacketClass = findClass(env, "java/net/DatagramPacket");
deflaterClass = findClass(env, "java/util/zip/Deflater");
doubleClass = findClass(env, "java/lang/Double");
errnoExceptionClass = findClass(env, "libcore/io/ErrnoException");
@@ -83,6 +84,7 @@
fieldPositionIteratorClass = findClass(env, "libcore/icu/NativeDecimalFormat$FieldPositionIterator");
fileDescriptorClass = findClass(env, "java/io/FileDescriptor");
gaiExceptionClass = findClass(env, "libcore/io/GaiException");
+ inet6AddressClass = findClass(env, "java/net/Inet6Address");
inetAddressClass = findClass(env, "java/net/InetAddress");
inetSocketAddressClass = findClass(env, "java/net/InetSocketAddress");
inflaterClass = findClass(env, "java/util/zip/Inflater");
@@ -103,6 +105,8 @@
structFlockClass = findClass(env, "libcore/io/StructFlock");
structGroupReqClass = findClass(env, "libcore/io/StructGroupReq");
structLingerClass = findClass(env, "libcore/io/StructLinger");
+ structPasswdClass = findClass(env, "libcore/io/StructPasswd");
+ structPollfdClass = findClass(env, "libcore/io/StructPollfd");
structStatClass = findClass(env, "libcore/io/StructStat");
structStatFsClass = findClass(env, "libcore/io/StructStatFs");
structTimevalClass = findClass(env, "libcore/io/StructTimeval");
diff --git a/luni/src/main/native/JniConstants.h b/luni/src/main/native/JniConstants.h
index af5056b..db52775 100644
--- a/luni/src/main/native/JniConstants.h
+++ b/luni/src/main/native/JniConstants.h
@@ -47,7 +47,6 @@
static jclass byteClass;
static jclass charsetICUClass;
static jclass constructorClass;
- static jclass datagramPacketClass;
static jclass deflaterClass;
static jclass doubleClass;
static jclass errnoExceptionClass;
@@ -55,6 +54,7 @@
static jclass fieldPositionIteratorClass;
static jclass fileDescriptorClass;
static jclass gaiExceptionClass;
+ static jclass inet6AddressClass;
static jclass inetAddressClass;
static jclass inetSocketAddressClass;
static jclass inflaterClass;
@@ -75,6 +75,8 @@
static jclass structFlockClass;
static jclass structGroupReqClass;
static jclass structLingerClass;
+ static jclass structPasswdClass;
+ static jclass structPollfdClass;
static jclass structStatClass;
static jclass structStatFsClass;
static jclass structTimevalClass;
diff --git a/luni/src/main/native/NetworkUtilities.cpp b/luni/src/main/native/NetworkUtilities.cpp
index 26df02d..1b2c11e 100644
--- a/luni/src/main/native/NetworkUtilities.cpp
+++ b/luni/src/main/native/NetworkUtilities.cpp
@@ -19,6 +19,7 @@
#include "NetworkUtilities.h"
#include "JNIHelp.h"
#include "JniConstants.h"
+#include "ScopedLocalRef.h"
#include <arpa/inet.h>
#include <fcntl.h>
@@ -26,42 +27,8 @@
#include <string.h>
#include <sys/socket.h>
-static bool byteArrayToSocketAddress(JNIEnv* env, jbyteArray byteArray, int port, sockaddr_storage* ss) {
- if (byteArray == NULL) {
- jniThrowNullPointerException(env, NULL);
- return false;
- }
-
- // Convert the IP address bytes to the proper IP address type.
- size_t addressLength = env->GetArrayLength(byteArray);
- memset(ss, 0, sizeof(*ss));
- if (addressLength == 4) {
- // IPv4 address.
- sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(ss);
- sin->sin_family = AF_INET;
- sin->sin_port = htons(port);
- jbyte* dst = reinterpret_cast<jbyte*>(&sin->sin_addr.s_addr);
- env->GetByteArrayRegion(byteArray, 0, 4, dst);
- } else if (addressLength == 16) {
- // IPv6 address.
- sockaddr_in6* sin6 = reinterpret_cast<sockaddr_in6*>(ss);
- sin6->sin6_family = AF_INET6;
- sin6->sin6_port = htons(port);
- jbyte* dst = reinterpret_cast<jbyte*>(&sin6->sin6_addr.s6_addr);
- env->GetByteArrayRegion(byteArray, 0, 16, dst);
- } else {
- // We can't throw SocketException. We aren't meant to see bad addresses, so seeing one
- // really does imply an internal error.
- // TODO: fix the code (native and Java) so we don't paint ourselves into this corner.
- jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
- "byteArrayToSocketAddress bad array length (%i)", addressLength);
- return false;
- }
- return true;
-}
-
-static jbyteArray socketAddressToByteArray(JNIEnv* env, const sockaddr_storage* ss) {
- // Convert IPv4-mapped addresses to IPv4 addresses.
+jobject sockaddrToInetAddress(JNIEnv* env, const sockaddr_storage* ss, jint* port) {
+ // Convert IPv4-mapped IPv6 addresses to IPv4 addresses.
// The RI states "Java will never return an IPv4-mapped address".
sockaddr_storage tmp;
memset(&tmp, 0, sizeof(tmp));
@@ -80,57 +47,117 @@
const void* rawAddress;
size_t addressLength;
+ int sin_port;
+ int scope_id = 0;
if (ss->ss_family == AF_INET) {
const sockaddr_in* sin = reinterpret_cast<const sockaddr_in*>(ss);
rawAddress = &sin->sin_addr.s_addr;
addressLength = 4;
+ sin_port = ntohs(sin->sin_port);
} else if (ss->ss_family == AF_INET6) {
const sockaddr_in6* sin6 = reinterpret_cast<const sockaddr_in6*>(ss);
rawAddress = &sin6->sin6_addr.s6_addr;
addressLength = 16;
+ sin_port = ntohs(sin6->sin6_port);
+ scope_id = sin6->sin6_scope_id;
} else {
// We can't throw SocketException. We aren't meant to see bad addresses, so seeing one
// really does imply an internal error.
- // TODO: fix the code (native and Java) so we don't paint ourselves into this corner.
jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
- "socketAddressToByteArray bad ss_family (%i)", ss->ss_family);
+ "sockaddrToInetAddress bad ss_family: %i", ss->ss_family);
return NULL;
}
+ if (port != NULL) {
+ *port = sin_port;
+ }
jbyteArray byteArray = env->NewByteArray(addressLength);
if (byteArray == NULL) {
return NULL;
}
env->SetByteArrayRegion(byteArray, 0, addressLength, reinterpret_cast<const jbyte*>(rawAddress));
- return byteArray;
-}
-static jobject byteArrayToInetAddress(JNIEnv* env, jbyteArray byteArray) {
- if (byteArray == NULL) {
- return NULL;
- }
- jmethodID getByAddressMethod = env->GetStaticMethodID(JniConstants::inetAddressClass,
- "getByAddress", "([B)Ljava/net/InetAddress;");
+ static jmethodID getByAddressMethod = env->GetStaticMethodID(JniConstants::inetAddressClass,
+ "getByAddress", "(Ljava/lang/String;[BI)Ljava/net/InetAddress;");
if (getByAddressMethod == NULL) {
return NULL;
}
- return env->CallStaticObjectMethod(JniConstants::inetAddressClass, getByAddressMethod, byteArray);
+ return env->CallStaticObjectMethod(JniConstants::inetAddressClass, getByAddressMethod,
+ NULL, byteArray, scope_id);
}
-jobject socketAddressToInetAddress(JNIEnv* env, const sockaddr_storage* ss) {
- jbyteArray byteArray = socketAddressToByteArray(env, ss);
- return byteArrayToInetAddress(env, byteArray);
-}
+static bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage* ss, bool map) {
+ memset(ss, 0, sizeof(*ss));
-bool inetAddressToSocketAddress(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage* ss) {
- // Get the byte array that stores the IP address bytes in the InetAddress.
if (inetAddress == NULL) {
jniThrowNullPointerException(env, NULL);
return false;
}
- static jfieldID fid = env->GetFieldID(JniConstants::inetAddressClass, "ipaddress", "[B");
- jbyteArray addressBytes = reinterpret_cast<jbyteArray>(env->GetObjectField(inetAddress, fid));
- return byteArrayToSocketAddress(env, addressBytes, port, ss);
+
+ // Get the address family.
+ static jfieldID familyFid = env->GetFieldID(JniConstants::inetAddressClass, "family", "I");
+ ss->ss_family = env->GetIntField(inetAddress, familyFid);
+ if (ss->ss_family == AF_UNSPEC) {
+ return true; // Job done!
+ }
+
+ // Check this is an address family we support.
+ if (ss->ss_family != AF_INET && ss->ss_family != AF_INET6) {
+ jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+ "inetAddressToSockaddr bad family: %i", ss->ss_family);
+ return false;
+ }
+
+ // Get the byte array that stores the IP address bytes in the InetAddress.
+ static jfieldID bytesFid = env->GetFieldID(JniConstants::inetAddressClass, "ipaddress", "[B");
+ ScopedLocalRef<jbyteArray> addressBytes(env, reinterpret_cast<jbyteArray>(env->GetObjectField(inetAddress, bytesFid)));
+ if (addressBytes.get() == NULL) {
+ jniThrowNullPointerException(env, NULL);
+ return false;
+ }
+
+ // We use AF_INET6 sockets, so we want an IPv6 address (which may be a IPv4-mapped address).
+ sockaddr_in6* sin6 = reinterpret_cast<sockaddr_in6*>(ss);
+ sin6->sin6_port = htons(port);
+ if (ss->ss_family == AF_INET6) {
+ // IPv6 address. Copy the bytes...
+ jbyte* dst = reinterpret_cast<jbyte*>(&sin6->sin6_addr.s6_addr);
+ env->GetByteArrayRegion(addressBytes.get(), 0, 16, dst);
+ // ...and set the scope id...
+ static jfieldID scopeFid = env->GetFieldID(JniConstants::inet6AddressClass, "scope_id", "I");
+ sin6->sin6_scope_id = env->GetIntField(inetAddress, scopeFid);
+ return true;
+ }
+
+ // Deal with Inet4Address instances.
+ if (map) {
+ // We should represent this Inet4Address as an IPv4-mapped IPv6 sockaddr_in6.
+ // Change the family...
+ sin6->sin6_family = AF_INET6;
+ // Copy the bytes...
+ jbyte* dst = reinterpret_cast<jbyte*>(&sin6->sin6_addr.s6_addr[12]);
+ env->GetByteArrayRegion(addressBytes.get(), 0, 4, dst);
+ // INADDR_ANY and in6addr_any are both all-zeros...
+ if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+ // ...but all other IPv4-mapped addresses are ::ffff:a.b.c.d, so insert the ffff...
+ memset(&(sin6->sin6_addr.s6_addr[10]), 0xff, 2);
+ }
+ } else {
+ // We should represent this Inet4Address as an IPv4 sockaddr_in.
+ sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(ss);
+ sin->sin_port = htons(port);
+ jbyte* dst = reinterpret_cast<jbyte*>(&sin->sin_addr.s_addr);
+ env->GetByteArrayRegion(addressBytes.get(), 0, 4, dst);
+ }
+ return true;
+}
+
+bool inetAddressToSockaddr_getnameinfo(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage* ss) {
+ return inetAddressToSockaddr(env, inetAddress, port, ss, false);
+}
+
+bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage* ss) {
+ return inetAddressToSockaddr(env, inetAddress, port, ss, true);
}
bool setBlocking(int fd, bool blocking) {
diff --git a/luni/src/main/native/NetworkUtilities.h b/luni/src/main/native/NetworkUtilities.h
index 5e5b19e..8b1b6f1 100644
--- a/luni/src/main/native/NetworkUtilities.h
+++ b/luni/src/main/native/NetworkUtilities.h
@@ -17,11 +17,20 @@
#include "jni.h"
#include <sys/socket.h>
-// Convert from sockaddr_storage to InetAddress.
-jobject socketAddressToInetAddress(JNIEnv* env, const sockaddr_storage* ss);
+// Convert from sockaddr_storage to InetAddress and an optional int port.
+jobject sockaddrToInetAddress(JNIEnv* env, const sockaddr_storage* ss, int* port);
-// Convert from InetAddress to sockaddr_storage.
-bool inetAddressToSocketAddress(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage* ss);
+// Convert from InetAddress to sockaddr_storage. An Inet4Address will be converted to
+// an IPv4-mapped AF_INET6 sockaddr_in6. This is what you want if you're about to perform an
+// operation on a socket, since all our sockets are AF_INET6.
+bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage* ss);
+
+// Convert from InetAddress to sockaddr_storage. An Inet6Address will be converted to
+// a sockaddr_in6 while an Inet4Address will be converted to a sockaddr_in. This is
+// probably only useful for getnameinfo(2), where we'll be presenting the result to
+// the user and the user may actually care whether the original address was pure IPv4
+// or an IPv4-mapped IPv6 address.
+bool inetAddressToSockaddr_getnameinfo(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage* ss);
diff --git a/luni/src/main/native/Register.cpp b/luni/src/main/native/Register.cpp
index e518944..a637a73 100644
--- a/luni/src/main/native/Register.cpp
+++ b/luni/src/main/native/Register.cpp
@@ -29,6 +29,7 @@
extern int register_java_lang_ProcessManager(JNIEnv* env);
extern int register_java_lang_RealToString(JNIEnv* env);
extern int register_java_lang_StrictMath(JNIEnv* env);
+extern int register_java_lang_StringToReal(JNIEnv* env);
extern int register_java_lang_System(JNIEnv* env);
extern int register_java_math_NativeBN(JNIEnv* env);
extern int register_java_nio_ByteOrder(JNIEnv* env);
@@ -49,13 +50,12 @@
extern int register_libcore_icu_NativeNormalizer(JNIEnv* env);
extern int register_libcore_icu_NativePluralRules(JNIEnv* env);
extern int register_libcore_icu_TimeZones(JNIEnv* env);
+extern int register_libcore_io_AsynchronousCloseMonitor(JNIEnv* env);
extern int register_libcore_io_Memory(JNIEnv* env);
extern int register_libcore_io_OsConstants(JNIEnv* env);
extern int register_libcore_io_Posix(JNIEnv* env);
extern int register_libcore_net_RawSocket(JNIEnv* env);
extern int register_org_apache_harmony_dalvik_NativeTestTarget(JNIEnv* env);
-extern int register_org_apache_harmony_luni_platform_OSNetworkSystem(JNIEnv* env);
-extern int register_org_apache_harmony_luni_util_fltparse(JNIEnv* env);
extern int register_org_apache_harmony_xml_ExpatParser(JNIEnv* env);
extern int register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(JNIEnv* env);
@@ -74,6 +74,7 @@
register_java_lang_ProcessManager(env) != -1 &&
register_java_lang_RealToString(env) != -1 &&
register_java_lang_StrictMath(env) != -1 &&
+ register_java_lang_StringToReal(env) != -1 &&
register_java_lang_System(env) != -1 &&
register_java_math_NativeBN(env) != -1 &&
register_java_nio_ByteOrder(env) != -1 &&
@@ -94,13 +95,12 @@
register_libcore_icu_NativeNormalizer(env) != -1 &&
register_libcore_icu_NativePluralRules(env) != -1 &&
register_libcore_icu_TimeZones(env) != -1 &&
+ register_libcore_io_AsynchronousCloseMonitor(env) != -1 &&
register_libcore_io_Memory(env) != -1 &&
register_libcore_io_OsConstants(env) != -1 &&
register_libcore_io_Posix(env) != -1 &&
register_libcore_net_RawSocket(env) != -1 &&
register_org_apache_harmony_dalvik_NativeTestTarget(env) != -1 &&
- register_org_apache_harmony_luni_platform_OSNetworkSystem(env) != -1 &&
- register_org_apache_harmony_luni_util_fltparse(env) != -1 &&
register_org_apache_harmony_xml_ExpatParser(env) != -1 &&
register_org_apache_harmony_xnet_provider_jsse_NativeCrypto(env) != -1 &&
true;
diff --git a/luni/src/main/native/java_lang_ProcessManager.cpp b/luni/src/main/native/java_lang_ProcessManager.cpp
index 2a1638f..3648a8f 100644
--- a/luni/src/main/native/java_lang_ProcessManager.cpp
+++ b/luni/src/main/native/java_lang_ProcessManager.cpp
@@ -18,10 +18,8 @@
#include <sys/resource.h>
#include <sys/types.h>
-#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
-#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
@@ -29,90 +27,9 @@
#include "jni.h"
#include "JNIHelp.h"
#include "JniConstants.h"
+#include "ScopedLocalRef.h"
#include "utils/Log.h"
-/*
- * These are constants shared with the higher level code in
- * ProcessManager.java.
- */
-#define WAIT_STATUS_UNKNOWN (-1) // unknown child status
-#define WAIT_STATUS_NO_CHILDREN (-2) // no children to wait for
-#define WAIT_STATUS_STRANGE_ERRNO (-3) // observed an undocumented errno
-
-/**
- * Loops indefinitely and calls ProcessManager.onExit() when children exit.
- */
-static void ProcessManager_watchChildren(JNIEnv* env, jclass processManagerClass, jobject processManager) {
- static jmethodID onExitMethod = env->GetMethodID(processManagerClass, "onExit", "(II)V");
- if (onExitMethod == NULL) {
- return;
- }
-
- while (true) {
- // Wait for children in our process group.
- int status;
- pid_t pid = waitpid(0, &status, 0);
-
- if (pid >= 0) {
- // Extract real status.
- if (WIFEXITED(status)) {
- status = WEXITSTATUS(status);
- } else if (WIFSIGNALED(status)) {
- status = WTERMSIG(status);
- } else if (WIFSTOPPED(status)) {
- status = WSTOPSIG(status);
- } else {
- status = WAIT_STATUS_UNKNOWN;
- }
- } else {
- /*
- * The pid should be -1 already, but force it here just in case
- * we somehow end up with some other negative value.
- */
- pid = -1;
-
- switch (errno) {
- case ECHILD: {
- /*
- * Expected errno: There are no children to wait()
- * for. The callback will sleep until it is
- * informed of another child coming to life.
- */
- status = WAIT_STATUS_NO_CHILDREN;
- break;
- }
- case EINTR: {
- /*
- * An unblocked signal came in while waiting; just
- * retry the wait().
- */
- continue;
- }
- default: {
- /*
- * Unexpected errno, so squawk! Note: Per the
- * Linux docs, there are no errnos defined for
- * wait() other than the two that are handled
- * immediately above.
- */
- LOGE("Error %d calling wait(): %s", errno, strerror(errno));
- status = WAIT_STATUS_STRANGE_ERRNO;
- break;
- }
- }
- }
-
- env->CallVoidMethod(processManager, onExitMethod, pid, status);
- if (env->ExceptionOccurred()) {
- /*
- * The callback threw, so break out of the loop and return,
- * letting the exception percolate up.
- */
- break;
- }
- }
-}
-
/** Close all open fds > 2 (i.e. everything but stdin/out/err), != skipFd. */
static void closeNonStandardFds(int skipFd1, int skipFd2) {
// TODO: rather than close all these non-open files, we could look in /proc/self/fd.
@@ -274,11 +191,11 @@
jsize length = env->GetArrayLength(javaArray);
char** array = new char*[length + 1];
array[length] = 0;
- for (jsize index = 0; index < length; index++) {
- jstring javaEntry = (jstring) env->GetObjectArrayElement(javaArray, index);
+ for (jsize i = 0; i < length; ++i) {
+ ScopedLocalRef<jstring> javaEntry(env, reinterpret_cast<jstring>(env->GetObjectArrayElement(javaArray, i)));
// We need to pass these strings to const-unfriendly code.
- char* entry = const_cast<char*>(env->GetStringUTFChars(javaEntry, NULL));
- array[index] = entry;
+ char* entry = const_cast<char*>(env->GetStringUTFChars(javaEntry.get(), NULL));
+ array[i] = entry;
}
return array;
@@ -291,9 +208,9 @@
}
jsize length = env->GetArrayLength(javaArray);
- for (jsize index = 0; index < length; index++) {
- jstring javaEntry = reinterpret_cast<jstring>(env->GetObjectArrayElement(javaArray, index));
- env->ReleaseStringUTFChars(javaEntry, array[index]);
+ for (jsize i = 0; i < length; ++i) {
+ ScopedLocalRef<jstring> javaEntry(env, reinterpret_cast<jstring>(env->GetObjectArrayElement(javaArray, i)));
+ env->ReleaseStringUTFChars(javaEntry.get(), array[i]);
}
delete[] array;
@@ -346,7 +263,6 @@
}
static JNINativeMethod methods[] = {
- NATIVE_METHOD(ProcessManager, watchChildren, "(Ljava/lang/ProcessManager;)V"),
NATIVE_METHOD(ProcessManager, exec, "([Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Z)I"),
};
int register_java_lang_ProcessManager(JNIEnv* env) {
diff --git a/luni/src/main/native/java_lang_RealToString.cpp b/luni/src/main/native/java_lang_RealToString.cpp
index f6d50b2..835151e 100644
--- a/luni/src/main/native/java_lang_RealToString.cpp
+++ b/luni/src/main/native/java_lang_RealToString.cpp
@@ -23,6 +23,7 @@
#include "JNIHelp.h"
#include "JniConstants.h"
+#include "ScopedLocalRef.h"
#include "ScopedPrimitiveArray.h"
#include "cbigint.h"
@@ -168,8 +169,8 @@
}
static jfieldID digitsFid = env->GetFieldID(JniConstants::realToStringClass, "digits", "[I");
- jintArray javaDigits = reinterpret_cast<jintArray>(env->GetObjectField(obj, digitsFid));
- ScopedIntArrayRW digits(env, javaDigits);
+ ScopedLocalRef<jintArray> javaDigits(env, reinterpret_cast<jintArray>(env->GetObjectField(obj, digitsFid)));
+ ScopedIntArrayRW digits(env, javaDigits.get());
if (digits.get() == NULL) {
return;
}
diff --git a/luni/src/main/native/org_apache_harmony_luni_util_FloatingPointParser.cpp b/luni/src/main/native/java_lang_StringToReal.cpp
similarity index 97%
rename from luni/src/main/native/org_apache_harmony_luni_util_FloatingPointParser.cpp
rename to luni/src/main/native/java_lang_StringToReal.cpp
index 70a6d4b..06aa076 100644
--- a/luni/src/main/native/org_apache_harmony_luni_util_FloatingPointParser.cpp
+++ b/luni/src/main/native/java_lang_StringToReal.cpp
@@ -989,7 +989,7 @@
return z;
}
-static jfloat FloatingPointParser_parseFltImpl(JNIEnv* env, jclass, jstring s, jint e) {
+static jfloat StringToReal_parseFltImpl(JNIEnv* env, jclass, jstring s, jint e) {
ScopedUtfChars str(env, s);
if (str.c_str() == NULL) {
return 0.0;
@@ -997,7 +997,7 @@
return createFloat(env, str.c_str(), e);
}
-static jdouble FloatingPointParser_parseDblImpl(JNIEnv* env, jclass, jstring s, jint e) {
+static jdouble StringToReal_parseDblImpl(JNIEnv* env, jclass, jstring s, jint e) {
ScopedUtfChars str(env, s);
if (str.c_str() == NULL) {
return 0.0;
@@ -1006,10 +1006,9 @@
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(FloatingPointParser, parseFltImpl, "(Ljava/lang/String;I)F"),
- NATIVE_METHOD(FloatingPointParser, parseDblImpl, "(Ljava/lang/String;I)D"),
+ NATIVE_METHOD(StringToReal, parseFltImpl, "(Ljava/lang/String;I)F"),
+ NATIVE_METHOD(StringToReal, parseDblImpl, "(Ljava/lang/String;I)D"),
};
-int register_org_apache_harmony_luni_util_fltparse(JNIEnv* env) {
- return jniRegisterNativeMethods(env, "org/apache/harmony/luni/util/FloatingPointParser",
- gMethods, NELEM(gMethods));
+int register_java_lang_StringToReal(JNIEnv* env) {
+ return jniRegisterNativeMethods(env, "java/lang/StringToReal", gMethods, NELEM(gMethods));
}
diff --git a/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp b/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp
new file mode 100644
index 0000000..d3fdabf
--- /dev/null
+++ b/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AsynchronousCloseMonitor"
+
+#include "AsynchronousSocketCloseMonitor.h"
+#include "JNIHelp.h"
+#include "JniConstants.h"
+#include "jni.h"
+
+static void AsynchronousCloseMonitor_signalBlockedThreads(JNIEnv* env, jclass, jobject javaFd) {
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ AsynchronousSocketCloseMonitor::signalBlockedThreads(fd);
+}
+
+static JNINativeMethod gMethods[] = {
+ NATIVE_METHOD(AsynchronousCloseMonitor, signalBlockedThreads, "(Ljava/io/FileDescriptor;)V"),
+};
+
+int register_libcore_io_AsynchronousCloseMonitor(JNIEnv* env) {
+ AsynchronousSocketCloseMonitor::init();
+ return jniRegisterNativeMethods(env, "libcore/io/AsynchronousCloseMonitor", gMethods, NELEM(gMethods));
+}
diff --git a/luni/src/main/native/libcore_io_OsConstants.cpp b/luni/src/main/native/libcore_io_OsConstants.cpp
index dd8ebf2..942cfce 100644
--- a/luni/src/main/native/libcore_io_OsConstants.cpp
+++ b/luni/src/main/native/libcore_io_OsConstants.cpp
@@ -18,7 +18,6 @@
#include "JNIHelp.h"
#include "JniConstants.h"
-#include "ScopedLocalRef.h"
#include <errno.h>
#include <fcntl.h>
@@ -26,6 +25,7 @@
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
+#include <poll.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/ioctl.h>
@@ -236,6 +236,16 @@
initConstant(env, c, "O_SYNC", O_SYNC);
initConstant(env, c, "O_TRUNC", O_TRUNC);
initConstant(env, c, "O_WRONLY", O_WRONLY);
+ initConstant(env, c, "POLLERR", POLLERR);
+ initConstant(env, c, "POLLHUP", POLLHUP);
+ initConstant(env, c, "POLLIN", POLLIN);
+ initConstant(env, c, "POLLNVAL", POLLNVAL);
+ initConstant(env, c, "POLLOUT", POLLOUT);
+ initConstant(env, c, "POLLPRI", POLLPRI);
+ initConstant(env, c, "POLLRDBAND", POLLRDBAND);
+ initConstant(env, c, "POLLRDNORM", POLLRDNORM);
+ initConstant(env, c, "POLLWRBAND", POLLWRBAND);
+ initConstant(env, c, "POLLWRNORM", POLLWRNORM);
initConstant(env, c, "PROT_EXEC", PROT_EXEC);
initConstant(env, c, "PROT_NONE", PROT_NONE);
initConstant(env, c, "PROT_READ", PROT_READ);
diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp
index aa69eac..5b02862 100644
--- a/luni/src/main/native/libcore_io_Posix.cpp
+++ b/luni/src/main/native/libcore_io_Posix.cpp
@@ -16,11 +16,13 @@
#define LOG_TAG "Posix"
+#include "AsynchronousSocketCloseMonitor.h"
#include "JNIHelp.h"
#include "JniConstants.h"
#include "JniException.h"
#include "NetworkUtilities.h"
#include "ScopedBytes.h"
+#include "ScopedLocalRef.h"
#include "ScopedPrimitiveArray.h"
#include "ScopedUtfChars.h"
#include "StaticAssert.h"
@@ -34,6 +36,8 @@
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/in.h>
+#include <poll.h>
+#include <pwd.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/ioctl.h>
@@ -47,8 +51,13 @@
#include <sys/uio.h>
#include <sys/utsname.h>
#include <sys/vfs.h> // Bionic doesn't have <sys/statvfs.h>
+#include <sys/wait.h>
#include <unistd.h>
+#define TO_JAVA_STRING(NAME, EXP) \
+ jstring NAME = env->NewStringUTF(EXP); \
+ if (NAME == NULL) return NULL;
+
struct addrinfo_deleter {
void operator()(addrinfo* p) const {
if (p != NULL) { // bionic's freeaddrinfo(3) crashes when passed NULL.
@@ -57,6 +66,43 @@
}
};
+/**
+ * Used to retry syscalls that can return EINTR. This differs from TEMP_FAILURE_RETRY in that
+ * it also considers the case where the reason for failure is that another thread called
+ * Socket.close.
+ *
+ * Assumes 'JNIEnv* env' and 'jobject javaFd' (which is a java.io.FileDescriptor) are in scope.
+ *
+ * Returns the result of 'exp', though a Java exception will be pending if the result is -1.
+ *
+ * Correct usage looks like this:
+ *
+ * void Posix_syscall(JNIEnv* env, jobject javaFd, ...) {
+ * ...
+ * int fd;
+ * NET_FAILURE_RETRY("syscall", syscall(fd, ...)); // Throws on error.
+ * }
+ */
+#define NET_FAILURE_RETRY(syscall_name, exp) ({ \
+ typeof (exp) _rc = -1; \
+ do { \
+ { \
+ fd = jniGetFDFromFileDescriptor(env, javaFd); \
+ AsynchronousSocketCloseMonitor monitor(fd); \
+ _rc = (exp); \
+ } \
+ if (_rc == -1) { \
+ if (jniGetFDFromFileDescriptor(env, javaFd) == -1) { \
+ jniThrowException(env, "java/net/SocketException", "Socket closed"); \
+ break; \
+ } else if (errno != EINTR) { \
+ throwErrnoException(env, syscall_name); \
+ break; \
+ } \
+ } \
+ } while (_rc == -1); \
+ _rc; })
+
static void throwException(JNIEnv* env, jclass exceptionClass, jmethodID ctor3, jmethodID ctor2,
const char* functionName, int error) {
jthrowable cause = NULL;
@@ -132,8 +178,9 @@
return false;
}
// TODO: Linux actually has a 1024 buffer limit. glibc works around this, and we should too.
+ // TODO: you can query the limit at runtime with sysconf(_SC_IOV_MAX).
for (size_t i = 0; i < mBufferCount; ++i) {
- jobject buffer = mEnv->GetObjectArrayElement(javaBuffers, i);
+ jobject buffer = mEnv->GetObjectArrayElement(javaBuffers, i); // We keep this local ref.
mScopedBuffers.push_back(new ScopedT(mEnv, buffer));
jbyte* ptr = const_cast<jbyte*>(mScopedBuffers.back()->get());
if (ptr == NULL) {
@@ -169,8 +216,10 @@
std::vector<ScopedT*> mScopedBuffers;
};
-static jobject makeInetSocketAddress(JNIEnv* env, const sockaddr_storage* ss, int port) {
- jobject inetAddress = socketAddressToInetAddress(env, ss);
+static jobject makeSocketAddress(JNIEnv* env, const sockaddr_storage* ss) {
+ // TODO: support AF_UNIX and AF_UNSPEC (and other families?)
+ jint port;
+ jobject inetAddress = sockaddrToInetAddress(env, ss, &port);
if (inetAddress == NULL) {
return NULL;
}
@@ -179,16 +228,14 @@
return env->NewObject(JniConstants::inetSocketAddressClass, ctor, inetAddress, port);
}
-static jobject makeSocketAddress(JNIEnv* env, const sockaddr_storage* ss) {
- if (ss->ss_family == AF_INET) {
- int port = ntohs(reinterpret_cast<const sockaddr_in*>(ss)->sin_port);
- return makeInetSocketAddress(env, ss, port);
- } else if (ss->ss_family == AF_INET6) {
- int port = ntohs(reinterpret_cast<const sockaddr_in6*>(ss)->sin6_port);
- return makeInetSocketAddress(env, ss, port);
- }
- // TODO: support AF_UNIX and AF_UNSPEC, and have some other behavior for other families
- return NULL;
+static jobject makeStructPasswd(JNIEnv* env, const struct passwd& pw) {
+ TO_JAVA_STRING(pw_name, pw.pw_name);
+ TO_JAVA_STRING(pw_dir, pw.pw_dir);
+ TO_JAVA_STRING(pw_shell, pw.pw_shell);
+ static jmethodID ctor = env->GetMethodID(JniConstants::structPasswdClass, "<init>",
+ "(Ljava/lang/String;IILjava/lang/String;Ljava/lang/String;)V");
+ return env->NewObject(JniConstants::structPasswdClass, ctor,
+ pw_name, static_cast<jint>(pw.pw_uid), static_cast<jint>(pw.pw_gid), pw_dir, pw_shell);
}
static jobject makeStructStat(JNIEnv* env, const struct stat& sb) {
@@ -230,17 +277,11 @@
}
static jobject makeStructUtsname(JNIEnv* env, const struct utsname& buf) {
-#define TO_JAVA_STRING(NAME) \
- jstring NAME = env->NewStringUTF(buf. NAME); \
- if (NAME == NULL) return NULL;
-
- TO_JAVA_STRING(sysname);
- TO_JAVA_STRING(nodename);
- TO_JAVA_STRING(release);
- TO_JAVA_STRING(version);
- TO_JAVA_STRING(machine);
-#undef TO_JAVA_STRING
-
+ TO_JAVA_STRING(sysname, buf.sysname);
+ TO_JAVA_STRING(nodename, buf.nodename);
+ TO_JAVA_STRING(release, buf.release);
+ TO_JAVA_STRING(version, buf.version);
+ TO_JAVA_STRING(machine, buf.machine);
static jmethodID ctor = env->GetMethodID(JniConstants::structUtsnameClass, "<init>",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
return env->NewObject(JniConstants::structUtsnameClass, ctor,
@@ -258,6 +299,23 @@
return true;
}
+static bool fillInetSocketAddress(JNIEnv* env, jint rc, jobject javaInetSocketAddress, const sockaddr_storage* ss) {
+ if (rc == -1 || javaInetSocketAddress == NULL) {
+ return true;
+ }
+ // Fill out the passed-in InetSocketAddress with the sender's IP address and port number.
+ jint port;
+ jobject sender = sockaddrToInetAddress(env, ss, &port);
+ if (sender == NULL) {
+ return false;
+ }
+ static jfieldID addressFid = env->GetFieldID(JniConstants::inetSocketAddressClass, "addr", "Ljava/net/InetAddress;");
+ static jfieldID portFid = env->GetFieldID(JniConstants::inetSocketAddressClass, "port", "I");
+ env->SetObjectField(javaInetSocketAddress, addressFid, sender);
+ env->SetIntField(javaInetSocketAddress, portFid, port);
+ return true;
+}
+
static jobject doStat(JNIEnv* env, jstring javaPath, bool isLstat) {
ScopedUtfChars path(env, javaPath);
if (path.c_str() == NULL) {
@@ -273,6 +331,62 @@
return makeStructStat(env, sb);
}
+class Passwd {
+public:
+ Passwd(JNIEnv* env) : mEnv(env), mResult(NULL) {
+ mBufferSize = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (mBufferSize == -1UL) {
+ // We're probably on bionic, where 1KiB should be enough for anyone.
+ // TODO: fix bionic to return 1024 like glibc.
+ mBufferSize = 1024;
+ }
+ mBuffer.reset(new char[mBufferSize]);
+ }
+
+ jobject getpwnam(const char* name) {
+ return process("getpwnam_r", getpwnam_r(name, &mPwd, mBuffer.get(), mBufferSize, &mResult));
+ }
+
+ jobject getpwuid(uid_t uid) {
+ return process("getpwuid_r", getpwuid_r(uid, &mPwd, mBuffer.get(), mBufferSize, &mResult));
+ }
+
+ struct passwd* get() {
+ return mResult;
+ }
+
+private:
+ jobject process(const char* syscall, int error) {
+ if (mResult == NULL) {
+ errno = error;
+ throwErrnoException(mEnv, syscall);
+ return NULL;
+ }
+ return makeStructPasswd(mEnv, *mResult);
+ }
+
+ JNIEnv* mEnv;
+ UniquePtr<char[]> mBuffer;
+ size_t mBufferSize;
+ struct passwd mPwd;
+ struct passwd* mResult;
+};
+
+static jobject Posix_accept(JNIEnv* env, jobject, jobject javaFd, jobject javaInetSocketAddress) {
+ sockaddr_storage ss;
+ socklen_t sl = sizeof(ss);
+ memset(&ss, 0, sizeof(ss));
+ int fd;
+ sockaddr* peer = (javaInetSocketAddress != NULL) ? reinterpret_cast<sockaddr*>(&ss) : NULL;
+ socklen_t* peerLength = (javaInetSocketAddress != NULL) ? &sl : 0;
+ jint clientFd = NET_FAILURE_RETRY("accept", accept(fd, peer, peerLength));
+ if (clientFd == -1 || !fillInetSocketAddress(env, clientFd, javaInetSocketAddress, &ss)) {
+ close(clientFd);
+ return NULL;
+ }
+ return (clientFd != -1) ? jniCreateFileDescriptor(env, clientFd) : NULL;
+}
+
static jboolean Posix_access(JNIEnv* env, jobject, jstring javaPath, jint mode) {
ScopedUtfChars path(env, javaPath);
if (path.c_str() == NULL) {
@@ -285,6 +399,16 @@
return (rc == 0);
}
+static void Posix_bind(JNIEnv* env, jobject, jobject javaFd, jobject javaAddress, jint port) {
+ sockaddr_storage ss;
+ if (!inetAddressToSockaddr(env, javaAddress, port, &ss)) {
+ return;
+ }
+ int fd;
+ const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss);
+ NET_FAILURE_RETRY("bind", bind(fd, sa, sizeof(sockaddr_storage)));
+}
+
static void Posix_chmod(JNIEnv* env, jobject, jstring javaPath, jint mode) {
ScopedUtfChars path(env, javaPath);
if (path.c_str() == NULL) {
@@ -305,6 +429,28 @@
throwIfMinusOne(env, "close", close(fd));
}
+static void Posix_connect(JNIEnv* env, jobject, jobject javaFd, jobject javaAddress, jint port) {
+ sockaddr_storage ss;
+ if (!inetAddressToSockaddr(env, javaAddress, port, &ss)) {
+ return;
+ }
+ int fd;
+ const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss);
+ NET_FAILURE_RETRY("connect", connect(fd, sa, sizeof(sockaddr_storage)));
+}
+
+static jobject Posix_dup(JNIEnv* env, jobject, jobject javaOldFd) {
+ int oldFd = jniGetFDFromFileDescriptor(env, javaOldFd);
+ int newFd = throwIfMinusOne(env, "dup", TEMP_FAILURE_RETRY(dup(oldFd)));
+ return (newFd != -1) ? jniCreateFileDescriptor(env, newFd) : NULL;
+}
+
+static jobject Posix_dup2(JNIEnv* env, jobject, jobject javaOldFd, jint newFd) {
+ int oldFd = jniGetFDFromFileDescriptor(env, javaOldFd);
+ int fd = throwIfMinusOne(env, "dup2", TEMP_FAILURE_RETRY(dup2(oldFd, newFd)));
+ return (fd != -1) ? jniCreateFileDescriptor(env, fd) : NULL;
+}
+
static jobjectArray Posix_environ(JNIEnv* env, jobject) {
extern char** environ; // Standard, but not in any header file.
return toStringArray(env, environ);
@@ -444,7 +590,7 @@
// Convert each IP address into a Java byte array.
sockaddr_storage* address = reinterpret_cast<sockaddr_storage*>(ai->ai_addr);
- ScopedLocalRef<jobject> inetAddress(env, socketAddressToInetAddress(env, address));
+ ScopedLocalRef<jobject> inetAddress(env, sockaddrToInetAddress(env, address, NULL));
if (inetAddress.get() == NULL) {
return NULL;
}
@@ -454,6 +600,18 @@
return result;
}
+static jint Posix_getegid(JNIEnv*, jobject) {
+ return getegid();
+}
+
+static jint Posix_geteuid(JNIEnv*, jobject) {
+ return geteuid();
+}
+
+static jint Posix_getgid(JNIEnv*, jobject) {
+ return getgid();
+}
+
static jstring Posix_getenv(JNIEnv* env, jobject, jstring javaName) {
ScopedUtfChars name(env, javaName);
if (name.c_str() == NULL) {
@@ -464,7 +622,7 @@
static jstring Posix_getnameinfo(JNIEnv* env, jobject, jobject javaAddress, jint flags) {
sockaddr_storage ss;
- if (!inetAddressToSocketAddress(env, javaAddress, 0, &ss)) {
+ if (!inetAddressToSockaddr_getnameinfo(env, javaAddress, 0, &ss)) {
return NULL;
}
// TODO: bionic's getnameinfo(3) seems to want its length parameter to be exactly
@@ -481,6 +639,26 @@
return env->NewStringUTF(buf);
}
+static jint Posix_getpid(JNIEnv*, jobject) {
+ return getpid();
+}
+
+static jint Posix_getppid(JNIEnv*, jobject) {
+ return getppid();
+}
+
+static jobject Posix_getpwnam(JNIEnv* env, jobject, jstring javaName) {
+ ScopedUtfChars name(env, javaName);
+ if (name.c_str() == NULL) {
+ return NULL;
+ }
+ return Passwd(env).getpwnam(name.c_str());
+}
+
+static jobject Posix_getpwuid(JNIEnv* env, jobject, jint uid) {
+ return Passwd(env).getpwuid(uid);
+}
+
static jobject Posix_getsockname(JNIEnv* env, jobject, jobject javaFd) {
int fd = jniGetFDFromFileDescriptor(env, javaFd);
sockaddr_storage ss;
@@ -515,7 +693,7 @@
throwErrnoException(env, "getsockopt");
return NULL;
}
- return socketAddressToInetAddress(env, &ss);
+ return sockaddrToInetAddress(env, &ss, NULL);
}
static jint Posix_getsockoptInt(JNIEnv* env, jobject, jobject javaFd, jint level, jint option) {
@@ -552,6 +730,10 @@
return makeStructTimeval(env, tv);
}
+static jint Posix_getuid(JNIEnv*, jobject) {
+ return getuid();
+}
+
static jstring Posix_if_indextoname(JNIEnv* env, jobject, jint index) {
char buf[IF_NAMESIZE];
char* name = if_indextoname(index, buf);
@@ -572,7 +754,7 @@
return NULL;
}
sin->sin_family = AF_INET; // inet_aton only supports IPv4.
- return socketAddressToInetAddress(env, &ss);
+ return sockaddrToInetAddress(env, &ss, NULL);
}
static jobject Posix_ioctlInetAddress(JNIEnv* env, jobject, jobject javaFd, jint cmd, jstring javaInterfaceName) {
@@ -585,7 +767,7 @@
if (rc == -1) {
return NULL;
}
- return socketAddressToInetAddress(env, reinterpret_cast<sockaddr_storage*>(&req.ifr_addr));
+ return sockaddrToInetAddress(env, reinterpret_cast<sockaddr_storage*>(&req.ifr_addr), NULL);
}
static jint Posix_ioctlInt(JNIEnv* env, jobject, jobject javaFd, jint cmd, jobject javaArg) {
@@ -701,6 +883,47 @@
return result;
}
+static jint Posix_poll(JNIEnv* env, jobject, jobjectArray javaStructs, jint timeoutMs) {
+ static jfieldID fdFid = env->GetFieldID(JniConstants::structPollfdClass, "fd", "Ljava/io/FileDescriptor;");
+ static jfieldID eventsFid = env->GetFieldID(JniConstants::structPollfdClass, "events", "S");
+ static jfieldID reventsFid = env->GetFieldID(JniConstants::structPollfdClass, "revents", "S");
+
+ // Turn the Java libcore.io.StructPollfd[] into a C++ struct pollfd[].
+ size_t arrayLength = env->GetArrayLength(javaStructs);
+ UniquePtr<struct pollfd[]> fds(new struct pollfd[arrayLength]);
+ memset(fds.get(), 0, sizeof(struct pollfd) * arrayLength);
+ size_t count = 0; // Some trailing array elements may be irrelevant. (See below.)
+ for (size_t i = 0; i < arrayLength; ++i) {
+ ScopedLocalRef<jobject> javaStruct(env, env->GetObjectArrayElement(javaStructs, i));
+ if (javaStruct.get() == NULL) {
+ break; // We allow trailing nulls in the array for caller convenience.
+ }
+ ScopedLocalRef<jobject> javaFd(env, env->GetObjectField(javaStruct.get(), fdFid));
+ if (javaFd.get() == NULL) {
+ break; // We also allow callers to just clear the fd field (this is what Selector does).
+ }
+ fds[count].fd = jniGetFDFromFileDescriptor(env, javaFd.get());
+ fds[count].events = env->GetShortField(javaStruct.get(), eventsFid);
+ ++count;
+ }
+
+ int rc = TEMP_FAILURE_RETRY(poll(fds.get(), count, timeoutMs));
+ if (rc == -1) {
+ throwErrnoException(env, "poll");
+ return -1;
+ }
+
+ // Update the revents fields in the Java libcore.io.StructPollfd[].
+ for (size_t i = 0; i < count; ++i) {
+ ScopedLocalRef<jobject> javaStruct(env, env->GetObjectArrayElement(javaStructs, i));
+ if (javaStruct.get() == NULL) {
+ return -1;
+ }
+ env->SetShortField(javaStruct.get(), reventsFid, fds[i].revents);
+ }
+ return rc;
+}
+
static jint Posix_readBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount) {
ScopedBytesRW bytes(env, javaBytes);
if (bytes.get() == NULL) {
@@ -719,6 +942,22 @@
return throwIfMinusOne(env, "readv", TEMP_FAILURE_RETRY(readv(fd, ioVec.get(), ioVec.size())));
}
+static jint Posix_recvfromBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jint flags, jobject javaInetSocketAddress) {
+ ScopedBytesRW bytes(env, javaBytes);
+ if (bytes.get() == NULL) {
+ return -1;
+ }
+ sockaddr_storage ss;
+ socklen_t sl = sizeof(ss);
+ memset(&ss, 0, sizeof(ss));
+ int fd;
+ sockaddr* from = (javaInetSocketAddress != NULL) ? reinterpret_cast<sockaddr*>(&ss) : NULL;
+ socklen_t* fromLength = (javaInetSocketAddress != NULL) ? &sl : 0;
+ jint recvCount = NET_FAILURE_RETRY("recvfrom", recvfrom(fd, bytes.get() + byteOffset, byteCount, flags, from, fromLength));
+ fillInetSocketAddress(env, recvCount, javaInetSocketAddress, &ss);
+ return recvCount;
+}
+
static void Posix_remove(JNIEnv* env, jobject, jstring javaPath) {
ScopedUtfChars path(env, javaPath);
if (path.c_str() == NULL) {
@@ -757,6 +996,33 @@
return result;
}
+static jint Posix_sendtoBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jint flags, jobject javaInetAddress, jint port) {
+ ScopedBytesRO bytes(env, javaBytes);
+ if (bytes.get() == NULL) {
+ return -1;
+ }
+ sockaddr_storage ss;
+ if (javaInetAddress != NULL && !inetAddressToSockaddr(env, javaInetAddress, port, &ss)) {
+ return -1;
+ }
+ int fd;
+ const sockaddr* to = (javaInetAddress != NULL) ? reinterpret_cast<const sockaddr*>(&ss) : NULL;
+ socklen_t toLength = (javaInetAddress != NULL) ? sizeof(ss) : 0;
+ return NET_FAILURE_RETRY("sendto", sendto(fd, bytes.get() + byteOffset, byteCount, flags, to, toLength));
+}
+
+static void Posix_setegid(JNIEnv* env, jobject, jint egid) {
+ throwIfMinusOne(env, "setegid", TEMP_FAILURE_RETRY(setegid(egid)));
+}
+
+static void Posix_seteuid(JNIEnv* env, jobject, jint euid) {
+ throwIfMinusOne(env, "seteuid", TEMP_FAILURE_RETRY(seteuid(euid)));
+}
+
+static void Posix_setgid(JNIEnv* env, jobject, jint gid) {
+ throwIfMinusOne(env, "setgid", TEMP_FAILURE_RETRY(setgid(gid)));
+}
+
static void Posix_setsockoptByte(JNIEnv* env, jobject, jobject javaFd, jint level, jint option, jint value) {
int fd = jniGetFDFromFileDescriptor(env, javaFd);
u_char byte = value;
@@ -792,8 +1058,8 @@
value.gr_interface = env->GetIntField(javaGroupReq, grInterfaceFid);
// Get the IPv4 or IPv6 multicast address to join or leave.
static jfieldID grGroupFid = env->GetFieldID(JniConstants::structGroupReqClass, "gr_group", "Ljava/net/InetAddress;");
- jobject javaGroup = env->GetObjectField(javaGroupReq, grGroupFid);
- if (!inetAddressToSocketAddress(env, javaGroup, 0, &value.gr_group)) {
+ ScopedLocalRef<jobject> javaGroup(env, env->GetObjectField(javaGroupReq, grGroupFid));
+ if (!inetAddressToSockaddr(env, javaGroup.get(), 0, &value.gr_group)) {
return;
}
@@ -835,6 +1101,10 @@
throwIfMinusOne(env, "setsockopt", TEMP_FAILURE_RETRY(setsockopt(fd, level, option, &value, sizeof(value))));
}
+static void Posix_setuid(JNIEnv* env, jobject, jint uid) {
+ throwIfMinusOne(env, "setuid", TEMP_FAILURE_RETRY(setuid(uid)));
+}
+
static void Posix_shutdown(JNIEnv* env, jobject, jobject javaFd, jint how) {
int fd = jniGetFDFromFileDescriptor(env, javaFd);
throwIfMinusOne(env, "shutdown", TEMP_FAILURE_RETRY(shutdown(fd, how)));
@@ -899,6 +1169,16 @@
return makeStructUtsname(env, buf);
}
+static jint Posix_waitpid(JNIEnv* env, jobject, jint pid, jobject javaStatus, jint options) {
+ int status;
+ int rc = throwIfMinusOne(env, "waitpid", TEMP_FAILURE_RETRY(waitpid(pid, &status, options)));
+ if (rc != -1) {
+ static jfieldID valueFid = env->GetFieldID(JniConstants::mutableIntClass, "value", "I");
+ env->SetIntField(javaStatus, valueFid, status);
+ }
+ return rc;
+}
+
static jint Posix_writeBytes(JNIEnv* env, jobject, jobject javaFd, jbyteArray javaBytes, jint byteOffset, jint byteCount) {
ScopedBytesRO bytes(env, javaBytes);
if (bytes.get() == NULL) {
@@ -918,9 +1198,14 @@
}
static JNINativeMethod gMethods[] = {
+ NATIVE_METHOD(Posix, accept, "(Ljava/io/FileDescriptor;Ljava/net/InetSocketAddress;)Ljava/io/FileDescriptor;"),
NATIVE_METHOD(Posix, access, "(Ljava/lang/String;I)Z"),
+ NATIVE_METHOD(Posix, bind, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"),
NATIVE_METHOD(Posix, chmod, "(Ljava/lang/String;I)V"),
NATIVE_METHOD(Posix, close, "(Ljava/io/FileDescriptor;)V"),
+ NATIVE_METHOD(Posix, connect, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"),
+ NATIVE_METHOD(Posix, dup, "(Ljava/io/FileDescriptor;)Ljava/io/FileDescriptor;"),
+ NATIVE_METHOD(Posix, dup2, "(Ljava/io/FileDescriptor;I)Ljava/io/FileDescriptor;"),
NATIVE_METHOD(Posix, environ, "()[Ljava/lang/String;"),
NATIVE_METHOD(Posix, fcntlVoid, "(Ljava/io/FileDescriptor;I)I"),
NATIVE_METHOD(Posix, fcntlLong, "(Ljava/io/FileDescriptor;IJ)I"),
@@ -932,14 +1217,22 @@
NATIVE_METHOD(Posix, ftruncate, "(Ljava/io/FileDescriptor;J)V"),
NATIVE_METHOD(Posix, gai_strerror, "(I)Ljava/lang/String;"),
NATIVE_METHOD(Posix, getaddrinfo, "(Ljava/lang/String;Llibcore/io/StructAddrinfo;)[Ljava/net/InetAddress;"),
+ NATIVE_METHOD(Posix, getegid, "()I"),
+ NATIVE_METHOD(Posix, geteuid, "()I"),
+ NATIVE_METHOD(Posix, getgid, "()I"),
NATIVE_METHOD(Posix, getenv, "(Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(Posix, getnameinfo, "(Ljava/net/InetAddress;I)Ljava/lang/String;"),
+ NATIVE_METHOD(Posix, getpid, "()I"),
+ NATIVE_METHOD(Posix, getppid, "()I"),
+ NATIVE_METHOD(Posix, getpwnam, "(Ljava/lang/String;)Llibcore/io/StructPasswd;"),
+ NATIVE_METHOD(Posix, getpwuid, "(I)Llibcore/io/StructPasswd;"),
NATIVE_METHOD(Posix, getsockname, "(Ljava/io/FileDescriptor;)Ljava/net/SocketAddress;"),
NATIVE_METHOD(Posix, getsockoptByte, "(Ljava/io/FileDescriptor;II)I"),
NATIVE_METHOD(Posix, getsockoptInAddr, "(Ljava/io/FileDescriptor;II)Ljava/net/InetAddress;"),
NATIVE_METHOD(Posix, getsockoptInt, "(Ljava/io/FileDescriptor;II)I"),
NATIVE_METHOD(Posix, getsockoptLinger, "(Ljava/io/FileDescriptor;II)Llibcore/io/StructLinger;"),
NATIVE_METHOD(Posix, getsockoptTimeval, "(Ljava/io/FileDescriptor;II)Llibcore/io/StructTimeval;"),
+ NATIVE_METHOD(Posix, getuid, "()I"),
NATIVE_METHOD(Posix, if_indextoname, "(I)Ljava/lang/String;"),
NATIVE_METHOD(Posix, inet_aton, "(Ljava/lang/String;)Ljava/net/InetAddress;"),
NATIVE_METHOD(Posix, ioctlInetAddress, "(Ljava/io/FileDescriptor;ILjava/lang/String;)Ljava/net/InetAddress;"),
@@ -958,11 +1251,17 @@
NATIVE_METHOD(Posix, munmap, "(JJ)V"),
NATIVE_METHOD(Posix, open, "(Ljava/lang/String;II)Ljava/io/FileDescriptor;"),
NATIVE_METHOD(Posix, pipe, "()[Ljava/io/FileDescriptor;"),
+ NATIVE_METHOD(Posix, poll, "([Llibcore/io/StructPollfd;I)I"),
NATIVE_METHOD(Posix, readBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;II)I"),
NATIVE_METHOD(Posix, readv, "(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I"),
+ NATIVE_METHOD(Posix, recvfromBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/InetSocketAddress;)I"),
NATIVE_METHOD(Posix, remove, "(Ljava/lang/String;)V"),
NATIVE_METHOD(Posix, rename, "(Ljava/lang/String;Ljava/lang/String;)V"),
NATIVE_METHOD(Posix, sendfile, "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Llibcore/util/MutableLong;J)J"),
+ NATIVE_METHOD(Posix, sendtoBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/InetAddress;I)I"),
+ NATIVE_METHOD(Posix, setegid, "(I)V"),
+ NATIVE_METHOD(Posix, seteuid, "(I)V"),
+ NATIVE_METHOD(Posix, setgid, "(I)V"),
NATIVE_METHOD(Posix, setsockoptByte, "(Ljava/io/FileDescriptor;III)V"),
NATIVE_METHOD(Posix, setsockoptIfreq, "(Ljava/io/FileDescriptor;IILjava/lang/String;)V"),
NATIVE_METHOD(Posix, setsockoptInt, "(Ljava/io/FileDescriptor;III)V"),
@@ -970,6 +1269,7 @@
NATIVE_METHOD(Posix, setsockoptGroupReq, "(Ljava/io/FileDescriptor;IILlibcore/io/StructGroupReq;)V"),
NATIVE_METHOD(Posix, setsockoptLinger, "(Ljava/io/FileDescriptor;IILlibcore/io/StructLinger;)V"),
NATIVE_METHOD(Posix, setsockoptTimeval, "(Ljava/io/FileDescriptor;IILlibcore/io/StructTimeval;)V"),
+ NATIVE_METHOD(Posix, setuid, "(I)V"),
NATIVE_METHOD(Posix, shutdown, "(Ljava/io/FileDescriptor;I)V"),
NATIVE_METHOD(Posix, socket, "(III)Ljava/io/FileDescriptor;"),
NATIVE_METHOD(Posix, stat, "(Ljava/lang/String;)Llibcore/io/StructStat;"),
@@ -978,6 +1278,7 @@
NATIVE_METHOD(Posix, symlink, "(Ljava/lang/String;Ljava/lang/String;)V"),
NATIVE_METHOD(Posix, sysconf, "(I)J"),
NATIVE_METHOD(Posix, uname, "()Llibcore/io/StructUtsname;"),
+ NATIVE_METHOD(Posix, waitpid, "(ILlibcore/util/MutableInt;I)I"),
NATIVE_METHOD(Posix, writeBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;II)I"),
NATIVE_METHOD(Posix, writev, "(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I"),
};
diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
deleted file mode 100644
index d7208b1..0000000
--- a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp
+++ /dev/null
@@ -1,684 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "OSNetworkSystem"
-
-#include "AsynchronousSocketCloseMonitor.h"
-#include "JNIHelp.h"
-#include "JniConstants.h"
-#include "JniException.h"
-#include "NetFd.h"
-#include "NetworkUtilities.h"
-#include "ScopedPrimitiveArray.h"
-#include "jni.h"
-#include "valueOf.h"
-
-#include <arpa/inet.h>
-#include <errno.h>
-#include <net/if.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-/* constants for OSNetworkSystem_selectImpl */
-#define SOCKET_OP_NONE 0
-#define SOCKET_OP_READ 1
-#define SOCKET_OP_WRITE 2
-
-static void jniThrowSocketTimeoutException(JNIEnv* env, int error) {
- jniThrowExceptionWithErrno(env, "java/net/SocketTimeoutException", error);
-}
-
-/**
- * Returns the port number in a sockaddr_storage structure.
- *
- * @param address the sockaddr_storage structure to get the port from
- *
- * @return the port number, or -1 if the address family is unknown.
- */
-static int getSocketAddressPort(sockaddr_storage* ss) {
- switch (ss->ss_family) {
- case AF_INET:
- return ntohs(reinterpret_cast<sockaddr_in*>(ss)->sin_port);
- case AF_INET6:
- return ntohs(reinterpret_cast<sockaddr_in6*>(ss)->sin6_port);
- default:
- return -1;
- }
-}
-
-// Creates mapped addresses.
-// TODO: can this move to inetAddressToSocketAddress?
-class CompatibleSocketAddress {
-public:
- CompatibleSocketAddress(const sockaddr_storage& ss, bool mapUnspecified) {
- mCompatibleAddress = reinterpret_cast<const sockaddr*>(&ss);
- if (ss.ss_family == AF_INET) {
- // Map the IPv4 address in ss into an IPv6 address in mTmp.
- const sockaddr_in* sin = reinterpret_cast<const sockaddr_in*>(&ss);
- sockaddr_in6* sin6 = reinterpret_cast<sockaddr_in6*>(&mTmp);
- memset(sin6, 0, sizeof(*sin6));
- sin6->sin6_family = AF_INET6;
- sin6->sin6_port = sin->sin_port;
- // TODO: mapUnspecified was introduced because kernels < 2.6.31 don't allow
- // you to bind to ::ffff:0.0.0.0. When we move to something >= 2.6.31, we
- // should make the code behave as if mapUnspecified were always true, and
- // remove the parameter.
- // TODO: this code still appears to be necessary on 2.6.32, so there's something
- // wrong in the above comment.
- if (sin->sin_addr.s_addr != 0 || mapUnspecified) {
- memset(&(sin6->sin6_addr.s6_addr[10]), 0xff, 2);
- }
- memcpy(&sin6->sin6_addr.s6_addr[12], &sin->sin_addr.s_addr, 4);
- mCompatibleAddress = reinterpret_cast<const sockaddr*>(&mTmp);
- }
- }
- // Returns a pointer to an IPv6 address.
- const sockaddr* get() const {
- return mCompatibleAddress;
- }
-private:
- const sockaddr* mCompatibleAddress;
- sockaddr_storage mTmp;
-};
-
-// Converts a number of milliseconds to a timeval.
-static timeval toTimeval(long ms) {
- timeval tv;
- tv.tv_sec = ms / 1000;
- tv.tv_usec = (ms - tv.tv_sec*1000) * 1000;
- return tv;
-}
-
-static void throwConnectException(JNIEnv* env, int error) {
- if (error == ECONNRESET || error == ECONNREFUSED || error == EADDRNOTAVAIL ||
- error == EADDRINUSE || error == ENETUNREACH) {
- jniThrowExceptionWithErrno(env, "java/net/ConnectException", error);
- } else if (error == EACCES) {
- jniThrowExceptionWithErrno(env, "java/lang/SecurityException", error);
- } else if (error == ETIMEDOUT) {
- jniThrowSocketTimeoutException(env, error);
- } else {
- jniThrowSocketException(env, error);
- }
-}
-
-static jint OSNetworkSystem_writeDirect(JNIEnv* env, jobject,
- jobject fileDescriptor, jint address, jint offset, jint count) {
- if (count <= 0) {
- return 0;
- }
-
- NetFd fd(env, fileDescriptor);
- if (fd.isClosed()) {
- return 0;
- }
-
- jbyte* src = reinterpret_cast<jbyte*>(static_cast<uintptr_t>(address + offset));
-
- ssize_t bytesSent;
- {
- int intFd = fd.get();
- AsynchronousSocketCloseMonitor monitor(intFd);
- bytesSent = NET_FAILURE_RETRY(fd, write(intFd, src, count));
- }
- if (env->ExceptionOccurred()) {
- return -1;
- }
-
- if (bytesSent == -1) {
- if (errno == EAGAIN || errno == EWOULDBLOCK) {
- // We were asked to write to a non-blocking socket, but were told
- // it would block, so report "no bytes written".
- return 0;
- } else {
- jniThrowSocketException(env, errno);
- return 0;
- }
- }
- return bytesSent;
-}
-
-static jint OSNetworkSystem_write(JNIEnv* env, jobject,
- jobject fileDescriptor, jbyteArray byteArray, jint offset, jint count) {
- ScopedByteArrayRW bytes(env, byteArray);
- if (bytes.get() == NULL) {
- return -1;
- }
- jint address = static_cast<jint>(reinterpret_cast<uintptr_t>(bytes.get()));
- int result = OSNetworkSystem_writeDirect(env, NULL, fileDescriptor, address, offset, count);
- return result;
-}
-
-static jboolean OSNetworkSystem_connect(JNIEnv* env, jobject, jobject fileDescriptor, jobject inetAddr, jint port) {
- NetFd fd(env, fileDescriptor);
- if (fd.isClosed()) {
- return JNI_FALSE;
- }
-
- sockaddr_storage ss;
- if (!inetAddressToSocketAddress(env, inetAddr, port, &ss)) {
- return JNI_FALSE;
- }
-
- // Initiate a connection attempt...
- const CompatibleSocketAddress compatibleAddress(ss, true);
- int rc = connect(fd.get(), compatibleAddress.get(), sizeof(sockaddr_storage));
- int connectErrno = errno;
-
- // Did we get interrupted?
- if (fd.isClosed()) {
- return JNI_FALSE;
- }
-
- // Did we fail to connect?
- if (rc == -1) {
- if (connectErrno != EINPROGRESS) {
- throwConnectException(env, connectErrno); // Permanent failure, so throw.
- }
- return JNI_FALSE;
- }
-
- // We connected straight away!
- return JNI_TRUE;
-}
-
-static jboolean OSNetworkSystem_isConnected(JNIEnv* env, jobject, jobject fileDescriptor, jint timeout) {
- NetFd netFd(env, fileDescriptor);
- if (netFd.isClosed()) {
- return JNI_FALSE;
- }
-
- // Initialize the fd sets and call select.
- int fd = netFd.get();
- int nfds = fd + 1;
- fd_set readSet;
- fd_set writeSet;
- FD_ZERO(&readSet);
- FD_ZERO(&writeSet);
- FD_SET(fd, &readSet);
- FD_SET(fd, &writeSet);
- timeval passedTimeout(toTimeval(timeout));
- int rc = select(nfds, &readSet, &writeSet, NULL, &passedTimeout);
- if (rc == -1) {
- if (errno == EINTR) {
- // We can't trivially retry a select with TEMP_FAILURE_RETRY, so punt and ask the
- // caller to try again.
- } else {
- throwConnectException(env, errno);
- }
- return JNI_FALSE;
- }
-
- // If the fd is just in the write set, we're connected.
- if (FD_ISSET(fd, &writeSet) && !FD_ISSET(fd, &readSet)) {
- return JNI_TRUE;
- }
-
- // If the fd is in both the read and write set, there was an error.
- if (FD_ISSET(fd, &readSet) || FD_ISSET(fd, &writeSet)) {
- // Get the pending error.
- int error = 0;
- socklen_t errorLen = sizeof(error);
- if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &errorLen) == -1) {
- error = errno; // Couldn't get the real error, so report why getsockopt failed.
- }
- throwConnectException(env, error);
- return JNI_FALSE;
- }
-
- // Timeout expired.
- return JNI_FALSE;
-}
-
-static void OSNetworkSystem_bind(JNIEnv* env, jobject, jobject fileDescriptor,
- jobject inetAddress, jint port) {
- sockaddr_storage ss;
- if (!inetAddressToSocketAddress(env, inetAddress, port, &ss)) {
- return;
- }
-
- NetFd fd(env, fileDescriptor);
- if (fd.isClosed()) {
- return;
- }
-
- const CompatibleSocketAddress compatibleAddress(ss, false);
- int rc = TEMP_FAILURE_RETRY(bind(fd.get(), compatibleAddress.get(), sizeof(sockaddr_storage)));
- if (rc == -1) {
- jniThrowExceptionWithErrno(env, "java/net/BindException", errno);
- }
-}
-
-static void OSNetworkSystem_accept(JNIEnv* env, jobject, jobject serverFileDescriptor,
- jobject newSocket, jobject clientFileDescriptor) {
-
- if (newSocket == NULL) {
- jniThrowNullPointerException(env, NULL);
- return;
- }
-
- NetFd serverFd(env, serverFileDescriptor);
- if (serverFd.isClosed()) {
- return;
- }
-
- sockaddr_storage ss;
- socklen_t addrLen = sizeof(ss);
- sockaddr* sa = reinterpret_cast<sockaddr*>(&ss);
-
- int clientFd;
- {
- int intFd = serverFd.get();
- AsynchronousSocketCloseMonitor monitor(intFd);
- clientFd = NET_FAILURE_RETRY(serverFd, accept(intFd, sa, &addrLen));
- }
- if (env->ExceptionOccurred()) {
- return;
- }
- if (clientFd == -1) {
- if (errno == EAGAIN || errno == EWOULDBLOCK) {
- jniThrowSocketTimeoutException(env, errno);
- } else {
- jniThrowSocketException(env, errno);
- }
- return;
- }
-
- // Reset the inherited read timeout to the Java-specified default of 0.
- timeval timeout(toTimeval(0));
- int rc = setsockopt(clientFd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
- if (rc == -1) {
- LOGE("couldn't reset SO_RCVTIMEO on accepted socket fd %i: %s", clientFd, strerror(errno));
- jniThrowSocketException(env, errno);
- }
-
- /*
- * For network sockets, put the peer address and port in instance variables.
- * We don't bother to do this for UNIX domain sockets, since most peers are
- * anonymous anyway.
- */
- if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6) {
- // Remote address and port.
- jobject remoteAddress = socketAddressToInetAddress(env, &ss);
- if (remoteAddress == NULL) {
- close(clientFd);
- return;
- }
- int remotePort = getSocketAddressPort(&ss);
-
- // Local port.
- memset(&ss, 0, addrLen);
- int rc = getsockname(clientFd, sa, &addrLen);
- if (rc == -1) {
- close(clientFd);
- jniThrowSocketException(env, errno);
- return;
- }
- int localPort = getSocketAddressPort(&ss);
-
- static jfieldID addressFid = env->GetFieldID(JniConstants::socketImplClass, "address", "Ljava/net/InetAddress;");
- static jfieldID localPortFid = env->GetFieldID(JniConstants::socketImplClass, "localport", "I");
- static jfieldID portFid = env->GetFieldID(JniConstants::socketImplClass, "port", "I");
- env->SetObjectField(newSocket, addressFid, remoteAddress);
- env->SetIntField(newSocket, portFid, remotePort);
- env->SetIntField(newSocket, localPortFid, localPort);
- }
-
- jniSetFileDescriptorOfFD(env, clientFileDescriptor, clientFd);
-}
-
-static void OSNetworkSystem_sendUrgentData(JNIEnv* env, jobject,
- jobject fileDescriptor, jbyte value) {
- NetFd fd(env, fileDescriptor);
- if (fd.isClosed()) {
- return;
- }
-
- int rc = send(fd.get(), &value, 1, MSG_OOB);
- if (rc == -1) {
- jniThrowSocketException(env, errno);
- }
-}
-
-static void OSNetworkSystem_disconnectDatagram(JNIEnv* env, jobject, jobject fileDescriptor) {
- NetFd fd(env, fileDescriptor);
- if (fd.isClosed()) {
- return;
- }
-
- // To disconnect a datagram socket, we connect to a bogus address with
- // the family AF_UNSPEC.
- sockaddr_storage ss;
- memset(&ss, 0, sizeof(ss));
- ss.ss_family = AF_UNSPEC;
- const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss);
- int rc = TEMP_FAILURE_RETRY(connect(fd.get(), sa, sizeof(ss)));
- if (rc == -1) {
- jniThrowSocketException(env, errno);
- }
-}
-
-// TODO: can we merge this with recvDirect?
-static jint OSNetworkSystem_readDirect(JNIEnv* env, jobject, jobject fileDescriptor,
- jint address, jint count) {
- NetFd fd(env, fileDescriptor);
- if (fd.isClosed()) {
- return 0;
- }
-
- jbyte* dst = reinterpret_cast<jbyte*>(static_cast<uintptr_t>(address));
- ssize_t bytesReceived;
- {
- int intFd = fd.get();
- AsynchronousSocketCloseMonitor monitor(intFd);
- bytesReceived = NET_FAILURE_RETRY(fd, read(intFd, dst, count));
- }
- if (env->ExceptionOccurred()) {
- return -1;
- }
- if (bytesReceived == 0) {
- return -1;
- } else if (bytesReceived == -1) {
- if (errno == EAGAIN || errno == EWOULDBLOCK) {
- // We were asked to read a non-blocking socket with no data
- // available, so report "no bytes read".
- return 0;
- } else {
- jniThrowSocketException(env, errno);
- return 0;
- }
- } else {
- return bytesReceived;
- }
-}
-
-static jint OSNetworkSystem_read(JNIEnv* env, jclass, jobject fileDescriptor,
- jbyteArray byteArray, jint offset, jint count) {
- ScopedByteArrayRW bytes(env, byteArray);
- if (bytes.get() == NULL) {
- return -1;
- }
- jint address = static_cast<jint>(reinterpret_cast<uintptr_t>(bytes.get() + offset));
- return OSNetworkSystem_readDirect(env, NULL, fileDescriptor, address, count);
-}
-
-// TODO: can we merge this with readDirect?
-static jint OSNetworkSystem_recvDirect(JNIEnv* env, jobject, jobject fileDescriptor, jobject packet,
- jint address, jint offset, jint length, jboolean peek, jboolean connected) {
- NetFd fd(env, fileDescriptor);
- if (fd.isClosed()) {
- return 0;
- }
-
- char* buf = reinterpret_cast<char*>(static_cast<uintptr_t>(address + offset));
- const int flags = peek ? MSG_PEEK : 0;
- sockaddr_storage ss;
- memset(&ss, 0, sizeof(ss));
- socklen_t sockAddrLen = sizeof(ss);
- sockaddr* from = connected ? NULL : reinterpret_cast<sockaddr*>(&ss);
- socklen_t* fromLength = connected ? NULL : &sockAddrLen;
-
- ssize_t bytesReceived;
- {
- int intFd = fd.get();
- AsynchronousSocketCloseMonitor monitor(intFd);
- bytesReceived = NET_FAILURE_RETRY(fd, recvfrom(intFd, buf, length, flags, from, fromLength));
- }
- if (env->ExceptionOccurred()) {
- return -1;
- }
- if (bytesReceived == -1) {
- if (connected && errno == ECONNREFUSED) {
- jniThrowException(env, "java/net/PortUnreachableException", "");
- } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
- jniThrowSocketTimeoutException(env, errno);
- } else {
- jniThrowSocketException(env, errno);
- }
- return 0;
- }
-
- static jfieldID addressFid = env->GetFieldID(JniConstants::datagramPacketClass, "address", "Ljava/net/InetAddress;");
- static jfieldID lengthFid = env->GetFieldID(JniConstants::datagramPacketClass, "length", "I");
- static jfieldID portFid = env->GetFieldID(JniConstants::datagramPacketClass, "port", "I");
- if (packet != NULL) {
- env->SetIntField(packet, lengthFid, bytesReceived);
- if (!connected) {
- jobject sender = socketAddressToInetAddress(env, &ss);
- if (sender == NULL) {
- return 0;
- }
- int port = getSocketAddressPort(&ss);
- env->SetObjectField(packet, addressFid, sender);
- env->SetIntField(packet, portFid, port);
- }
- }
- return bytesReceived;
-}
-
-static jint OSNetworkSystem_recv(JNIEnv* env, jobject, jobject fd, jobject packet,
- jbyteArray javaBytes, jint offset, jint length, jboolean peek, jboolean connected) {
- ScopedByteArrayRW bytes(env, javaBytes);
- if (bytes.get() == NULL) {
- return -1;
- }
- uintptr_t address = reinterpret_cast<uintptr_t>(bytes.get());
- return OSNetworkSystem_recvDirect(env, NULL, fd, packet, address, offset, length, peek,
- connected);
-}
-
-
-
-
-
-
-
-
-static jint OSNetworkSystem_sendDirect(JNIEnv* env, jobject, jobject fileDescriptor, jint address, jint offset, jint length, jint port, jobject inetAddress) {
- NetFd fd(env, fileDescriptor);
- if (fd.isClosed()) {
- return -1;
- }
-
- sockaddr_storage receiver;
- if (inetAddress != NULL && !inetAddressToSocketAddress(env, inetAddress, port, &receiver)) {
- return -1;
- }
-
- int flags = 0;
- char* buf = reinterpret_cast<char*>(static_cast<uintptr_t>(address + offset));
- sockaddr* to = inetAddress ? reinterpret_cast<sockaddr*>(&receiver) : NULL;
- socklen_t toLength = inetAddress ? sizeof(receiver) : 0;
-
- ssize_t bytesSent;
- {
- int intFd = fd.get();
- AsynchronousSocketCloseMonitor monitor(intFd);
- bytesSent = NET_FAILURE_RETRY(fd, sendto(intFd, buf, length, flags, to, toLength));
- }
- if (env->ExceptionOccurred()) {
- return -1;
- }
- if (bytesSent == -1) {
- if (errno == ECONNRESET || errno == ECONNREFUSED) {
- return 0;
- } else {
- jniThrowSocketException(env, errno);
- }
- }
- return bytesSent;
-}
-
-static jint OSNetworkSystem_send(JNIEnv* env, jobject, jobject fd,
- jbyteArray data, jint offset, jint length,
- jint port, jobject inetAddress) {
- ScopedByteArrayRO bytes(env, data);
- if (bytes.get() == NULL) {
- return -1;
- }
- return OSNetworkSystem_sendDirect(env, NULL, fd,
- reinterpret_cast<uintptr_t>(bytes.get()), offset, length, port, inetAddress);
-}
-
-
-
-
-
-
-
-
-static bool isValidFd(int fd) {
- return fd >= 0 && fd < FD_SETSIZE;
-}
-
-static bool initFdSet(JNIEnv* env, jobjectArray fdArray, jint count, fd_set* fdSet, int* maxFd) {
- for (int i = 0; i < count; ++i) {
- jobject fileDescriptor = env->GetObjectArrayElement(fdArray, i);
- if (fileDescriptor == NULL) {
- return false;
- }
-
- const int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- if (!isValidFd(fd)) {
- LOGE("selectImpl: ignoring invalid fd %i", fd);
- continue;
- }
-
- FD_SET(fd, fdSet);
-
- if (fd > *maxFd) {
- *maxFd = fd;
- }
- }
- return true;
-}
-
-/*
- * Note: fdSet has to be non-const because although on Linux FD_ISSET() is sane
- * and takes a const fd_set*, it takes fd_set* on Mac OS. POSIX is not on our
- * side here:
- * http://www.opengroup.org/onlinepubs/000095399/functions/select.html
- */
-static bool translateFdSet(JNIEnv* env, jobjectArray fdArray, jint count, fd_set& fdSet, jint* flagArray, size_t offset, jint op) {
- for (int i = 0; i < count; ++i) {
- jobject fileDescriptor = env->GetObjectArrayElement(fdArray, i);
- if (fileDescriptor == NULL) {
- return false;
- }
-
- const int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- if (isValidFd(fd) && FD_ISSET(fd, &fdSet)) {
- flagArray[i + offset] = op;
- } else {
- flagArray[i + offset] = SOCKET_OP_NONE;
- }
- }
- return true;
-}
-
-static jboolean OSNetworkSystem_selectImpl(JNIEnv* env, jclass,
- jobjectArray readFDArray, jobjectArray writeFDArray, jint countReadC,
- jint countWriteC, jintArray outFlags, jlong timeoutMs) {
-
- // Initialize the fd_sets.
- int maxFd = -1;
- fd_set readFds;
- fd_set writeFds;
- FD_ZERO(&readFds);
- FD_ZERO(&writeFds);
- bool initialized = initFdSet(env, readFDArray, countReadC, &readFds, &maxFd) &&
- initFdSet(env, writeFDArray, countWriteC, &writeFds, &maxFd);
- if (!initialized) {
- return -1;
- }
-
- // Initialize the timeout, if any.
- timeval tv;
- timeval* tvp = NULL;
- if (timeoutMs >= 0) {
- tv = toTimeval(timeoutMs);
- tvp = &tv;
- }
-
- // Perform the select.
- int result = select(maxFd + 1, &readFds, &writeFds, NULL, tvp);
- if (result == 0) {
- // Timeout.
- return JNI_FALSE;
- } else if (result == -1) {
- // Error.
- if (errno == EINTR) {
- return JNI_FALSE;
- } else {
- jniThrowSocketException(env, errno);
- return JNI_FALSE;
- }
- }
-
- // Translate the result into the int[] we're supposed to fill in.
- ScopedIntArrayRW flagArray(env, outFlags);
- if (flagArray.get() == NULL) {
- return JNI_FALSE;
- }
- return translateFdSet(env, readFDArray, countReadC, readFds, flagArray.get(), 0, SOCKET_OP_READ) &&
- translateFdSet(env, writeFDArray, countWriteC, writeFds, flagArray.get(), countReadC, SOCKET_OP_WRITE);
-}
-
-static void OSNetworkSystem_close(JNIEnv* env, jobject, jobject fileDescriptor) {
- NetFd fd(env, fileDescriptor);
- if (fd.isClosed()) {
- // Socket.close doesn't throw if you try to close an already-closed socket.
- env->ExceptionClear();
- return;
- }
-
- int oldFd = fd.get();
- jniSetFileDescriptorOfFD(env, fileDescriptor, -1);
- AsynchronousSocketCloseMonitor::signalBlockedThreads(oldFd);
- close(oldFd);
-}
-
-static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(OSNetworkSystem, accept, "(Ljava/io/FileDescriptor;Ljava/net/SocketImpl;Ljava/io/FileDescriptor;)V"),
- NATIVE_METHOD(OSNetworkSystem, bind, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"),
- NATIVE_METHOD(OSNetworkSystem, close, "(Ljava/io/FileDescriptor;)V"),
- NATIVE_METHOD(OSNetworkSystem, connect, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)Z"),
- NATIVE_METHOD(OSNetworkSystem, disconnectDatagram, "(Ljava/io/FileDescriptor;)V"),
- NATIVE_METHOD(OSNetworkSystem, isConnected, "(Ljava/io/FileDescriptor;I)Z"),
- NATIVE_METHOD(OSNetworkSystem, read, "(Ljava/io/FileDescriptor;[BII)I"),
- NATIVE_METHOD(OSNetworkSystem, readDirect, "(Ljava/io/FileDescriptor;II)I"),
- NATIVE_METHOD(OSNetworkSystem, recv, "(Ljava/io/FileDescriptor;Ljava/net/DatagramPacket;[BIIZZ)I"),
- NATIVE_METHOD(OSNetworkSystem, recvDirect, "(Ljava/io/FileDescriptor;Ljava/net/DatagramPacket;IIIZZ)I"),
- NATIVE_METHOD(OSNetworkSystem, selectImpl, "([Ljava/io/FileDescriptor;[Ljava/io/FileDescriptor;II[IJ)Z"),
- NATIVE_METHOD(OSNetworkSystem, send, "(Ljava/io/FileDescriptor;[BIIILjava/net/InetAddress;)I"),
- NATIVE_METHOD(OSNetworkSystem, sendDirect, "(Ljava/io/FileDescriptor;IIIILjava/net/InetAddress;)I"),
- NATIVE_METHOD(OSNetworkSystem, sendUrgentData, "(Ljava/io/FileDescriptor;B)V"),
- NATIVE_METHOD(OSNetworkSystem, write, "(Ljava/io/FileDescriptor;[BII)I"),
- NATIVE_METHOD(OSNetworkSystem, writeDirect, "(Ljava/io/FileDescriptor;III)I"),
-};
-
-int register_org_apache_harmony_luni_platform_OSNetworkSystem(JNIEnv* env) {
- AsynchronousSocketCloseMonitor::init();
- return jniRegisterNativeMethods(env, "org/apache/harmony/luni/platform/OSNetworkSystem", gMethods, NELEM(gMethods));
-}
diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk
index c7620b3..9535ce6 100644
--- a/luni/src/main/native/sub.mk
+++ b/luni/src/main/native/sub.mk
@@ -18,6 +18,7 @@
java_lang_ProcessManager.cpp \
java_lang_RealToString.cpp \
java_lang_StrictMath.cpp \
+ java_lang_StringToReal.cpp \
java_lang_System.cpp \
java_math_NativeBN.cpp \
java_nio_ByteOrder.cpp \
@@ -38,12 +39,11 @@
libcore_icu_NativeNormalizer.cpp \
libcore_icu_NativePluralRules.cpp \
libcore_icu_TimeZones.cpp \
+ libcore_io_AsynchronousCloseMonitor.cpp \
libcore_io_Memory.cpp \
libcore_io_OsConstants.cpp \
libcore_io_Posix.cpp \
libcore_net_RawSocket.cpp \
- org_apache_harmony_luni_platform_OSNetworkSystem.cpp \
- org_apache_harmony_luni_util_FloatingPointParser.cpp \
org_apache_harmony_xml_ExpatParser.cpp \
org_apache_harmony_xnet_provider_jsse_NativeCrypto.cpp \
readlink.cpp \
diff --git a/luni/src/test/java/libcore/io/Base64Test.java b/luni/src/test/java/libcore/io/Base64Test.java
new file mode 100644
index 0000000..edf24d4
--- /dev/null
+++ b/luni/src/test/java/libcore/io/Base64Test.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.io;
+
+import java.util.Arrays;
+import junit.framework.TestCase;
+
+public final class Base64Test extends TestCase {
+
+ public void testDecodeEmpty() throws Exception {
+ assertEquals("[]", Arrays.toString(Base64.decode(new byte[0])));
+ }
+
+ public void testEncode() throws Exception {
+ assertEncoded("");
+ assertEncoded("Eg==", 0x12);
+ assertEncoded("EjQ=", 0x12, 0x34 );
+ assertEncoded("EjRW", 0x12, 0x34, 0x56);
+ assertEncoded("EjRWeA==", 0x12, 0x34, 0x56, 0x78);
+ assertEncoded("EjRWeJo=", 0x12, 0x34, 0x56, 0x78, 0x9A);
+ assertEncoded("EjRWeJq8", 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc);
+ }
+
+ public void testEncodeDoesNotWrap() {
+ int[] data = new int[61];
+ Arrays.fill(data, 0xff);
+ String expected = "///////////////////////////////////////////////////////////////////////"
+ + "//////////w=="; // 84 chars
+ assertEncoded(expected, data);
+ }
+
+ public void assertEncoded(String expected , int... data) {
+ byte[] dataBytes = new byte[data.length];
+ for (int i = 0; i < data.length; i++) {
+ dataBytes[i] = (byte) data[i];
+ }
+ assertEquals(expected, Base64.encode(dataBytes));
+ }
+}
+
diff --git a/luni/src/test/java/libcore/io/DiskLruCacheTest.java b/luni/src/test/java/libcore/io/DiskLruCacheTest.java
new file mode 100644
index 0000000..808918d
--- /dev/null
+++ b/luni/src/test/java/libcore/io/DiskLruCacheTest.java
@@ -0,0 +1,755 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.io;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import junit.framework.TestCase;
+import static libcore.io.DiskLruCache.JOURNAL_FILE;
+import static libcore.io.DiskLruCache.MAGIC;
+import static libcore.io.DiskLruCache.VERSION_1;
+import tests.io.MockOs;
+
+public final class DiskLruCacheTest extends TestCase {
+ private final int appVersion = 100;
+ private String javaTmpDir;
+ private File cacheDir;
+ private File journalFile;
+ private DiskLruCache cache;
+ private final MockOs mockOs = new MockOs();
+
+ @Override public void setUp() throws Exception {
+ super.setUp();
+ javaTmpDir = System.getProperty("java.io.tmpdir");
+ cacheDir = new File(javaTmpDir, "DiskLruCacheTest");
+ cacheDir.mkdir();
+ journalFile = new File(cacheDir, JOURNAL_FILE);
+ for (File file : cacheDir.listFiles()) {
+ file.delete();
+ }
+ cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE);
+ mockOs.install();
+ }
+
+ @Override protected void tearDown() throws Exception {
+ mockOs.uninstall();
+ cache.close();
+ super.tearDown();
+ }
+
+ public void testEmptyCache() throws Exception {
+ cache.close();
+ assertJournalEquals();
+ }
+
+ public void testWriteAndReadEntry() throws Exception {
+ DiskLruCache.Editor creator = cache.edit("k1");
+ creator.set(0, "ABC");
+ creator.set(1, "DE");
+ assertNull(creator.getString(0));
+ assertNull(creator.newInputStream(0));
+ assertNull(creator.getString(1));
+ assertNull(creator.newInputStream(1));
+ creator.commit();
+
+ DiskLruCache.Snapshot snapshot = cache.get("k1");
+ assertEquals("ABC", snapshot.getString(0));
+ assertEquals("DE", snapshot.getString(1));
+ }
+
+ public void testReadAndWriteEntryAcrossCacheOpenAndClose() throws Exception {
+ DiskLruCache.Editor creator = cache.edit("k1");
+ creator.set(0, "A");
+ creator.set(1, "B");
+ creator.commit();
+ cache.close();
+
+ cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE);
+ DiskLruCache.Snapshot snapshot = cache.get("k1");
+ assertEquals("A", snapshot.getString(0));
+ assertEquals("B", snapshot.getString(1));
+ snapshot.close();
+ }
+
+ public void testJournalWithEditAndPublish() throws Exception {
+ DiskLruCache.Editor creator = cache.edit("k1");
+ assertJournalEquals("DIRTY k1"); // DIRTY must always be flushed
+ creator.set(0, "AB");
+ creator.set(1, "C");
+ creator.commit();
+ cache.close();
+ assertJournalEquals("DIRTY k1", "CLEAN k1 2 1");
+ }
+
+ public void testRevertedNewFileIsRemoveInJournal() throws Exception {
+ DiskLruCache.Editor creator = cache.edit("k1");
+ assertJournalEquals("DIRTY k1"); // DIRTY must always be flushed
+ creator.set(0, "AB");
+ creator.set(1, "C");
+ creator.abort();
+ cache.close();
+ assertJournalEquals("DIRTY k1", "REMOVE k1");
+ }
+
+ public void testUnterminatedEditIsRevertedOnClose() throws Exception {
+ cache.edit("k1");
+ cache.close();
+ assertJournalEquals("DIRTY k1", "REMOVE k1");
+ }
+
+ public void testJournalDoesNotIncludeReadOfYetUnpublishedValue() throws Exception {
+ DiskLruCache.Editor creator = cache.edit("k1");
+ assertNull(cache.get("k1"));
+ creator.set(0, "A");
+ creator.set(1, "BC");
+ creator.commit();
+ cache.close();
+ assertJournalEquals("DIRTY k1", "CLEAN k1 1 2");
+ }
+
+ public void testJournalWithEditAndPublishAndRead() throws Exception {
+ DiskLruCache.Editor k1Creator = cache.edit("k1");
+ k1Creator.set(0, "AB");
+ k1Creator.set(1, "C");
+ k1Creator.commit();
+ DiskLruCache.Editor k2Creator = cache.edit("k2");
+ k2Creator.set(0, "DEF");
+ k2Creator.set(1, "G");
+ k2Creator.commit();
+ DiskLruCache.Snapshot k1Snapshot = cache.get("k1");
+ k1Snapshot.close();
+ cache.close();
+ assertJournalEquals("DIRTY k1", "CLEAN k1 2 1",
+ "DIRTY k2", "CLEAN k2 3 1",
+ "READ k1");
+ }
+
+ public void testCannotOperateOnEditAfterPublish() throws Exception {
+ DiskLruCache.Editor editor = cache.edit("k1");
+ editor.set(0, "A");
+ editor.set(1, "B");
+ editor.commit();
+ assertInoperable(editor);
+ }
+
+ public void testCannotOperateOnEditAfterRevert() throws Exception {
+ DiskLruCache.Editor editor = cache.edit("k1");
+ editor.set(0, "A");
+ editor.set(1, "B");
+ editor.abort();
+ assertInoperable(editor);
+ }
+
+ public void testExplicitRemoveAppliedToDiskImmediately() throws Exception {
+ DiskLruCache.Editor editor = cache.edit("k1");
+ editor.set(0, "ABC");
+ editor.set(1, "B");
+ editor.commit();
+ File k1 = getCleanFile("k1", 0);
+ assertEquals("ABC", readFile(k1));
+ cache.remove("k1");
+ assertFalse(k1.exists());
+ }
+
+ /**
+ * Each read sees a snapshot of the file at the time read was called.
+ * This means that two reads of the same key can see different data.
+ */
+ public void testReadAndWriteOverlapsMaintainConsistency() throws Exception {
+ DiskLruCache.Editor v1Creator = cache.edit("k1");
+ v1Creator.set(0, "AAaa");
+ v1Creator.set(1, "BBbb");
+ v1Creator.commit();
+
+ DiskLruCache.Snapshot snapshot1 = cache.get("k1");
+ InputStream inV1 = snapshot1.getInputStream(0);
+ assertEquals('A', inV1.read());
+ assertEquals('A', inV1.read());
+
+ DiskLruCache.Editor v1Updater = cache.edit("k1");
+ v1Updater.set(0, "CCcc");
+ v1Updater.set(1, "DDdd");
+ v1Updater.commit();
+
+ DiskLruCache.Snapshot snapshot2 = cache.get("k1");
+ assertEquals("CCcc", snapshot2.getString(0));
+ assertEquals("DDdd", snapshot2.getString(1));
+ snapshot2.close();
+
+ assertEquals('a', inV1.read());
+ assertEquals('a', inV1.read());
+ assertEquals("BBbb", snapshot1.getString(1));
+ snapshot1.close();
+ }
+
+ public void testOpenWithDirtyKeyDeletesAllFilesForThatKey() throws Exception {
+ cache.close();
+ File cleanFile0 = getCleanFile("k1", 0);
+ File cleanFile1 = getCleanFile("k1", 1);
+ File dirtyFile0 = getDirtyFile("k1", 0);
+ File dirtyFile1 = getDirtyFile("k1", 1);
+ writeFile(cleanFile0, "A");
+ writeFile(cleanFile1, "B");
+ writeFile(dirtyFile0, "C");
+ writeFile(dirtyFile1, "D");
+ createJournal("CLEAN k1 1 1", "DIRTY k1");
+ cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE);
+ assertFalse(cleanFile0.exists());
+ assertFalse(cleanFile1.exists());
+ assertFalse(dirtyFile0.exists());
+ assertFalse(dirtyFile1.exists());
+ assertNull(cache.get("k1"));
+ }
+
+ public void testOpenWithInvalidVersionClearsDirectory() throws Exception {
+ cache.close();
+ generateSomeGarbageFiles();
+ createJournalWithHeader(MAGIC, "0", "100", "2", "");
+ cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE);
+ assertGarbageFilesAllDeleted();
+ }
+
+ public void testOpenWithInvalidAppVersionClearsDirectory() throws Exception {
+ cache.close();
+ generateSomeGarbageFiles();
+ createJournalWithHeader(MAGIC, "1", "101", "2", "");
+ cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE);
+ assertGarbageFilesAllDeleted();
+ }
+
+ public void testOpenWithInvalidValueCountClearsDirectory() throws Exception {
+ cache.close();
+ generateSomeGarbageFiles();
+ createJournalWithHeader(MAGIC, "1", "100", "1", "");
+ cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE);
+ assertGarbageFilesAllDeleted();
+ }
+
+ public void testOpenWithInvalidBlankLineClearsDirectory() throws Exception {
+ cache.close();
+ generateSomeGarbageFiles();
+ createJournalWithHeader(MAGIC, "1", "100", "2", "x");
+ cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE);
+ assertGarbageFilesAllDeleted();
+ }
+
+ public void testOpenWithInvalidJournalLineClearsDirectory() throws Exception {
+ cache.close();
+ generateSomeGarbageFiles();
+ createJournal("CLEAN k1 1 1", "BOGUS");
+ cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE);
+ assertGarbageFilesAllDeleted();
+ assertNull(cache.get("k1"));
+ }
+
+ public void testOpenWithInvalidFileSizeClearsDirectory() throws Exception {
+ cache.close();
+ generateSomeGarbageFiles();
+ createJournal("CLEAN k1 0000x001 1");
+ cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE);
+ assertGarbageFilesAllDeleted();
+ assertNull(cache.get("k1"));
+ }
+
+ public void testOpenWithTruncatedLineDiscardsThatLine() throws Exception {
+ cache.close();
+ writeFile(getCleanFile("k1", 0), "A");
+ writeFile(getCleanFile("k1", 1), "B");
+ Writer writer = new FileWriter(journalFile);
+ writer.write(MAGIC + "\n" + VERSION_1 + "\n100\n2\n\nCLEAN k1 1 1"); // no trailing newline
+ writer.close();
+ cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE);
+ assertNull(cache.get("k1"));
+ }
+
+ public void testOpenWithTooManyFileSizesClearsDirectory() throws Exception {
+ cache.close();
+ generateSomeGarbageFiles();
+ createJournal("CLEAN k1 1 1 1");
+ cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE);
+ assertGarbageFilesAllDeleted();
+ assertNull(cache.get("k1"));
+ }
+
+ public void testKeyWithSpaceNotPermitted() throws Exception {
+ try {
+ cache.edit("my key");
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testKeyWithNewlineNotPermitted() throws Exception {
+ try {
+ cache.edit("my\nkey");
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testKeyWithCarriageReturnNotPermitted() throws Exception {
+ try {
+ cache.edit("my\rkey");
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testNullKeyThrows() throws Exception {
+ try {
+ cache.edit(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ public void testCreateNewEntryWithTooFewValuesFails() throws Exception {
+ DiskLruCache.Editor creator = cache.edit("k1");
+ creator.set(1, "A");
+ try {
+ creator.commit();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+
+ assertFalse(getCleanFile("k1", 0).exists());
+ assertFalse(getCleanFile("k1", 1).exists());
+ assertFalse(getDirtyFile("k1", 0).exists());
+ assertFalse(getDirtyFile("k1", 1).exists());
+ assertNull(cache.get("k1"));
+
+ DiskLruCache.Editor creator2 = cache.edit("k1");
+ creator2.set(0, "B");
+ creator2.set(1, "C");
+ creator2.commit();
+ }
+
+ public void testRevertWithTooFewValues() throws Exception {
+ DiskLruCache.Editor creator = cache.edit("k1");
+ creator.set(1, "A");
+ creator.abort();
+ assertFalse(getCleanFile("k1", 0).exists());
+ assertFalse(getCleanFile("k1", 1).exists());
+ assertFalse(getDirtyFile("k1", 0).exists());
+ assertFalse(getDirtyFile("k1", 1).exists());
+ assertNull(cache.get("k1"));
+ }
+
+ public void testUpdateExistingEntryWithTooFewValuesReusesPreviousValues() throws Exception {
+ DiskLruCache.Editor creator = cache.edit("k1");
+ creator.set(0, "A");
+ creator.set(1, "B");
+ creator.commit();
+
+ DiskLruCache.Editor updater = cache.edit("k1");
+ updater.set(0, "C");
+ updater.commit();
+
+ DiskLruCache.Snapshot snapshot = cache.get("k1");
+ assertEquals("C", snapshot.getString(0));
+ assertEquals("B", snapshot.getString(1));
+ snapshot.close();
+ }
+
+ public void testEvictOnInsert() throws Exception {
+ cache.close();
+ cache = DiskLruCache.open(cacheDir, appVersion, 2, 10);
+
+ set("A", "a", "aaa"); // size 4
+ set("B", "bb", "bbbb"); // size 6
+ assertEquals(10, cache.size());
+
+ // cause the size to grow to 12 should evict 'A'
+ set("C", "c", "c");
+ cache.flush();
+ assertEquals(8, cache.size());
+ assertAbsent("A");
+ assertValue("B", "bb", "bbbb");
+ assertValue("C", "c", "c");
+
+ // causing the size to grow to 10 should evict nothing
+ set("D", "d", "d");
+ cache.flush();
+ assertEquals(10, cache.size());
+ assertAbsent("A");
+ assertValue("B", "bb", "bbbb");
+ assertValue("C", "c", "c");
+ assertValue("D", "d", "d");
+
+ // causing the size to grow to 18 should evict 'B' and 'C'
+ set("E", "eeee", "eeee");
+ cache.flush();
+ assertEquals(10, cache.size());
+ assertAbsent("A");
+ assertAbsent("B");
+ assertAbsent("C");
+ assertValue("D", "d", "d");
+ assertValue("E", "eeee", "eeee");
+ }
+
+ public void testEvictOnUpdate() throws Exception {
+ cache.close();
+ cache = DiskLruCache.open(cacheDir, appVersion, 2, 10);
+
+ set("A", "a", "aa"); // size 3
+ set("B", "b", "bb"); // size 3
+ set("C", "c", "cc"); // size 3
+ assertEquals(9, cache.size());
+
+ // causing the size to grow to 11 should evict 'A'
+ set("B", "b", "bbbb");
+ cache.flush();
+ assertEquals(8, cache.size());
+ assertAbsent("A");
+ assertValue("B", "b", "bbbb");
+ assertValue("C", "c", "cc");
+ }
+
+ public void testEvictionHonorsLruFromCurrentSession() throws Exception {
+ cache.close();
+ cache = DiskLruCache.open(cacheDir, appVersion, 2, 10);
+ set("A", "a", "a");
+ set("B", "b", "b");
+ set("C", "c", "c");
+ set("D", "d", "d");
+ set("E", "e", "e");
+ cache.get("B").close(); // 'B' is now least recently used
+
+ // causing the size to grow to 12 should evict 'A'
+ set("F", "f", "f");
+ // causing the size to grow to 12 should evict 'C'
+ set("G", "g", "g");
+ cache.flush();
+ assertEquals(10, cache.size());
+ assertAbsent("A");
+ assertValue("B", "b", "b");
+ assertAbsent("C");
+ assertValue("D", "d", "d");
+ assertValue("E", "e", "e");
+ assertValue("F", "f", "f");
+ }
+
+ public void testEvictionHonorsLruFromPreviousSession() throws Exception {
+ set("A", "a", "a");
+ set("B", "b", "b");
+ set("C", "c", "c");
+ set("D", "d", "d");
+ set("E", "e", "e");
+ set("F", "f", "f");
+ cache.get("B").close(); // 'B' is now least recently used
+ assertEquals(12, cache.size());
+ cache.close();
+ cache = DiskLruCache.open(cacheDir, appVersion, 2, 10);
+
+ set("G", "g", "g");
+ cache.flush();
+ assertEquals(10, cache.size());
+ assertAbsent("A");
+ assertValue("B", "b", "b");
+ assertAbsent("C");
+ assertValue("D", "d", "d");
+ assertValue("E", "e", "e");
+ assertValue("F", "f", "f");
+ assertValue("G", "g", "g");
+ }
+
+ public void testCacheSingleEntryOfSizeGreaterThanMaxSize() throws Exception {
+ cache.close();
+ cache = DiskLruCache.open(cacheDir, appVersion, 2, 10);
+ set("A", "aaaaa", "aaaaaa"); // size=11
+ cache.flush();
+ assertAbsent("A");
+ }
+
+ public void testCacheSingleValueOfSizeGreaterThanMaxSize() throws Exception {
+ cache.close();
+ cache = DiskLruCache.open(cacheDir, appVersion, 2, 10);
+ set("A", "aaaaaaaaaaa", "a"); // size=12
+ cache.flush();
+ assertAbsent("A");
+ }
+
+ public void testConstructorDoesNotAllowZeroCacheSize() throws Exception {
+ try {
+ DiskLruCache.open(cacheDir, appVersion, 2, 0);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testConstructorDoesNotAllowZeroValuesPerEntry() throws Exception {
+ try {
+ DiskLruCache.open(cacheDir, appVersion, 0, 10);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testRemoveAbsentElement() throws Exception {
+ cache.remove("A");
+ }
+
+ public void testReadingTheSameStreamMultipleTimes() throws Exception {
+ set("A", "a", "b");
+ DiskLruCache.Snapshot snapshot = cache.get("A");
+ assertSame(snapshot.getInputStream(0), snapshot.getInputStream(0));
+ snapshot.close();
+ }
+
+ public void testRebuildJournalOnRepeatedReads() throws Exception {
+ set("A", "a", "a");
+ set("B", "b", "b");
+ long lastJournalLength = 0;
+ while (true) {
+ long journalLength = journalFile.length();
+ assertValue("A", "a", "a");
+ assertValue("B", "b", "b");
+ if (journalLength < lastJournalLength) {
+ System.out.printf("Journal compacted from %s bytes to %s bytes\n",
+ lastJournalLength, journalLength);
+ break; // test passed!
+ }
+ lastJournalLength = journalLength;
+ }
+ }
+
+ public void testRebuildJournalOnRepeatedEdits() throws Exception {
+ long lastJournalLength = 0;
+ while (true) {
+ long journalLength = journalFile.length();
+ set("A", "a", "a");
+ set("B", "b", "b");
+ if (journalLength < lastJournalLength) {
+ System.out.printf("Journal compacted from %s bytes to %s bytes\n",
+ lastJournalLength, journalLength);
+ break;
+ }
+ lastJournalLength = journalLength;
+ }
+
+ // sanity check that a rebuilt journal behaves normally
+ assertValue("A", "a", "a");
+ assertValue("B", "b", "b");
+ }
+
+ public void testOpenCreatesDirectoryIfNecessary() throws Exception {
+ cache.close();
+ File dir = new File(javaTmpDir, "testOpenCreatesDirectoryIfNecessary");
+ cache = DiskLruCache.open(dir, appVersion, 2, Integer.MAX_VALUE);
+ set("A", "a", "a");
+ assertTrue(new File(dir, "A.0").exists());
+ assertTrue(new File(dir, "A.1").exists());
+ assertTrue(new File(dir, "journal").exists());
+ }
+
+ public void testFileDeletedExternally() throws Exception {
+ set("A", "a", "a");
+ getCleanFile("A", 1).delete();
+ assertNull(cache.get("A"));
+ }
+
+ public void testFileBecomesInaccessibleDuringReadResultsInIoException() throws Exception {
+ set("A", "aaaaa", "a");
+ DiskLruCache.Snapshot snapshot = cache.get("A");
+ InputStream in = snapshot.getInputStream(0);
+ assertEquals('a', in.read());
+ mockOs.enqueueFault("read");
+ try {
+ in.read();
+ fail();
+ } catch (IOException expected) {
+ }
+ snapshot.close();
+ }
+
+ public void testFileBecomesInaccessibleDuringWriteIsSilentlyDiscarded() throws Exception {
+ set("A", "a", "a");
+ DiskLruCache.Editor editor = cache.edit("A");
+ OutputStream out0 = editor.newOutputStream(0);
+ out0.write('b');
+ out0.close();
+ OutputStream out1 = editor.newOutputStream(1);
+ out1.write('c');
+ mockOs.enqueueFault("write");
+ out1.write('c'); // this doesn't throw...
+ out1.close();
+ editor.commit(); // ... but this will abort
+ assertAbsent("A");
+ }
+
+ private void assertJournalEquals(String... expectedBodyLines) throws Exception {
+ List<String> expectedLines = new ArrayList<String>();
+ expectedLines.add(MAGIC);
+ expectedLines.add(VERSION_1);
+ expectedLines.add("100");
+ expectedLines.add("2");
+ expectedLines.add("");
+ expectedLines.addAll(Arrays.asList(expectedBodyLines));
+ assertEquals(expectedLines, readJournalLines());
+ }
+
+ private void createJournal(String... bodyLines) throws Exception {
+ createJournalWithHeader(MAGIC, VERSION_1, "100", "2", "", bodyLines);
+ }
+
+ private void createJournalWithHeader(String magic, String version, String appVersion,
+ String valueCount, String blank, String... bodyLines) throws Exception {
+ Writer writer = new FileWriter(journalFile);
+ writer.write(magic + "\n");
+ writer.write(version + "\n");
+ writer.write(appVersion + "\n");
+ writer.write(valueCount + "\n");
+ writer.write(blank + "\n");
+ for (String line : bodyLines) {
+ writer.write(line);
+ writer.write('\n');
+ }
+ writer.close();
+ }
+
+ private List<String> readJournalLines() throws Exception {
+ List<String> result = new ArrayList<String>();
+ BufferedReader reader = new BufferedReader(new FileReader(journalFile));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ result.add(line);
+ }
+ reader.close();
+ return result;
+ }
+
+ private File getCleanFile(String key, int index) {
+ return new File(cacheDir, key + "." + index);
+ }
+
+ private File getDirtyFile(String key, int index) {
+ return new File(cacheDir, key + "." + index + ".tmp");
+ }
+
+ private String readFile(File file) throws Exception {
+ Reader reader = new FileReader(file);
+ StringWriter writer = new StringWriter();
+ char[] buffer = new char[1024];
+ int count;
+ while ((count = reader.read(buffer)) != -1) {
+ writer.write(buffer, 0, count);
+ }
+ reader.close();
+ return writer.toString();
+ }
+
+ public void writeFile(File file, String content) throws Exception {
+ FileWriter writer = new FileWriter(file);
+ writer.write(content);
+ writer.close();
+ }
+
+ private void assertInoperable(DiskLruCache.Editor editor) throws Exception {
+ try {
+ editor.getString(0);
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ editor.set(0, "A");
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ editor.newInputStream(0);
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ editor.newOutputStream(0);
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ editor.commit();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ editor.abort();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ private void generateSomeGarbageFiles() throws Exception {
+ File dir1 = new File(cacheDir, "dir1");
+ File dir2 = new File(dir1, "dir2");
+ writeFile(getCleanFile("g1", 0), "A");
+ writeFile(getCleanFile("g1", 1), "B");
+ writeFile(getCleanFile("g2", 0), "C");
+ writeFile(getCleanFile("g2", 1), "D");
+ writeFile(getCleanFile("g2", 1), "D");
+ writeFile(new File(cacheDir, "otherFile0"), "E");
+ dir1.mkdir();
+ dir2.mkdir();
+ writeFile(new File(dir2, "otherFile1"), "F");
+ }
+
+ private void assertGarbageFilesAllDeleted() throws Exception {
+ assertFalse(getCleanFile("g1", 0).exists());
+ assertFalse(getCleanFile("g1", 1).exists());
+ assertFalse(getCleanFile("g2", 0).exists());
+ assertFalse(getCleanFile("g2", 1).exists());
+ assertFalse(new File(cacheDir, "otherFile0").exists());
+ assertFalse(new File(cacheDir, "dir1").exists());
+ }
+
+ private void set(String key, String value0, String value1) throws Exception {
+ DiskLruCache.Editor editor = cache.edit(key);
+ editor.set(0, value0);
+ editor.set(1, value1);
+ editor.commit();
+ }
+
+ private void assertAbsent(String key) throws Exception {
+ DiskLruCache.Snapshot snapshot = cache.get(key);
+ if (snapshot != null) {
+ snapshot.close();
+ fail();
+ }
+ assertFalse(getCleanFile(key, 0).exists());
+ assertFalse(getCleanFile(key, 1).exists());
+ assertFalse(getDirtyFile(key, 0).exists());
+ assertFalse(getDirtyFile(key, 1).exists());
+ }
+
+ private void assertValue(String key, String value0, String value1) throws Exception {
+ DiskLruCache.Snapshot snapshot = cache.get(key);
+ assertEquals(value0, snapshot.getString(0));
+ assertEquals(value1, snapshot.getString(1));
+ assertTrue(getCleanFile(key, 0).exists());
+ assertTrue(getCleanFile(key, 1).exists());
+ snapshot.close();
+ }
+}
\ No newline at end of file
diff --git a/luni/src/test/java/libcore/java/io/FileOutputStreamTest.java b/luni/src/test/java/libcore/java/io/FileOutputStreamTest.java
index 8b706cd..a1b9920 100644
--- a/luni/src/test/java/libcore/java/io/FileOutputStreamTest.java
+++ b/luni/src/test/java/libcore/java/io/FileOutputStreamTest.java
@@ -40,4 +40,31 @@
} catch (IOException expected) {
}
}
+
+ public void testClose() throws Exception {
+ FileOutputStream fos = new FileOutputStream(File.createTempFile("FileOutputStreamTest", "tmp"));
+
+ // Closing an already-closed stream is a no-op...
+ fos.close();
+ // ...as is flushing...
+ fos.flush();
+
+ // ...but any explicit write is an error.
+ byte[] bytes = "hello".getBytes();
+ try {
+ fos.write(bytes);
+ fail();
+ } catch (IOException expected) {
+ }
+ try {
+ fos.write(bytes, 0, 2);
+ fail();
+ } catch (IOException expected) {
+ }
+ try {
+ fos.write(42);
+ fail();
+ } catch (IOException expected) {
+ }
+ }
}
diff --git a/luni/src/test/java/libcore/java/io/OldFileOutputStreamTest.java b/luni/src/test/java/libcore/java/io/OldFileOutputStreamTest.java
deleted file mode 100644
index 3661b3e..0000000
--- a/luni/src/test/java/libcore/java/io/OldFileOutputStreamTest.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package libcore.java.io;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-public class OldFileOutputStreamTest extends junit.framework.TestCase {
-
- public String fileName;
-
- FileOutputStream fos;
-
- FileInputStream fis;
-
- File f;
-
- String tmpDirName = System.getProperty("java.io.tmpdir");
-
- File tmpDir = new File(tmpDirName);
-
- public String fileString = "Test_All_Tests\nTest_java_io_BufferedInputStream\nTest_java_io_BufferedOutputStream\nTest_java_io_ByteArrayInputStream\nTest_java_io_ByteArrayOutputStream\nTest_java_io_DataInputStream\nTest_java_io_File\nTest_java_io_FileDescriptor\nTest_java_io_FileInputStream\nTest_java_io_FileNotFoundException\nTest_FileOutputStream\nTest_java_io_FilterInputStream\nTest_java_io_FilterOutputStream\nTest_java_io_InputStream\nTest_java_io_IOException\nTest_java_io_OutputStream\nTest_java_io_PrintStream\nTest_java_io_RandomAccessFile\nTest_java_io_SyncFailedException\nTest_java_lang_AbstractMethodError\nTest_java_lang_ArithmeticException\nTest_java_lang_ArrayIndexOutOfBoundsException\nTest_java_lang_ArrayStoreException\nTest_java_lang_Boolean\nTest_java_lang_Byte\nTest_java_lang_Character\nTest_java_lang_Class\nTest_java_lang_ClassCastException\nTest_java_lang_ClassCircularityError\nTest_java_lang_ClassFormatError\nTest_java_lang_ClassLoader\nTest_java_lang_ClassNotFoundException\nTest_java_lang_CloneNotSupportedException\nTest_java_lang_Double\nTest_java_lang_Error\nTest_java_lang_Exception\nTest_java_lang_ExceptionInInitializerError\nTest_java_lang_Float\nTest_java_lang_IllegalAccessError\nTest_java_lang_IllegalAccessException\nTest_java_lang_IllegalArgumentException\nTest_java_lang_IllegalMonitorStateException\nTest_java_lang_IllegalThreadStateException\nTest_java_lang_IncompatibleClassChangeError\nTest_java_lang_IndexOutOfBoundsException\nTest_java_lang_InstantiationError\nTest_java_lang_InstantiationException\nTest_java_lang_Integer\nTest_java_lang_InternalError\nTest_java_lang_InterruptedException\nTest_java_lang_LinkageError\nTest_java_lang_Long\nTest_java_lang_Math\nTest_java_lang_NegativeArraySizeException\nTest_java_lang_NoClassDefFoundError\nTest_java_lang_NoSuchFieldError\nTest_java_lang_NoSuchMethodError\nTest_java_lang_NullPointerException\nTest_java_lang_Number\nTest_java_lang_NumberFormatException\nTest_java_lang_Object\nTest_java_lang_OutOfMemoryError\nTest_java_lang_RuntimeException\nTest_java_lang_SecurityManager\nTest_java_lang_Short\nTest_java_lang_StackOverflowError\nTest_java_lang_String\nTest_java_lang_StringBuffer\nTest_java_lang_StringIndexOutOfBoundsException\nTest_java_lang_System\nTest_java_lang_Thread\nTest_java_lang_ThreadDeath\nTest_java_lang_ThreadGroup\nTest_java_lang_Throwable\nTest_java_lang_UnknownError\nTest_java_lang_UnsatisfiedLinkError\nTest_java_lang_VerifyError\nTest_java_lang_VirtualMachineError\nTest_java_lang_vm_Image\nTest_java_lang_vm_MemorySegment\nTest_java_lang_vm_ROMStoreException\nTest_java_lang_vm_VM\nTest_java_lang_Void\nTest_java_net_BindException\nTest_java_net_ConnectException\nTest_java_net_DatagramPacket\nTest_java_net_DatagramSocket\nTest_java_net_DatagramSocketImpl\nTest_java_net_InetAddress\nTest_java_net_NoRouteToHostException\nTest_java_net_PlainDatagramSocketImpl\nTest_java_net_PlainSocketImpl\nTest_java_net_Socket\nTest_java_net_SocketException\nTest_java_net_SocketImpl\nTest_java_net_SocketInputStream\nTest_java_net_SocketOutputStream\nTest_java_net_UnknownHostException\nTest_java_util_ArrayEnumerator\nTest_java_util_Date\nTest_java_util_EventObject\nTest_java_util_HashEnumerator\nTest_java_util_Hashtable\nTest_java_util_Properties\nTest_java_util_ResourceBundle\nTest_java_util_tm\nTest_java_util_Vector\n";
-
- public void test_ConstructorLjava_io_File() throws Exception {
- try {
- fos = new FileOutputStream(tmpDir);
- fail("Test 1: FileNotFoundException expected.");
- } catch (FileNotFoundException e) {
- // Expected.
- }
-
- f = new File(fileName = System.getProperty("java.io.tmpdir"), "fos.tst");
- fos = new FileOutputStream(f);
- }
-
- public void test_ConstructorLjava_io_FileZ() throws Exception {
- try {
- fos = new FileOutputStream(tmpDir, false);
- fail("Test 1: FileNotFoundException expected.");
- } catch (FileNotFoundException e) {
- // Expected.
- }
-
- f = new File(tmpDirName, "fos.tst");
- fos = new FileOutputStream(f, false);
- fos.write("FZ1".getBytes(), 0, 3);
- fos.close();
- // Append data to existing file
- fos = new FileOutputStream(f, true);
- fos.write(fileString.getBytes());
- fos.close();
- byte[] buf = new byte[fileString.length() + 3];
- fis = new FileInputStream(f);
- fis.read(buf, 0, buf.length);
- assertTrue("Test 2: Failed to create appending stream.", new String(buf, 0,
- buf.length).equals("FZ1" + fileString));
- fis.close();
-
- // Check that the existing file is overwritten
- fos = new FileOutputStream(f, false);
- fos.write("FZ2".getBytes(), 0, 3);
- fos.close();
- fis = new FileInputStream(f);
- int bytesRead = fis.read(buf, 0, buf.length);
- assertTrue("Test 3: Failed to overwrite stream.", new String(buf, 0,
- bytesRead).equals("FZ2"));
- }
-
- public void test_ConstructorLjava_lang_String() throws Exception {
- try {
- fos = new FileOutputStream(tmpDirName);
- fail("Test 1: FileNotFoundException expected.");
- } catch (FileNotFoundException e) {
- // Expected.
- }
- }
-
- public void test_ConstructorLjava_lang_StringZ() throws Exception {
- try {
- fos = new FileOutputStream(tmpDirName, true);
- fail("Test 1: FileNotFoundException expected.");
- } catch (FileNotFoundException e) {
- // Expected.
- }
-
- f = new File(tmpDirName, "fos.tst");
- fos = new FileOutputStream(f.getPath(), false);
- fos.write("HI".getBytes(), 0, 2);
- fos.close();
- // Append data to existing file
- fos = new FileOutputStream(f.getPath(), true);
- fos.write(fileString.getBytes());
- fos.close();
- byte[] buf = new byte[fileString.length() + 2];
- fis = new FileInputStream(f.getPath());
- fis.read(buf, 0, buf.length);
- assertTrue("Failed to create appending stream", new String(buf, 0,
- buf.length).equals("HI" + fileString));
- fis.close();
-
- // Check that the existing file is overwritten
- fos = new FileOutputStream(f.getPath(), false);
- fos.write("HI".getBytes(), 0, 2);
- fos.close();
- fis = new FileInputStream(f.getPath());
- int bytesRead = fis.read(buf, 0, buf.length);
- assertTrue("Failed to overwrite stream", new String(buf, 0,
- bytesRead).equals("HI"));
- }
-
- public void test_write$B() throws Exception {
- // Test for method void java.io.FileOutputStream.write(byte [])
- f = new File(System.getProperty("java.io.tmpdir"), "output.tst");
- fos = new FileOutputStream(f.getPath());
- fos.write(fileString.getBytes());
- fos.close();
- try {
- fos.write(fileString.getBytes());
- fail("Test 1: IOException expected.");
- } catch (IOException e) {
- // Expected.
- }
-
- fis = new FileInputStream(f.getPath());
- byte rbytes[] = new byte[4000];
- fis.read(rbytes, 0, fileString.length());
- assertTrue("Test 2: Incorrect string written or read.",
- new String(rbytes, 0, fileString.length()).equals(fileString));
- }
-
- public void test_writeI() throws IOException {
- // Test for method void java.io.FileOutputStream.write(int)
- f = new File(System.getProperty("java.io.tmpdir"), "output.tst");
- fos = new FileOutputStream(f.getPath());
- fos.write('t');
- fos.close();
- try {
- fos.write(42);
- fail("Test: IOException expected.");
- } catch (IOException e) {
- // Expected.
- }
-
- fis = new FileInputStream(f.getPath());
- assertEquals("Test 1: Incorrect char written or read.",
- 't', fis.read());
- }
-
- public void test_write$BII3() {
- try {
- new FileOutputStream(new FileDescriptor()).write(new byte[1], 0, 0);
- } catch (Exception e) {
- fail("Unexpected exception: " + e);
- }
- }
-
- public void test_getChannel() throws Exception {
- // Make sure that system properties are set correctly
- if (tmpDir == null) {
- throw new Exception("System property java.io.tmpdir not defined.");
- }
- File tmpfile = File.createTempFile("FileOutputStream", "tmp");
- tmpfile.deleteOnExit();
- FileOutputStream fos = new FileOutputStream(tmpfile);
- byte[] b = new byte[10];
- for (int i = 10; i < b.length; i++) {
- b[i] = (byte) i;
- }
- fos.write(b);
- fos.flush();
- fos.close();
- FileOutputStream f = new FileOutputStream(tmpfile, true);
- assertEquals(10, f.getChannel().position());
- }
-
- protected void tearDown() throws Exception {
- super.tearDown();
- if (f != null)
- f.delete();
- if (fis != null)
- fis.close();
- if (fos != null)
- fos.close();
- }
-}
diff --git a/luni/src/test/java/libcore/java/io/SerializationTest.java b/luni/src/test/java/libcore/java/io/SerializationTest.java
new file mode 100644
index 0000000..74becfe
--- /dev/null
+++ b/luni/src/test/java/libcore/java/io/SerializationTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.io;
+
+import java.io.Serializable;
+import junit.framework.TestCase;
+import libcore.java.util.SerializableTester;
+
+public final class SerializationTest extends TestCase {
+
+ // http://b/4471249
+ public void testSerializeFieldMadeTransient() throws Exception {
+ // this was created by serializing a FieldMadeTransient with a non-0 transientInt
+ String s = "aced0005737200346c6962636f72652e6a6176612e696f2e53657269616c697a6174696f6e54657"
+ + "374244669656c644d6164655472616e7369656e74000000000000000002000149000c7472616e736"
+ + "9656e74496e747870abababab";
+ FieldMadeTransient deserialized = (FieldMadeTransient) SerializableTester.deserializeHex(s);
+ assertEquals(0, deserialized.transientInt);
+ }
+
+ static class FieldMadeTransient implements Serializable {
+ private static final long serialVersionUID = 0L;
+ private transient int transientInt;
+ }
+}
diff --git a/luni/src/test/java/libcore/java/lang/IntegralToStringTest.java b/luni/src/test/java/libcore/java/lang/IntegralToStringTest.java
index 470c2e4..d2b1b0a 100644
--- a/luni/src/test/java/libcore/java/lang/IntegralToStringTest.java
+++ b/luni/src/test/java/libcore/java/lang/IntegralToStringTest.java
@@ -31,4 +31,11 @@
assertEquals("ffffffff", IntegralToString.intToHexString(-1, false, 0));
}
+
+ public void testBytesToHexString() {
+ assertEquals("abcdef", IntegralToString.bytesToHexString(
+ new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef }, false));
+ assertEquals("ABCDEF", IntegralToString.bytesToHexString(
+ new byte[] { (byte) 0xab, (byte) 0xcd, (byte) 0xef }, true));
+ }
}
diff --git a/luni/src/test/java/libcore/java/net/CookiesTest.java b/luni/src/test/java/libcore/java/net/CookiesTest.java
index 69dea61..87aa571 100644
--- a/luni/src/test/java/libcore/java/net/CookiesTest.java
+++ b/luni/src/test/java/libcore/java/net/CookiesTest.java
@@ -268,6 +268,39 @@
+ "b=\"banana\";$Path=\"/\";$Domain=\".local\"");
}
+ public void testRedirectsDoNotIncludeTooManyCookies() throws Exception {
+ MockWebServer redirectTarget = new MockWebServer();
+ redirectTarget.enqueue(new MockResponse().setBody("A"));
+ redirectTarget.play();
+
+ MockWebServer redirectSource = new MockWebServer();
+ redirectSource.enqueue(new MockResponse()
+ .setResponseCode(HttpURLConnection.HTTP_MOVED_TEMP)
+ .addHeader("Location: " + redirectTarget.getUrl("/")));
+ redirectSource.play();
+
+ CookieManager cookieManager = new CookieManager(null, ACCEPT_ORIGINAL_SERVER);
+ HttpCookie cookie = new HttpCookie("c", "cookie");
+ cookie.setDomain(".local");
+ cookie.setPath("/");
+ String portList = Integer.toString(redirectSource.getPort());
+ cookie.setPortlist(portList);
+ cookieManager.getCookieStore().add(redirectSource.getUrl("/").toURI(), cookie);
+ CookieHandler.setDefault(cookieManager);
+
+ get(redirectSource, "/");
+ RecordedRequest request = redirectSource.takeRequest();
+
+ assertContains(request.getHeaders(), "Cookie: $Version=\"1\"; "
+ + "c=\"cookie\";$Path=\"/\";$Domain=\".local\";$Port=\"" + portList + "\"");
+
+ for (String header : redirectTarget.takeRequest().getHeaders()) {
+ if (header.startsWith("Cookie")) {
+ fail(header);
+ }
+ }
+ }
+
/**
* Test which headers show up where. The cookie manager should be notified of both
* user-specified and derived headers like {@code Content-Length}. Headers named {@code Cookie}
diff --git a/luni/src/test/java/libcore/java/net/OldDatagramSocketTest.java b/luni/src/test/java/libcore/java/net/OldDatagramSocketTest.java
index 07e0897..0b4009c 100644
--- a/luni/src/test/java/libcore/java/net/OldDatagramSocketTest.java
+++ b/luni/src/test/java/libcore/java/net/OldDatagramSocketTest.java
@@ -267,7 +267,6 @@
fail("Exception during test : " + e.getMessage());
}
- if ("true".equals(System.getProperty("run.ipv6tests"))) {
System.out
.println("Running test_connectLjava_net_InetAddressI" +
"(DatagramSocketTest) with IPv6GlobalAddressJcl4: "
@@ -285,7 +284,6 @@
} catch (Exception e) {
fail("Exception during test : " + e.getMessage());
}
- }
try {
// Create a connected datagram socket to test
@@ -627,7 +625,6 @@
+ e.toString());
}
- if ("true".equals(System.getProperty("run.ipv6tests"))) {
System.out
.println("Running test_connectLjava_net_InetAddressI(DatagramSocketTest) with IPv6 address");
try {
@@ -643,7 +640,6 @@
"Unexcpected exception when trying to connect at native level with bad IPv6 address for signature with no exception to be returned: "
+ e.toString());
}
- }
}
public void test_disconnect() {
@@ -659,7 +655,6 @@
fail("Exception during test : " + e.getMessage());
}
- if ("true".equals(System.getProperty("run.ipv6tests"))) {
System.out
.println("Running test_disconnect(DatagramSocketTest) with IPv6GlobalAddressJcl4: "
+ Support_Configuration.IPv6GlobalAddressJcl4);
@@ -675,8 +670,6 @@
} catch (Exception e) {
fail("Exception during test : " + e.getMessage());
}
- }
-
}
public void test_getInetAddress() {
@@ -719,49 +712,6 @@
}
- public void test_getLocalAddress() {
- // Test for method java.net.InetAddress
- // java.net.DatagramSocket.getLocalAddress()
- InetAddress local = null;
- try {
- int portNumber = Support_PortManager.getNextPortForUDP();
- local = InetAddress.getLocalHost();
- ds = new java.net.DatagramSocket(portNumber, local);
- assertTrue("Returned incorrect address. Got:"
- + ds.getLocalAddress()
- + " wanted: "
- + InetAddress.getByName(InetAddress.getLocalHost()
- .getHostName()), InetAddress.getByName(
- InetAddress.getLocalHost().getHostName()).equals(
- ds.getLocalAddress()));
-
- // now validate thet behaviour when the any address is returned
- String preferIPv4StackValue = System
- .getProperty("java.net.preferIPv4Stack");
- String preferIPv6AddressesValue = System
- .getProperty("java.net.preferIPv6Addresses");
- DatagramSocket s = new DatagramSocket(0);
- if (((preferIPv4StackValue == null) || preferIPv4StackValue
- .equalsIgnoreCase("false"))
- && (preferIPv6AddressesValue != null)
- && (preferIPv6AddressesValue.equals("true"))) {
- assertTrue(
- "ANY address not returned correctly (getLocalAddress) with preferIPv6Addresses=true, preferIPv4Stack=false "
- + s.getLocalSocketAddress(), s
- .getLocalAddress() instanceof Inet6Address);
- } else {
- assertTrue(
- "ANY address not returned correctly (getLocalAddress) with preferIPv6Addresses=true, preferIPv4Stack=true "
- + s.getLocalSocketAddress(), s
- .getLocalAddress() instanceof Inet4Address);
- }
- s.close();
- } catch (Exception e) {
- fail(
- "Exception during getLocalAddress: " + local + " - " + e);
- }
- }
-
public void test_getLocalPort() {
// Test for method int java.net.DatagramSocket.getLocalPort()
try {
@@ -1314,150 +1264,94 @@
}
public void test_bindLjava_net_SocketAddress() throws Exception {
- class mySocketAddress extends SocketAddress {
+ int[] ports = Support_PortManager.getNextPortsForUDP(3);
+ int serverPortNumber = ports[1];
- public mySocketAddress() {
- }
- }
+ // now create a socket that is not bound and then bind it
+ InetAddress localHost = InetAddress.getLocalHost();
+ InetSocketAddress localAddress1 = new InetSocketAddress(localHost, ports[0]);
+ DatagramSocket theSocket = new DatagramSocket(localAddress1);
- DatagramServer server = null;
- try {
- // now create a socket that is not bound and then bind it
- int[] ports = Support_PortManager.getNextPortsForUDP(3);
- int portNumber = ports[0];
- int serverPortNumber = ports[1];
- DatagramSocket theSocket = new DatagramSocket(
- new InetSocketAddress(InetAddress.getLocalHost(),
- portNumber));
+ // validate that the localSocketAddress reflects the address we bound to
+ assertEquals(localAddress1, theSocket.getLocalSocketAddress());
- // validate that the localSocketAddress reflects the address we
- // bound to
- assertTrue("Local address not correct after bind:"
- + theSocket.getLocalSocketAddress().toString()
- + "Expected: "
- + (new InetSocketAddress(InetAddress.getLocalHost(),
- portNumber)).toString(), theSocket
- .getLocalSocketAddress().equals(
- new InetSocketAddress(InetAddress.getLocalHost(),
- portNumber)));
+ // now make sure that datagrams sent from this socket appear to come
+ // from the address we bound to
+ InetSocketAddress localAddress2 = new InetSocketAddress(localHost, ports[2]);
+ DatagramSocket ds = new DatagramSocket((SocketAddress) null);
+ ds.bind(localAddress2);
- // now make sure that datagrams sent from this socket appear to come
- // from the address we bound to
- InetAddress localHost = InetAddress.getLocalHost();
- portNumber = ports[2];
- DatagramSocket ds = new DatagramSocket((SocketAddress) null);
- ds.bind(new InetSocketAddress(localHost, portNumber));
+ DatagramServer server = new DatagramServer(serverPortNumber, localHost);
+ server.start();
+ Thread.sleep(1000);
- try {
- server = new DatagramServer(serverPortNumber, localHost);
- server.start();
- Thread.sleep(1000);
- } catch (Exception e) {
- fail(
- "Failed to set up datagram server for bin datagram socket test ");
- }
+ ds.connect(new InetSocketAddress(localHost, serverPortNumber));
- ds.connect(new InetSocketAddress(localHost, serverPortNumber));
-
- byte[] sendBytes = { 'T', 'e', 's', 't', 0 };
- DatagramPacket send = new DatagramPacket(sendBytes,
- sendBytes.length);
- ds.send(send);
- Thread.sleep(1000);
- ds.close();
- assertTrue(
- "Address in packet sent does not match address bound to:"
- + server.rdp.getAddress() + ":"
- + server.rdp.getPort() + ":" + localHost + ":"
- + portNumber, (server.rdp.getAddress()
- .equals(localHost))
- && (server.rdp.getPort() == portNumber));
-
- // validate if we pass in null that it picks an address for us and
- // all is ok
- theSocket = new DatagramSocket((SocketAddress) null);
- theSocket.bind(null);
- assertNotNull("Bind with null did not work", theSocket
- .getLocalSocketAddress());
- theSocket.close();
-
- // now check the error conditions
-
- // Address we cannot bind to
- theSocket = new DatagramSocket((SocketAddress) null);
- try {
- theSocket
- .bind(new InetSocketAddress(
- InetAddress
- .getByAddress(Support_Configuration.nonLocalAddressBytes),
- Support_PortManager.getNextPortForUDP()));
- fail("No exception when binding to bad address");
- } catch (SocketException ex) {
- }
- theSocket.close();
-
- // Address that we have allready bound to
- ports = Support_PortManager.getNextPortsForUDP(2);
- theSocket = new DatagramSocket((SocketAddress) null);
- DatagramSocket theSocket2 = new DatagramSocket(ports[0]);
- try {
- InetSocketAddress theAddress = new InetSocketAddress(
- InetAddress.getLocalHost(), ports[1]);
- theSocket.bind(theAddress);
- theSocket2.bind(theAddress);
- fail("No exception binding to address that is not available");
- } catch (SocketException ex) {
- //expected
- }
- theSocket.close();
- theSocket2.close();
-
- /*
- SecurityManager sm = new SecurityManager() {
-
- public void checkPermission(Permission perm) {
- }
-
- public void checkListen(int port) {
- throw new SecurityException();
- }
- };
-
- ports = Support_PortManager.getNextPortsForUDP(2);
- ds = new DatagramSocket(null);
- SecurityManager oldSm = System.getSecurityManager();
- System.setSecurityManager(sm);
- try {
-
- ds.bind(new InetSocketAddress(localHost, ports[0]));
- fail("SecurityException should be thrown.");
- } catch (SecurityException e) {
- // expected
- } catch (SocketException e) {
- fail("SocketException was thrown.");
- } finally {
- System.setSecurityManager(oldSm);
- }
- */
-
- // unsupported SocketAddress subclass
- theSocket = new DatagramSocket((SocketAddress) null);
- try {
- theSocket.bind(new mySocketAddress());
- fail("No exception when binding using unsupported SocketAddress subclass");
- } catch (IllegalArgumentException ex) {
- }
- theSocket.close();
-
- } catch (Exception e) {
- fail("Unexpected exception during bind test : " + e.getMessage());
- }
+ byte[] sendBytes = { 'T', 'e', 's', 't', 0 };
+ DatagramPacket send = new DatagramPacket(sendBytes, sendBytes.length);
+ ds.send(send);
+ Thread.sleep(1000);
+ ds.close();
+ // Check that the address in the packet matches the bound address.
+ assertEquals(localAddress2, server.rdp.getSocketAddress());
if (server != null) {
server.stopServer();
}
}
+ public void test_bindLjava_net_SocketAddress_null() throws Exception {
+ // validate if we pass in null that it picks an address for us.
+ DatagramSocket theSocket = new DatagramSocket((SocketAddress) null);
+ theSocket.bind(null);
+ assertNotNull(theSocket.getLocalSocketAddress());
+ theSocket.close();
+ }
+
+ public void test_bindLjava_net_SocketAddress_bad_address() throws Exception {
+ // Address we cannot bind to
+ DatagramSocket theSocket = new DatagramSocket((SocketAddress) null);
+ try {
+ InetAddress badAddress = InetAddress.getByAddress(Support_Configuration.nonLocalAddressBytes);
+ theSocket.bind(new InetSocketAddress(badAddress, Support_PortManager.getNextPortForUDP()));
+ fail("No exception when binding to bad address");
+ } catch (SocketException expected) {
+ }
+ theSocket.close();
+ }
+
+ public void test_bindLjava_net_SocketAddress_address_in_use() throws Exception {
+ // Address that we have already bound to
+ int[] ports = Support_PortManager.getNextPortsForUDP(2);
+ DatagramSocket theSocket1 = new DatagramSocket((SocketAddress) null);
+ DatagramSocket theSocket2 = new DatagramSocket(ports[0]);
+ try {
+ InetSocketAddress theAddress = new InetSocketAddress(InetAddress.getLocalHost(), ports[1]);
+ theSocket1.bind(theAddress);
+ theSocket2.bind(theAddress);
+ fail("No exception binding to address that is not available");
+ } catch (SocketException expected) {
+ }
+ theSocket1.close();
+ theSocket2.close();
+ }
+
+ public void test_bindLjava_net_SocketAddress_unsupported_address_type() throws Exception {
+ class mySocketAddress extends SocketAddress {
+ public mySocketAddress() {
+ }
+ }
+
+ // unsupported SocketAddress subclass
+ DatagramSocket theSocket = new DatagramSocket((SocketAddress) null);
+ try {
+ theSocket.bind(new mySocketAddress());
+ fail("No exception when binding using unsupported SocketAddress subclass");
+ } catch (IllegalArgumentException expected) {
+ }
+ theSocket.close();
+ }
+
public void test_connectLjava_net_SocketAddress() {
// validate that we get the PortUnreachable exception if we try to
@@ -1884,70 +1778,6 @@
}
}
- public void test_getLocalSocketAddress() throws Exception {
- int portNumber = Support_PortManager.getNextPortForUDP();
- DatagramSocket s = new DatagramSocket(new InetSocketAddress(
- InetAddress.getLocalHost(), portNumber));
- assertTrue("Returned incorrect InetSocketAddress(1):"
- + s.getLocalSocketAddress().toString()
- + "Expected: "
- + (new InetSocketAddress(InetAddress.getLocalHost(),
- portNumber)).toString(), s.getLocalSocketAddress()
- .equals(
- new InetSocketAddress(InetAddress.getLocalHost(),
- portNumber)));
- s.close();
-
- InetSocketAddress remoteAddress = (InetSocketAddress) s
- .getRemoteSocketAddress();
-
- // now create a socket that is not bound and validate we get the
- // right answer
- DatagramSocket theSocket = new DatagramSocket((SocketAddress) null);
- assertNull(
- "Returned incorrect InetSocketAddress -unbound socket- Expected null",
- theSocket.getLocalSocketAddress());
-
- // now bind the socket and make sure we get the right answer
- portNumber = Support_PortManager.getNextPortForUDP();
- theSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(),
- portNumber));
- assertTrue("Returned incorrect InetSocketAddress(2):"
- + theSocket.getLocalSocketAddress().toString()
- + "Expected: "
- + (new InetSocketAddress(InetAddress.getLocalHost(),
- portNumber)).toString(), theSocket
- .getLocalSocketAddress().equals(
- new InetSocketAddress(InetAddress.getLocalHost(),
- portNumber)));
- theSocket.close();
-
- // now validate thet behaviour when the any address is returned
- s = new DatagramSocket(0);
-
- String preferIPv4StackValue = System
- .getProperty("java.net.preferIPv4Stack");
- String preferIPv6AddressesValue = System
- .getProperty("java.net.preferIPv6Addresses");
- if (((preferIPv4StackValue == null) || preferIPv4StackValue
- .equalsIgnoreCase("false"))
- && (preferIPv6AddressesValue != null)
- && (preferIPv6AddressesValue.equals("true"))) {
- assertTrue(
- "ANY address not returned correctly with preferIPv6Addresses=true, preferIPv4Stack=false "
- + s.getLocalSocketAddress(),
- ((InetSocketAddress) s.getLocalSocketAddress())
- .getAddress() instanceof Inet6Address);
- } else {
- assertTrue(
- "ANY address not returned correctly with preferIPv6Addresses=true, preferIPv4Stack=true "
- + s.getLocalSocketAddress(),
- ((InetSocketAddress) s.getLocalSocketAddress())
- .getAddress() instanceof Inet4Address);
- }
- s.close();
- }
-
public void test_setReuseAddressZ() throws Exception {
// test case were we set it to false
DatagramSocket theSocket1 = null;
@@ -2198,6 +2028,7 @@
DatagramChannel channel = DatagramChannel.open();
DatagramSocket socket = channel.socket();
assertEquals(channel, socket.getChannel());
+ socket.close();
}
class TestDatagramSocketImplFactory implements DatagramSocketImplFactory {
diff --git a/luni/src/test/java/libcore/java/net/OldResponseCacheTest.java b/luni/src/test/java/libcore/java/net/OldResponseCacheTest.java
deleted file mode 100644
index d292414..0000000
--- a/luni/src/test/java/libcore/java/net/OldResponseCacheTest.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.java.net;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.CacheRequest;
-import java.net.CacheResponse;
-import java.net.HttpURLConnection;
-import java.net.NetPermission;
-import java.net.ResponseCache;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.net.URLConnection;
-import java.security.Permission;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import junit.framework.TestCase;
-import tests.support.Support_PortManager;
-import tests.support.Support_TestWebData;
-import tests.support.Support_TestWebServer;
-
-public class OldResponseCacheTest extends TestCase {
-
-
-
- public void test_GetDefault() throws Exception {
- assertNull(ResponseCache.getDefault());
- }
-
- public void test_SetDefaultLjava_net_ResponseCache_Normal() throws Exception {
- ResponseCache rc1 = new MockResponseCache();
- ResponseCache rc2 = new MockResponseCache();
- ResponseCache.setDefault(rc1);
- assertSame(ResponseCache.getDefault(), rc1);
- ResponseCache.setDefault(rc2);
- assertSame(ResponseCache.getDefault(), rc2);
- ResponseCache.setDefault(null);
- assertNull(ResponseCache.getDefault());
- }
-
- public void test_get() throws Exception {
- String uri = "http://localhost/";
- URL url = new URL(uri);
- TestResponseCache cache = new TestResponseCache(uri, true);
- ResponseCache.setDefault(cache);
- HttpURLConnection httpCon = (HttpURLConnection) url.openConnection();
- httpCon.setUseCaches(true);
- httpCon.connect();
- try {
- Thread.sleep(5000);
- } catch(Exception e) {}
-
- InputStream is = httpCon.getInputStream();
- byte[] array = new byte [10];
- is.read(array);
- assertEquals(url.toURI(), cache.getWasCalled);
- assertEquals("Cache test", new String(array));
- is.close();
- httpCon.disconnect();
-
- }
-
- public void test_put() throws Exception {
- // Create test ResponseCache
- TestResponseCache cache = new TestResponseCache(
- "http://localhost/not_cached", false);
- ResponseCache.setDefault(cache);
-
- // Start Server
- int port = Support_PortManager.getNextPort();
- Support_TestWebServer s = new Support_TestWebServer();
- try {
- s.initServer(port, 10000, false);
- Thread.currentThread().sleep(2500);
-
- // Create connection to server
- URL url = new URL("http://localhost:" + port + "/test1");
- HttpURLConnection httpCon = (HttpURLConnection) url.openConnection();
- httpCon.setUseCaches(true);
- httpCon.connect();
- Thread.currentThread().sleep(2500);
-
- // Check that a call to the cache was made.
- assertEquals(url.toURI(), cache.getWasCalled);
- // Make the HttpConnection get the content. It should try to
- // put it into the cache.
- httpCon.getContent();
- // Check if put was called
- assertEquals(url.toURI(), cache.putWasCalled);
-
- // get the
- InputStream is = httpCon.getInputStream();
-
- byte[] array = new byte[Support_TestWebData.test1.length];
- is.read(array);
- assertTrue(Arrays.equals(Support_TestWebData.tests[0], array));
- is.close();
- httpCon.disconnect();
- } finally {
- s.close();
- }
- }
-
- /*
- * MockResponseCache for testSetDefault(ResponseCache)
- */
- class MockResponseCache extends ResponseCache {
-
- public CacheResponse get(URI arg0, String arg1, Map arg2)
- throws IOException {
- return null;
- }
-
- public CacheRequest put(URI arg0, URLConnection arg1)
- throws IOException {
- return null;
- }
- }
-
- /*
- * MockSecurityMaanger. It denies NetPermission("getResponseCache") and
- * NetPermission("setResponseCache").
- */
- class MockSM extends SecurityManager {
- public void checkPermission(Permission permission) {
- if (permission instanceof NetPermission) {
- if ("setResponseCache".equals(permission.getName())) {
- throw new SecurityException();
- }
- }
-
- if (permission instanceof NetPermission) {
- if ("getResponseCache".equals(permission.getName())) {
-
- throw new SecurityException();
- }
- }
-
- if (permission instanceof RuntimePermission) {
- if ("setSecurityManager".equals(permission.getName())) {
- return;
- }
- }
- }
- }
-
- class TestCacheResponse extends CacheResponse {
- InputStream is = null;
-
- public TestCacheResponse(String body) {
- is = new ByteArrayInputStream(body.getBytes());
- }
-
- @Override
- public InputStream getBody() {
- return is;
- }
-
- @Override
- public Map<String, List<String>> getHeaders() throws IOException {
- return new HashMap<String, List<String>>();
- }
- }
-
- class TestCacheRequest extends CacheRequest {
-
- @Override
- public OutputStream getBody() {
- return null;
- }
-
- @Override
- public void abort() {
- }
- }
-
- class TestResponseCache extends ResponseCache {
-
- URI uri1 = null;
- boolean testGet = false;
-
- public URI getWasCalled = null;
- public URI putWasCalled = null;
-
- TestResponseCache(String uri, boolean testGet) {
- try {
- uri1 = new URI(uri);
- } catch (URISyntaxException e) {
- }
- this.testGet = testGet;
- }
-
- @Override
- public CacheResponse get(URI uri, String rqstMethod, Map rqstHeaders) {
- getWasCalled = uri;
- if (testGet && uri.equals(uri1)) {
- return new TestCacheResponse("Cache test");
- }
- return null;
- }
-
- @Override
- public CacheRequest put(URI uri, URLConnection conn) {
- putWasCalled = uri;
- if (!testGet && uri.equals(uri1)) {
- return new TestCacheRequest();
- }
- return null;
- }
- }
-}
diff --git a/luni/src/test/java/libcore/java/net/OldSocketTest.java b/luni/src/test/java/libcore/java/net/OldSocketTest.java
index 6247719..42f7b78 100644
--- a/luni/src/test/java/libcore/java/net/OldSocketTest.java
+++ b/luni/src/test/java/libcore/java/net/OldSocketTest.java
@@ -20,6 +20,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.net.BindException;
import java.net.ConnectException;
import java.net.Inet4Address;
import java.net.Inet6Address;
@@ -104,13 +105,7 @@
try {
socket = new Socket(InetAddress.getByName(null), sport);
InetAddress address = socket.getLocalAddress();
- if (Boolean.getBoolean("java.net.preferIPv6Addresses")) {
- assertTrue(
- address.equals(InetAddress.getByName("::1")) ||
- address.equals(InetAddress.getByName("0:0:0:0:0:0:0:1")));
- } else {
- assertEquals(address, InetAddress.getByName("127.0.0.1"));
- }
+ assertTrue(address.isLoopbackAddress());
} finally {
try {
socket.close();
@@ -118,111 +113,31 @@
}
}
- public void test_ConstructorLjava_lang_StringILjava_net_InetAddressI()
- throws IOException {
- // Test for method java.net.Socket(java.lang.String, int,
- // java.net.InetAddress, int)
+ public void test_ConstructorLjava_lang_StringILjava_net_InetAddressI1() throws IOException {
int sport = startServer("Cons String,I,InetAddress,I");
int portNumber = Support_PortManager.getNextPort();
s = new Socket(InetAddress.getLocalHost().getHostName(), sport,
InetAddress.getLocalHost(), portNumber);
assertTrue("Failed to create socket", s.getPort() == sport);
+ }
- if (("true".equals(System.getProperty("java.net.preferIPv6Addresses")))
- && !("true".equals(System
- .getProperty("java.net.preferIPv4Stack")))) {
-
- // ALTERNATE IPv6 TEST
- if ("true".equals(System.getProperty("run.ipv6tests"))) {
- System.out
- .println("Running testConstructorLjava_lang_StringILjava_net_InetAddressI(OldSocketTest) with IPv6GlobalAddressJcl4: "
- + Support_Configuration.IPv6GlobalAddressJcl4);
- int testPort = Support_PortManager.getNextPort();
- Socket s1 = null, s2 = null;
- try {
- s1 = new Socket(
- Support_Configuration.IPv6GlobalAddressJcl4, 80,
- InetAddress.getLocalHost(), testPort);
- } catch (IOException e) {
- // check here if InetAddress.getLocalHost() is returning the
- // loopback address.
- // if so that is likely the cause of the failure
- String warning = "";
- try {
- InetAddress returnedLocalHost = InetAddress
- .getLocalHost();
- // don't use isLoopbackAddress for some configurations
- // as they do not have it
- if (returnedLocalHost.isLoopbackAddress()) {
- warning = " - WARNING RETURNED LOCAL HOST IS THE LOOPBACK ADDRESS - MACHINE IS LIKELY NOT CONFIGURED CORRECTLY - THIS LIKELY CAUSED THE FAILURE";
-
- }
- } catch (Exception ex) {
- warning = " - WARNING COULD NOT GET LOCAL HOST - " + ex;
- }
-
- fail("Exception creating 1st socket" + warning + ": " + e);
- }
- boolean exception = false;
- try {
- s2 = new Socket(
- Support_Configuration.IPv6GlobalAddressJcl4, 80,
- InetAddress.getLocalHost(), testPort);
- } catch (IOException e) {
- exception = true;
- }
- try {
- s1.close();
- if (!exception)
- s2.close();
- } catch (IOException e) {
- }
- assertTrue("Was able to create two sockets on same port",
- exception);
- }
-
- } else {
- int testPort = Support_PortManager.getNextPort();
- Socket s1 = null, s2 = null;
- int serverPort = ss.getLocalPort();
+ public void test_ConstructorLjava_lang_StringILjava_net_InetAddressI2() throws IOException {
+ int testPort = Support_PortManager.getNextPort();
+ Socket s1 = new Socket("www.google.com", 80, null, testPort);
+ try {
+ Socket s2 = new Socket("www.google.com", 80, null, testPort);
try {
- s1 = new Socket("127.0.0.1", serverPort, InetAddress
- .getLocalHost(), testPort);
- } catch (IOException e) {
- e.printStackTrace();
-
- // check here if InetAddress.getLocalHost() is returning the
- // loopback address.
- // if so that is likely the cause of the failure
- String warning = "";
- try {
- InetAddress returnedLocalHost = InetAddress.getLocalHost();
- // don't use isLoopbackAddress for some configurations as
- // they do not have it
- if (returnedLocalHost.isLoopbackAddress()) {
- warning = " - WARNING RETURNED LOCAL HOST IS THE LOOPBACK ADDRESS - MACHINE IS LIKELY NOT CONFIGURED CORRECTLY - THIS LIKELY CAUSED THE FAILURE";
-
- }
- } catch (Exception ex) {
- warning = " - WARNING COULD NOT GET LOCAL HOST - " + ex;
- }
-
- fail("Exception creating 1st socket" + warning + ": " + e);
+ s2.close();
+ } catch (IOException ignored) {
}
- boolean exception = false;
- try {
- s2 = new Socket("127.0.0.1", serverPort, InetAddress
- .getLocalHost(), testPort);
- } catch (IOException e) {
- exception = true;
- }
+ fail("second connect should have failed with EADDRINUSE");
+ } catch (BindException expected) {
+ // success!
+ } finally {
try {
s1.close();
- if (!exception)
- s2.close();
- } catch (IOException e) {
+ } catch (IOException ignored) {
}
- assertTrue("Was able to create two sockets on same port", exception);
}
}
@@ -344,31 +259,13 @@
assertEquals("Returned incorrect InetAddress",
InetAddress.getLocalHost(), s.getLocalAddress());
- // now validate that behaviour when the any address is returned
- String preferIPv4StackValue = System
- .getProperty("java.net.preferIPv4Stack");
- String preferIPv6AddressesValue = System
- .getProperty("java.net.preferIPv6Addresses");
-
+ // now check behavior when the ANY address is returned
s = new Socket();
s.bind(new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 0));
- if (((preferIPv4StackValue == null) || preferIPv4StackValue
- .equalsIgnoreCase("false"))
- && (preferIPv6AddressesValue != null)
- && (preferIPv6AddressesValue.equals("true"))) {
- assertTrue(
- "ANY address not returned correctly (getLocalAddress) with preferIPv6Addresses=true, preferIPv4Stack=false "
- + s.getLocalSocketAddress(),
+ assertTrue("ANY address not IPv6: " + s.getLocalSocketAddress(),
s.getLocalAddress() instanceof Inet6Address);
- } else {
- assertTrue(
- "ANY address not returned correctly (getLocalAddress) with preferIPv6Addresses=true, preferIPv4Stack=true "
- + s.getLocalSocketAddress(),
- s.getLocalAddress() instanceof Inet4Address);
- }
s.close();
-
}
public void test_getLocalPort() throws IOException {
@@ -865,47 +762,15 @@
s = new Socket();
s.bind(new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 0));
- String preferIPv4StackValue = System
- .getProperty("java.net.preferIPv4Stack");
- String preferIPv6AddressesValue = System
- .getProperty("java.net.preferIPv6Addresses");
- if (((preferIPv4StackValue == null) || preferIPv4StackValue
- .equalsIgnoreCase("false"))
- && (preferIPv6AddressesValue != null)
- && (preferIPv6AddressesValue.equals("true"))) {
- assertTrue(
- "ANY address not returned correctly with preferIPv6Addresses=true, preferIPv4Stack=false "
- + s.getLocalSocketAddress(),
- ((InetSocketAddress) s.getLocalSocketAddress())
- .getAddress() instanceof Inet6Address);
- } else {
- assertTrue(
- "ANY address not returned correctly with preferIPv6Addresses=true, preferIPv4Stack=true "
- + s.getLocalSocketAddress(),
- ((InetSocketAddress) s.getLocalSocketAddress())
- .getAddress() instanceof Inet4Address);
- }
+ assertTrue("ANY address not IPv6: " + s.getLocalSocketAddress(),
+ ((InetSocketAddress) s.getLocalSocketAddress()).getAddress() instanceof Inet6Address);
s.close();
// now validate the same for getLocalAddress
s = new Socket();
s.bind(new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 0));
- if (((preferIPv4StackValue == null) || preferIPv4StackValue
- .equalsIgnoreCase("false"))
- && (preferIPv6AddressesValue != null)
- && (preferIPv6AddressesValue.equals("true"))) {
- assertTrue(
- "ANY address not returned correctly with preferIPv6Addresses=true, preferIPv4Stack=false "
- + s.getLocalSocketAddress(),
- ((InetSocketAddress) s.getLocalSocketAddress())
- .getAddress() instanceof Inet6Address);
- } else {
- assertTrue(
- "ANY address not returned correctly with preferIPv6Addresses=true, preferIPv4Stack=true "
- + s.getLocalSocketAddress(),
- ((InetSocketAddress) s.getLocalSocketAddress())
- .getAddress() instanceof Inet4Address);
- }
+ assertTrue("ANY address not IPv6: " + s.getLocalSocketAddress(),
+ ((InetSocketAddress) s.getLocalSocketAddress()).getAddress() instanceof Inet6Address);
s.close();
}
diff --git a/luni/src/test/java/libcore/java/net/ServerSocketTest.java b/luni/src/test/java/libcore/java/net/ServerSocketTest.java
new file mode 100644
index 0000000..fe9d423
--- /dev/null
+++ b/luni/src/test/java/libcore/java/net/ServerSocketTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.net;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+public class ServerSocketTest extends junit.framework.TestCase {
+ public void testTimeoutAfterAccept() throws Exception {
+ final ServerSocket ss = new ServerSocket(0);
+ ss.setReuseAddress(true);
+ // On Unix, the receive timeout is inherited by the result of accept(2).
+ // Java specifies that it should always be 0 instead.
+ ss.setSoTimeout(1234);
+ final Socket[] result = new Socket[1];
+ Thread t = new Thread(new Runnable() {
+ public void run() {
+ try {
+ result[0] = ss.accept();
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ fail();
+ }
+ }
+ });
+ t.start();
+ new Socket(ss.getInetAddress(), ss.getLocalPort());
+ t.join();
+ assertEquals(0, result[0].getSoTimeout());
+ }
+}
diff --git a/luni/src/test/java/libcore/java/net/SocketTest.java b/luni/src/test/java/libcore/java/net/SocketTest.java
index 9fa09b7..e796e1e 100644
--- a/luni/src/test/java/libcore/java/net/SocketTest.java
+++ b/luni/src/test/java/libcore/java/net/SocketTest.java
@@ -53,7 +53,7 @@
try {
// Bind to an ephemeral local port.
s.bind(new InetSocketAddress("localhost", 0));
- assertTrue(s.getLocalAddress().isLoopbackAddress());
+ assertTrue(s.getLocalAddress().toString(), s.getLocalAddress().isLoopbackAddress());
// What local port did we get?
int localPort = s.getLocalPort();
assertTrue(localPort > 0);
diff --git a/luni/src/test/java/libcore/java/net/URLConnectionTest.java b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
index 68cff88..6393e02 100644
--- a/luni/src/test/java/libcore/java/net/URLConnectionTest.java
+++ b/luni/src/test/java/libcore/java/net/URLConnectionTest.java
@@ -57,6 +57,7 @@
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
+import junit.framework.TestCase;
import libcore.java.security.TestKeyStore;
import libcore.javax.net.ssl.TestSSLContext;
import tests.http.MockResponse;
@@ -69,7 +70,7 @@
import static tests.http.SocketPolicy.SHUTDOWN_OUTPUT_AT_END;
import tests.net.StuckServer;
-public class URLConnectionTest extends junit.framework.TestCase {
+public final class URLConnectionTest extends TestCase {
private static final Authenticator SIMPLE_AUTHENTICATOR = new Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
@@ -77,6 +78,9 @@
}
};
+ /** base64("username:password") */
+ private static final String BASE_64_CREDENTIALS = "dXNlcm5hbWU6cGFzc3dvcmQ=";
+
private MockWebServer server = new MockWebServer();
private String hostName;
@@ -105,8 +109,11 @@
HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection();
urlConnection.addRequestProperty("D", "e");
urlConnection.addRequestProperty("D", "f");
+ assertEquals("f", urlConnection.getRequestProperty("D"));
+ assertEquals("f", urlConnection.getRequestProperty("d"));
Map<String, List<String>> requestHeaders = urlConnection.getRequestProperties();
assertEquals(newSet("e", "f"), new HashSet<String>(requestHeaders.get("D")));
+ assertEquals(newSet("e", "f"), new HashSet<String>(requestHeaders.get("d")));
try {
requestHeaders.put("G", Arrays.asList("h"));
fail("Modified an unmodifiable view.");
@@ -128,7 +135,9 @@
} catch (NullPointerException expected) {
}
urlConnection.setRequestProperty("NullValue", null); // should fail silently!
+ assertNull(urlConnection.getRequestProperty("NullValue"));
urlConnection.addRequestProperty("AnotherNullValue", null); // should fail silently!
+ assertNull(urlConnection.getRequestProperty("AnotherNullValue"));
urlConnection.getResponseCode();
RecordedRequest request = server.takeRequest();
@@ -149,13 +158,27 @@
fail("Set header after connect");
} catch (IllegalStateException expected) {
}
+ try {
+ urlConnection.getRequestProperties();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testGetRequestPropertyReturnsLastValue() throws Exception {
+ server.play();
+ HttpURLConnection urlConnection = (HttpURLConnection) server.getUrl("/").openConnection();
+ urlConnection.addRequestProperty("A", "value1");
+ urlConnection.addRequestProperty("A", "value2");
+ assertEquals("value2", urlConnection.getRequestProperty("A"));
}
public void testResponseHeaders() throws IOException, InterruptedException {
server.enqueue(new MockResponse()
.setStatus("HTTP/1.0 200 Fantastic")
- .addHeader("A: b")
.addHeader("A: c")
+ .addHeader("B: d")
+ .addHeader("A: e")
.setChunkedBody("ABCDE\nFGHIJ\nKLMNO\nPQR", 8));
server.play();
@@ -165,17 +188,38 @@
assertEquals("HTTP/1.0 200 Fantastic", urlConnection.getHeaderField(null));
Map<String, List<String>> responseHeaders = urlConnection.getHeaderFields();
assertEquals(Arrays.asList("HTTP/1.0 200 Fantastic"), responseHeaders.get(null));
- assertEquals(newSet("b", "c"), new HashSet<String>(responseHeaders.get("A")));
+ assertEquals(newSet("c", "e"), new HashSet<String>(responseHeaders.get("A")));
+ assertEquals(newSet("c", "e"), new HashSet<String>(responseHeaders.get("a")));
try {
responseHeaders.put("N", Arrays.asList("o"));
fail("Modified an unmodifiable view.");
} catch (UnsupportedOperationException expected) {
}
try {
- responseHeaders.get("A").add("d");
+ responseHeaders.get("A").add("f");
fail("Modified an unmodifiable view.");
} catch (UnsupportedOperationException expected) {
}
+ assertEquals("A", urlConnection.getHeaderFieldKey(0));
+ assertEquals("c", urlConnection.getHeaderField(0));
+ assertEquals("B", urlConnection.getHeaderFieldKey(1));
+ assertEquals("d", urlConnection.getHeaderField(1));
+ assertEquals("A", urlConnection.getHeaderFieldKey(2));
+ assertEquals("e", urlConnection.getHeaderField(2));
+ }
+
+ public void testGetErrorStreamOnSuccessfulRequest() throws Exception {
+ server.enqueue(new MockResponse().setBody("A"));
+ server.play();
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ assertNull(connection.getErrorStream());
+ }
+
+ public void testGetErrorStreamOnUnsuccessfulRequest() throws Exception {
+ server.enqueue(new MockResponse().setResponseCode(404).setBody("A"));
+ server.play();
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ assertEquals("A", readAscii(connection.getErrorStream(), Integer.MAX_VALUE));
}
// Check that if we don't read to the end of a response, the next request on the
@@ -541,7 +585,9 @@
RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier();
server.useHttps(testSSLContext.serverContext.getSocketFactory(), true);
- server.enqueue(new MockResponse().clearHeaders()); // for CONNECT
+ server.enqueue(new MockResponse()
+ .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END)
+ .clearHeaders());
server.enqueue(new MockResponse().setBody("this response comes via a secure proxy"));
server.play();
@@ -572,7 +618,9 @@
TestSSLContext testSSLContext = TestSSLContext.create();
server.useHttps(testSSLContext.serverContext.getSocketFactory(), true);
- server.enqueue(new MockResponse().clearHeaders()); // for CONNECT
+ server.enqueue(new MockResponse()
+ .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END)
+ .clearHeaders());
server.enqueue(new MockResponse().setBody("encrypted response from the origin server"));
server.play();
@@ -598,6 +646,39 @@
assertEquals(Arrays.asList("verify android.com"), hostnameVerifier.calls);
}
+ public void testProxyAuthenticateOnConnect() throws Exception {
+ Authenticator.setDefault(SIMPLE_AUTHENTICATOR);
+ TestSSLContext testSSLContext = TestSSLContext.create();
+ server.useHttps(testSSLContext.serverContext.getSocketFactory(), true);
+ server.enqueue(new MockResponse()
+ .setResponseCode(407)
+ .addHeader("Proxy-Authenticate: Basic realm=\"localhost\""));
+ server.enqueue(new MockResponse()
+ .setSocketPolicy(SocketPolicy.UPGRADE_TO_SSL_AT_END)
+ .clearHeaders());
+ server.enqueue(new MockResponse().setBody("A"));
+ server.play();
+
+ URL url = new URL("https://android.com/foo");
+ HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(
+ server.toProxyAddress());
+ connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
+ connection.setHostnameVerifier(new RecordingHostnameVerifier());
+ assertContent("A", connection);
+
+ RecordedRequest connect1 = server.takeRequest();
+ assertEquals("CONNECT android.com:443 HTTP/1.1", connect1.getRequestLine());
+ assertContainsNoneMatching(connect1.getHeaders(), "Proxy\\-Authorization.*");
+
+ RecordedRequest connect2 = server.takeRequest();
+ assertEquals("CONNECT android.com:443 HTTP/1.1", connect2.getRequestLine());
+ assertContains(connect2.getHeaders(), "Proxy-Authorization: Basic " + BASE_64_CREDENTIALS);
+
+ RecordedRequest get = server.takeRequest();
+ assertEquals("GET /foo HTTP/1.1", get.getRequestLine());
+ assertContainsNoneMatching(get.getHeaders(), "Proxy\\-Authorization.*");
+ }
+
public void testDisconnectedConnection() throws IOException {
server.enqueue(new MockResponse().setBody("ABCDEFGHIJKLMNOPQR"));
server.play();
@@ -613,6 +694,22 @@
}
}
+ public void testDisconnectBeforeConnect() throws IOException {
+ server.enqueue(new MockResponse().setBody("A"));
+ server.play();
+
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ connection.disconnect();
+
+ assertContent("A", connection);
+ assertEquals(200, connection.getResponseCode());
+ }
+
+ public void testDefaultRequestProperty() throws Exception {
+ URLConnection.setDefaultRequestProperty("X-testSetDefaultRequestProperty", "A");
+ assertNull(URLConnection.getDefaultRequestProperty("X-setDefaultRequestProperty"));
+ }
+
/**
* Reads {@code count} characters from the stream. If the stream is
* exhausted before {@code count} characters can be read, the remaining
@@ -863,6 +960,104 @@
assertEquals(Arrays.toString(requestBody), Arrays.toString(request.getBody()));
}
+ public void testSetValidRequestMethod() throws Exception {
+ server.play();
+ assertValidRequestMethod("GET");
+ assertValidRequestMethod("DELETE");
+ assertValidRequestMethod("HEAD");
+ assertValidRequestMethod("OPTIONS");
+ assertValidRequestMethod("POST");
+ assertValidRequestMethod("PUT");
+ assertValidRequestMethod("TRACE");
+ }
+
+ private void assertValidRequestMethod(String requestMethod) throws Exception {
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ connection.setRequestMethod(requestMethod);
+ assertEquals(requestMethod, connection.getRequestMethod());
+ }
+
+ public void testSetInvalidRequestMethodLowercase() throws Exception {
+ server.play();
+ assertInvalidRequestMethod("get");
+ }
+
+ public void testSetInvalidRequestMethodConnect() throws Exception {
+ server.play();
+ assertInvalidRequestMethod("CONNECT");
+ }
+
+ private void assertInvalidRequestMethod(String requestMethod) throws Exception {
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ try {
+ connection.setRequestMethod(requestMethod);
+ fail();
+ } catch (ProtocolException expected) {
+ }
+ }
+
+ public void testCannotSetNegativeFixedLengthStreamingMode() throws Exception {
+ server.play();
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ try {
+ connection.setFixedLengthStreamingMode(-2);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testCanSetNegativeChunkedStreamingMode() throws Exception {
+ server.play();
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ connection.setChunkedStreamingMode(-2);
+ }
+
+ public void testCannotSetFixedLengthStreamingModeAfterConnect() throws Exception {
+ server.enqueue(new MockResponse().setBody("A"));
+ server.play();
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
+ try {
+ connection.setFixedLengthStreamingMode(1);
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testCannotSetChunkedStreamingModeAfterConnect() throws Exception {
+ server.enqueue(new MockResponse().setBody("A"));
+ server.play();
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
+ try {
+ connection.setChunkedStreamingMode(1);
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testCannotSetFixedLengthStreamingModeAfterChunkedStreamingMode() throws Exception {
+ server.play();
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ connection.setChunkedStreamingMode(1);
+ try {
+ connection.setFixedLengthStreamingMode(1);
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
+ public void testCannotSetChunkedStreamingModeAfterFixedLengthStreamingMode() throws Exception {
+ server.play();
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ connection.setFixedLengthStreamingMode(1);
+ try {
+ connection.setChunkedStreamingMode(1);
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ }
+
public void testSecureFixedLengthStreaming() throws Exception {
testSecureStreamingPost(StreamingMode.FIXED_LENGTH);
}
@@ -939,8 +1134,7 @@
for (int i = 0; i < 3; i++) {
request = server.takeRequest();
assertEquals("POST / HTTP/1.1", request.getRequestLine());
- assertContains(request.getHeaders(), "Authorization: Basic "
- + "dXNlcm5hbWU6cGFzc3dvcmQ="); // "dXNl..." == base64("username:password")
+ assertContains(request.getHeaders(), "Authorization: Basic " + BASE_64_CREDENTIALS);
assertEquals(Arrays.toString(requestBody), Arrays.toString(request.getBody()));
}
}
@@ -970,8 +1164,7 @@
for (int i = 0; i < 3; i++) {
request = server.takeRequest();
assertEquals("GET / HTTP/1.1", request.getRequestLine());
- assertContains(request.getHeaders(), "Authorization: Basic "
- + "dXNlcm5hbWU6cGFzc3dvcmQ="); // "dXNl..." == base64("username:password")
+ assertContains(request.getHeaders(), "Authorization: Basic " + BASE_64_CREDENTIALS);
}
}
@@ -1089,6 +1282,66 @@
server2.shutdown();
}
+ public void testResponse300MultipleChoiceWithPost() throws Exception {
+ // Chrome doesn't follow the redirect, but Firefox and the RI both do
+ testResponseRedirectedWithPost(HttpURLConnection.HTTP_MULT_CHOICE);
+ }
+
+ public void testResponse301MovedPermanentlyWithPost() throws Exception {
+ testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_PERM);
+ }
+
+ public void testResponse302MovedTemporarilyWithPost() throws Exception {
+ testResponseRedirectedWithPost(HttpURLConnection.HTTP_MOVED_TEMP);
+ }
+
+ public void testResponse303SeeOtherWithPost() throws Exception {
+ testResponseRedirectedWithPost(HttpURLConnection.HTTP_SEE_OTHER);
+ }
+
+ private void testResponseRedirectedWithPost(int redirectCode) throws Exception {
+ server.enqueue(new MockResponse()
+ .setResponseCode(redirectCode)
+ .addHeader("Location: /page2")
+ .setBody("This page has moved!"));
+ server.enqueue(new MockResponse().setBody("Page 2"));
+ server.play();
+
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/page1").openConnection();
+ connection.setDoOutput(true);
+ byte[] requestBody = { 'A', 'B', 'C', 'D' };
+ OutputStream outputStream = connection.getOutputStream();
+ outputStream.write(requestBody);
+ outputStream.close();
+ assertEquals("Page 2", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
+ assertTrue(connection.getDoOutput());
+
+ RecordedRequest page1 = server.takeRequest();
+ assertEquals("POST /page1 HTTP/1.1", page1.getRequestLine());
+ assertEquals(Arrays.toString(requestBody), Arrays.toString(page1.getBody()));
+
+ RecordedRequest page2 = server.takeRequest();
+ assertEquals("GET /page2 HTTP/1.1", page2.getRequestLine());
+ }
+
+ public void testResponse305UseProxy() throws Exception {
+ server.play();
+ server.enqueue(new MockResponse()
+ .setResponseCode(HttpURLConnection.HTTP_USE_PROXY)
+ .addHeader("Location: " + server.getUrl("/"))
+ .setBody("This page has moved!"));
+ server.enqueue(new MockResponse().setBody("Proxy Response"));
+
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/foo").openConnection();
+ // Fails on the RI, which gets "Proxy Response"
+ assertEquals("This page has moved!",
+ readAscii(connection.getInputStream(), Integer.MAX_VALUE));
+
+ RecordedRequest page1 = server.takeRequest();
+ assertEquals("GET /foo HTTP/1.1", page1.getRequestLine());
+ assertEquals(1, server.getRequestCount());
+ }
+
public void testHttpsWithCustomTrustManager() throws Exception {
RecordingHostnameVerifier hostnameVerifier = new RecordingHostnameVerifier();
RecordingTrustManager trustManager = new RecordingTrustManager();
@@ -1494,6 +1747,115 @@
assertEquals(-1, in.read()); // throws IOException in Gingerbread
}
+ public void testGetContent() throws Exception {
+ server.enqueue(new MockResponse().setBody("A"));
+ server.play();
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ InputStream in = (InputStream) connection.getContent();
+ assertEquals("A", readAscii(in, Integer.MAX_VALUE));
+ }
+
+ public void testGetContentOfType() throws Exception {
+ server.enqueue(new MockResponse().setBody("A"));
+ server.play();
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ try {
+ connection.getContent(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ try {
+ connection.getContent(new Class[] { null });
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ assertNull(connection.getContent(new Class[] { getClass() }));
+ connection.disconnect();
+ }
+
+ public void testGetOutputStreamOnGetFails() throws Exception {
+ server.enqueue(new MockResponse());
+ server.play();
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ try {
+ connection.getOutputStream();
+ fail();
+ } catch (ProtocolException expected) {
+ }
+ }
+
+ public void testGetOutputAfterGetInputStreamFails() throws Exception {
+ server.enqueue(new MockResponse());
+ server.play();
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ connection.setDoOutput(true);
+ try {
+ connection.getInputStream();
+ connection.getOutputStream();
+ fail();
+ } catch (ProtocolException expected) {
+ }
+ }
+
+ public void testSetDoOutputOrDoInputAfterConnectFails() throws Exception {
+ server.enqueue(new MockResponse());
+ server.play();
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ connection.connect();
+ try {
+ connection.setDoOutput(true);
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ try {
+ connection.setDoInput(true);
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ connection.disconnect();
+ }
+
+ public void testClientSendsContentLength() throws Exception {
+ server.enqueue(new MockResponse().setBody("A"));
+ server.play();
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ connection.setDoOutput(true);
+ OutputStream out = connection.getOutputStream();
+ out.write(new byte[] { 'A', 'B', 'C' });
+ out.close();
+ assertEquals("A", readAscii(connection.getInputStream(), Integer.MAX_VALUE));
+ RecordedRequest request = server.takeRequest();
+ assertContains(request.getHeaders(), "Content-Length: 3");
+ }
+
+ public void testGetContentLengthConnects() throws Exception {
+ server.enqueue(new MockResponse().setBody("ABC"));
+ server.play();
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ assertEquals(3, connection.getContentLength());
+ connection.disconnect();
+ }
+
+ public void testGetContentTypeConnects() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Content-Type: text/plain")
+ .setBody("ABC"));
+ server.play();
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ assertEquals("text/plain", connection.getContentType());
+ connection.disconnect();
+ }
+
+ public void testGetContentEncodingConnects() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Content-Encoding: identity")
+ .setBody("ABC"));
+ server.play();
+ HttpURLConnection connection = (HttpURLConnection) server.getUrl("/").openConnection();
+ assertEquals("identity", connection.getContentEncoding());
+ connection.disconnect();
+ }
+
/**
* Returns a gzipped copy of {@code bytes}.
*/
diff --git a/luni/src/test/java/libcore/java/net/URLTest.java b/luni/src/test/java/libcore/java/net/URLTest.java
index 1765630..f00cb25 100644
--- a/luni/src/test/java/libcore/java/net/URLTest.java
+++ b/luni/src/test/java/libcore/java/net/URLTest.java
@@ -18,10 +18,28 @@
import java.net.Inet6Address;
import java.net.InetAddress;
+import java.net.MalformedURLException;
import java.net.URL;
import junit.framework.TestCase;
+import libcore.java.util.SerializableTester;
public class URLTest extends TestCase {
+
+ public void testUrlParts() throws Exception {
+ URL url = new URL("http://username:password@host:8080/directory/file?query#ref");
+ assertEquals("http", url.getProtocol());
+ assertEquals("username:password@host:8080", url.getAuthority());
+ assertEquals("/directory/file", url.getPath());
+ assertEquals("ref", url.getRef());
+
+ assertEquals("username:password", url.getUserInfo());
+ assertEquals("host", url.getHost());
+ assertEquals(8080, url.getPort());
+ assertEquals("/directory/file?query", url.getFile());
+ assertEquals("query", url.getQuery());
+
+ assertEquals(80, url.getDefaultPort());
+ }
// http://code.google.com/p/android/issues/detail?id=12724
public void testExplicitPort() throws Exception {
URL url = new URL("http://www.google.com:80/example?language[id]=2");
@@ -78,4 +96,44 @@
assertFalse(new URL("file", null, -1, "/a/").equals(new URL("file:/a/")));
assertFalse(new URL("http", null, 80, "/a/").equals(new URL("http:/a/")));
}
+
+ public void testUrlSerialization() throws Exception {
+ String s = "aced00057372000c6a6176612e6e65742e55524c962537361afce472030006490004706f72744c0"
+ + "009617574686f726974797400124c6a6176612f6c616e672f537472696e673b4c000466696c65710"
+ + "07e00014c0004686f737471007e00014c000870726f746f636f6c71007e00014c000372656671007"
+ + "e00017870ffffffff74000e757365723a7061737340686f73747400102f706174682f66696c653f7"
+ + "175657279740004686f7374740004687474707400046861736878";
+ URL url = new URL("http://user:pass@host/path/file?query#hash");
+ new SerializableTester<URL>(url, s).test();
+ }
+
+ /**
+ * The serialized form of a URL includes its hash code. But the hash code
+ * is not documented. Check that we don't return a deserialized hash code
+ * from a deserialized value.
+ */
+ public void testUrlSerializationWithHashCode() throws Exception {
+ String s = "aced00057372000c6a6176612e6e65742e55524c962537361afce47203000749000868617368436"
+ + "f6465490004706f72744c0009617574686f726974797400124c6a6176612f6c616e672f537472696"
+ + "e673b4c000466696c6571007e00014c0004686f737471007e00014c000870726f746f636f6c71007"
+ + "e00014c000372656671007e00017870cdf0efacffffffff74000e757365723a7061737340686f737"
+ + "47400102f706174682f66696c653f7175657279740004686f7374740004687474707400046861736"
+ + "878";
+ final URL url = new URL("http://user:pass@host/path/file?query#hash");
+ new SerializableTester<URL>(url, s) {
+ @Override protected void verify(URL deserialized) {
+ assertEquals(url.hashCode(), deserialized.hashCode());
+ }
+ }.test();
+ }
+
+ public void testOnlySupportedProtocols() {
+ try {
+ new URL("abcd://host");
+ fail();
+ } catch (MalformedURLException expected) {
+ }
+ }
+
+ // TODO: test resolve relative URL
}
diff --git a/luni/src/test/java/libcore/java/nio/BufferTest.java b/luni/src/test/java/libcore/java/nio/BufferTest.java
index 6f3f948..255db36 100644
--- a/luni/src/test/java/libcore/java/nio/BufferTest.java
+++ b/luni/src/test/java/libcore/java/nio/BufferTest.java
@@ -638,4 +638,20 @@
} catch (BufferOverflowException expected) {
}
}
+
+ public void testCharBufferSubSequence() throws Exception {
+ ByteBuffer b = ByteBuffer.allocateDirect(10).order(ByteOrder.nativeOrder());
+ b.putChar('H');
+ b.putChar('e');
+ b.putChar('l');
+ b.putChar('l');
+ b.putChar('o');
+ b.flip();
+
+ assertEquals("Hello", b.asCharBuffer().toString());
+
+ CharBuffer cb = b.asCharBuffer();
+ CharSequence cs = cb.subSequence(0, cb.length());
+ assertEquals("Hello", cs.toString());
+ }
}
diff --git a/luni/src/test/java/libcore/java/nio/SelectorTest.java b/luni/src/test/java/libcore/java/nio/SelectorTest.java
deleted file mode 100644
index 9bc1b04..0000000
--- a/luni/src/test/java/libcore/java/nio/SelectorTest.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.java.nio;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.ServerSocket;
-import java.nio.channels.NoConnectionPendingException;
-import java.nio.channels.SelectionKey;
-import java.nio.channels.Selector;
-import java.nio.channels.ServerSocketChannel;
-import java.nio.channels.SocketChannel;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import junit.framework.TestCase;
-import tests.support.Support_PortManager;
-
-public class SelectorTest extends TestCase {
- private static final int WAIT_TIME = 100;
- private static final int PORT = Support_PortManager.getNextPort();
- private static final InetSocketAddress LOCAL_ADDRESS = new InetSocketAddress(
- "127.0.0.1", PORT);
-
- Selector selector;
- ServerSocketChannel ssc;
-
- protected void setUp() throws Exception {
- super.setUp();
- ssc = ServerSocketChannel.open();
- ssc.configureBlocking(false);
- ServerSocket ss = ssc.socket();
- InetSocketAddress address = new InetSocketAddress(PORT);
- ss.bind(address);
- selector = Selector.open();
- }
-
- protected void tearDown() throws Exception {
- try {
- ssc.close();
- } catch (Exception e) {
- // do nothing
- }
- try {
- selector.close();
- } catch (Exception e) {
- // do nothing
- }
- super.tearDown();
- }
-
- // http://code.google.com/p/android/issues/detail?id=4237
- public void test_connectFinish_fails() throws Exception {
- final SocketChannel channel = SocketChannel.open();
- channel.configureBlocking(false);
- channel.register(selector, SelectionKey.OP_CONNECT);
- final Boolean[] fail = new Boolean[1];
- new Thread() {
- public void run() {
- try {
- while (selector.isOpen()) {
- if (selector.select() != 0) {
- for (SelectionKey key : selector.selectedKeys()) {
- if (key.isValid() && key.isConnectable()) {
- try {
- channel.finishConnect();
- synchronized (fail) {
- fail[0] = Boolean.FALSE;
- fail.notify();
- }
- }
- catch (NoConnectionPendingException _) {
- synchronized (fail) {
- fail[0] = Boolean.TRUE;
- fail.notify();
- }
- }
- }
- }
- }
- }
- } catch (Exception _) {}
- }
- }.start();
- Thread.sleep(WAIT_TIME);
- channel.connect(LOCAL_ADDRESS);
- long time = System.currentTimeMillis();
- synchronized (fail) {
- while (System.currentTimeMillis() - time < WAIT_TIME || fail[0] == null) {
- fail.wait(WAIT_TIME);
- }
- }
- if (fail[0] == null) {
- fail("test does not work");
- } else if (fail[0].booleanValue()) {
- fail();
- }
- }
-
- // http://code.google.com/p/android/issues/detail?id=15388
- public void testInterrupted() throws IOException {
- Thread.currentThread().interrupt();
- int count = selector.select();
- assertEquals(0, count);
- }
-
- public void testManyWakeupCallsTriggerOnlyOneWakeup() throws Exception {
- selector.wakeup();
- selector.wakeup();
- selector.wakeup();
- selector.select();
-
- // create a latch that will reach 0 when select returns
- final CountDownLatch selectReturned = new CountDownLatch(1);
- Thread thread = new Thread(new Runnable() {
- @Override public void run() {
- try {
- selector.select();
- selectReturned.countDown();
- } catch (IOException ignored) {
- }
- }
- });
- thread.start();
-
- // select doesn't ever return, so await() times out and returns false
- assertFalse(selectReturned.await(500, TimeUnit.MILLISECONDS));
- }
-}
-
diff --git a/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java
index d982785..13757d2 100644
--- a/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java
@@ -42,4 +42,17 @@
} catch (IllegalArgumentException expected) {
}
}
+
+ // http://code.google.com/p/android/issues/detail?id=16579
+ public void testNonBlockingRecv() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ try {
+ dc.configureBlocking(false);
+ dc.socket().bind(null);
+ // Should return immediately, since we're non-blocking.
+ assertNull(dc.receive(ByteBuffer.allocate(2048)));
+ } finally {
+ dc.close();
+ }
+ }
}
diff --git a/luni/src/test/java/libcore/java/nio/channels/FileChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/FileChannelTest.java
index e221675..415ae0a 100644
--- a/luni/src/test/java/libcore/java/nio/channels/FileChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/FileChannelTest.java
@@ -92,6 +92,22 @@
assertEquals(8, fc.write(buffers));
fc.close();
assertEquals(8, tmp.length());
- assertEquals("abcdABCD", new String(IoUtils.readFileAsByteArray(tmp.getPath()), "US-ASCII"));
+ assertEquals("abcdABCD", new String(IoUtils.readFileAsString(tmp.getPath())));
+ }
+
+ public void test_append() throws Exception {
+ File tmp = File.createTempFile("FileChannelTest", "tmp");
+ FileOutputStream fos = new FileOutputStream(tmp, true);
+ FileChannel fc = fos.getChannel();
+
+ fc.write(ByteBuffer.wrap("hello".getBytes("US-ASCII")));
+ fc.position(0);
+ // The RI reports whatever position you set...
+ assertEquals(0, fc.position());
+ // ...but writes to the end of the file.
+ fc.write(ByteBuffer.wrap(" world".getBytes("US-ASCII")));
+ fos.close();
+
+ assertEquals("hello world", new String(IoUtils.readFileAsString(tmp.getPath())));
}
}
diff --git a/luni/src/test/java/libcore/java/nio/channels/OldFileChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/OldFileChannelTest.java
index 9388888..7e2310b 100644
--- a/luni/src/test/java/libcore/java/nio/channels/OldFileChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/OldFileChannelTest.java
@@ -156,11 +156,13 @@
writeOnlyFileChannel.write(writeBuffer);
writeOnlyFileChannel.force(false);
+ fis.close();
readBuffer = new byte[CONTENT_AS_BYTES_LENGTH];
fis = new FileInputStream(fileOfWriteOnlyFileChannel);
fis.read(readBuffer);
assertTrue(Arrays.equals(CONTENT_AS_BYTES, readBuffer));
+ fis.close();
}
diff --git a/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java b/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java
index 96cec03..aa958ad 100644
--- a/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/SelectorTest.java
@@ -16,10 +16,98 @@
package libcore.java.nio.channels;
import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.nio.channels.NoConnectionPendingException;
+import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import junit.framework.TestCase;
+import tests.net.StuckServer;
public class SelectorTest extends TestCase {
+ public void testNonBlockingConnect_immediate() throws Exception {
+ // Test the case where we [probably] connect immediately.
+ Selector selector = Selector.open();
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ try {
+ ssc.configureBlocking(false);
+ ssc.socket().bind(null);
+
+ SocketChannel sc = SocketChannel.open();
+ sc.configureBlocking(false);
+ sc.connect(ssc.socket().getLocalSocketAddress());
+ SelectionKey key = sc.register(selector, SelectionKey.OP_CONNECT);
+ assertEquals(1, selector.select());
+ assertEquals(SelectionKey.OP_CONNECT, key.readyOps());
+ sc.finishConnect();
+ } finally {
+ selector.close();
+ ssc.close();
+ }
+ }
+
+ public void testNonBlockingConnect_slow() throws Exception {
+ // Test the case where we have to wait for the connection.
+ Selector selector = Selector.open();
+ StuckServer ss = new StuckServer();
+ try {
+ SocketChannel sc = SocketChannel.open();
+ sc.configureBlocking(false);
+ ss.unblockAfterMs(2000);
+ sc.connect(ss.getLocalSocketAddress());
+ SelectionKey key = sc.register(selector, SelectionKey.OP_CONNECT);
+ assertEquals(1, selector.select());
+ assertEquals(SelectionKey.OP_CONNECT, key.readyOps());
+ sc.finishConnect();
+ } finally {
+ selector.close();
+ ss.close();
+ }
+ }
+
+ // http://code.google.com/p/android/issues/detail?id=15388
+ public void testInterrupted() throws IOException {
+ Selector selector = Selector.open();
+ try {
+ Thread.currentThread().interrupt();
+ int count = selector.select();
+ assertEquals(0, count);
+ } finally {
+ selector.close();
+ }
+ }
+
+ public void testManyWakeupCallsTriggerOnlyOneWakeup() throws Exception {
+ final Selector selector = Selector.open();
+ try {
+ selector.wakeup();
+ selector.wakeup();
+ selector.wakeup();
+ selector.select();
+
+ // create a latch that will reach 0 when select returns
+ final CountDownLatch selectReturned = new CountDownLatch(1);
+ Thread thread = new Thread(new Runnable() {
+ @Override public void run() {
+ try {
+ selector.select();
+ selectReturned.countDown();
+ } catch (IOException ignored) {
+ }
+ }
+ });
+ thread.start();
+
+ // select doesn't ever return, so await() times out and returns false
+ assertFalse(selectReturned.await(500, TimeUnit.MILLISECONDS));
+ } finally {
+ selector.close();
+ }
+ }
/**
* We previously leaked a file descriptor for each selector instance created.
diff --git a/luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java
new file mode 100644
index 0000000..e66096c
--- /dev/null
+++ b/luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.nio.channels;
+
+import java.nio.channels.ServerSocketChannel;
+
+public class ServerSocketChannelTest extends junit.framework.TestCase {
+ // http://code.google.com/p/android/issues/detail?id=16579
+ public void testNonBlockingAccept() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ try {
+ ssc.configureBlocking(false);
+ ssc.socket().bind(null);
+ // Should return immediately, since we're non-blocking.
+ assertNull(ssc.accept());
+ } finally {
+ ssc.close();
+ }
+ }
+}
diff --git a/luni/src/test/java/libcore/java/util/EventObjectTest.java b/luni/src/test/java/libcore/java/util/EventObjectTest.java
new file mode 100644
index 0000000..a80e8c7
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/EventObjectTest.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.java.util;
+
+import java.util.EventObject;
+import junit.framework.TestCase;
+
+public final class EventObjectTest extends TestCase {
+
+ public void testConstructor() {
+ try {
+ new EventObject(null);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ public void testGetSource() {
+ Object source = new Object();
+ assertSame(source, new EventObject(source).getSource());
+ }
+
+ public void testToString() {
+ assertEquals("java.util.EventObject[source=x]", new EventObject("x").toString());
+ }
+
+ public void testSerializationNullsOutSource() {
+ String s = "aced0005737200156a6176612e7574696c2e4576656e744f626a6563744"
+ + "c8d094e186d7da80200007870";
+ Object source = new Object();
+ EventObject eventObject = new EventObject(source);
+ new SerializableTester<EventObject>(eventObject, s) {
+ @Override protected boolean equals(EventObject a, EventObject b) {
+ return a.getSource() == null
+ || b.getSource() == null
+ || a.getSource() == b.getSource();
+ }
+ }.test();
+ }
+}
diff --git a/luni/src/test/java/libcore/java/util/SerializableTester.java b/luni/src/test/java/libcore/java/util/SerializableTester.java
index 20318d7..50b6435 100644
--- a/luni/src/test/java/libcore/java/util/SerializableTester.java
+++ b/luni/src/test/java/libcore/java/util/SerializableTester.java
@@ -78,20 +78,20 @@
}
}
- private byte[] serialize(Object object) throws IOException {
+ private static byte[] serialize(Object object) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
new ObjectOutputStream(out).writeObject(object);
return out.toByteArray();
}
- private Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
+ private static Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bytes));
Object result = in.readObject();
assertEquals(-1, in.read());
return result;
}
- private String hexEncode(byte[] bytes) {
+ private static String hexEncode(byte[] bytes) {
StringBuilder result = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
result.append(String.format("%02x", b));
@@ -99,12 +99,19 @@
return result.toString();
}
- private byte[] hexDecode(String s) {
+ private static byte[] hexDecode(String s) {
byte[] result = new byte[s.length() / 2];
for (int i = 0; i < result.length; i++) {
result[i] = (byte) Integer.parseInt(s.substring(i*2, i*2 + 2), 16);
}
return result;
}
-}
+ public static String serializeHex(Object object) throws IOException {
+ return hexEncode(serialize(object));
+ }
+
+ public static Object deserializeHex(String hex) throws IOException, ClassNotFoundException {
+ return deserialize(hexDecode(hex));
+ }
+}
diff --git a/luni/src/test/java/libcore/java/util/TimeZoneTest.java b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
index dde2ec8..39ac19d 100644
--- a/luni/src/test/java/libcore/java/util/TimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
@@ -132,6 +132,16 @@
}
}
+ // http://code.google.com/p/android/issues/detail?id=16608
+ public void testHelsinkiOverflow() throws Exception {
+ long time = 2147483648000L;// Tue, 19 Jan 2038 03:14:08 GMT
+ TimeZone timeZone = TimeZone.getTimeZone("Europe/Helsinki");
+ long offset = timeZone.getOffset(time);
+ // This might cause us trouble if Europe/Helsinki changes its rules. See the bug for
+ // details of the intent of this test.
+ assertEquals(7200000, offset);
+ }
+
// http://code.google.com/p/android/issues/detail?id=11918
public void testHasSameRules() throws Exception {
TimeZone denver = TimeZone.getTimeZone ("America/Denver") ;
diff --git a/luni/src/test/java/libcore/java/net/HttpResponseCacheTest.java b/luni/src/test/java/libcore/net/http/HttpResponseCacheTest.java
similarity index 67%
rename from luni/src/test/java/libcore/java/net/HttpResponseCacheTest.java
rename to luni/src/test/java/libcore/net/http/HttpResponseCacheTest.java
index ca8c790..5981b0b 100644
--- a/luni/src/test/java/libcore/java/net/HttpResponseCacheTest.java
+++ b/luni/src/test/java/libcore/net/http/HttpResponseCacheTest.java
@@ -14,17 +14,22 @@
* limitations under the License.
*/
-package libcore.java.net;
+package libcore.net.http;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.lang.reflect.InvocationHandler;
import java.net.CacheRequest;
import java.net.CacheResponse;
+import java.net.CookieHandler;
+import java.net.CookieManager;
+import java.net.HttpCookie;
import java.net.HttpURLConnection;
import java.net.ResponseCache;
import java.net.SecureCacheResponse;
@@ -40,12 +45,13 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
+import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.Set;
import java.util.TimeZone;
+import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@@ -53,24 +59,35 @@
import javax.net.ssl.HttpsURLConnection;
import junit.framework.TestCase;
import libcore.javax.net.ssl.TestSSLContext;
-import libcore.net.http.HttpResponseCache;
import tests.http.MockResponse;
import tests.http.MockWebServer;
import tests.http.RecordedRequest;
import static tests.http.SocketPolicy.DISCONNECT_AT_END;
+import tests.io.MockOs;
public final class HttpResponseCacheTest extends TestCase {
private MockWebServer server = new MockWebServer();
- private HttpResponseCache cache = new HttpResponseCache();
+ private HttpResponseCache cache;
+ private final MockOs mockOs = new MockOs();
+ private final CookieManager cookieManager = new CookieManager();
@Override protected void setUp() throws Exception {
super.setUp();
+
+ String tmp = System.getProperty("java.io.tmpdir");
+ File cacheDir = new File(tmp, "HttpCache-" + UUID.randomUUID());
+ cache = new HttpResponseCache(cacheDir, Integer.MAX_VALUE);
ResponseCache.setDefault(cache);
+ mockOs.install();
+ CookieHandler.setDefault(cookieManager);
}
@Override protected void tearDown() throws Exception {
- ResponseCache.setDefault(null);
+ mockOs.uninstall();
server.shutdown();
+ ResponseCache.setDefault(null);
+ cache.getCache().delete();
+ CookieHandler.setDefault(null);
super.tearDown();
}
@@ -153,11 +170,14 @@
// exhaust the content stream
readAscii(conn);
- Set<URI> expectedCachedUris = shouldPut
- ? Collections.singleton(url.toURI())
- : Collections.<URI>emptySet();
- assertEquals(Integer.toString(responseCode),
- expectedCachedUris, cache.getContents().keySet());
+ CacheResponse cached = cache.get(url.toURI(), "GET",
+ Collections.<String, List<String>>emptyMap());
+ if (shouldPut) {
+ assertNotNull(Integer.toString(responseCode), cached);
+ cached.getBody().close();
+ } else {
+ assertNull(Integer.toString(responseCode), cached);
+ }
server.shutdown(); // tearDown() isn't sufficient; this test starts multiple servers
}
@@ -249,8 +269,8 @@
assertEquals("spiders", readAscii(urlConnection, "spiders".length()));
assertEquals(-1, in.read());
in.close();
- assertEquals(1, cache.getSuccessCount());
- assertEquals(0, cache.getAbortCount());
+ assertEquals(1, cache.getWriteSuccessCount());
+ assertEquals(0, cache.getWriteAbortCount());
urlConnection = (HttpURLConnection) server.getUrl("/").openConnection(); // cached!
in = urlConnection.getInputStream();
@@ -260,10 +280,11 @@
assertEquals("Fantastic", urlConnection.getResponseMessage());
assertEquals(-1, in.read());
- assertEquals(1, cache.getMissCount());
+ in.close();
+ assertEquals(1, cache.getWriteSuccessCount());
+ assertEquals(0, cache.getWriteAbortCount());
+ assertEquals(2, cache.getRequestCount());
assertEquals(1, cache.getHitCount());
- assertEquals(1, cache.getSuccessCount());
- assertEquals(0, cache.getAbortCount());
}
public void testSecureResponseCaching() throws IOException {
@@ -290,7 +311,8 @@
connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
assertEquals("ABC", readAscii(connection));
- assertEquals(1, cache.getMissCount());
+ assertEquals(2, cache.getRequestCount());
+ assertEquals(1, cache.getNetworkCount());
assertEquals(1, cache.getHitCount());
assertEquals(suite, connection.getCipherSuite());
@@ -318,7 +340,7 @@
assertEquals("DEF", readAscii(connection));
}
- public void testResponseCachingAndRedirects() throws IOException {
+ public void testResponseCachingAndRedirects() throws Exception {
server.enqueue(new MockResponse()
.addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
.addHeader("Expires: " + formatDate(1, TimeUnit.HOURS))
@@ -337,10 +359,38 @@
connection = server.getUrl("/").openConnection(); // cached!
assertEquals("ABC", readAscii(connection));
- assertEquals(2, cache.getMissCount()); // 1 redirect + 1 final response = 2
+ assertEquals(4, cache.getRequestCount()); // 2 requests + 2 redirects
+ assertEquals(2, cache.getNetworkCount());
assertEquals(2, cache.getHitCount());
}
+ public void testRedirectToCachedResult() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Cache-Control: max-age=60")
+ .setBody("ABC"));
+ server.enqueue(new MockResponse()
+ .setResponseCode(HttpURLConnection.HTTP_MOVED_PERM)
+ .addHeader("Location: /foo"));
+ server.enqueue(new MockResponse().setBody("DEF"));
+ server.play();
+
+ assertEquals("ABC", readAscii(server.getUrl("/foo").openConnection()));
+ RecordedRequest request1 = server.takeRequest();
+ assertEquals("GET /foo HTTP/1.1", request1.getRequestLine());
+ assertEquals(0, request1.getSequenceNumber());
+
+ assertEquals("ABC", readAscii(server.getUrl("/bar").openConnection()));
+ RecordedRequest request2 = server.takeRequest();
+ assertEquals("GET /bar HTTP/1.1", request2.getRequestLine());
+ assertEquals(1, request2.getSequenceNumber());
+
+ // an unrelated request should reuse the pooled connection
+ assertEquals("DEF", readAscii(server.getUrl("/baz").openConnection()));
+ RecordedRequest request3 = server.takeRequest();
+ assertEquals("GET /baz HTTP/1.1", request3.getRequestLine());
+ assertEquals(2, request3.getSequenceNumber());
+ }
+
public void testSecureResponseCachingAndRedirects() throws IOException {
TestSSLContext testSSLContext = TestSSLContext.create();
server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
@@ -364,7 +414,7 @@
connection.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
assertEquals("ABC", readAscii(connection));
- assertEquals(2, cache.getMissCount()); // 1 redirect + 1 final response = 2
+ assertEquals(4, cache.getRequestCount()); // 2 direct + 2 redirect = 4
assertEquals(2, cache.getHitCount());
}
@@ -423,14 +473,16 @@
reader.readLine();
fail("This implementation silently ignored a truncated HTTP body.");
} catch (IOException expected) {
+ } finally {
+ reader.close();
}
- assertEquals(1, cache.getAbortCount());
- assertEquals(0, cache.getSuccessCount());
+ assertEquals(1, cache.getWriteAbortCount());
+ assertEquals(0, cache.getWriteSuccessCount());
URLConnection connection = server.getUrl("/").openConnection();
assertEquals("Request #2", readAscii(connection));
- assertEquals(1, cache.getAbortCount());
- assertEquals(1, cache.getSuccessCount());
+ assertEquals(1, cache.getWriteAbortCount());
+ assertEquals(1, cache.getWriteSuccessCount());
}
public void testClientPrematureDisconnectWithContentLengthHeader() throws IOException {
@@ -462,12 +514,12 @@
} catch (IOException expected) {
}
- assertEquals(1, cache.getAbortCount());
- assertEquals(0, cache.getSuccessCount());
+ assertEquals(1, cache.getWriteAbortCount());
+ assertEquals(0, cache.getWriteSuccessCount());
connection = server.getUrl("/").openConnection();
assertEquals("Request #2", readAscii(connection));
- assertEquals(1, cache.getAbortCount());
- assertEquals(1, cache.getSuccessCount());
+ assertEquals(1, cache.getWriteAbortCount());
+ assertEquals(1, cache.getWriteSuccessCount());
}
public void testDefaultExpirationDateFullyCachedForLessThan24Hours() throws Exception {
@@ -881,6 +933,20 @@
connection.getHeaderField("Warning"));
}
+ public void testRequestMaxStaleNotHonoredWithMustRevalidate() throws IOException {
+ server.enqueue(new MockResponse().setBody("A")
+ .addHeader("Cache-Control: max-age=120, must-revalidate")
+ .addHeader("Date: " + formatDate(-4, TimeUnit.MINUTES)));
+ server.enqueue(new MockResponse().setBody("B"));
+
+ server.play();
+ assertEquals("A", readAscii(server.getUrl("/").openConnection()));
+
+ URLConnection connection = server.getUrl("/").openConnection();
+ connection.addRequestProperty("Cache-Control", "max-stale=180");
+ assertEquals("B", readAscii(connection));
+ }
+
public void testRequestOnlyIfCachedWithNoResponseCached() throws IOException {
// (no responses enqueued)
server.play();
@@ -999,6 +1065,19 @@
return server.takeRequest();
}
+ public void testSetIfModifiedSince() throws Exception {
+ Date since = new Date();
+ server.enqueue(new MockResponse().setBody("A"));
+ server.play();
+
+ URL url = server.getUrl("/");
+ URLConnection connection = url.openConnection();
+ connection.setIfModifiedSince(since.getTime());
+ assertEquals("A", readAscii(connection));
+ RecordedRequest request = server.takeRequest();
+ assertTrue(request.getHeaders().contains("If-Modified-Since: " + formatDate(since)));
+ }
+
public void testClientSuppliedConditionWithoutCachedResult() throws Exception {
server.enqueue(new MockResponse()
.setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
@@ -1055,28 +1134,6 @@
assertEquals("A", readAscii(url.openConnection()));
}
- public void testCacheControlMustRevalidate() throws Exception {
- fail("Cache-Control: must-revalidate"); // TODO
- }
-
- public void testVaryResponsesAreNotSupported() throws Exception {
- server.enqueue(new MockResponse()
- .addHeader("Cache-Control: max-age=60")
- .addHeader("Vary: Accept-Language")
- .setBody("A"));
- server.enqueue(new MockResponse().setBody("B"));
- server.play();
-
- URL url = server.getUrl("/");
- URLConnection connection1 = url.openConnection();
- connection1.addRequestProperty("Accept-Language", "fr-CA");
- assertEquals("A", readAscii(connection1));
-
- URLConnection connection2 = url.openConnection();
- connection2.addRequestProperty("Accept-Language", "fr-CA");
- assertEquals("B", readAscii(connection2));
- }
-
public void testContentLocationDoesNotPopulateCache() throws Exception {
server.enqueue(new MockResponse()
.addHeader("Cache-Control: max-age=60")
@@ -1089,13 +1146,483 @@
assertEquals("B", readAscii(server.getUrl("/bar").openConnection()));
}
+ public void testUseCachesFalseDoesNotWriteToCache() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Cache-Control: max-age=60")
+ .setBody("A").setBody("A"));
+ server.enqueue(new MockResponse().setBody("B"));
+ server.play();
+
+ URLConnection connection = server.getUrl("/").openConnection();
+ connection.setUseCaches(false);
+ assertEquals("A", readAscii(connection));
+ assertEquals("B", readAscii(server.getUrl("/").openConnection()));
+ }
+
+ public void testUseCachesFalseDoesNotReadFromCache() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Cache-Control: max-age=60")
+ .setBody("A").setBody("A"));
+ server.enqueue(new MockResponse().setBody("B"));
+ server.play();
+
+ assertEquals("A", readAscii(server.getUrl("/").openConnection()));
+ URLConnection connection = server.getUrl("/").openConnection();
+ connection.setUseCaches(false);
+ assertEquals("B", readAscii(connection));
+ }
+
+ public void testDefaultUseCachesSetsInitialValueOnly() throws Exception {
+ URL url = new URL("http://localhost/");
+ URLConnection c1 = url.openConnection();
+ URLConnection c2 = url.openConnection();
+ assertTrue(c1.getDefaultUseCaches());
+ c1.setDefaultUseCaches(false);
+ try {
+ assertTrue(c1.getUseCaches());
+ assertTrue(c2.getUseCaches());
+ URLConnection c3 = url.openConnection();
+ assertFalse(c3.getUseCaches());
+ } finally {
+ c1.setDefaultUseCaches(true);
+ }
+ }
+
+ public void testConnectionIsReturnedToPoolAfterConditionalSuccess() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
+ .addHeader("Cache-Control: max-age=0")
+ .setBody("A"));
+ server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
+ server.enqueue(new MockResponse().setBody("B"));
+ server.play();
+
+ assertEquals("A", readAscii(server.getUrl("/a").openConnection()));
+ assertEquals("A", readAscii(server.getUrl("/a").openConnection()));
+ assertEquals("B", readAscii(server.getUrl("/b").openConnection()));
+
+ assertEquals(0, server.takeRequest().getSequenceNumber());
+ assertEquals(1, server.takeRequest().getSequenceNumber());
+ assertEquals(2, server.takeRequest().getSequenceNumber());
+ }
+
+ public void testStatisticsConditionalCacheMiss() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
+ .addHeader("Cache-Control: max-age=0")
+ .setBody("A"));
+ server.enqueue(new MockResponse().setBody("B"));
+ server.enqueue(new MockResponse().setBody("C"));
+ server.play();
+
+ assertEquals("A", readAscii(server.getUrl("/").openConnection()));
+ assertEquals(1, cache.getRequestCount());
+ assertEquals(1, cache.getNetworkCount());
+ assertEquals(0, cache.getHitCount());
+ assertEquals("B", readAscii(server.getUrl("/").openConnection()));
+ assertEquals("C", readAscii(server.getUrl("/").openConnection()));
+ assertEquals(3, cache.getRequestCount());
+ assertEquals(3, cache.getNetworkCount());
+ assertEquals(0, cache.getHitCount());
+ }
+
+ public void testStatisticsConditionalCacheHit() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
+ .addHeader("Cache-Control: max-age=0")
+ .setBody("A"));
+ server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
+ server.enqueue(new MockResponse().setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
+ server.play();
+
+ assertEquals("A", readAscii(server.getUrl("/").openConnection()));
+ assertEquals(1, cache.getRequestCount());
+ assertEquals(1, cache.getNetworkCount());
+ assertEquals(0, cache.getHitCount());
+ assertEquals("A", readAscii(server.getUrl("/").openConnection()));
+ assertEquals("A", readAscii(server.getUrl("/").openConnection()));
+ assertEquals(3, cache.getRequestCount());
+ assertEquals(3, cache.getNetworkCount());
+ assertEquals(2, cache.getHitCount());
+ }
+
+ public void testStatisticsFullCacheHit() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Cache-Control: max-age=60")
+ .setBody("A"));
+ server.play();
+
+ assertEquals("A", readAscii(server.getUrl("/").openConnection()));
+ assertEquals(1, cache.getRequestCount());
+ assertEquals(1, cache.getNetworkCount());
+ assertEquals(0, cache.getHitCount());
+ assertEquals("A", readAscii(server.getUrl("/").openConnection()));
+ assertEquals("A", readAscii(server.getUrl("/").openConnection()));
+ assertEquals(3, cache.getRequestCount());
+ assertEquals(1, cache.getNetworkCount());
+ assertEquals(2, cache.getHitCount());
+ }
+
+ public void testVaryMatchesChangedRequestHeaderField() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Cache-Control: max-age=60")
+ .addHeader("Vary: Accept-Language")
+ .setBody("A"));
+ server.enqueue(new MockResponse().setBody("B"));
+ server.play();
+
+ URL url = server.getUrl("/");
+ HttpURLConnection frConnection = (HttpURLConnection) url.openConnection();
+ frConnection.addRequestProperty("Accept-Language", "fr-CA");
+ assertEquals("A", readAscii(frConnection));
+
+ HttpURLConnection enConnection = (HttpURLConnection) url.openConnection();
+ enConnection.addRequestProperty("Accept-Language", "en-US");
+ assertEquals("B", readAscii(enConnection));
+ }
+
+ public void testVaryMatchesUnchangedRequestHeaderField() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Cache-Control: max-age=60")
+ .addHeader("Vary: Accept-Language")
+ .setBody("A"));
+ server.enqueue(new MockResponse().setBody("B"));
+ server.play();
+
+ URL url = server.getUrl("/");
+ URLConnection connection1 = url.openConnection();
+ connection1.addRequestProperty("Accept-Language", "fr-CA");
+ assertEquals("A", readAscii(connection1));
+ URLConnection connection2 = url.openConnection();
+ connection2.addRequestProperty("Accept-Language", "fr-CA");
+ assertEquals("A", readAscii(connection2));
+ }
+
+ public void testVaryMatchesAbsentRequestHeaderField() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Cache-Control: max-age=60")
+ .addHeader("Vary: Foo")
+ .setBody("A"));
+ server.enqueue(new MockResponse().setBody("B"));
+ server.play();
+
+ assertEquals("A", readAscii(server.getUrl("/").openConnection()));
+ assertEquals("A", readAscii(server.getUrl("/").openConnection()));
+ }
+
+ public void testVaryMatchesAddedRequestHeaderField() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Cache-Control: max-age=60")
+ .addHeader("Vary: Foo")
+ .setBody("A"));
+ server.enqueue(new MockResponse().setBody("B"));
+ server.play();
+
+ assertEquals("A", readAscii(server.getUrl("/").openConnection()));
+ URLConnection fooConnection = server.getUrl("/").openConnection();
+ fooConnection.addRequestProperty("Foo", "bar");
+ assertEquals("B", readAscii(fooConnection));
+ }
+
+ public void testVaryMatchesRemovedRequestHeaderField() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Cache-Control: max-age=60")
+ .addHeader("Vary: Foo")
+ .setBody("A"));
+ server.enqueue(new MockResponse().setBody("B"));
+ server.play();
+
+ URLConnection fooConnection = server.getUrl("/").openConnection();
+ fooConnection.addRequestProperty("Foo", "bar");
+ assertEquals("A", readAscii(fooConnection));
+ assertEquals("B", readAscii(server.getUrl("/").openConnection()));
+ }
+
+ public void testVaryFieldsAreCaseInsensitive() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Cache-Control: max-age=60")
+ .addHeader("Vary: ACCEPT-LANGUAGE")
+ .setBody("A"));
+ server.enqueue(new MockResponse().setBody("B"));
+ server.play();
+
+ URL url = server.getUrl("/");
+ URLConnection connection1 = url.openConnection();
+ connection1.addRequestProperty("Accept-Language", "fr-CA");
+ assertEquals("A", readAscii(connection1));
+ URLConnection connection2 = url.openConnection();
+ connection2.addRequestProperty("accept-language", "fr-CA");
+ assertEquals("A", readAscii(connection2));
+ }
+
+ public void testVaryMultipleFieldsWithMatch() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Cache-Control: max-age=60")
+ .addHeader("Vary: Accept-Language, Accept-Charset")
+ .addHeader("Vary: Accept-Encoding")
+ .setBody("A"));
+ server.enqueue(new MockResponse().setBody("B"));
+ server.play();
+
+ URL url = server.getUrl("/");
+ URLConnection connection1 = url.openConnection();
+ connection1.addRequestProperty("Accept-Language", "fr-CA");
+ connection1.addRequestProperty("Accept-Charset", "UTF-8");
+ connection1.addRequestProperty("Accept-Encoding", "identity");
+ assertEquals("A", readAscii(connection1));
+ URLConnection connection2 = url.openConnection();
+ connection2.addRequestProperty("Accept-Language", "fr-CA");
+ connection2.addRequestProperty("Accept-Charset", "UTF-8");
+ connection2.addRequestProperty("Accept-Encoding", "identity");
+ assertEquals("A", readAscii(connection2));
+ }
+
+ public void testVaryMultipleFieldsWithNoMatch() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Cache-Control: max-age=60")
+ .addHeader("Vary: Accept-Language, Accept-Charset")
+ .addHeader("Vary: Accept-Encoding")
+ .setBody("A"));
+ server.enqueue(new MockResponse().setBody("B"));
+ server.play();
+
+ URL url = server.getUrl("/");
+ URLConnection frConnection = url.openConnection();
+ frConnection.addRequestProperty("Accept-Language", "fr-CA");
+ frConnection.addRequestProperty("Accept-Charset", "UTF-8");
+ frConnection.addRequestProperty("Accept-Encoding", "identity");
+ assertEquals("A", readAscii(frConnection));
+ URLConnection enConnection = url.openConnection();
+ enConnection.addRequestProperty("Accept-Language", "en-CA");
+ enConnection.addRequestProperty("Accept-Charset", "UTF-8");
+ enConnection.addRequestProperty("Accept-Encoding", "identity");
+ assertEquals("B", readAscii(enConnection));
+ }
+
+ public void testVaryMultipleFieldValuesWithMatch() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Cache-Control: max-age=60")
+ .addHeader("Vary: Accept-Language")
+ .setBody("A"));
+ server.enqueue(new MockResponse().setBody("B"));
+ server.play();
+
+ URL url = server.getUrl("/");
+ URLConnection connection1 = url.openConnection();
+ connection1.addRequestProperty("Accept-Language", "fr-CA, fr-FR");
+ connection1.addRequestProperty("Accept-Language", "en-US");
+ assertEquals("A", readAscii(connection1));
+
+ URLConnection connection2 = url.openConnection();
+ connection2.addRequestProperty("Accept-Language", "fr-CA, fr-FR");
+ connection2.addRequestProperty("Accept-Language", "en-US");
+ assertEquals("A", readAscii(connection2));
+ }
+
+ public void testVaryMultipleFieldValuesWithNoMatch() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Cache-Control: max-age=60")
+ .addHeader("Vary: Accept-Language")
+ .setBody("A"));
+ server.enqueue(new MockResponse().setBody("B"));
+ server.play();
+
+ URL url = server.getUrl("/");
+ URLConnection connection1 = url.openConnection();
+ connection1.addRequestProperty("Accept-Language", "fr-CA, fr-FR");
+ connection1.addRequestProperty("Accept-Language", "en-US");
+ assertEquals("A", readAscii(connection1));
+
+ URLConnection connection2 = url.openConnection();
+ connection2.addRequestProperty("Accept-Language", "fr-CA");
+ connection2.addRequestProperty("Accept-Language", "en-US");
+ assertEquals("B", readAscii(connection2));
+ }
+
+ public void testVaryAsterisk() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Cache-Control: max-age=60")
+ .addHeader("Vary: *")
+ .setBody("A"));
+ server.enqueue(new MockResponse().setBody("B"));
+ server.play();
+
+ assertEquals("A", readAscii(server.getUrl("/").openConnection()));
+ assertEquals("B", readAscii(server.getUrl("/").openConnection()));
+ }
+
+ public void testVaryAndHttps() throws Exception {
+ TestSSLContext testSSLContext = TestSSLContext.create();
+ server.useHttps(testSSLContext.serverContext.getSocketFactory(), false);
+ server.enqueue(new MockResponse()
+ .addHeader("Cache-Control: max-age=60")
+ .addHeader("Vary: Accept-Language")
+ .setBody("A"));
+ server.enqueue(new MockResponse().setBody("B"));
+ server.play();
+
+ URL url = server.getUrl("/");
+ HttpsURLConnection connection1 = (HttpsURLConnection) url.openConnection();
+ connection1.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
+ connection1.addRequestProperty("Accept-Language", "en-US");
+ assertEquals("A", readAscii(connection1));
+
+ HttpsURLConnection connection2 = (HttpsURLConnection) url.openConnection();
+ connection2.setSSLSocketFactory(testSSLContext.clientContext.getSocketFactory());
+ connection2.addRequestProperty("Accept-Language", "en-US");
+ assertEquals("A", readAscii(connection2));
+ }
+
+ public void testDiskWriteFailureCacheDegradation() throws Exception {
+ Deque<InvocationHandler> writeHandlers = mockOs.getHandlers("write");
+ int i = 0;
+ boolean hasMoreScenarios = true;
+ while (hasMoreScenarios) {
+ mockOs.enqueueNormal("write", i++);
+ mockOs.enqueueFault("write");
+ exercisePossiblyFaultyCache(false);
+ hasMoreScenarios = writeHandlers.isEmpty();
+ writeHandlers.clear();
+ }
+ System.out.println("Exercising the cache performs " + (i - 1) + " writes.");
+ }
+
+ public void testDiskReadFailureCacheDegradation() throws Exception {
+ Deque<InvocationHandler> readHandlers = mockOs.getHandlers("read");
+ int i = 0;
+ boolean hasMoreScenarios = true;
+ while (hasMoreScenarios) {
+ mockOs.enqueueNormal("read", i++);
+ mockOs.enqueueFault("read");
+ exercisePossiblyFaultyCache(true);
+ hasMoreScenarios = readHandlers.isEmpty();
+ readHandlers.clear();
+ }
+ System.out.println("Exercising the cache performs " + (i - 1) + " reads.");
+ }
+
+ public void testCachePlusCookies() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Set-Cookie: a=FIRST; domain=.local;")
+ .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
+ .addHeader("Cache-Control: max-age=0")
+ .setBody("A"));
+ server.enqueue(new MockResponse()
+ .addHeader("Set-Cookie: a=SECOND; domain=.local;")
+ .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
+ server.play();
+
+ URL url = server.getUrl("/");
+ assertEquals("A", readAscii(url.openConnection()));
+ assertCookies(url, "a=FIRST");
+ assertEquals("A", readAscii(url.openConnection()));
+ assertCookies(url, "a=SECOND");
+ }
+
+ public void testGetHeadersReturnsNetworkEndToEndHeaders() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Allow: GET, HEAD")
+ .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
+ .addHeader("Cache-Control: max-age=0")
+ .setBody("A"));
+ server.enqueue(new MockResponse()
+ .addHeader("Allow: GET, HEAD, PUT")
+ .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
+ server.play();
+
+ URLConnection connection1 = server.getUrl("/").openConnection();
+ assertEquals("A", readAscii(connection1));
+ assertEquals("GET, HEAD", connection1.getHeaderField("Allow"));
+
+ URLConnection connection2 = server.getUrl("/").openConnection();
+ assertEquals("A", readAscii(connection2));
+ assertEquals("GET, HEAD, PUT", connection2.getHeaderField("Allow"));
+ }
+
+ public void testGetHeadersReturnsCachedHopByHopHeaders() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Transfer-Encoding: identity")
+ .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
+ .addHeader("Cache-Control: max-age=0")
+ .setBody("A"));
+ server.enqueue(new MockResponse()
+ .addHeader("Transfer-Encoding: none")
+ .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
+ server.play();
+
+ URLConnection connection1 = server.getUrl("/").openConnection();
+ assertEquals("A", readAscii(connection1));
+ assertEquals("identity", connection1.getHeaderField("Transfer-Encoding"));
+
+ URLConnection connection2 = server.getUrl("/").openConnection();
+ assertEquals("A", readAscii(connection2));
+ assertEquals("identity", connection2.getHeaderField("Transfer-Encoding"));
+ }
+
+ public void testGetHeadersDeletesCached100LevelWarnings() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Warning: 199 test danger")
+ .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
+ .addHeader("Cache-Control: max-age=0")
+ .setBody("A"));
+ server.enqueue(new MockResponse()
+ .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
+ server.play();
+
+ URLConnection connection1 = server.getUrl("/").openConnection();
+ assertEquals("A", readAscii(connection1));
+ assertEquals("199 test danger", connection1.getHeaderField("Warning"));
+
+ URLConnection connection2 = server.getUrl("/").openConnection();
+ assertEquals("A", readAscii(connection2));
+ assertEquals(null, connection2.getHeaderField("Warning"));
+ }
+
+ public void testGetHeadersRetainsCached200LevelWarnings() throws Exception {
+ server.enqueue(new MockResponse()
+ .addHeader("Warning: 299 test danger")
+ .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))
+ .addHeader("Cache-Control: max-age=0")
+ .setBody("A"));
+ server.enqueue(new MockResponse()
+ .setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
+ server.play();
+
+ URLConnection connection1 = server.getUrl("/").openConnection();
+ assertEquals("A", readAscii(connection1));
+ assertEquals("299 test danger", connection1.getHeaderField("Warning"));
+
+ URLConnection connection2 = server.getUrl("/").openConnection();
+ assertEquals("A", readAscii(connection2));
+ assertEquals("299 test danger", connection2.getHeaderField("Warning"));
+ }
+
+ public void assertCookies(URL url, String... expectedCookies) throws Exception {
+ List<String> actualCookies = new ArrayList<String>();
+ for (HttpCookie cookie : cookieManager.getCookieStore().get(url.toURI())) {
+ actualCookies.add(cookie.toString());
+ }
+ assertEquals(Arrays.asList(expectedCookies), actualCookies);
+ }
+
+ public void testCachePlusRange() throws Exception {
+ assertNotCached(new MockResponse()
+ .setResponseCode(HttpURLConnection.HTTP_PARTIAL)
+ .addHeader("Date: " + formatDate(0, TimeUnit.HOURS))
+ .addHeader("Content-Range: bytes 100-100/200")
+ .addHeader("Cache-Control: max-age=60"));
+ }
+
/**
* @param delta the offset from the current date to use. Negative
* values yield dates in the past; positive values yield dates in the
* future.
*/
private String formatDate(long delta, TimeUnit timeUnit) {
- Date date = new Date(System.currentTimeMillis() + timeUnit.toMillis(delta));
+ return formatDate(new Date(System.currentTimeMillis() + timeUnit.toMillis(delta)));
+ }
+
+ private String formatDate(Date date) {
DateFormat rfc1123 = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US);
rfc1123.setTimeZone(TimeZone.getTimeZone("UTC"));
return rfc1123.format(date);
@@ -1121,6 +1648,31 @@
assertEquals("B", readAscii(url.openConnection()));
}
+ private void exercisePossiblyFaultyCache(boolean permitReadBodyFailures) throws Exception {
+ server.shutdown();
+ server = new MockWebServer();
+ server.enqueue(new MockResponse()
+ .addHeader("Cache-Control: max-age=60")
+ .setBody("A"));
+ server.enqueue(new MockResponse().setBody("B"));
+ server.play();
+
+ URL url = server.getUrl("/" + UUID.randomUUID());
+ assertEquals("A", readAscii(url.openConnection()));
+
+ URLConnection connection = url.openConnection();
+ InputStream in = connection.getInputStream();
+ try {
+ int bodyChar = in.read();
+ assertTrue(bodyChar == 'A' || bodyChar == 'B');
+ assertEquals(-1, in.read());
+ } catch (IOException e) {
+ if (!permitReadBodyFailures) {
+ throw e;
+ }
+ }
+ }
+
/**
* @return the request with the conditional get headers.
*/
@@ -1261,16 +1813,14 @@
return bytesOut.toByteArray();
}
- private static class InsecureResponseCache extends ResponseCache {
- private final HttpResponseCache delegate = new HttpResponseCache();
-
+ private class InsecureResponseCache extends ResponseCache {
@Override public CacheRequest put(URI uri, URLConnection connection) throws IOException {
- return delegate.put(uri, connection);
+ return cache.put(uri, connection);
}
@Override public CacheResponse get(URI uri, String requestMethod,
Map<String, List<String>> requestHeaders) throws IOException {
- final CacheResponse response = delegate.get(uri, requestMethod, requestHeaders);
+ final CacheResponse response = cache.get(uri, requestMethod, requestHeaders);
if (response instanceof SecureCacheResponse) {
return new CacheResponse() {
@Override public InputStream getBody() throws IOException {
diff --git a/luni/src/test/java/libcore/net/http/ParsedHeadersTest.java b/luni/src/test/java/libcore/net/http/ParsedHeadersTest.java
index 0f38a5d..ae684c3 100644
--- a/luni/src/test/java/libcore/net/http/ParsedHeadersTest.java
+++ b/luni/src/test/java/libcore/net/http/ParsedHeadersTest.java
@@ -57,16 +57,15 @@
public void testQuotedFieldName() {
RawHeaders headers = new RawHeaders();
- headers.add("Cache-Control", "private=\"Set-Cookie\"");
+ headers.add("Cache-Control", "private=\"Set-Cookie\", no-store");
ResponseHeaders parsedHeaders = new ResponseHeaders(uri, headers);
- assertEquals("Set-Cookie", parsedHeaders.privateField);
+ assertTrue(parsedHeaders.noStore);
}
public void testUnquotedValue() {
RawHeaders headers = new RawHeaders();
headers.add("Cache-Control", "private=Set-Cookie, no-store");
ResponseHeaders parsedHeaders = new ResponseHeaders(uri, headers);
- assertEquals("Set-Cookie", parsedHeaders.privateField);
assertTrue(parsedHeaders.noStore);
}
@@ -74,7 +73,6 @@
RawHeaders headers = new RawHeaders();
headers.add("Cache-Control", "private=\" a, no-cache, c \", no-store");
ResponseHeaders parsedHeaders = new ResponseHeaders(uri, headers);
- assertEquals(" a, no-cache, c ", parsedHeaders.privateField);
assertTrue(parsedHeaders.noStore);
assertFalse(parsedHeaders.noCache);
}
@@ -83,7 +81,6 @@
RawHeaders headers = new RawHeaders();
headers.add("Cache-Control", "private=\"a, no-cache, c");
ResponseHeaders parsedHeaders = new ResponseHeaders(uri, headers);
- assertEquals("a, no-cache, c", parsedHeaders.privateField);
assertFalse(parsedHeaders.noCache);
}
@@ -92,14 +89,12 @@
headers.add("Cache-Control", "public,");
ResponseHeaders parsedHeaders = new ResponseHeaders(uri, headers);
assertTrue(parsedHeaders.isPublic);
- assertNull(parsedHeaders.privateField);
}
public void testTrailingEquals() {
RawHeaders headers = new RawHeaders();
headers.add("Cache-Control", "private=");
ResponseHeaders parsedHeaders = new ResponseHeaders(uri, headers);
- assertEquals("", parsedHeaders.privateField);
}
public void testSpaceBeforeEquals() {
@@ -145,7 +140,6 @@
headers.add("Cache-Control", "MAX-AGE=60");
headers.add("Cache-Control", "S-MAXAGE=70");
headers.add("Cache-Control", "PUBLIC");
- headers.add("Cache-Control", "PRIVATE=a");
headers.add("Cache-Control", "MUST-REVALIDATE");
ResponseHeaders parsedHeaders = new ResponseHeaders(uri, headers);
assertTrue(parsedHeaders.noCache);
@@ -153,7 +147,6 @@
assertEquals(60, parsedHeaders.maxAgeSeconds);
assertEquals(70, parsedHeaders.sMaxAgeSeconds);
assertTrue(parsedHeaders.isPublic);
- assertEquals("a", parsedHeaders.privateField);
assertTrue(parsedHeaders.mustRevalidate);
}
diff --git a/luni/src/test/java/libcore/xml/ExpatSaxParserTest.java b/luni/src/test/java/libcore/xml/ExpatSaxParserTest.java
index 2db8e82..84b7942 100644
--- a/luni/src/test/java/libcore/xml/ExpatSaxParserTest.java
+++ b/luni/src/test/java/libcore/xml/ExpatSaxParserTest.java
@@ -16,6 +16,16 @@
package libcore.xml;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.apache.harmony.xml.ExpatReader;
@@ -27,20 +37,8 @@
import org.xml.sax.XMLReader;
import org.xml.sax.ext.DefaultHandler2;
import org.xml.sax.helpers.DefaultHandler;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.net.ServerSocket;
-import java.net.Socket;
+import tests.http.MockResponse;
+import tests.http.MockWebServer;
public class ExpatSaxParserTest extends TestCase {
@@ -586,98 +584,44 @@
}
public void testExternalEntityDownload() throws IOException, SAXException {
- class Server implements Runnable {
-
- private final ServerSocket serverSocket;
-
- Server() throws IOException {
- serverSocket = new ServerSocket(8080);
- }
-
- public void run() {
- try {
- Socket socket = serverSocket.accept();
-
- final InputStream in = socket.getInputStream();
- Thread inputThread = new Thread() {
- public void run() {
- try {
- byte[] buffer = new byte[1024];
- while (in.read(buffer) > -1) { /* ignore */ }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- };
- inputThread.setDaemon(true);
- inputThread.start();
-
- OutputStream out = socket.getOutputStream();
-
- String body = "<bar></bar>";
- String response = "HTTP/1.0 200 OK\n"
- + "Content-Length: " + body.length() + "\n"
- + "\n"
- + body;
-
- out.write(response.getBytes("UTF-8"));
- out.close();
- serverSocket.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
+ final MockWebServer server = new MockWebServer();
+ server.enqueue(new MockResponse().setBody("<bar></bar>"));
+ server.play();
class Handler extends DefaultHandler {
+ final List<String> elementNames = new ArrayList<String>();
- List<String> elementNames = new ArrayList<String>();
-
- public InputSource resolveEntity(String publicId, String systemId)
- throws IOException, SAXException {
+ @Override public InputSource resolveEntity(String publicId, String systemId)
+ throws IOException {
// The parser should have resolved the systemId.
- assertEquals("http://localhost:8080/systemBar", systemId);
+ assertEquals(server.getUrl("/systemBar").toString(), systemId);
return new InputSource(systemId);
}
- @Override
- public void startElement(String uri, String localName, String qName,
- Attributes attributes) throws SAXException {
+ @Override public void startElement(String uri, String localName, String qName,
+ Attributes attributes) {
elementNames.add(localName);
}
- @Override
- public void endElement(String uri, String localName, String qName)
- throws SAXException {
+ @Override public void endElement(String uri, String localName, String qName) {
elementNames.add("/" + localName);
}
}
- // Start server to serve up the XML for 'systemBar'.
- Thread serverThread = new Thread(new Server());
- serverThread.setDaemon(true);
- serverThread.start();
-
// 'systemBar', the external entity, is relative to 'systemFoo':
Reader in = new StringReader("<?xml version=\"1.0\"?>\n"
+ "<!DOCTYPE foo [\n"
+ " <!ENTITY bar SYSTEM 'systemBar'>\n"
+ "]>\n"
+ "<foo>&bar;</foo>");
-
ExpatReader reader = new ExpatReader();
-
Handler handler = new Handler();
-
reader.setContentHandler(handler);
reader.setEntityResolver(handler);
-
InputSource source = new InputSource(in);
- source.setSystemId("http://localhost:8080/systemFoo");
+ source.setSystemId(server.getUrl("/systemFoo").toString());
reader.parse(source);
-
- assertEquals(Arrays.asList("foo", "bar", "/bar", "/foo"),
- handler.elementNames);
+ assertEquals(Arrays.asList("foo", "bar", "/bar", "/foo"), handler.elementNames);
}
/**
diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/internal/net/www/protocol/https/HttpsURLConnectionTest.java b/luni/src/test/java/org/apache/harmony/luni/tests/internal/net/www/protocol/https/HttpsURLConnectionTest.java
index 216af26..c516f67 100644
--- a/luni/src/test/java/org/apache/harmony/luni/tests/internal/net/www/protocol/https/HttpsURLConnectionTest.java
+++ b/luni/src/test/java/org/apache/harmony/luni/tests/internal/net/www/protocol/https/HttpsURLConnectionTest.java
@@ -973,7 +973,7 @@
log("Authentication required...");
// send Authentication Request
os.write(respAuthenticationRequired.getBytes());
- // read response
+ // read request
num = is.read(buff);
if (num == -1) {
// this connection was closed,
@@ -990,8 +990,8 @@
log("Got authenticated request:\n" + message);
log("------------------");
// check provided authorization credentials
- assertTrue("Received message does not contain authorization credentials",
- message.toLowerCase().indexOf("proxy-authorization:") > 0);
+ assertTrue("no proxy-authorization credentials: " + message,
+ message.toLowerCase().indexOf("proxy-authorization:") != -1);
}
assertTrue(message.startsWith("CONNECT"));
diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/java/net/InetAddressTest.java b/luni/src/test/java/org/apache/harmony/luni/tests/java/net/InetAddressTest.java
index 5284749..534cb86 100644
--- a/luni/src/test/java/org/apache/harmony/luni/tests/java/net/InetAddressTest.java
+++ b/luni/src/test/java/org/apache/harmony/luni/tests/java/net/InetAddressTest.java
@@ -360,8 +360,8 @@
public void test_hashCode() {
int hashCode = getHashCode(Support_Configuration.InetTestIP);
- int ip6HashCode = getHashCode(Support_Configuration.InetTestIP6);
- int ip6LOHashCode = getHashCode(Support_Configuration.InetTestIP6LO);
+ int ip6HashCode = getHashCode("fe80::20d:60ff:fe24:7410");
+ int ip6LOHashCode = getHashCode("::1");
assertFalse("Hash collision", hashCode == ip6HashCode);
assertFalse("Hash collision", ip6HashCode == ip6LOHashCode);
assertFalse("Hash collision", hashCode == ip6LOHashCode);
diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java b/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java
index dd6a0f0..f2d64cd 100644
--- a/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java
+++ b/luni/src/test/java/org/apache/harmony/luni/tests/java/net/URLConnectionTest.java
@@ -71,24 +71,8 @@
private JarURLConnection jarURLCon;
- private URL jarURL;
-
private URLConnection gifURLCon;
- private URL gifURL;
-
- public boolean isGetCalled;
-
- public boolean isPutCalled;
-
- private Map<String, List<String>> mockHeaderMap;
-
- private InputStream mockIs = new MockInputStream();
-
- public boolean isCacheWriteCalled;
-
- public boolean isAbortCalled;
-
/**
* {@link java.net.URLConnection#addRequestProperty(String, String)}
*/
@@ -220,107 +204,8 @@
}
}
- class MockCachedResponseCache extends ResponseCache {
-
- public CacheResponse get(URI arg0, String arg1, Map arg2)
- throws IOException {
- if (null == arg0 || null == arg1 || null == arg2) {
- throw new NullPointerException();
- }
- isGetCalled = true;
- return new MockCacheResponse();
- }
-
- public CacheRequest put(URI arg0, URLConnection arg1)
- throws IOException {
- if (null == arg0 || null == arg1) {
- throw new NullPointerException();
- }
- isPutCalled = true;
- return new MockCacheRequest();
- }
- }
-
- class MockNonCachedResponseCache extends ResponseCache {
-
- public CacheResponse get(URI arg0, String arg1, Map arg2)
- throws IOException {
- isGetCalled = true;
- return null;
- }
-
- public CacheRequest put(URI arg0, URLConnection arg1)
- throws IOException {
- isPutCalled = true;
- return new MockCacheRequest();
- }
- }
-
- class MockCacheRequest extends CacheRequest {
-
- public OutputStream getBody() throws IOException {
- isCacheWriteCalled = true;
- return new MockOutputStream();
- }
-
- public void abort() {
- isAbortCalled = true;
- }
-
- }
-
- class MockInputStream extends InputStream {
-
- public int read() throws IOException {
- return 4711;
- }
-
- public int read(byte[] arg0, int arg1, int arg2) throws IOException {
- return 1;
- }
-
- public int read(byte[] arg0) throws IOException {
- return 1;
- }
-
- }
-
- class MockOutputStream extends OutputStream {
-
- public void write(int b) throws IOException {
- isCacheWriteCalled = true;
- }
-
- public void write(byte[] b, int off, int len) throws IOException {
- isCacheWriteCalled = true;
- }
-
- public void write(byte[] b) throws IOException {
- isCacheWriteCalled = true;
- }
- }
-
- class MockCacheResponse extends CacheResponse {
-
- public Map<String, List<String>> getHeaders() throws IOException {
- return mockHeaderMap;
- }
-
- public InputStream getBody() throws IOException {
- return mockIs;
- }
- }
-
-
private static int port;
- static String getContentType(String fileName) throws IOException {
- String resourceName = "org/apache/harmony/luni/tests/" + fileName;
- URL url = ClassLoader.getSystemClassLoader().getResource(resourceName);
- assertNotNull("Cannot find test resource " + resourceName, url);
- return url.openConnection().getContentType();
- }
-
URL url;
URL url2;
@@ -349,10 +234,7 @@
fileURLCon = fileURL.openConnection();
jarURLCon = openJarURLConnection();
- jarURL = jarURLCon.getURL();
-
gifURLCon = openGifURLConnection();
- gifURL = gifURLCon.getURL();
}
@Override
@@ -679,7 +561,7 @@
public void test_getDate() {
// should be greater than 930000000000L which represents the past
assertTrue("getDate gave wrong date: " + uc.getDate(),
- uc.getDate() > 930000000000L);
+ uc.getDate() > 930000000000L);
}
/**
@@ -722,65 +604,6 @@
/**
* @throws IOException
- * {@link java.net.URLConnection#getDefaultUseCaches()}
- */
- public void test_getDefaultUseCaches_CachedRC() throws IOException {
- boolean oldSetting = uc.getDefaultUseCaches();
-
- ResponseCache old = ResponseCache.getDefault();
- ResponseCache rc = new MockCachedResponseCache();
- ResponseCache.setDefault(rc);
-
- // Recreate the connection so that we get the cache from ResponseCache.
- uc2 = url2.openConnection();
-
- uc2.setUseCaches(true);
-
- uc.setDefaultUseCaches(false);
-
- // uc unaffected
- assertTrue(uc.getUseCaches());
- // uc2 unaffected
- assertTrue(uc2.getUseCaches());
-
- //test get
- assertFalse("getDefaultUseCaches should have returned false", uc
- .getDefaultUseCaches());
-
- // subsequent connections should have default value
- URL url3 = new URL("http://localhost:" + port + "/test2");
- URLConnection uc3 = url3.openConnection();
- assertFalse(uc3.getUseCaches());
-
- // test if uc does not cache but uc2 does
- isGetCalled = false;
- isPutCalled = false;
-
- // test uc
- uc.setDoOutput(true);
- assertFalse(isGetCalled);
- uc.connect();
- assertFalse(isGetCalled);
- assertFalse(isPutCalled);
- OutputStream os = uc.getOutputStream();
- assertFalse(isPutCalled);
- assertFalse(isGetCalled);
-
- os.close();
-
- //uc2 should be unaffected
- uc2.setDoOutput(true);
- assertFalse(isGetCalled);
- uc2.connect();
- assertTrue(isGetCalled);
- assertFalse(isPutCalled);
-
- uc.setDefaultUseCaches(oldSetting);
- ResponseCache.setDefault(null);
- }
-
- /**
- * @throws IOException
* {@link java.net.URLConnection#getDoInput()}
*/
public void test_getDoInput() throws IOException {
diff --git a/luni/src/test/java/org/apache/harmony/luni/tests/util/Base64Test.java b/luni/src/test/java/org/apache/harmony/luni/tests/util/Base64Test.java
deleted file mode 100644
index 70318ed..0000000
--- a/luni/src/test/java/org/apache/harmony/luni/tests/util/Base64Test.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.harmony.luni.tests.util;
-
-import org.apache.harmony.luni.util.Base64;
-
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-
-/**
- * Base64 encoder/decoder test.
- */
-public class Base64Test extends TestCase {
-
- /**
- * Checks the result on empty parameter.
- */
- public static void testDecodeEmpty() throws Exception {
- // Regression for HARMONY-1513
- byte[] result = Base64.decode(new byte[0]);
- assertEquals("The length of the result differs from expected",
- 0, result.length);
- }
-
- public static Test suite() {
- return new TestSuite(Base64Test.class);
- }
-}
-
diff --git a/luni/src/test/java/tests/api/java/util/EventObjectTest.java b/luni/src/test/java/tests/api/java/util/EventObjectTest.java
deleted file mode 100644
index 201d9f3..0000000
--- a/luni/src/test/java/tests/api/java/util/EventObjectTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package tests.api.java.util;
-
-import java.util.EventObject;
-
-public class EventObjectTest extends junit.framework.TestCase {
-
- Object myObject;
-
- EventObject myEventObject;
-
- /**
- * java.util.EventObject#EventObject(java.lang.Object)
- */
- public void test_ConstructorLjava_lang_Object() {
- try {
- new EventObject(null);
- fail ("IllegalArgumentException expected");
- } catch (IllegalArgumentException e) {
- //expected
- }
- }
-
- /**
- * java.util.EventObject#getSource()
- */
- public void test_getSource() {
- // Test for method java.lang.Object java.util.EventObject.getSource()
- assertTrue("Wrong source returned",
- myEventObject.getSource() == myObject);
- }
-
- /**
- * java.util.EventObject#toString()
- */
- public void test_toString() {
- // Test for method java.lang.String java.util.EventObject.toString()
- assertTrue("Incorrect toString returned: " + myEventObject.toString(),
- myEventObject.toString().indexOf(
- "java.util.EventObject[source=java.lang.Object@") == 0);
- }
-
- /**
- * Sets up the fixture, for example, open a network connection. This method
- * is called before a test is executed.
- */
- protected void setUp() {
- myObject = new Object();
- myEventObject = new EventObject(myObject);
- }
-
- /**
- * Tears down the fixture, for example, close a network connection. This
- * method is called after a test is executed.
- */
- protected void tearDown() {
- }
-}
diff --git a/luni/src/test/java/tests/api/javax/net/ssl/HandshakeCompletedEventTest.java b/luni/src/test/java/tests/api/javax/net/ssl/HandshakeCompletedEventTest.java
index d868cb2..8a02f9c 100644
--- a/luni/src/test/java/tests/api/javax/net/ssl/HandshakeCompletedEventTest.java
+++ b/luni/src/test/java/tests/api/javax/net/ssl/HandshakeCompletedEventTest.java
@@ -40,7 +40,7 @@
import javax.net.ssl.X509TrustManager;
import javax.security.cert.X509Certificate;
import junit.framework.TestCase;
-import org.apache.harmony.luni.util.Base64;
+import libcore.io.Base64;
import org.apache.harmony.xnet.tests.support.mySSLSession;
import tests.support.Support_PortManager;
diff --git a/luni/src/test/java/tests/api/javax/net/ssl/HttpsURLConnectionTest.java b/luni/src/test/java/tests/api/javax/net/ssl/HttpsURLConnectionTest.java
index 3c49fc4..e2ebdbd 100644
--- a/luni/src/test/java/tests/api/javax/net/ssl/HttpsURLConnectionTest.java
+++ b/luni/src/test/java/tests/api/javax/net/ssl/HttpsURLConnectionTest.java
@@ -46,71 +46,45 @@
/**
* javax.net.ssl.HttpsURLConnection#HttpsURLConnection(java_net_URL)
*/
- public final void test_Constructor() {
- try {
- MyHttpsURLConnection huc = new MyHttpsURLConnection(new URL("https://www.fortify.net/"));
- } catch (Exception e) {
- fail("Unexpected exception: " + e.toString());
- }
- try {
- MyHttpsURLConnection huc = new MyHttpsURLConnection(null);
- } catch (Exception e) {
- fail("Unexpected exception " + e.toString());
- }
+ public final void test_Constructor() throws Exception {
+ new MyHttpsURLConnection(new URL("https://www.fortify.net/"));
+ new MyHttpsURLConnection(null);
}
/**
* javax.net.ssl.HttpsURLConnection#getCipherSuite()
*/
- public final void test_getCipherSuite() {
+ public final void test_getCipherSuite() throws Exception {
+ URL url = new URL("https://localhost:55555");
+ HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
try {
- URL url = new URL("https://localhost:55555");
- HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
- try {
- connection.getCipherSuite();
- fail("IllegalStateException wasn't thrown");
- } catch (IllegalStateException ise) {
- //expected
- }
- } catch (Exception e) {
- fail("Unexpected exception " + e + " for exception case");
+ connection.getCipherSuite();
+ fail("IllegalStateException wasn't thrown");
+ } catch (IllegalStateException expected) {
}
- try {
- HttpsURLConnection con = new MyHttpsURLConnection(new URL("https://www.fortify.net/"));
- assertEquals("CipherSuite", con.getCipherSuite());
- } catch (Exception e) {
- fail("Unexpected exception " + e);
- }
+ HttpsURLConnection con = new MyHttpsURLConnection(new URL("https://www.fortify.net/"));
+ assertEquals("CipherSuite", con.getCipherSuite());
}
/**
* javax.net.ssl.HttpsURLConnection#getLocalCertificates()
*/
- public final void test_getLocalCertificates() {
+ public final void test_getLocalCertificates() throws Exception {
+ URL url = new URL("https://localhost:55555");
+ HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
try {
- URL url = new URL("https://localhost:55555");
- HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
- try {
- connection.getLocalCertificates();
- fail("IllegalStateException wasn't thrown");
- } catch (IllegalStateException ise) {
- //expected
- }
- } catch (Exception e) {
- fail("Unexpected exception " + e + " for exception case");
+ connection.getLocalCertificates();
+ fail("IllegalStateException wasn't thrown");
+ } catch (IllegalStateException expected) {
}
- try {
- HttpsURLConnection con = new MyHttpsURLConnection(new URL("https://www.fortify.net/"), "X.508");
- assertNull(con.getLocalCertificates());
- con = new MyHttpsURLConnection(new URL("https://www.fortify.net/"), "X.509");
- Certificate[] cert = con.getLocalCertificates();
- assertNotNull(cert);
- assertEquals(1, cert.length);
- } catch (Exception e) {
- fail("Unexpected exception " + e);
- }
+ HttpsURLConnection con = new MyHttpsURLConnection(new URL("https://www.fortify.net/"), "X.508");
+ assertNull(con.getLocalCertificates());
+ con = new MyHttpsURLConnection(new URL("https://www.fortify.net/"), "X.509");
+ Certificate[] cert = con.getLocalCertificates();
+ assertNotNull(cert);
+ assertEquals(1, cert.length);
}
/**
@@ -148,96 +122,67 @@
/**
* javax.net.ssl.HttpsURLConnection#getLocalPrincipal()
*/
- public final void test_getLocalPrincipal() {
+ public final void test_getLocalPrincipal() throws Exception {
+ URL url = new URL("https://localhost:55555");
+ HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
try {
- URL url = new URL("https://localhost:55555");
- HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
- try {
- connection.getLocalPrincipal();
- fail("IllegalStateException wasn't thrown");
- } catch (IllegalStateException ise) {
- //expected
- }
- } catch (Exception e) {
- fail("Unexpected exception " + e + " for exception case");
+ connection.getLocalPrincipal();
+ fail("IllegalStateException wasn't thrown");
+ } catch (IllegalStateException expected) {
}
- try {
- HttpsURLConnection con = new MyHttpsURLConnection(new URL("https://www.fortify.net/"), "X.508");
- assertNull(con.getLocalPrincipal());
- con = new MyHttpsURLConnection(new URL("https://www.fortify.net/"), "X.509");
- assertNotNull("Local principal is null", con.getLocalPrincipal());
- } catch (Exception e) {
- fail("Unexpected exception " + e);
- }
+ HttpsURLConnection con = new MyHttpsURLConnection(new URL("https://www.fortify.net/"), "X.508");
+ assertNull(con.getLocalPrincipal());
+ con = new MyHttpsURLConnection(new URL("https://www.fortify.net/"), "X.509");
+ assertNotNull("Local principal is null", con.getLocalPrincipal());
}
/**
* javax.net.ssl.HttpsURLConnection#getPeerPrincipal()
*/
public final void test_getPeerPrincipal() throws Exception {
+ URL url = new URL("https://localhost:55555");
+ HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
try {
- URL url = new URL("https://localhost:55555");
- HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
- try {
- connection.getPeerPrincipal();
- fail("IllegalStateException wasn't thrown");
- } catch (IllegalStateException ise) {
- //expected
- }
- } catch (Exception e) {
- fail("Unexpected exception " + e + " for exception case");
+ connection.getPeerPrincipal();
+ fail("IllegalStateException wasn't thrown");
+ } catch (IllegalStateException expected) {
}
HttpsURLConnection con = new MyHttpsURLConnection(new URL("https://www.fortify.net/"), "X.508");
try {
Principal p = con.getPeerPrincipal();
fail("SSLPeerUnverifiedException wasn't thrown");
- } catch (SSLPeerUnverifiedException e) {
- //expected
+ } catch (SSLPeerUnverifiedException expected) {
}
con = new MyHttpsURLConnection(new URL("https://www.fortify.net/"), "X.509");
- try {
- Principal p = con.getPeerPrincipal();
- assertNotNull(p);
- } catch (Exception e) {
- fail("Unexpected exception " + e);
- }
+ Principal p = con.getPeerPrincipal();
+ assertNotNull(p);
}
/**
* javax.net.ssl.HttpsURLConnection#getServerCertificates()
*/
public final void test_getServerCertificates() throws Exception {
+ URL url = new URL("https://localhost:55555");
+ HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
try {
- URL url = new URL("https://localhost:55555");
- HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
- try {
- connection.getServerCertificates();
- fail("IllegalStateException wasn't thrown");
- } catch (IllegalStateException ise) {
- //expected
- }
- } catch (Exception e) {
- fail("Unexpected exception " + e + " for exception case");
+ connection.getServerCertificates();
+ fail("IllegalStateException wasn't thrown");
+ } catch (IllegalStateException expected) {
}
HttpsURLConnection con = new MyHttpsURLConnection(new URL("https://www.fortify.net/"), "X.508");
try {
- Certificate[] cert = con.getServerCertificates();
+ con.getServerCertificates();
fail("SSLPeerUnverifiedException wasn't thrown");
- } catch (SSLPeerUnverifiedException e) {
- //expected
+ } catch (SSLPeerUnverifiedException expected) {
}
con = new MyHttpsURLConnection(new URL("https://www.fortify.net/"), "X.509");
- try {
- Certificate[] cert = con.getServerCertificates();
- assertNotNull(cert);
- assertEquals(1, cert.length);
- } catch (Exception e) {
- fail("Unexpected exception " + e);
- }
+ Certificate[] cert = con.getServerCertificates();
+ assertNotNull(cert);
+ assertEquals(1, cert.length);
}
/**
@@ -258,16 +203,13 @@
try {
HttpsURLConnection.setDefaultHostnameVerifier(null);
fail("No expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // expected
+ } catch (IllegalArgumentException expected) {
}
HostnameVerifier def = HttpsURLConnection.getDefaultHostnameVerifier();
try {
myHostnameVerifier hnv = new myHostnameVerifier();
HttpsURLConnection.setDefaultHostnameVerifier(hnv);
assertEquals(hnv, HttpsURLConnection.getDefaultHostnameVerifier());
- } catch (Exception e) {
- fail("Unexpected exception " + e);
} finally {
HttpsURLConnection.setDefaultHostnameVerifier(def);
}
@@ -281,14 +223,10 @@
try {
con.setHostnameVerifier(null);
fail("No expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
+ } catch (IllegalArgumentException expected) {
}
- try {
- myHostnameVerifier hnv = new myHostnameVerifier();
- con.setHostnameVerifier(hnv);
- } catch (Exception e) {
- fail("Unexpected exception " + e);
- }
+ myHostnameVerifier hnv = new myHostnameVerifier();
+ con.setHostnameVerifier(hnv);
}
/**
@@ -298,15 +236,11 @@
try {
HttpsURLConnection.setDefaultSSLSocketFactory(null);
fail("No expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
+ } catch (IllegalArgumentException expected) {
}
- try {
- SSLSocketFactory ssf = (SSLSocketFactory) SSLSocketFactory
- .getDefault();
- HttpsURLConnection.setDefaultSSLSocketFactory(ssf);
- } catch (Exception e) {
- fail("Unexpected exception " + e);
- }
+ SSLSocketFactory ssf = (SSLSocketFactory) SSLSocketFactory
+ .getDefault();
+ HttpsURLConnection.setDefaultSSLSocketFactory(ssf);
}
/**
@@ -317,15 +251,11 @@
try {
con.setSSLSocketFactory(null);
fail("No expected IllegalArgumentException");
- } catch (IllegalArgumentException e) {
+ } catch (IllegalArgumentException expected) {
}
- try {
- SSLSocketFactory ssf = (SSLSocketFactory) SSLSocketFactory
- .getDefault();
- con.setSSLSocketFactory(ssf);
- } catch (Exception e) {
- fail("Unexpected exception " + e);
- }
+ SSLSocketFactory ssf = (SSLSocketFactory) SSLSocketFactory
+ .getDefault();
+ con.setSSLSocketFactory(ssf);
}
}
@@ -353,32 +283,30 @@
* @see javax.net.ssl.HttpsURLConnection#getLocalCertificates()
*/
public Certificate[] getLocalCertificates() {
- Certificate cert = null;
try {
CertificateFactory cf = CertificateFactory.getInstance(typeDone);
byte[] barr = TestUtils.getX509Certificate_v1();
ByteArrayInputStream bis = new ByteArrayInputStream(barr);
- cert = cf.generateCertificate(bis);
+ Certificate cert = cf.generateCertificate(bis);
+ return new Certificate[] { cert };
} catch (CertificateException se) {
- cert = null;
+ return null;
}
- return cert == null ? null : new Certificate[]{cert};
}
/*
* @see javax.net.ssl.HttpsURLConnection#getServerCertificates()
*/
public Certificate[] getServerCertificates() throws SSLPeerUnverifiedException {
- Certificate cert = null;
try {
CertificateFactory cf = CertificateFactory.getInstance(typeDone);
byte[] barr = TestUtils.getX509Certificate_v3();
ByteArrayInputStream bis = new ByteArrayInputStream(barr);
- cert = cf.generateCertificate(bis);
+ Certificate cert = cf.generateCertificate(bis);
+ return new Certificate[] { cert };
} catch (CertificateException se) {
throw new SSLPeerUnverifiedException("No server's end-entity certificate");
}
- return cert == null ? null : new Certificate[]{cert};
}
/*
diff --git a/luni/src/test/java/tests/api/javax/net/ssl/SSLServerSocketTest.java b/luni/src/test/java/tests/api/javax/net/ssl/SSLServerSocketTest.java
index 6c8e657..cc96782 100644
--- a/luni/src/test/java/tests/api/javax/net/ssl/SSLServerSocketTest.java
+++ b/luni/src/test/java/tests/api/javax/net/ssl/SSLServerSocketTest.java
@@ -18,7 +18,7 @@
import junit.framework.TestCase;
-import org.apache.harmony.luni.util.Base64;
+import libcore.io.Base64;
import tests.support.Support_PortManager;
@@ -470,13 +470,13 @@
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, null, null);
for (int i = 0; i < 2048; ++i) {
- sslContext.getServerSocketFactory().createServerSocket();
+ sslContext.getServerSocketFactory().createServerSocket().close();
}
// Test the other codepath, which copies a seed from a byte[].
sslContext.init(keyManagers, null, new SecureRandom());
for (int i = 0; i < 2048; ++i) {
- sslContext.getServerSocketFactory().createServerSocket();
+ sslContext.getServerSocketFactory().createServerSocket().close();
}
}
}
diff --git a/luni/src/test/java/tests/api/javax/net/ssl/SSLSessionTest.java b/luni/src/test/java/tests/api/javax/net/ssl/SSLSessionTest.java
index 70c0eb2..ec23cae 100644
--- a/luni/src/test/java/tests/api/javax/net/ssl/SSLSessionTest.java
+++ b/luni/src/test/java/tests/api/javax/net/ssl/SSLSessionTest.java
@@ -36,7 +36,7 @@
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import junit.framework.TestCase;
-import org.apache.harmony.luni.util.Base64;
+import libcore.io.Base64;
import tests.api.javax.net.ssl.HandshakeCompletedEventTest.MyHandshakeListener;
import tests.api.javax.net.ssl.HandshakeCompletedEventTest.TestTrustManager;
import tests.support.Support_PortManager;
diff --git a/luni/src/test/java/tests/api/javax/net/ssl/SSLSocketTest.java b/luni/src/test/java/tests/api/javax/net/ssl/SSLSocketTest.java
index 84ac212..ab60f72 100644
--- a/luni/src/test/java/tests/api/javax/net/ssl/SSLSocketTest.java
+++ b/luni/src/test/java/tests/api/javax/net/ssl/SSLSocketTest.java
@@ -16,31 +16,34 @@
package tests.api.javax.net.ssl;
import dalvik.annotation.AndroidOnly;
-
-import javax.net.ssl.*;
-import javax.security.cert.X509Certificate;
-
-import java.net.*;
-import java.security.KeyStore;
-import java.security.SecureRandom;
-import java.util.Arrays;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
-
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.security.cert.X509Certificate;
import junit.framework.TestCase;
-
-import org.apache.harmony.luni.util.Base64;
-
+import libcore.io.Base64;
import tests.api.javax.net.ssl.HandshakeCompletedEventTest.TestTrustManager;
import tests.support.Support_PortManager;
+import libcore.java.security.StandardNames;
public class SSLSocketTest extends TestCase {
public class HandshakeCL implements HandshakeCompletedListener {
- HandshakeCL() {
- super();
- }
public void handshakeCompleted(HandshakeCompletedEvent event) {
}
}
@@ -48,257 +51,164 @@
/**
* javax.net.ssl.SSLSocket#SSLSocket()
*/
- public void testConstructor_01() {
- try {
- SSLSocket ssl = getSSLSocket();
- } catch (Exception e) {
- fail("Unexpected exception " + e);
- }
+ public void testConstructor_01() throws Exception {
+ SSLSocket ssl = getSSLSocket();
+ assertNotNull(ssl);
+ ssl.close();
}
/**
- * @throws IOException
- * @throws UnknownHostException
* javax.net.ssl.SSLSocket#SSLSocket(InetAddress address, int port)
*/
public void testConstructor_02() throws UnknownHostException, IOException {
- SSLSocket ssl;
int sport = startServer("Cons InetAddress,I");
int[] invalidPort = {-1, Integer.MIN_VALUE, 65536, Integer.MAX_VALUE};
- ssl = getSSLSocket(InetAddress.getLocalHost(), sport);
+ SSLSocket ssl = getSSLSocket(InetAddress.getLocalHost(), sport);
assertNotNull(ssl);
assertEquals(sport, ssl.getPort());
+ ssl.close();
try {
- ssl = getSSLSocket(InetAddress.getLocalHost(), sport + 1);
- fail("IOException wasn't thrown ...");
- } catch (IOException e) {
- //expected
+ getSSLSocket(InetAddress.getLocalHost(), sport + 1);
+ fail();
+ } catch (IOException expected) {
}
for (int i = 0; i < invalidPort.length; i++) {
try {
- ssl = getSSLSocket(InetAddress.getLocalHost(), invalidPort[i]);
- fail("IllegalArgumentException wasn't thrown for " + invalidPort[i]);
- } catch (IllegalArgumentException iae) {
- // expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException for " + invalidPort[i]);
+ getSSLSocket(InetAddress.getLocalHost(), invalidPort[i]);
+ fail();
+ } catch (IllegalArgumentException expected) {
}
}
}
/**
- * @throws IOException
- * @throws UnknownHostException
* javax.net.ssl.SSLSocket#SSLSocket(InetAddress address, int port,
* InetAddress clientAddress, int clientPort)
*/
public void testConstructor_03() throws UnknownHostException, IOException {
- SSLSocket ssl;
int sport = startServer("Cons InetAddress,I,InetAddress,I");
int portNumber = Support_PortManager.getNextPort();
- ssl = getSSLSocket(InetAddress.getLocalHost(), sport,
- InetAddress.getLocalHost(), portNumber);
+ SSLSocket ssl = getSSLSocket(InetAddress.getLocalHost(), sport,
+ InetAddress.getLocalHost(), portNumber);
assertNotNull(ssl);
assertEquals(sport, ssl.getPort());
assertEquals(portNumber, ssl.getLocalPort());
+ ssl.close();
try {
- ssl = getSSLSocket(InetAddress.getLocalHost(), 8081, InetAddress.getLocalHost(), 8082);
- fail("IOException wasn't thrown ...");
- } catch (IOException e) {
- //expected
+ getSSLSocket(InetAddress.getLocalHost(), 8081, InetAddress.getLocalHost(), 8082);
+ fail();
+ } catch (IOException expected) {
}
try {
- ssl = getSSLSocket(InetAddress.getLocalHost(), -1,
- InetAddress.getLocalHost(), sport + 1);
- fail("IllegalArgumentException wasn't thrown for -1");
- } catch (IllegalArgumentException iae) {
- // expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException for -1");
+ getSSLSocket(InetAddress.getLocalHost(), -1, InetAddress.getLocalHost(), sport + 1);
+ fail();
+ } catch (IllegalArgumentException expected) {
}
try {
- ssl = getSSLSocket(InetAddress.getLocalHost(), sport,
- InetAddress.getLocalHost(), -1);
- fail("IllegalArgumentException wasn't thrown for -1");
- } catch (IllegalArgumentException iae) {
- // expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException for -1");
+ getSSLSocket(InetAddress.getLocalHost(), sport, InetAddress.getLocalHost(), -1);
+ fail();
+ } catch (IllegalArgumentException expected) {
}
try {
- ssl = getSSLSocket(InetAddress.getLocalHost(), Integer.MIN_VALUE,
- InetAddress.getLocalHost(), sport + 1);
- fail("IOException wasn't thrown for " + Integer.MIN_VALUE);
- } catch (IOException ioe) {
- // expected on RI
- } catch (IllegalArgumentException iae) {
- // expected on Android
- } catch (Exception e) {
- fail(e + " was thrown instead of IOException for "
- + Integer.MIN_VALUE);
+ getSSLSocket(InetAddress.getLocalHost(), Integer.MIN_VALUE,
+ InetAddress.getLocalHost(), sport + 1);
+ fail();
+ } catch (IOException expectedOnRI) {
+ assertTrue(StandardNames.IS_RI);
+ } catch (IllegalArgumentException expectedOnAndroid) {
+ assertFalse(StandardNames.IS_RI);
}
try {
- ssl = getSSLSocket(InetAddress.getLocalHost(), sport,
- InetAddress.getLocalHost(), Integer.MIN_VALUE);
- fail("IllegalArgumentException wasn't thrown for "
- + Integer.MIN_VALUE);
- } catch (IllegalArgumentException iae) {
- // expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException for "
- + Integer.MIN_VALUE);
- }
-
- try {
- ssl = getSSLSocket(InetAddress.getLocalHost(), 65536,
- InetAddress.getLocalHost(), sport + 1);
- fail("IOException wasn't thrown for 65536");
- } catch (IOException ioe) {
- // expected on RI
- } catch (IllegalArgumentException iae) {
- // expected on Android
- } catch (Exception e) {
- fail(e + " was thrown instead of IOException for 65536");
- }
- try {
- ssl = getSSLSocket(InetAddress.getLocalHost(), sport,
- InetAddress.getLocalHost(), 65536);
- fail("IllegalArgumentException wasn't thrown for 65536");
- } catch (IllegalArgumentException iae) {
- // expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException for 65536");
- }
-
- try {
- ssl = getSSLSocket(InetAddress.getLocalHost(), Integer.MAX_VALUE,
- InetAddress.getLocalHost(), sport + 1);
- fail("IOException wasn't thrown for " + Integer.MAX_VALUE);
- } catch (IOException ioe) {
- // expected on RI
- } catch (IllegalArgumentException iae) {
- // expected on Android
- } catch (Exception e) {
- fail(e + " was thrown instead of IOException for "
- + Integer.MAX_VALUE);
- }
- try {
- ssl = getSSLSocket(InetAddress.getLocalHost(), sport,
- InetAddress.getLocalHost(), Integer.MAX_VALUE);
- fail("IllegalArgumentException wasn't thrown for "
- + Integer.MAX_VALUE);
- } catch (IllegalArgumentException iae) {
- // expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException for "
- + Integer.MAX_VALUE);
+ getSSLSocket(InetAddress.getLocalHost(), sport,
+ InetAddress.getLocalHost(), Integer.MAX_VALUE);
+ fail();
+ } catch (IllegalArgumentException expectedOnAndroid) {
+ assertFalse(StandardNames.IS_RI);
}
}
/**
- * @throws IOException
- * @throws UnknownHostException
* javax.net.ssl.SSLSocket#SSLSocket(String host, int port)
*/
public void testConstructor_04() throws UnknownHostException, IOException {
- SSLSocket ssl;
int sport = startServer("Cons String,I");
int[] invalidPort = {-1, Integer.MIN_VALUE, 65536, Integer.MAX_VALUE};
- ssl = getSSLSocket(InetAddress.getLocalHost().getHostName(), sport);
+ SSLSocket ssl = getSSLSocket(InetAddress.getLocalHost().getHostName(), sport);
assertNotNull(ssl);
assertEquals(sport, ssl.getPort());
+ ssl.close();
try {
- ssl = getSSLSocket("localhost", 8082);
- fail("IOException wasn't thrown ...");
- } catch (IOException e) {
- //expected
+ getSSLSocket("localhost", 8082);
+ fail();
+ } catch (IOException expected) {
}
for (int i = 0; i < invalidPort.length; i++) {
try {
- ssl = getSSLSocket(InetAddress.getLocalHost().getHostName(), invalidPort[i]);
- fail("IllegalArgumentException wasn't thrown for " + invalidPort[i]);
- } catch (IllegalArgumentException iae) {
- // expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException for " + invalidPort[i]);
+ getSSLSocket(InetAddress.getLocalHost().getHostName(), invalidPort[i]);
+ fail();
+ } catch (IllegalArgumentException expected) {
}
}
try {
- ssl = getSSLSocket("bla-bla", sport);
- fail("UnknownHostException wasn't thrown");
- } catch (UnknownHostException uhp) {
- // expected
- } catch (Exception e) {
- fail(e + " was thrown instead of UnknownHostException");
+ getSSLSocket("bla-bla", sport);
+ fail();
+ } catch (UnknownHostException expected) {
}
}
/**
- * @throws IOException
- * @throws UnknownHostException
* javax.net.ssl.SSLSocket#SSLSocket(String host, int port, InetAddress clientAddress,
* int clientPort)
*/
public void testConstructor_05() throws UnknownHostException, IOException {
- SSLSocket ssl;
int sport = startServer("Cons String,I,InetAddress,I");
int portNumber = Support_PortManager.getNextPort();
int[] invalidPort = {-1, Integer.MIN_VALUE, 65536, Integer.MAX_VALUE};
- ssl = getSSLSocket(InetAddress.getLocalHost().getHostName(), sport,
- InetAddress.getLocalHost(), portNumber);
+ SSLSocket ssl = getSSLSocket(InetAddress.getLocalHost().getHostName(), sport,
+ InetAddress.getLocalHost(), portNumber);
assertNotNull(ssl);
assertEquals(sport, ssl.getPort());
assertEquals(portNumber, ssl.getLocalPort());
try {
- ssl = getSSLSocket("localhost", 8081, InetAddress.getLocalHost(), 8082);
- fail("IOException wasn't thrown ...");
- } catch (IOException e) {
- //expected
+ getSSLSocket("localhost", 8081, InetAddress.getLocalHost(), 8082);
+ fail();
+ } catch (IOException expected) {
}
for (int i = 0; i < invalidPort.length; i++) {
portNumber = Support_PortManager.getNextPort();
try {
- ssl = getSSLSocket(InetAddress.getLocalHost().getHostName(), invalidPort[i],
- InetAddress.getLocalHost(), portNumber);
- fail("IllegalArgumentException wasn't thrown for " + invalidPort[i]);
- } catch (IllegalArgumentException iae) {
- // expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException for " + invalidPort[i]);
+ getSSLSocket(InetAddress.getLocalHost().getHostName(), invalidPort[i],
+ InetAddress.getLocalHost(), portNumber);
+ fail();
+ } catch (IllegalArgumentException expected) {
}
try {
- ssl = getSSLSocket(InetAddress.getLocalHost().getHostName(), sport,
- InetAddress.getLocalHost(), invalidPort[i]);
- fail("IllegalArgumentException wasn't thrown for " + invalidPort[i]);
- } catch (IllegalArgumentException iae) {
- // expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException for " + invalidPort[i]);
+ getSSLSocket(InetAddress.getLocalHost().getHostName(), sport,
+ InetAddress.getLocalHost(), invalidPort[i]);
+ fail();
+ } catch (IllegalArgumentException expected) {
}
}
portNumber = Support_PortManager.getNextPort();
try {
- ssl = getSSLSocket("bla-bla", sport, InetAddress.getLocalHost(), portNumber);
- fail("UnknownHostException wasn't thrown");
- } catch (UnknownHostException uhp) {
- // expected
- } catch (Exception e) {
- fail(e + " was thrown instead of UnknownHostException");
+ getSSLSocket("bla-bla", sport, InetAddress.getLocalHost(), portNumber);
+ fail();
+ } catch (UnknownHostException expected) {
}
}
@@ -307,18 +217,17 @@
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, null, null);
for (int i = 0; i < 2048; ++i) {
- sslContext.getSocketFactory().createSocket();
+ sslContext.getSocketFactory().createSocket().close();
}
// Test the other codepath, which copies a seed from a byte[].
sslContext.init(null, null, new SecureRandom());
for (int i = 0; i < 2048; ++i) {
- sslContext.getSocketFactory().createSocket();
+ sslContext.getSocketFactory().createSocket().close();
}
}
/**
- * @throws IOException
* javax.net.ssl.SSLSocket#addHandshakeCompletedListener(HandshakeCompletedListener listener)
*/
@AndroidOnly("RI doesn't throw the specified IAE")
@@ -327,19 +236,14 @@
HandshakeCompletedListener ls = new HandshakeCL();
try {
ssl.addHandshakeCompletedListener(null);
- fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iae) {
- //expected
+ fail();
+ } catch (IllegalArgumentException expected) {
}
- try {
- ssl.addHandshakeCompletedListener(ls);
- } catch (Exception e) {
- fail("Unexpected exception " + e);
- }
+ ssl.addHandshakeCompletedListener(ls);
+ ssl.close();
}
/**
- * @throws IOException
* javax.net.ssl.SSLSocket#removeHandshakeCompletedListener(HandshakeCompletedListener listener)
*/
public void test_removeHandshakeCompletedListener() throws IOException {
@@ -347,29 +251,21 @@
HandshakeCompletedListener ls = new HandshakeCL();
try {
ssl.removeHandshakeCompletedListener(null);
- fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iae) {
- //expected
+ fail();
+ } catch (IllegalArgumentException expected) {
}
try {
ssl.removeHandshakeCompletedListener(ls);
- } catch (IllegalArgumentException iae) {
- //expected
- } catch (Exception e) {
- fail("Unexpected exception " + e);
+ } catch (IllegalArgumentException expected) {
}
ssl.addHandshakeCompletedListener(ls);
- try {
- ssl.removeHandshakeCompletedListener(ls);
- } catch (Exception e) {
- fail("Unexpected exception " + e);
- }
+ ssl.removeHandshakeCompletedListener(ls);
+ ssl.close();
}
/**
- * @throws IOException
* javax.net.ssl.SSLSocket#setEnableSessionCreation(boolean flag)
* javax.net.ssl.SSLSocket#getEnableSessionCreation()
*/
@@ -380,11 +276,10 @@
assertFalse(ssl.getEnableSessionCreation());
ssl.setEnableSessionCreation(true);
assertTrue(ssl.getEnableSessionCreation());
+ ssl.close();
}
/**
- * @throws IOException
- * @throws UnknownHostException
* javax.net.ssl.SSLSocket#setNeedClientAuth(boolean need)
* javax.net.ssl.SSLSocket#getNeedClientAuthCreation()
*/
@@ -394,11 +289,10 @@
assertTrue(ssl.getNeedClientAuth());
ssl.setNeedClientAuth(false);
assertFalse(ssl.getNeedClientAuth());
+ ssl.close();
}
/**
- * @throws IOException
- * @throws UnknownHostException
* javax.net.ssl.SSLSocket#setWantClientAuth(boolean want)
* javax.net.ssl.SSLSocket#getWantClientAuthCreation()
*/
@@ -408,20 +302,20 @@
assertTrue(ssl.getWantClientAuth());
ssl.setWantClientAuth(false);
assertFalse(ssl.getWantClientAuth());
+ ssl.close();
}
/**
- * @throws IOException
* javax.net.ssl.SSLSocket#getSupportedProtocols()
*/
public void test_getSupportedProtocols() throws IOException {
SSLSocket ssl = getSSLSocket();
String[] res = ssl.getSupportedProtocols();
assertTrue("No supported protocols found", res.length > 0);
+ ssl.close();
}
/**
- * @throws IOException
* javax.net.ssl.SSLSocket#getEnabledProtocols()
* javax.net.ssl.SSLSocket#setEnabledProtocols(String[] protocols)
*/
@@ -429,50 +323,42 @@
SSLSocket ssl = getSSLSocket();
try {
ssl.setEnabledProtocols(null);
- } catch (IllegalArgumentException iae) {
- //expected
+ fail();
+ } catch (IllegalArgumentException expected) {
}
- try {
- ssl.setEnabledProtocols(new String[] {});
- } catch (IllegalArgumentException iae) {
- //expected
- }
+ ssl.setEnabledProtocols(new String[] {});
try {
ssl.setEnabledProtocols(new String[] {"blubb"});
- } catch (IllegalArgumentException iae) {
- //expected
+ fail();
+ } catch (IllegalArgumentException expected) {
}
ssl.setEnabledProtocols(ssl.getEnabledProtocols());
String[] res = ssl.getEnabledProtocols();
assertEquals("no enabled protocols set",
- ssl.getEnabledProtocols().length, res.length);
+ ssl.getEnabledProtocols().length, res.length);
+ ssl.close();
}
/**
- * @throws IOException
* javax.net.ssl.SSLSocket#getSession()
*/
public void test_getSession() throws IOException {
SSLSocket ssl = getSSLSocket();
- try {
- assertNotNull(ssl.getSession());
- } catch (Exception e) {
- fail("Unexpected exception " + e);
- }
+ assertNotNull(ssl.getSession());
+ ssl.close();
}
/**
- * @throws IOException
* javax.net.ssl.SSLSocket#getSupportedCipherSuites()
*/
public void test_getSupportedCipherSuites() throws IOException {
SSLSocket ssl = getSSLSocket();
String[] res = ssl.getSupportedCipherSuites();
assertTrue("no supported cipher suites", res.length > 0);
+ ssl.close();
}
/**
- * @throws IOException
* javax.net.ssl.SSLSocket#getEnabledCipherSuites()
* javax.net.ssl.SSLSocket#setEnabledCipherSuites(String[] suites)
*/
@@ -480,18 +366,14 @@
SSLSocket ssl = getSSLSocket();
try {
ssl.setEnabledCipherSuites(null);
- } catch (IllegalArgumentException iae) {
- //expected
+ fail();
+ } catch (IllegalArgumentException expected) {
}
- try {
- ssl.setEnabledCipherSuites(new String[] {});
- } catch (IllegalArgumentException iae) {
- //expected
- }
+ ssl.setEnabledCipherSuites(new String[] {});
try {
ssl.setEnabledCipherSuites(new String[] {"blubb"});
- } catch (IllegalArgumentException iae) {
- //expected
+ fail();
+ } catch (IllegalArgumentException expected) {
}
ssl.setEnabledCipherSuites(ssl.getSupportedCipherSuites());
String[] res = ssl.getEnabledCipherSuites();
@@ -499,10 +381,10 @@
assertEquals("not all supported cipher suites were enabled",
Arrays.asList(ssl.getSupportedCipherSuites()),
Arrays.asList(res));
+ ssl.close();
}
/**
- * @throws IOException
* javax.net.ssl.SSLSocket#getUseClientMode()
* javax.net.ssl.SSLSocket#setUseClientMode(boolean mode)
*/
@@ -511,6 +393,7 @@
assertTrue(ssl.getUseClientMode());
ssl.setUseClientMode(false);
assertFalse(ssl.getUseClientMode());
+ ssl.close();
ssl = getSSLSocket("localhost", startServer("UseClientMode"));
try {
@@ -520,32 +403,26 @@
}
try {
ssl.setUseClientMode(false);
- fail("IllegalArgumentException wasn't thrown");
- } catch (IllegalArgumentException iae) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IllegalArgumentException");
+ fail();
+ } catch (IllegalArgumentException expected) {
}
+ ssl.close();
}
/**
- * @throws IOException
* javax.net.ssl.SSLSocket#startHandshake()
*/
public void test_startHandshake() throws IOException {
SSLSocket ssl = getSSLSocket();
try {
ssl.startHandshake();
- fail("IOException wasn't thrown");
- } catch (IOException ioe) {
- //expected
- } catch (Exception e) {
- fail(e + " was thrown instead of IOException");
+ fail();
+ } catch (IOException expected) {
}
+ ssl.close();
}
- // Change this to false if on RI
- boolean useBKS = true;
+ boolean useBKS = !StandardNames.IS_RI;
private String PASSWORD = "android";
@@ -557,61 +434,61 @@
* Defines the keystore contents for the server, BKS version. Holds just a
* single self-generated key. The subject name is "Test Server".
*/
- private static final String SERVER_KEYS_BKS =
- "AAAAAQAAABQDkebzoP1XwqyWKRCJEpn/t8dqIQAABDkEAAVteWtleQAAARpYl20nAAAAAQAFWC41" +
- "MDkAAAJNMIICSTCCAbKgAwIBAgIESEfU1jANBgkqhkiG9w0BAQUFADBpMQswCQYDVQQGEwJVUzET" +
- "MBEGA1UECBMKQ2FsaWZvcm5pYTEMMAoGA1UEBxMDTVRWMQ8wDQYDVQQKEwZHb29nbGUxEDAOBgNV" +
- "BAsTB0FuZHJvaWQxFDASBgNVBAMTC1Rlc3QgU2VydmVyMB4XDTA4MDYwNTExNTgxNFoXDTA4MDkw" +
- "MzExNTgxNFowaTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDDAKBgNVBAcTA01U" +
- "VjEPMA0GA1UEChMGR29vZ2xlMRAwDgYDVQQLEwdBbmRyb2lkMRQwEgYDVQQDEwtUZXN0IFNlcnZl" +
- "cjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0LIdKaIr9/vsTq8BZlA3R+NFWRaH4lGsTAQy" +
- "DPMF9ZqEDOaL6DJuu0colSBBBQ85hQTPa9m9nyJoN3pEi1hgamqOvQIWcXBk+SOpUGRZZFXwniJV" +
- "zDKU5nE9MYgn2B9AoiH3CSuMz6HRqgVaqtppIe1jhukMc/kHVJvlKRNy9XMCAwEAATANBgkqhkiG" +
- "9w0BAQUFAAOBgQC7yBmJ9O/eWDGtSH9BH0R3dh2NdST3W9hNZ8hIa8U8klhNHbUCSSktZmZkvbPU" +
- "hse5LI3dh6RyNDuqDrbYwcqzKbFJaq/jX9kCoeb3vgbQElMRX8D2ID1vRjxwlALFISrtaN4VpWzV" +
- "yeoHPW4xldeZmoVtjn8zXNzQhLuBqX2MmAAAAqwAAAAUvkUScfw9yCSmALruURNmtBai7kQAAAZx" +
- "4Jmijxs/l8EBaleaUru6EOPioWkUAEVWCxjM/TxbGHOi2VMsQWqRr/DZ3wsDmtQgw3QTrUK666sR" +
- "MBnbqdnyCyvM1J2V1xxLXPUeRBmR2CXorYGF9Dye7NkgVdfA+9g9L/0Au6Ugn+2Cj5leoIgkgApN" +
- "vuEcZegFlNOUPVEs3SlBgUF1BY6OBM0UBHTPwGGxFBBcetcuMRbUnu65vyDG0pslT59qpaR0TMVs" +
- "P+tcheEzhyjbfM32/vwhnL9dBEgM8qMt0sqF6itNOQU/F4WGkK2Cm2v4CYEyKYw325fEhzTXosck" +
- "MhbqmcyLab8EPceWF3dweoUT76+jEZx8lV2dapR+CmczQI43tV9btsd1xiBbBHAKvymm9Ep9bPzM" +
- "J0MQi+OtURL9Lxke/70/MRueqbPeUlOaGvANTmXQD2OnW7PISwJ9lpeLfTG0LcqkoqkbtLKQLYHI" +
- "rQfV5j0j+wmvmpMxzjN3uvNajLa4zQ8l0Eok9SFaRr2RL0gN8Q2JegfOL4pUiHPsh64WWya2NB7f" +
- "V+1s65eA5ospXYsShRjo046QhGTmymwXXzdzuxu8IlnTEont6P4+J+GsWk6cldGbl20hctuUKzyx" +
- "OptjEPOKejV60iDCYGmHbCWAzQ8h5MILV82IclzNViZmzAapeeCnexhpXhWTs+xDEYSKEiG/camt" +
- "bhmZc3BcyVJrW23PktSfpBQ6D8ZxoMfF0L7V2GQMaUg+3r7ucrx82kpqotjv0xHghNIm95aBr1Qw" +
- "1gaEjsC/0wGmmBDg1dTDH+F1p9TInzr3EFuYD0YiQ7YlAHq3cPuyGoLXJ5dXYuSBfhDXJSeddUkl" +
- "k1ufZyOOcskeInQge7jzaRfmKg3U94r+spMEvb0AzDQVOKvjjo1ivxMSgFRZaDb/4qw=";
+ private static final String SERVER_KEYS_BKS = ""
+ + "AAAAAQAAABQDkebzoP1XwqyWKRCJEpn/t8dqIQAABDkEAAVteWtleQAAARpYl20nAAAAAQAFWC41"
+ + "MDkAAAJNMIICSTCCAbKgAwIBAgIESEfU1jANBgkqhkiG9w0BAQUFADBpMQswCQYDVQQGEwJVUzET"
+ + "MBEGA1UECBMKQ2FsaWZvcm5pYTEMMAoGA1UEBxMDTVRWMQ8wDQYDVQQKEwZHb29nbGUxEDAOBgNV"
+ + "BAsTB0FuZHJvaWQxFDASBgNVBAMTC1Rlc3QgU2VydmVyMB4XDTA4MDYwNTExNTgxNFoXDTA4MDkw"
+ + "MzExNTgxNFowaTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDDAKBgNVBAcTA01U"
+ + "VjEPMA0GA1UEChMGR29vZ2xlMRAwDgYDVQQLEwdBbmRyb2lkMRQwEgYDVQQDEwtUZXN0IFNlcnZl"
+ + "cjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0LIdKaIr9/vsTq8BZlA3R+NFWRaH4lGsTAQy"
+ + "DPMF9ZqEDOaL6DJuu0colSBBBQ85hQTPa9m9nyJoN3pEi1hgamqOvQIWcXBk+SOpUGRZZFXwniJV"
+ + "zDKU5nE9MYgn2B9AoiH3CSuMz6HRqgVaqtppIe1jhukMc/kHVJvlKRNy9XMCAwEAATANBgkqhkiG"
+ + "9w0BAQUFAAOBgQC7yBmJ9O/eWDGtSH9BH0R3dh2NdST3W9hNZ8hIa8U8klhNHbUCSSktZmZkvbPU"
+ + "hse5LI3dh6RyNDuqDrbYwcqzKbFJaq/jX9kCoeb3vgbQElMRX8D2ID1vRjxwlALFISrtaN4VpWzV"
+ + "yeoHPW4xldeZmoVtjn8zXNzQhLuBqX2MmAAAAqwAAAAUvkUScfw9yCSmALruURNmtBai7kQAAAZx"
+ + "4Jmijxs/l8EBaleaUru6EOPioWkUAEVWCxjM/TxbGHOi2VMsQWqRr/DZ3wsDmtQgw3QTrUK666sR"
+ + "MBnbqdnyCyvM1J2V1xxLXPUeRBmR2CXorYGF9Dye7NkgVdfA+9g9L/0Au6Ugn+2Cj5leoIgkgApN"
+ + "vuEcZegFlNOUPVEs3SlBgUF1BY6OBM0UBHTPwGGxFBBcetcuMRbUnu65vyDG0pslT59qpaR0TMVs"
+ + "P+tcheEzhyjbfM32/vwhnL9dBEgM8qMt0sqF6itNOQU/F4WGkK2Cm2v4CYEyKYw325fEhzTXosck"
+ + "MhbqmcyLab8EPceWF3dweoUT76+jEZx8lV2dapR+CmczQI43tV9btsd1xiBbBHAKvymm9Ep9bPzM"
+ + "J0MQi+OtURL9Lxke/70/MRueqbPeUlOaGvANTmXQD2OnW7PISwJ9lpeLfTG0LcqkoqkbtLKQLYHI"
+ + "rQfV5j0j+wmvmpMxzjN3uvNajLa4zQ8l0Eok9SFaRr2RL0gN8Q2JegfOL4pUiHPsh64WWya2NB7f"
+ + "V+1s65eA5ospXYsShRjo046QhGTmymwXXzdzuxu8IlnTEont6P4+J+GsWk6cldGbl20hctuUKzyx"
+ + "OptjEPOKejV60iDCYGmHbCWAzQ8h5MILV82IclzNViZmzAapeeCnexhpXhWTs+xDEYSKEiG/camt"
+ + "bhmZc3BcyVJrW23PktSfpBQ6D8ZxoMfF0L7V2GQMaUg+3r7ucrx82kpqotjv0xHghNIm95aBr1Qw"
+ + "1gaEjsC/0wGmmBDg1dTDH+F1p9TInzr3EFuYD0YiQ7YlAHq3cPuyGoLXJ5dXYuSBfhDXJSeddUkl"
+ + "k1ufZyOOcskeInQge7jzaRfmKg3U94r+spMEvb0AzDQVOKvjjo1ivxMSgFRZaDb/4qw=";
/**
* Defines the keystore contents for the server, JKS version. Holds just a
* single self-generated key. The subject name is "Test Server".
*/
- private static final String SERVER_KEYS_JKS =
- "/u3+7QAAAAIAAAABAAAAAQAFbXlrZXkAAAEaWFfBeAAAArowggK2MA4GCisGAQQBKgIRAQEFAASC" +
- "AqI2kp5XjnF8YZkhcF92YsJNQkvsmH7zqMM87j23zSoV4DwyE3XeC/gZWq1ToScIhoqZkzlbWcu4" +
- "T/Zfc/DrfGk/rKbBL1uWKGZ8fMtlZk8KoAhxZk1JSyJvdkyKxqmzUbxk1OFMlN2VJNu97FPVH+du" +
- "dvjTvmpdoM81INWBW/1fZJeQeDvn4mMbbe0IxgpiLnI9WSevlaDP/sm1X3iO9yEyzHLL+M5Erspo" +
- "Cwa558fOu5DdsICMXhvDQxjWFKFhPHnKtGe+VvwkG9/bAaDgx3kfhk0w5zvdnkKb+8Ed9ylNRzdk" +
- "ocAa/mxlMTOsTvDKXjjsBupNPIIj7OP4GNnZaxkJjSs98pEO67op1GX2qhy6FSOPNuq8k/65HzUc" +
- "PYn6voEeh6vm02U/sjEnzRevQ2+2wXoAdp0EwtQ/DlMe+NvcwPGWKuMgX4A4L93DZGb04N2VmAU3" +
- "YLOtZwTO0LbuWrcCM/q99G/7LcczkxIVrO2I/rh8RXVczlf9QzcrFObFv4ATuspWJ8xG7DhsMbnk" +
- "rT94Pq6TogYeoz8o8ZMykesAqN6mt/9+ToIemmXv+e+KU1hI5oLwWMnUG6dXM6hIvrULY6o+QCPH" +
- "172YQJMa+68HAeS+itBTAF4Clm/bLn6reHCGGU6vNdwU0lYldpiOj9cB3t+u2UuLo6tiFWjLf5Zs" +
- "EQJETd4g/EK9nHxJn0GAKrWnTw7pEHQJ08elzUuy04C/jEEG+4QXU1InzS4o/kR0Sqz2WTGDoSoq" +
- "ewuPRU5bzQs/b9daq3mXrnPtRBL6HfSDAdpTK76iHqLCGdqx3avHjVSBm4zFvEuYBCev+3iKOBmg" +
- "yh7eQRTjz4UOWfy85omMBr7lK8PtfVBDzOXpasxS0uBgdUyBDX4tO6k9jZ8a1kmQRQAAAAEABVgu" +
- "NTA5AAACSDCCAkQwggGtAgRIR8SKMA0GCSqGSIb3DQEBBAUAMGkxCzAJBgNVBAYTAlVTMRMwEQYD" +
- "VQQIEwpDYWxpZm9ybmlhMQwwCgYDVQQHEwNNVFYxDzANBgNVBAoTBkdvb2dsZTEQMA4GA1UECxMH" +
- "QW5kcm9pZDEUMBIGA1UEAxMLVGVzdCBTZXJ2ZXIwHhcNMDgwNjA1MTA0ODQyWhcNMDgwOTAzMTA0" +
- "ODQyWjBpMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEMMAoGA1UEBxMDTVRWMQ8w" +
- "DQYDVQQKEwZHb29nbGUxEDAOBgNVBAsTB0FuZHJvaWQxFDASBgNVBAMTC1Rlc3QgU2VydmVyMIGf" +
- "MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwoC6chqCI84rj1PrXuJgbiit4EV909zR6N0jNlYfg" +
- "itwB39bP39wH03rFm8T59b3mbSptnGmCIpLZn25KPPFsYD3JJ+wFlmiUdEP9H05flfwtFQJnw9uT" +
- "3rRIdYVMPcQ3RoZzwAMliGr882I2thIDbA6xjGU/1nRIdvk0LtxH3QIDAQABMA0GCSqGSIb3DQEB" +
- "BAUAA4GBAJn+6YgUlY18Ie+0+Vt8oEi81DNi/bfPrAUAh63fhhBikx/3R9dl3wh09Z6p7cIdNxjW" +
- "n2ll+cRW9eqF7z75F0Omm0C7/KAEPjukVbszmzeU5VqzkpSt0j84YWi+TfcHRrfvhLbrlmGITVpY" +
- "ol5pHLDyqGmDs53pgwipWqsn/nEXEBgj3EoqPeqHbDf7YaP8h/5BSt0=";
+ private static final String SERVER_KEYS_JKS = ""
+ + "/u3+7QAAAAIAAAABAAAAAQAFbXlrZXkAAAEaWFfBeAAAArowggK2MA4GCisGAQQBKgIRAQEFAASC"
+ + "AqI2kp5XjnF8YZkhcF92YsJNQkvsmH7zqMM87j23zSoV4DwyE3XeC/gZWq1ToScIhoqZkzlbWcu4"
+ + "T/Zfc/DrfGk/rKbBL1uWKGZ8fMtlZk8KoAhxZk1JSyJvdkyKxqmzUbxk1OFMlN2VJNu97FPVH+du"
+ + "dvjTvmpdoM81INWBW/1fZJeQeDvn4mMbbe0IxgpiLnI9WSevlaDP/sm1X3iO9yEyzHLL+M5Erspo"
+ + "Cwa558fOu5DdsICMXhvDQxjWFKFhPHnKtGe+VvwkG9/bAaDgx3kfhk0w5zvdnkKb+8Ed9ylNRzdk"
+ + "ocAa/mxlMTOsTvDKXjjsBupNPIIj7OP4GNnZaxkJjSs98pEO67op1GX2qhy6FSOPNuq8k/65HzUc"
+ + "PYn6voEeh6vm02U/sjEnzRevQ2+2wXoAdp0EwtQ/DlMe+NvcwPGWKuMgX4A4L93DZGb04N2VmAU3"
+ + "YLOtZwTO0LbuWrcCM/q99G/7LcczkxIVrO2I/rh8RXVczlf9QzcrFObFv4ATuspWJ8xG7DhsMbnk"
+ + "rT94Pq6TogYeoz8o8ZMykesAqN6mt/9+ToIemmXv+e+KU1hI5oLwWMnUG6dXM6hIvrULY6o+QCPH"
+ + "172YQJMa+68HAeS+itBTAF4Clm/bLn6reHCGGU6vNdwU0lYldpiOj9cB3t+u2UuLo6tiFWjLf5Zs"
+ + "EQJETd4g/EK9nHxJn0GAKrWnTw7pEHQJ08elzUuy04C/jEEG+4QXU1InzS4o/kR0Sqz2WTGDoSoq"
+ + "ewuPRU5bzQs/b9daq3mXrnPtRBL6HfSDAdpTK76iHqLCGdqx3avHjVSBm4zFvEuYBCev+3iKOBmg"
+ + "yh7eQRTjz4UOWfy85omMBr7lK8PtfVBDzOXpasxS0uBgdUyBDX4tO6k9jZ8a1kmQRQAAAAEABVgu"
+ + "NTA5AAACSDCCAkQwggGtAgRIR8SKMA0GCSqGSIb3DQEBBAUAMGkxCzAJBgNVBAYTAlVTMRMwEQYD"
+ + "VQQIEwpDYWxpZm9ybmlhMQwwCgYDVQQHEwNNVFYxDzANBgNVBAoTBkdvb2dsZTEQMA4GA1UECxMH"
+ + "QW5kcm9pZDEUMBIGA1UEAxMLVGVzdCBTZXJ2ZXIwHhcNMDgwNjA1MTA0ODQyWhcNMDgwOTAzMTA0"
+ + "ODQyWjBpMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEMMAoGA1UEBxMDTVRWMQ8w"
+ + "DQYDVQQKEwZHb29nbGUxEDAOBgNVBAsTB0FuZHJvaWQxFDASBgNVBAMTC1Rlc3QgU2VydmVyMIGf"
+ + "MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCwoC6chqCI84rj1PrXuJgbiit4EV909zR6N0jNlYfg"
+ + "itwB39bP39wH03rFm8T59b3mbSptnGmCIpLZn25KPPFsYD3JJ+wFlmiUdEP9H05flfwtFQJnw9uT"
+ + "3rRIdYVMPcQ3RoZzwAMliGr882I2thIDbA6xjGU/1nRIdvk0LtxH3QIDAQABMA0GCSqGSIb3DQEB"
+ + "BAUAA4GBAJn+6YgUlY18Ie+0+Vt8oEi81DNi/bfPrAUAh63fhhBikx/3R9dl3wh09Z6p7cIdNxjW"
+ + "n2ll+cRW9eqF7z75F0Omm0C7/KAEPjukVbszmzeU5VqzkpSt0j84YWi+TfcHRrfvhLbrlmGITVpY"
+ + "ol5pHLDyqGmDs53pgwipWqsn/nEXEBgj3EoqPeqHbDf7YaP8h/5BSt0=";
protected int startServer(String name) {
String keys = useBKS ? SERVER_KEYS_BKS : SERVER_KEYS_JKS;
@@ -620,12 +497,15 @@
serverThread.start();
try {
while (!serverReady) {
+ Exception e = server.getException();
+ if (e != null) {
+ throw new AssertionError(e);
+ }
Thread.currentThread().sleep(50);
}
// give the server 100 millis to accept
Thread.currentThread().sleep(100);
- } catch (InterruptedException e) {
- // ignore
+ } catch (InterruptedException ignore) {
}
return server.sport;
}
@@ -668,27 +548,34 @@
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null);
- SSLServerSocket serverSocket = (SSLServerSocket)sslContext.getServerSocketFactory().createServerSocket();
+ SSLServerSocket serverSocket = (SSLServerSocket)
+ sslContext.getServerSocketFactory().createServerSocket();
+ try {
+ serverSocket.bind(new InetSocketAddress(port));
+ sport = serverSocket.getLocalPort();
+ serverReady = true;
- serverSocket.bind(new InetSocketAddress(port));
- sport = serverSocket.getLocalPort();
- serverReady = true;
+ SSLSocket clientSocket = (SSLSocket)serverSocket.accept();
- SSLSocket clientSocket = (SSLSocket)serverSocket.accept();
-
- InputStream stream = clientSocket.getInputStream();
-
- for (int i = 0; i < 256; i++) {
- int j = stream.read();
- if (i != j) {
- throw new RuntimeException("Error reading socket, expected " + i + ", got " + j);
+ try {
+ InputStream stream = clientSocket.getInputStream();
+ try {
+ for (int i = 0; i < 256; i++) {
+ int j = stream.read();
+ if (i != j) {
+ throw new RuntimeException("Error reading socket, expected " + i
+ + ", got " + j);
+ }
+ }
+ } finally {
+ stream.close();
+ }
+ } finally {
+ clientSocket.close();
}
+ } finally {
+ serverSocket.close();
}
-
- stream.close();
- clientSocket.close();
- serverSocket.close();
-
} catch (Exception ex) {
exception = ex;
}
@@ -724,32 +611,30 @@
}
private SSLSocket getSSLSocket() throws IOException {
- SSLSocket ssl = null;
- ssl = (SSLSocket) SSLSocketFactory.getDefault().createSocket();
- return ssl;
+ return (SSLSocket) SSLSocketFactory.getDefault().createSocket();
}
private SSLSocket getSSLSocket(InetAddress host, int port) throws IOException {
- SSLSocket ssl = null;
- ssl = (SSLSocket) SSLSocketFactory.getDefault().createSocket(host, port);
- return ssl;
+ return (SSLSocket) SSLSocketFactory.getDefault().createSocket(host, port);
}
private SSLSocket getSSLSocket(String host, int port) throws UnknownHostException, IOException {
- SSLSocket ssl = null;
- ssl = (SSLSocket) SSLSocketFactory.getDefault().createSocket(host, port);
- return ssl;
+ return (SSLSocket) SSLSocketFactory.getDefault().createSocket(host, port);
}
- private SSLSocket getSSLSocket(InetAddress host, int port, InetAddress localHost, int localPort) throws IOException {
- SSLSocket ssl = null;
- ssl = (SSLSocket) SSLSocketFactory.getDefault().createSocket(host, port, localHost, localPort);
- return ssl;
+ private SSLSocket getSSLSocket(InetAddress host, int port, InetAddress localHost, int localPort)
+ throws IOException {
+ return (SSLSocket) SSLSocketFactory.getDefault().createSocket(host,
+ port,
+ localHost,
+ localPort);
}
- private SSLSocket getSSLSocket(String host, int port, InetAddress localHost, int localPort) throws UnknownHostException, IOException {
- SSLSocket ssl = null;
- ssl = (SSLSocket) SSLSocketFactory.getDefault().createSocket(host, port, localHost, localPort);
- return ssl;
+ private SSLSocket getSSLSocket(String host, int port, InetAddress localHost, int localPort)
+ throws UnknownHostException, IOException {
+ return (SSLSocket) SSLSocketFactory.getDefault().createSocket(host,
+ port,
+ localHost,
+ localPort);
}
}
diff --git a/run-libcore-tests b/run-libcore-tests
new file mode 100755
index 0000000..a1a66ae
--- /dev/null
+++ b/run-libcore-tests
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# make sure there's a vogar on the path, but prefer the user's one.
+export PATH=$PATH:~dalvik-prebuild/vogar/bin
+
+VOGAR="vogar $VOGAR_FLAGS $*"
+
+# We enumerate the test packages for vogar rather than just giving it the classes.jar
+# so hundreds of packages can be tested in parallel, rather than one big jar file serially.
+all_test_packages=$(find libcore/*/src/test -name "*.java" | \
+ fgrep -v junit | \
+ fgrep -v org/w3c/domts | \
+ xargs grep -h '^package ' | sed 's/^package //' | sed 's/;$//' | sort | uniq | tr "\n" " ")
+
+echo "Running tests for following test packages:"
+echo $all_test_packages | tr " " "\n"
+
+$VOGAR \
+ --vm-arg -Xmx32M \
+ --classpath out/target/common/obj/JAVA_LIBRARIES/core-tests_intermediates/javalib.jar \
+ --classpath out/target/common/obj/JAVA_LIBRARIES/sqlite-jdbc_intermediates/classes.jar \
+ $all_test_packages \
+ tests.api.org.w3c.dom \
+ || true
diff --git a/support/src/test/java/libcore/net/http/HttpResponseCache.java b/support/src/test/java/libcore/net/http/HttpResponseCache.java
deleted file mode 100644
index a551e27..0000000
--- a/support/src/test/java/libcore/net/http/HttpResponseCache.java
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package libcore.net.http;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.CacheRequest;
-import java.net.CacheResponse;
-import java.net.HttpURLConnection;
-import java.net.ResponseCache;
-import java.net.SecureCacheResponse;
-import java.net.URI;
-import java.net.URLConnection;
-import java.security.Principal;
-import java.security.cert.Certificate;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLPeerUnverifiedException;
-
-/**
- * Cache all responses in memory by URI.
- *
- * TODO: disk storage, tuning knobs, LRU
- * TODO: move this class to android.util
- */
-public final class HttpResponseCache extends ResponseCache {
- private final Map<URI, Entry> entries = new HashMap<URI, Entry>();
- private int abortCount;
- private int successCount;
- private int hitCount;
- private int missCount;
-
- @Override public synchronized CacheResponse get(URI uri, String requestMethod,
- Map<String, List<String>> requestHeaders) throws IOException {
- Entry entry = entries.get(uri);
- if (entry == null) {
- missCount++;
- return null;
- }
-
- if (!requestMethod.equals(entry.requestMethod)) {
- return null;
- }
-
- // RawHeaders headers = RawHeaders.fromMultimap(entry.responseHeaders);
-
- hitCount++;
- return entry.asResponse();
- }
-
- @Override public CacheRequest put(URI uri, URLConnection urlConnection)
- throws IOException {
- if (!(urlConnection instanceof HttpURLConnection)) {
- return null;
- }
-
- HttpURLConnection httpConnection = (HttpURLConnection) urlConnection;
- String requestMethod = httpConnection.getRequestMethod();
-
- // Invalidate the cache on POST, PUT and DELETE.
- if (requestMethod.equals("POST") || requestMethod.equals("PUT")
- || requestMethod.equals("DELETE")) {
- entries.remove(uri);
- }
-
- /*
- * Don't cache non-GET responses. We're technically allowed to cache
- * HEAD requests and some POST requests, but the complexity of doing so
- * is high and the benefit is low.
- */
- if (!requestMethod.equals("GET")) {
- return null;
- }
-
- // For implementation simplicity, don't cache responses that have a Vary field.
- if (httpConnection.getHeaderField("Vary") != null) {
- return null;
- }
-
- return new Entry(uri, httpConnection).asRequest();
- }
-
- public synchronized Map<URI, Entry> getContents() {
- return new HashMap<URI, Entry>(entries);
- }
-
- /**
- * Returns the number of requests that were aborted before they were closed.
- */
- public synchronized int getAbortCount() {
- return abortCount;
- }
-
- /**
- * Returns the number of requests that were closed successfully.
- */
- public synchronized int getSuccessCount() {
- return successCount;
- }
-
- /**
- * Returns the number of responses served by the cache.
- */
- public synchronized int getHitCount() {
- return hitCount;
- }
-
- /**
- * Returns the number of responses that couldn't be served by the cache.
- */
- public synchronized int getMissCount() {
- return missCount;
- }
-
- public final class Entry {
- private final ByteArrayOutputStream body = new ByteArrayOutputStream() {
- private boolean closed;
- @Override public void close() throws IOException {
- synchronized (HttpResponseCache.this) {
- if (closed) {
- return;
- }
-
- super.close();
- entries.put(uri, Entry.this);
- successCount++;
- closed = true;
- }
- }
- };
-
- private final String requestMethod;
- private final Map<String, List<String>> responseHeaders;
- private final URI uri;
- private final CacheResponse cacheResponse;
-
- private Entry(URI uri, HttpURLConnection connection) {
- this.uri = uri;
- this.requestMethod = connection.getRequestMethod();
- this.responseHeaders = deepCopy(connection.getHeaderFields());
- this.cacheResponse = connection instanceof HttpsURLConnection
- ? new SecureCacheResponseImpl(responseHeaders, body,
- (HttpsURLConnection) connection)
- : new CacheResponseImpl(responseHeaders, body);
- }
-
- public CacheRequest asRequest() {
- return new CacheRequest() {
- private boolean aborted;
- @Override public void abort() {
- synchronized (HttpResponseCache.this) {
- if (aborted) {
- return;
- }
-
- abortCount++;
- aborted = true;
- }
- }
- @Override public OutputStream getBody() throws IOException {
- return body;
- }
- };
- }
-
- public CacheResponse asResponse() {
- return cacheResponse;
- }
-
- public byte[] getBytes() {
- return body.toByteArray();
- }
- }
-
- private final class CacheResponseImpl extends CacheResponse {
- private final Map<String, List<String>> headers;
- private final ByteArrayOutputStream bytesOut;
-
- public CacheResponseImpl(Map<String, List<String>> headers,
- ByteArrayOutputStream bytesOut) {
- this.headers = headers;
- this.bytesOut = bytesOut;
- }
-
- @Override public Map<String, List<String>> getHeaders() {
- return deepCopy(headers);
- }
-
- @Override public InputStream getBody() {
- return new ByteArrayInputStream(bytesOut.toByteArray());
- }
- }
-
- private final class SecureCacheResponseImpl extends SecureCacheResponse {
- private final Map<String, List<String>> headers;
- private final ByteArrayOutputStream bytesOut;
- private final String cipherSuite;
- private final Certificate[] localCertificates;
- private final List<Certificate> serverCertificates;
- private final Principal peerPrincipal;
- private final Principal localPrincipal;
-
- public SecureCacheResponseImpl(Map<String, List<String>> headers,
- ByteArrayOutputStream bytesOut,
- HttpsURLConnection httpsConnection) {
- this.headers = headers;
- this.bytesOut = bytesOut;
-
- /*
- * Retrieve the fields eagerly to avoid needing a strong
- * reference to the connection. We do acrobatics for the two
- * methods that can throw so that the cache response also
- * throws.
- */
- List<Certificate> serverCertificatesNonFinal = null;
- try {
- serverCertificatesNonFinal = Arrays.asList(
- httpsConnection.getServerCertificates());
- } catch (SSLPeerUnverifiedException ignored) {
- }
- Principal peerPrincipalNonFinal = null;
- try {
- peerPrincipalNonFinal = httpsConnection.getPeerPrincipal();
- } catch (SSLPeerUnverifiedException ignored) {
- }
- this.cipherSuite = httpsConnection.getCipherSuite();
- this.localCertificates = httpsConnection.getLocalCertificates();
- this.serverCertificates = serverCertificatesNonFinal;
- this.peerPrincipal = peerPrincipalNonFinal;
- this.localPrincipal = httpsConnection.getLocalPrincipal();
- }
-
- @Override public Map<String, List<String>> getHeaders() {
- return deepCopy(headers);
- }
-
- @Override public InputStream getBody() {
- return new ByteArrayInputStream(bytesOut.toByteArray());
- }
-
- @Override public String getCipherSuite() {
- return cipherSuite;
- }
-
- @Override public List<Certificate> getLocalCertificateChain() {
- return localCertificates != null
- ? Arrays.asList(localCertificates.clone())
- : null;
- }
-
- @Override public List<Certificate> getServerCertificateChain()
- throws SSLPeerUnverifiedException {
- if (serverCertificates == null) {
- throw new SSLPeerUnverifiedException(null);
- }
- return new ArrayList<Certificate>(serverCertificates);
- }
-
- @Override public Principal getPeerPrincipal() throws SSLPeerUnverifiedException {
- if (peerPrincipal == null) {
- throw new SSLPeerUnverifiedException(null);
- }
- return peerPrincipal;
- }
-
- @Override public Principal getLocalPrincipal() {
- return localPrincipal;
- }
- }
-
- private static Map<String, List<String>> deepCopy(Map<String, List<String>> input) {
- Map<String, List<String>> result = new LinkedHashMap<String, List<String>>(input);
- for (Map.Entry<String, List<String>> entry : result.entrySet()) {
- entry.setValue(new ArrayList<String>(entry.getValue()));
- }
- return result;
- }
-}
diff --git a/support/src/test/java/tests/http/MockWebServer.java b/support/src/test/java/tests/http/MockWebServer.java
index 2d8215c..a130dde 100644
--- a/support/src/test/java/tests/http/MockWebServer.java
+++ b/support/src/test/java/tests/http/MockWebServer.java
@@ -251,9 +251,7 @@
Socket socket;
if (sslSocketFactory != null) {
if (tunnelProxy) {
- if (!processOneRequest(raw.getInputStream(), raw.getOutputStream(), raw)) {
- throw new IllegalStateException("Tunnel without any CONNECT!");
- }
+ createTunnel();
}
socket = sslSocketFactory.createSocket(
raw, raw.getInetAddress().getHostAddress(), raw.getPort(), true);
@@ -283,6 +281,22 @@
}
/**
+ * Respond to CONNECT requests until a SWITCH_TO_SSL_AT_END response
+ * is dispatched.
+ */
+ private void createTunnel() throws IOException, InterruptedException {
+ while (true) {
+ MockResponse connect = responseQueue.peek();
+ if (!processOneRequest(raw.getInputStream(), raw.getOutputStream(), raw)) {
+ throw new IllegalStateException("Tunnel without any CONNECT!");
+ }
+ if (connect.getSocketPolicy() == SocketPolicy.UPGRADE_TO_SSL_AT_END) {
+ return;
+ }
+ }
+ }
+
+ /**
* Reads a request and writes its response. Returns true if a request
* was processed.
*/
diff --git a/support/src/test/java/tests/http/SocketPolicy.java b/support/src/test/java/tests/http/SocketPolicy.java
index 549c65d..820b400 100644
--- a/support/src/test/java/tests/http/SocketPolicy.java
+++ b/support/src/test/java/tests/http/SocketPolicy.java
@@ -34,6 +34,12 @@
DISCONNECT_AT_END,
/**
+ * Wrap the socket with SSL at the completion of this request/response
+ * pair. Used for CONNECT messages to tunnel SSL over an HTTP proxy.
+ */
+ UPGRADE_TO_SSL_AT_END,
+
+ /**
* Request immediate close of connection without even reading the
* request.
*
diff --git a/support/src/test/java/tests/io/MockOs.java b/support/src/test/java/tests/io/MockOs.java
new file mode 100644
index 0000000..0cfe836
--- /dev/null
+++ b/support/src/test/java/tests/io/MockOs.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tests.io;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Map;
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+import libcore.io.Os;
+import libcore.io.OsConstants;
+
+/**
+ * A mocking interceptor that wraps another {@link Os} to add faults. This can
+ * be useful to test otherwise hard-to-test scenarios such as a full disk.
+ */
+public final class MockOs {
+ private final InheritableThreadLocal<Map<String, Deque<InvocationHandler>>> handlers
+ = new InheritableThreadLocal<Map<String, Deque<InvocationHandler>>>() {
+ @Override protected Map<String, Deque<InvocationHandler>> initialValue() {
+ return new HashMap<String, Deque<InvocationHandler>>();
+ }
+ };
+
+ private Os delegate;
+ private final InvocationHandler delegateHandler = new InvocationHandler() {
+ @Override public Object invoke(Object o, Method method, Object[] args) throws Throwable {
+ try {
+ return method.invoke(delegate, args);
+ } catch (InvocationTargetException e) {
+ throw e.getCause();
+ }
+ }
+ };
+
+ private final InvocationHandler invocationHandler = new InvocationHandler() {
+ @Override public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+ InvocationHandler handler = getHandlers(method.getName()).poll();
+ if (handler == null) {
+ handler = delegateHandler;
+ }
+ return handler.invoke(proxy, method, args);
+ }
+ };
+
+ private final Os mockOs = (Os) Proxy.newProxyInstance(MockOs.class.getClassLoader(),
+ new Class[] { Os.class }, invocationHandler);
+
+ public void install() {
+ if (delegate != null) {
+ throw new IllegalStateException("MockOs already installed!");
+ }
+ delegate = Libcore.os;
+ Libcore.os = mockOs;
+ }
+
+ public void uninstall() {
+ if (delegate == null) {
+ throw new IllegalStateException("MockOs not installed!");
+ }
+ Libcore.os = delegate;
+ }
+
+ /**
+ * Returns the invocation handlers to handle upcoming invocations of
+ * {@code methodName}. If empty, calls will be handled by the delegate.
+ */
+ public Deque<InvocationHandler> getHandlers(String methodName) {
+ Map<String, Deque<InvocationHandler>> threadFaults = handlers.get();
+ Deque<InvocationHandler> result = threadFaults.get(methodName);
+ if (result == null) {
+ result = new ArrayDeque<InvocationHandler>();
+ threadFaults.put(methodName, result);
+ }
+ return result;
+ }
+
+ /**
+ * Enqueues the specified number of normal operations. Useful to delay
+ * faults.
+ */
+ public void enqueueNormal(String methodName, int count) {
+ Deque<InvocationHandler> handlers = getHandlers(methodName);
+ for (int i = 0; i < count; i++) {
+ handlers.add(delegateHandler);
+ }
+ }
+
+ public void enqueueFault(String methodName) {
+ enqueueFault(methodName, OsConstants.EIO);
+ }
+
+ public void enqueueFault(String methodName, final int errno) {
+ getHandlers(methodName).add(new InvocationHandler() {
+ @Override public Object invoke(Object proxy, Method method, Object[] args) {
+ throw new ErrnoException(method.getName(), errno);
+ }
+ });
+ }
+}
diff --git a/support/src/test/java/tests/net/StuckServer.java b/support/src/test/java/tests/net/StuckServer.java
index 4230f17..eababce 100644
--- a/support/src/test/java/tests/net/StuckServer.java
+++ b/support/src/test/java/tests/net/StuckServer.java
@@ -40,6 +40,24 @@
}
}
+ public void unblockAfterMs(final int ms) {
+ Thread t = new Thread(new Runnable() {
+ @Override public void run() {
+ try {
+ Thread.sleep(ms);
+ for (Socket client : clients) {
+ client.close();
+ }
+ clients.clear();
+ clients.add(serverSocket.accept());
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ });
+ t.start();
+ }
+
public InetSocketAddress getLocalSocketAddress() {
return (InetSocketAddress) serverSocket.getLocalSocketAddress();
}
diff --git a/support/src/test/java/tests/support/Support_Configuration.java b/support/src/test/java/tests/support/Support_Configuration.java
index fb5b468..fed0bc8 100644
--- a/support/src/test/java/tests/support/Support_Configuration.java
+++ b/support/src/test/java/tests/support/Support_Configuration.java
@@ -46,8 +46,6 @@
public static String HomeAddressSoftware = "Jetty(6.0.x)";
- public static String ProxyServerTestHost = "jcltest.apache.org";
-
public static String SocksServerTestHost = "jcltest.apache.org";
public static int SocksServerTestPort = 1080;
@@ -75,15 +73,7 @@
public static byte[] InetTestCaddr = { 9, 26, -56, -111 };
- public static final String HomeAddress6 = "jcltest6.apache.org";
-
- public static String IPv6GlobalAddressJcl4 = "FE80:0000:0000:0000:020D:60FF:FE0F:A776%4"; // this
-
- public static String ProxyServerTestHostIPv6 = "jcltest6.apache.org";
-
- public static String InetTestIP6 = "fe80::20d:60ff:fe24:7410";
-
- public static String InetTestIP6LO = "::1";
+ public static String IPv6GlobalAddressJcl4 = "2001:4860:8004::67"; // ipv6.google.com
// ip address that resolves to a host that is not present on the local
// network
@@ -131,12 +121,6 @@
public static long URLConnectionDate = 929106872000L;
- public static boolean RunCommTests = false;
-
- public static String Port1 = "COM1";
-
- public static String Port2 = "COM2";
-
static Hashtable<String, String> props = null;
static {
loadProperties();
@@ -149,9 +133,6 @@
Hashtable<String, String> props = new Hashtable<String, String>();
String iniName = System.getProperty("test.ini.file", "JCLAuto.ini");
- if (System.getProperty("test.comm") != null) {
- RunCommTests = true;
- }
try {
in = new FileInputStream(iniName);
@@ -204,11 +185,6 @@
HomeAddressSoftware = value;
}
- value = props.get("ProxyServerTestHost");
- if (value != null) {
- ProxyServerTestHost = value;
- }
-
value = props.get("SocksServerTestHost");
if (value != null) {
SocksServerTestHost = value;
@@ -313,31 +289,6 @@
URLConnectionDate = Long.parseLong(value);
}
- value = props.get("Port1");
- if (value != null) {
- Port1 = value;
- }
-
- value = props.get("Port2");
- if (value != null) {
- Port2 = value;
- }
-
- value = props.get("InetTestIP6");
- if (value != null) {
- InetTestIP6 = value;
- }
-
- value = props.get("InetTestIP6LO");
- if (value != null) {
- InetTestIP6LO = value;
- }
-
- value = props.get("ProxyServerTestHostIPv6");
- if (value != null) {
- ProxyServerTestHostIPv6 = value;
- }
-
value = props.get("ResolvedNotExistingHost");
if (value != null) {
ResolvedNotExistingHost = value;