Merge "Expose rethrow* methods in the public SDK API"
diff --git a/dalvik/src/main/java/dalvik/system/DexFile.java b/dalvik/src/main/java/dalvik/system/DexFile.java
index 8d32931..486ee90 100644
--- a/dalvik/src/main/java/dalvik/system/DexFile.java
+++ b/dalvik/src/main/java/dalvik/system/DexFile.java
@@ -110,8 +110,9 @@
//System.out.println("DEX FILE cookie is " + mCookie + " fileName=" + fileName);
}
- DexFile(ByteBuffer[] bufs) throws IOException {
- mCookie = openInMemoryDexFiles(bufs);
+ DexFile(ByteBuffer[] bufs, ClassLoader loader, DexPathList.Element[] elements)
+ throws IOException {
+ mCookie = openInMemoryDexFiles(bufs, loader, elements);
mInternalCookie = mCookie;
mFileName = null;
}
@@ -370,7 +371,8 @@
elements);
}
- private static Object openInMemoryDexFiles(ByteBuffer[] bufs) throws IOException {
+ private static Object openInMemoryDexFiles(ByteBuffer[] bufs, ClassLoader loader,
+ DexPathList.Element[] elements) throws IOException {
// Preprocess the ByteBuffers for openInMemoryDexFilesNative. We extract
// the backing array (non-direct buffers only) and start/end positions
// so that the native method does not have to call Java methods anymore.
@@ -382,11 +384,27 @@
starts[i] = bufs[i].position();
ends[i] = bufs[i].limit();
}
- return openInMemoryDexFilesNative(bufs, arrays, starts, ends);
+ return openInMemoryDexFilesNative(bufs, arrays, starts, ends, loader, elements);
}
private static native Object openInMemoryDexFilesNative(ByteBuffer[] bufs, byte[][] arrays,
- int[] starts, int[] ends);
+ int[] starts, int[] ends, ClassLoader loader, DexPathList.Element[] elements);
+
+ /*
+ * Initiates background verification of this DexFile. This is a sepearate down-call
+ * from openDexFile and openInMemoryDexFiles because it requires the class loader's
+ * DexPathList to have been initialized for its classes to be resolvable by ART.
+ * DexPathList will open the dex files first, finalize `dexElements` and then call this.
+ */
+ /*package*/ void verifyInBackground(ClassLoader classLoader, String classLoaderContext) {
+ verifyInBackgroundNative(mCookie, classLoader, classLoaderContext);
+ }
+
+ private static native void verifyInBackgroundNative(Object mCookie, ClassLoader classLoader,
+ String classLoaderContext);
+
+ /*package*/ static native String getClassLoaderContext(ClassLoader classLoader,
+ DexPathList.Element[] elements);
/*
* Returns true if the dex file is backed by a valid oat file.
diff --git a/dalvik/src/main/java/dalvik/system/DexPathList.java b/dalvik/src/main/java/dalvik/system/DexPathList.java
index fc57dc0..c63bb13 100644
--- a/dalvik/src/main/java/dalvik/system/DexPathList.java
+++ b/dalvik/src/main/java/dalvik/system/DexPathList.java
@@ -266,8 +266,24 @@
try {
Element[] null_elements = null;
- DexFile dex = new DexFile(dexFiles);
+ DexFile dex = new DexFile(dexFiles, definingContext, null_elements);
+ // Capture class loader context from *before* `dexElements` is set (see comment below).
+ String classLoaderContext = dex.isBackedByOatFile()
+ ? null : DexFile.getClassLoaderContext(definingContext, null_elements);
dexElements = new Element[] { new Element(dex) };
+ // Spawn background thread to verify all classes and cache verification results.
+ // Must be called *after* `dexElements` has been initialized for ART to find
+ // its classes (the field is hardcoded in ART and dex files iterated over in
+ // the order of the array), but with class loader context from *before*
+ // `dexElements` was set because that is what it will be compared against next
+ // time the same bytecode is loaded.
+ // We only spawn the background thread if the bytecode is not backed by an oat
+ // file, i.e. this is the first time this bytecode is being loaded and/or
+ // verification results have not been cached yet. Skip spawning the thread on
+ // all subsequent loads of the same bytecode in the same class loader context.
+ if (classLoaderContext != null) {
+ dex.verifyInBackground(definingContext, classLoaderContext);
+ }
} catch (IOException suppressed) {
System.logE("Unable to load dex files", suppressed);
suppressedExceptions.add(suppressed);
@@ -330,7 +346,8 @@
int elementPos = 0;
for (ByteBuffer buf : dexFiles) {
try {
- DexFile dex = new DexFile(new ByteBuffer[] { buf });
+ DexFile dex = new DexFile(new ByteBuffer[] { buf }, /* classLoader */ null,
+ /* dexElements */ null);
elements[elementPos++] = new Element(dex);
} catch (IOException suppressed) {
System.logE("Unable to load dex file: " + buf, suppressed);
diff --git a/expectations/knownfailures.txt b/expectations/knownfailures.txt
index e3e36aa..5dd1d1e 100644
--- a/expectations/knownfailures.txt
+++ b/expectations/knownfailures.txt
@@ -1471,7 +1471,7 @@
bug: 19764047,
result: EXEC_FAILED,
names: [
- "libcore.libcore.io.OsTest#test_PacketSocketAddress"
+ "libcore.android.system.OsTest#test_PacketSocketAddress"
]
},
{
diff --git a/libart/src/main/java/dalvik/system/VMRuntime.java b/libart/src/main/java/dalvik/system/VMRuntime.java
index 0260e68..7d70680 100644
--- a/libart/src/main/java/dalvik/system/VMRuntime.java
+++ b/libart/src/main/java/dalvik/system/VMRuntime.java
@@ -696,4 +696,10 @@
*/
@libcore.api.CorePlatformApi
public static native void setProcessPackageName(String packageName);
+
+ /**
+ * Sets the full path to data directory of the app running in this process.
+ */
+ @libcore.api.CorePlatformApi
+ public static native void setProcessDataDirectory(String dataDir);
}
diff --git a/luni/src/main/java/android/system/Os.java b/luni/src/main/java/android/system/Os.java
index 8c96524..ded3b1c 100644
--- a/luni/src/main/java/android/system/Os.java
+++ b/luni/src/main/java/android/system/Os.java
@@ -40,10 +40,18 @@
public final class Os {
private Os() {}
+ // Ideally we'd just have the version that accepts SocketAddress but we're stuck with
+ // this one for legacy reasons. http://b/123568439
/**
* See <a href="http://man7.org/linux/man-pages/man2/accept.2.html">accept(2)</a>.
*/
- public static FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException { return Libcore.os.accept(fd, peerAddress); }
+ public static FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException { return accept(fd, (SocketAddress) peerAddress); }
+
+ /**
+ * See <a href="http://man7.org/linux/man-pages/man2/accept.2.html">accept(2)</a>.
+ * @hide
+ */
+ public static FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException { return Libcore.os.accept(fd, peerAddress); }
/**
* See <a href="http://man7.org/linux/man-pages/man2/access.2.html">access(2)</a>.
diff --git a/luni/src/main/java/libcore/net/android.mime.types b/luni/src/main/java/libcore/net/android.mime.types
index 8a090fc..dd3b21a 100644
--- a/luni/src/main/java/libcore/net/android.mime.types
+++ b/luni/src/main/java/libcore/net/android.mime.types
@@ -90,4 +90,5 @@
video/3gpp 3gpp!
video/mpeg mpeg!
video/quicktime mov!
+video/vnd.youtube.yt yt
video/x-matroska mkv!
diff --git a/luni/src/main/java/libcore/util/HexEncoding.java b/luni/src/main/java/libcore/util/HexEncoding.java
index eceec6b..f5eed4f 100644
--- a/luni/src/main/java/libcore/util/HexEncoding.java
+++ b/luni/src/main/java/libcore/util/HexEncoding.java
@@ -23,17 +23,43 @@
@libcore.api.CorePlatformApi
public class HexEncoding {
+ private static final char[] LOWER_CASE_DIGITS = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+ };
+
+ private static final char[] UPPER_CASE_DIGITS = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+
/** Hidden constructor to prevent instantiation. */
private HexEncoding() {}
- private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray();
+ /**
+ * Encodes the provided byte as a two-digit hexadecimal String value.
+ */
+ @libcore.api.CorePlatformApi
+ public static String encodeToString(byte b, boolean upperCase) {
+ char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS;
+ char[] buf = new char[2]; // We always want two digits.
+ buf[0] = digits[(b >> 4) & 0xf];
+ buf[1] = digits[b & 0xf];
+ return new String(buf, 0, 2);
+ }
/**
* Encodes the provided data as a sequence of hexadecimal characters.
*/
@libcore.api.CorePlatformApi
public static char[] encode(byte[] data) {
- return encode(data, 0, data.length);
+ return encode(data, 0, data.length, true /* upperCase */);
+ }
+
+ /**
+ * Encodes the provided data as a sequence of hexadecimal characters.
+ */
+ @libcore.api.CorePlatformApi
+ public static char[] encode(byte[] data, boolean upperCase) {
+ return encode(data, 0, data.length, upperCase);
}
/**
@@ -41,12 +67,20 @@
*/
@libcore.api.CorePlatformApi
public static char[] encode(byte[] data, int offset, int len) {
+ return encode(data, offset, len, true /* upperCase */);
+ }
+
+ /**
+ * Encodes the provided data as a sequence of hexadecimal characters.
+ */
+ private static char[] encode(byte[] data, int offset, int len, boolean upperCase) {
+ char[] digits = upperCase ? UPPER_CASE_DIGITS : LOWER_CASE_DIGITS;
char[] result = new char[len * 2];
for (int i = 0; i < len; i++) {
byte b = data[offset + i];
int resultIndex = 2 * i;
- result[resultIndex] = (HEX_DIGITS[(b >>> 4) & 0x0f]);
- result[resultIndex + 1] = (HEX_DIGITS[b & 0x0f]);
+ result[resultIndex] = (digits[(b >> 4) & 0x0f]);
+ result[resultIndex + 1] = (digits[b & 0x0f]);
}
return result;
@@ -57,7 +91,15 @@
*/
@libcore.api.CorePlatformApi
public static String encodeToString(byte[] data) {
- return new String(encode(data));
+ return encodeToString(data, true /* upperCase */);
+ }
+
+ /**
+ * Encodes the provided data as a sequence of hexadecimal characters.
+ */
+ @libcore.api.CorePlatformApi
+ public static String encodeToString(byte[] data, boolean upperCase) {
+ return new String(encode(data, upperCase));
}
/**
diff --git a/luni/src/main/java/libcore/util/ZoneInfo.java b/luni/src/main/java/libcore/util/ZoneInfo.java
index 65df611..abd98b1 100644
--- a/luni/src/main/java/libcore/util/ZoneInfo.java
+++ b/luni/src/main/java/libcore/util/ZoneInfo.java
@@ -37,8 +37,9 @@
* Our concrete TimeZone implementation, backed by zoneinfo data.
*
* <p>This reads time zone information from a binary file stored on the platform. The binary file
- * is essentially a single file containing compacted versions of all the tzfile (see
- * {@code man 5 tzfile} for details of the source) and an index by long name, e.g. Europe/London.
+ * is essentially a single file containing compacted versions of all the tzfiles produced by the
+ * zone info compiler (zic) tool (see {@code man 5 tzfile} for details of the format and
+ * {@code man 8 zic}) and an index by long name, e.g. Europe/London.
*
* <p>The compacted form is created by {@code external/icu/tools/ZoneCompactor.java} and is used
* by both this and Bionic. {@link ZoneInfoDB} is responsible for mapping the binary file, and
@@ -307,6 +308,8 @@
// Use the latest non-daylight offset (if any) as the raw offset.
if (mTransitions.length == 0) {
+ // This case is no longer expected to occur in the data used on Android after changes
+ // made in zic version 2014c. It is kept as a fallback.
// If there are no transitions then use the first GMT offset.
mRawOffset = gmtOffsets[0];
} else {
diff --git a/luni/src/test/java/libcore/android/system/OsTest.java b/luni/src/test/java/libcore/android/system/OsTest.java
new file mode 100644
index 0000000..dd5e698
--- /dev/null
+++ b/luni/src/test/java/libcore/android/system/OsTest.java
@@ -0,0 +1,1418 @@
+/*
+ * 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.android.system;
+
+import android.system.ErrnoException;
+import android.system.Int64Ref;
+import android.system.NetlinkSocketAddress;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.PacketSocketAddress;
+import android.system.StructRlimit;
+import android.system.StructStat;
+import android.system.StructTimeval;
+import android.system.StructUcred;
+import android.system.UnixSocketAddress;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.ServerSocket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.atomic.AtomicReference;
+
+import junit.framework.TestCase;
+
+import libcore.io.IoUtils;
+import libcore.testing.io.TestIoUtils;
+
+import static android.system.OsConstants.*;
+
+public class OsTest extends TestCase {
+
+ public void testIsSocket() throws Exception {
+ File f = new File("/dev/null");
+ FileInputStream fis = new FileInputStream(f);
+ assertFalse(S_ISSOCK(Os.fstat(fis.getFD()).st_mode));
+ fis.close();
+
+ ServerSocket s = new ServerSocket();
+ assertTrue(S_ISSOCK(Os.fstat(s.getImpl().getFD$()).st_mode));
+ s.close();
+ }
+
+ public void testFcntlInt() throws Exception {
+ File f = File.createTempFile("OsTest", "tst");
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(f);
+ Os.fcntlInt(fis.getFD(), F_SETFD, FD_CLOEXEC);
+ int flags = Os.fcntlVoid(fis.getFD(), F_GETFD);
+ assertTrue((flags & FD_CLOEXEC) != 0);
+ } finally {
+ TestIoUtils.closeQuietly(fis);
+ f.delete();
+ }
+ }
+
+ public void testUnixDomainSockets_in_file_system() throws Exception {
+ String path = System.getProperty("java.io.tmpdir") + "/test_unix_socket";
+ new File(path).delete();
+ checkUnixDomainSocket(UnixSocketAddress.createFileSystem(path), false);
+ }
+
+ public void testUnixDomainSocket_abstract_name() throws Exception {
+ // Linux treats a sun_path starting with a NUL byte as an abstract name. See unix(7).
+ checkUnixDomainSocket(UnixSocketAddress.createAbstract("/abstract_name_unix_socket"), true);
+ }
+
+ public void testUnixDomainSocket_unnamed() throws Exception {
+ final FileDescriptor fd = Os.socket(AF_UNIX, SOCK_STREAM, 0);
+ // unix(7) says an unbound socket is unnamed.
+ checkNoSockName(fd);
+ Os.close(fd);
+ }
+
+ private void checkUnixDomainSocket(final UnixSocketAddress address, final boolean isAbstract)
+ throws Exception {
+ final FileDescriptor serverFd = Os.socket(AF_UNIX, SOCK_STREAM, 0);
+ Os.bind(serverFd, address);
+ Os.listen(serverFd, 5);
+
+ checkSockName(serverFd, isAbstract, address);
+
+ Thread server = new Thread(new Runnable() {
+ public void run() {
+ try {
+ UnixSocketAddress peerAddress = UnixSocketAddress.createUnnamed();
+ FileDescriptor clientFd = Os.accept(serverFd, peerAddress);
+ checkSockName(clientFd, isAbstract, address);
+ checkNoName(peerAddress);
+
+ checkNoPeerName(clientFd);
+
+ StructUcred credentials = Os.getsockoptUcred(clientFd, SOL_SOCKET, SO_PEERCRED);
+ assertEquals(Os.getpid(), credentials.pid);
+ assertEquals(Os.getuid(), credentials.uid);
+ assertEquals(Os.getgid(), credentials.gid);
+
+ byte[] request = new byte[256];
+ Os.read(clientFd, request, 0, request.length);
+
+ String s = new String(request, "UTF-8");
+ byte[] response = s.toUpperCase(Locale.ROOT).getBytes("UTF-8");
+ Os.write(clientFd, response, 0, response.length);
+
+ Os.close(clientFd);
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ });
+ server.start();
+
+ FileDescriptor clientFd = Os.socket(AF_UNIX, SOCK_STREAM, 0);
+
+ Os.connect(clientFd, address);
+ checkNoSockName(clientFd);
+
+ String string = "hello, world!";
+
+ byte[] request = string.getBytes("UTF-8");
+ assertEquals(request.length, Os.write(clientFd, request, 0, request.length));
+
+ byte[] response = new byte[request.length];
+ assertEquals(response.length, Os.read(clientFd, response, 0, response.length));
+
+ assertEquals(string.toUpperCase(Locale.ROOT), new String(response, "UTF-8"));
+
+ Os.close(clientFd);
+ }
+
+ private static void checkSockName(FileDescriptor fd, boolean isAbstract,
+ UnixSocketAddress address) throws Exception {
+ UnixSocketAddress isa = (UnixSocketAddress) Os.getsockname(fd);
+ assertEquals(address, isa);
+ if (isAbstract) {
+ assertEquals(0, isa.getSunPath()[0]);
+ }
+ }
+
+ private void checkNoName(UnixSocketAddress usa) {
+ assertEquals(0, usa.getSunPath().length);
+ }
+
+ private void checkNoPeerName(FileDescriptor fd) throws Exception {
+ checkNoName((UnixSocketAddress) Os.getpeername(fd));
+ }
+
+ private void checkNoSockName(FileDescriptor fd) throws Exception {
+ checkNoName((UnixSocketAddress) Os.getsockname(fd));
+ }
+
+ public void test_strsignal() throws Exception {
+ assertEquals("Killed", Os.strsignal(9));
+ assertEquals("Unknown signal -1", Os.strsignal(-1));
+ }
+
+ public void test_byteBufferPositions_write_pwrite() throws Exception {
+ FileOutputStream fos = new FileOutputStream(new File("/dev/null"));
+ FileDescriptor fd = fos.getFD();
+ final byte[] contents = new String("goodbye, cruel world")
+ .getBytes(StandardCharsets.US_ASCII);
+ ByteBuffer byteBuffer = ByteBuffer.wrap(contents);
+
+ byteBuffer.position(0);
+ int written = Os.write(fd, byteBuffer);
+ assertTrue(written > 0);
+ assertEquals(written, byteBuffer.position());
+
+ byteBuffer.position(4);
+ written = Os.write(fd, byteBuffer);
+ assertTrue(written > 0);
+ assertEquals(written + 4, byteBuffer.position());
+
+ byteBuffer.position(0);
+ written = Os.pwrite(fd, byteBuffer, 64 /* offset */);
+ assertTrue(written > 0);
+ assertEquals(written, byteBuffer.position());
+
+ byteBuffer.position(4);
+ written = Os.pwrite(fd, byteBuffer, 64 /* offset */);
+ assertTrue(written > 0);
+ assertEquals(written + 4, byteBuffer.position());
+
+ fos.close();
+ }
+
+ public void test_byteBufferPositions_read_pread() throws Exception {
+ FileInputStream fis = new FileInputStream(new File("/dev/zero"));
+ FileDescriptor fd = fis.getFD();
+ ByteBuffer byteBuffer = ByteBuffer.allocate(64);
+
+ byteBuffer.position(0);
+ int read = Os.read(fd, byteBuffer);
+ assertTrue(read > 0);
+ assertEquals(read, byteBuffer.position());
+
+ byteBuffer.position(4);
+ read = Os.read(fd, byteBuffer);
+ assertTrue(read > 0);
+ assertEquals(read + 4, byteBuffer.position());
+
+ byteBuffer.position(0);
+ read = Os.pread(fd, byteBuffer, 64 /* offset */);
+ assertTrue(read > 0);
+ assertEquals(read, byteBuffer.position());
+
+ byteBuffer.position(4);
+ read = Os.pread(fd, byteBuffer, 64 /* offset */);
+ assertTrue(read > 0);
+ assertEquals(read + 4, byteBuffer.position());
+
+ fis.close();
+ }
+
+ static void checkByteBufferPositions_sendto_recvfrom(
+ int family, InetAddress loopback) throws Exception {
+ final FileDescriptor serverFd = Os.socket(family, SOCK_STREAM, 0);
+ Os.bind(serverFd, loopback, 0);
+ Os.listen(serverFd, 5);
+
+ InetSocketAddress address = (InetSocketAddress) Os.getsockname(serverFd);
+
+ final Thread server = new Thread(new Runnable() {
+ public void run() {
+ try {
+ InetSocketAddress peerAddress = new InetSocketAddress();
+ FileDescriptor clientFd = Os.accept(serverFd, peerAddress);
+
+ // Attempt to receive a maximum of 24 bytes from the client, and then
+ // close the connection.
+ ByteBuffer buffer = ByteBuffer.allocate(16);
+ int received = Os.recvfrom(clientFd, buffer, 0, null);
+ assertTrue(received > 0);
+ assertEquals(received, buffer.position());
+
+ ByteBuffer buffer2 = ByteBuffer.allocate(16);
+ buffer2.position(8);
+ received = Os.recvfrom(clientFd, buffer2, 0, null);
+ assertTrue(received > 0);
+ assertEquals(received + 8, buffer.position());
+
+ Os.close(clientFd);
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+ });
+
+ server.start();
+
+ FileDescriptor clientFd = Os.socket(family, SOCK_STREAM, 0);
+ Os.connect(clientFd, address.getAddress(), address.getPort());
+
+ final byte[] bytes = "good bye, cruel black hole with fancy distortion"
+ .getBytes(StandardCharsets.US_ASCII);
+ assertTrue(bytes.length > 24);
+
+ ByteBuffer input = ByteBuffer.wrap(bytes);
+ input.position(0);
+ input.limit(16);
+
+ int sent = Os.sendto(clientFd, input, 0, address.getAddress(), address.getPort());
+ assertTrue(sent > 0);
+ assertEquals(sent, input.position());
+
+ input.position(16);
+ input.limit(24);
+ sent = Os.sendto(clientFd, input, 0, address.getAddress(), address.getPort());
+ assertTrue(sent > 0);
+ assertEquals(sent + 16, input.position());
+
+ Os.close(clientFd);
+ }
+
+ interface ExceptionalRunnable {
+
+ public void run() throws Exception;
+ }
+
+ /**
+ * Expects that the given Runnable will throw an exception of the specified class. If the class
+ * is ErrnoException, and expectedErrno is non-null, also checks that the errno is equal to
+ * expectedErrno.
+ */
+ private static void expectException(ExceptionalRunnable r, Class<? extends Exception> exClass,
+ Integer expectedErrno, String msg) {
+ try {
+ r.run();
+ fail(msg + " did not throw exception");
+ } catch (Exception e) {
+ assertEquals(msg + " threw unexpected exception", exClass, e.getClass());
+
+ if (expectedErrno != null) {
+ if (e instanceof ErrnoException) {
+ assertEquals(msg + "threw ErrnoException with unexpected error number",
+ (int) expectedErrno, ((ErrnoException) e).errno);
+ } else {
+ fail("Can only pass expectedErrno when expecting ErrnoException");
+ }
+ }
+
+ }
+ }
+
+ private static void expectBindException(FileDescriptor socket, SocketAddress addr,
+ Class exClass, Integer expectedErrno) {
+ String msg = String.format("bind(%s, %s)", socket, addr);
+ expectException(() -> {
+ Os.bind(socket, addr);
+ }, exClass, expectedErrno, msg);
+ }
+
+ private static void expectConnectException(FileDescriptor socket, SocketAddress addr,
+ Class exClass, Integer expectedErrno) {
+ String msg = String.format("connect(%s, %s)", socket, addr);
+ expectException(() -> {
+ Os.connect(socket, addr);
+ }, exClass, expectedErrno, msg);
+ }
+
+ private static void expectSendtoException(FileDescriptor socket, SocketAddress addr,
+ Class exClass, Integer expectedErrno) {
+ String msg = String.format("sendto(%s, %s)", socket, addr);
+ byte[] packet = new byte[42];
+ expectException(() -> {
+ Os.sendto(socket, packet, 0, packet.length, 0, addr);
+ },
+ exClass, expectedErrno, msg);
+ }
+
+ private static void expectBindConnectSendtoSuccess(FileDescriptor socket, String socketDesc,
+ SocketAddress addr) {
+ String msg = socketDesc + " socket to " + addr.toString();
+
+ try {
+ try {
+ // Expect that bind throws when any of its arguments are null.
+ expectBindException(null, addr, ErrnoException.class, EBADF);
+ expectBindException(socket, null, NullPointerException.class, null);
+ expectBindException(null, null, NullPointerException.class, null);
+
+ // Expect bind to succeed.
+ Os.bind(socket, addr);
+
+ // Find out which port we're actually bound to, and use that in subsequent connect()
+ // and send() calls. We can't send to addr because that has a port of 0.
+ if (addr instanceof InetSocketAddress) {
+ InetSocketAddress addrISA = (InetSocketAddress) addr;
+ InetSocketAddress socknameISA = (InetSocketAddress) Os.getsockname(socket);
+
+ assertEquals(addrISA.getAddress(), socknameISA.getAddress());
+ assertEquals(0, addrISA.getPort());
+ assertFalse(0 == socknameISA.getPort());
+ addr = socknameISA;
+ }
+
+ // Expect sendto with a null address to throw because the socket is not connected,
+ // but to succeed with a non-null address.
+ byte[] packet = new byte[42];
+ Os.sendto(socket, packet, 0, packet.length, 0, addr);
+ // UNIX and IP sockets return different errors for this operation, so we can't check
+ // errno.
+ expectSendtoException(socket, null, ErrnoException.class, null);
+ expectSendtoException(null, null, ErrnoException.class, EBADF);
+
+ // Expect that connect throws when any of its arguments are null.
+ expectConnectException(null, addr, ErrnoException.class, EBADF);
+ expectConnectException(socket, null, NullPointerException.class, null);
+ expectConnectException(null, null, NullPointerException.class, null);
+
+ // Expect connect to succeed.
+ Os.connect(socket, addr);
+ assertEquals(Os.getsockname(socket), Os.getpeername(socket));
+
+ // Expect sendto to succeed both when given an explicit address and a null address.
+ Os.sendto(socket, packet, 0, packet.length, 0, addr);
+ Os.sendto(socket, packet, 0, packet.length, 0, null);
+ } catch (SocketException | ErrnoException e) {
+ fail("Expected success for " + msg + ", but got: " + e);
+ }
+
+ } finally {
+ IoUtils.closeQuietly(socket);
+ }
+ }
+
+ private static void expectBindConnectSendtoErrno(int bindErrno, int connectErrno,
+ int sendtoErrno, FileDescriptor socket, String socketDesc, SocketAddress addr) {
+ try {
+
+ // Expect bind to fail with bindErrno.
+ String msg = "bind " + socketDesc + " socket to " + addr.toString();
+ try {
+ Os.bind(socket, addr);
+ fail("Expected to fail " + msg);
+ } catch (ErrnoException e) {
+ assertEquals("Expected errno " + bindErrno + " " + msg, bindErrno, e.errno);
+ } catch (SocketException e) {
+ fail("Unexpected SocketException " + msg);
+ }
+
+ // Expect connect to fail with connectErrno.
+ msg = "connect " + socketDesc + " socket to " + addr.toString();
+ try {
+ Os.connect(socket, addr);
+ fail("Expected to fail " + msg);
+ } catch (ErrnoException e) {
+ assertEquals("Expected errno " + connectErrno + " " + msg, connectErrno, e.errno);
+ } catch (SocketException e) {
+ fail("Unexpected SocketException " + msg);
+ }
+
+ // Expect sendto to fail with sendtoErrno.
+ byte[] packet = new byte[42];
+ msg = "sendto " + socketDesc + " socket to " + addr.toString();
+ try {
+ Os.sendto(socket, packet, 0, packet.length, 0, addr);
+ fail("Expected to fail " + msg);
+ } catch (ErrnoException e) {
+ assertEquals("Expected errno " + sendtoErrno + " " + msg, sendtoErrno, e.errno);
+ } catch (SocketException e) {
+ fail("Unexpected SocketException " + msg);
+ }
+
+ } finally {
+ // No matter what happened, close the socket.
+ IoUtils.closeQuietly(socket);
+ }
+ }
+
+ private FileDescriptor makeIpv4Socket() throws Exception {
+ return Os.socket(AF_INET, SOCK_DGRAM, 0);
+ }
+
+ private FileDescriptor makeIpv6Socket() throws Exception {
+ return Os.socket(AF_INET6, SOCK_DGRAM, 0);
+ }
+
+ private FileDescriptor makeUnixSocket() throws Exception {
+ return Os.socket(AF_UNIX, SOCK_DGRAM, 0);
+ }
+
+ public void testCrossFamilyBindConnectSendto() throws Exception {
+ SocketAddress addrIpv4 = new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 0);
+ SocketAddress addrIpv6 = new InetSocketAddress(InetAddress.getByName("::1"), 0);
+ SocketAddress addrUnix = UnixSocketAddress.createAbstract("/abstract_name_unix_socket");
+
+ expectBindConnectSendtoSuccess(makeIpv4Socket(), "ipv4", addrIpv4);
+ expectBindConnectSendtoErrno(EAFNOSUPPORT, EAFNOSUPPORT, EAFNOSUPPORT,
+ makeIpv4Socket(), "ipv4", addrIpv6);
+ expectBindConnectSendtoErrno(EAFNOSUPPORT, EAFNOSUPPORT, EAFNOSUPPORT,
+ makeIpv4Socket(), "ipv4", addrUnix);
+
+ // This succeeds because Java always uses dual-stack sockets and all InetAddress and
+ // InetSocketAddress objects represent IPv4 addresses using IPv4-mapped IPv6 addresses.
+ expectBindConnectSendtoSuccess(makeIpv6Socket(), "ipv6", addrIpv4);
+ expectBindConnectSendtoSuccess(makeIpv6Socket(), "ipv6", addrIpv6);
+ expectBindConnectSendtoErrno(EAFNOSUPPORT, EAFNOSUPPORT, EINVAL,
+ makeIpv6Socket(), "ipv6", addrUnix);
+
+ expectBindConnectSendtoErrno(EINVAL, EINVAL, EINVAL,
+ makeUnixSocket(), "unix", addrIpv4);
+ expectBindConnectSendtoErrno(EINVAL, EINVAL, EINVAL,
+ makeUnixSocket(), "unix", addrIpv6);
+ expectBindConnectSendtoSuccess(makeUnixSocket(), "unix", addrUnix);
+ }
+
+ public void testUnknownSocketAddressSubclass() throws Exception {
+ class MySocketAddress extends SocketAddress {
+
+ }
+ MySocketAddress myaddr = new MySocketAddress();
+
+ for (int family : new int[] { AF_INET, AF_INET6, AF_NETLINK }) {
+ FileDescriptor s = Os.socket(family, SOCK_DGRAM, 0);
+ try {
+
+ try {
+ Os.bind(s, myaddr);
+ fail("bind socket family " + family
+ + " to unknown SocketAddress subclass succeeded");
+ } catch (UnsupportedOperationException expected) {
+ }
+
+ try {
+ Os.connect(s, myaddr);
+ fail("connect socket family " + family
+ + " to unknown SocketAddress subclass succeeded");
+ } catch (UnsupportedOperationException expected) {
+ }
+
+ byte[] msg = new byte[42];
+ try {
+ Os.sendto(s, msg, 0, msg.length, 0, myaddr);
+ fail("sendto socket family " + family
+ + " to unknown SocketAddress subclass succeeded");
+ } catch (UnsupportedOperationException expected) {
+ }
+
+ } finally {
+ Os.close(s);
+ }
+ }
+ }
+
+ public void test_NetlinkSocket() throws Exception {
+ FileDescriptor nlSocket = Os.socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ Os.bind(nlSocket, new NetlinkSocketAddress());
+ NetlinkSocketAddress address = (NetlinkSocketAddress) Os.getsockname(nlSocket);
+ assertTrue(address.getPortId() > 0);
+ assertEquals(0, address.getGroupsMask());
+
+ NetlinkSocketAddress nlKernel = new NetlinkSocketAddress();
+ Os.connect(nlSocket, nlKernel);
+ NetlinkSocketAddress nlPeer = (NetlinkSocketAddress) Os.getpeername(nlSocket);
+ assertEquals(0, nlPeer.getPortId());
+ assertEquals(0, nlPeer.getGroupsMask());
+ Os.close(nlSocket);
+ }
+
+ public void test_PacketSocketAddress() throws Exception {
+ NetworkInterface lo = NetworkInterface.getByName("lo");
+ FileDescriptor fd = Os.socket(AF_PACKET, SOCK_DGRAM, ETH_P_IPV6);
+ PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IPV6, lo.getIndex());
+ Os.bind(fd, addr);
+
+ PacketSocketAddress bound = (PacketSocketAddress) Os.getsockname(fd);
+ assertEquals((short) ETH_P_IPV6, bound.sll_protocol); // ETH_P_IPV6 is an int.
+ assertEquals(lo.getIndex(), bound.sll_ifindex);
+ assertEquals(ARPHRD_LOOPBACK, bound.sll_hatype);
+ assertEquals(0, bound.sll_pkttype);
+
+ // The loopback address is ETH_ALEN bytes long and is all zeros.
+ // http://lxr.free-electrons.com/source/drivers/net/loopback.c?v=3.10#L167
+ assertEquals(6, bound.sll_addr.length);
+ for (int i = 0; i < 6; i++) {
+ assertEquals(0, bound.sll_addr[i]);
+ }
+ }
+
+ public void test_byteBufferPositions_sendto_recvfrom_af_inet() throws Exception {
+ checkByteBufferPositions_sendto_recvfrom(AF_INET, InetAddress.getByName("127.0.0.1"));
+ }
+
+ public void test_byteBufferPositions_sendto_recvfrom_af_inet6() throws Exception {
+ checkByteBufferPositions_sendto_recvfrom(AF_INET6, InetAddress.getByName("::1"));
+ }
+
+ private void checkSendToSocketAddress(int family, InetAddress loopback) throws Exception {
+ FileDescriptor recvFd = Os.socket(family, SOCK_DGRAM, 0);
+ Os.bind(recvFd, loopback, 0);
+ StructTimeval tv = StructTimeval.fromMillis(20);
+ Os.setsockoptTimeval(recvFd, SOL_SOCKET, SO_RCVTIMEO, tv);
+
+ InetSocketAddress to = ((InetSocketAddress) Os.getsockname(recvFd));
+ FileDescriptor sendFd = Os.socket(family, SOCK_DGRAM, 0);
+ byte[] msg = ("Hello, I'm going to a socket address: " + to.toString()).getBytes("UTF-8");
+ int len = msg.length;
+
+ assertEquals(len, Os.sendto(sendFd, msg, 0, len, 0, to));
+ byte[] received = new byte[msg.length + 42];
+ InetSocketAddress from = new InetSocketAddress();
+ assertEquals(len, Os.recvfrom(recvFd, received, 0, received.length, 0, from));
+ assertEquals(loopback, from.getAddress());
+ }
+
+ public void test_sendtoSocketAddress_af_inet() throws Exception {
+ checkSendToSocketAddress(AF_INET, InetAddress.getByName("127.0.0.1"));
+ }
+
+ public void test_sendtoSocketAddress_af_inet6() throws Exception {
+ checkSendToSocketAddress(AF_INET6, InetAddress.getByName("::1"));
+ }
+
+ public void test_socketFamilies() throws Exception {
+ FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
+ Os.bind(fd, InetAddress.getByName("::"), 0);
+ InetSocketAddress localSocketAddress = (InetSocketAddress) Os.getsockname(fd);
+ assertEquals(Inet6Address.ANY, localSocketAddress.getAddress());
+
+ fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
+ Os.bind(fd, InetAddress.getByName("0.0.0.0"), 0);
+ localSocketAddress = (InetSocketAddress) Os.getsockname(fd);
+ assertEquals(Inet6Address.ANY, localSocketAddress.getAddress());
+
+ fd = Os.socket(AF_INET, SOCK_STREAM, 0);
+ Os.bind(fd, InetAddress.getByName("0.0.0.0"), 0);
+ localSocketAddress = (InetSocketAddress) Os.getsockname(fd);
+ assertEquals(Inet4Address.ANY, localSocketAddress.getAddress());
+ try {
+ Os.bind(fd, InetAddress.getByName("::"), 0);
+ fail("Expected ErrnoException binding IPv4 socket to ::");
+ } catch (ErrnoException expected) {
+ assertEquals("Expected EAFNOSUPPORT binding IPv4 socket to ::", EAFNOSUPPORT,
+ expected.errno);
+ }
+ }
+
+ private static void assertArrayEquals(byte[] expected, byte[] actual) {
+ assertTrue("Expected=" + Arrays.toString(expected) + ", actual=" + Arrays.toString(actual),
+ Arrays.equals(expected, actual));
+ }
+
+ private static void checkSocketPing(FileDescriptor fd, InetAddress to, byte[] packet,
+ byte type, byte responseType, boolean useSendto) throws Exception {
+ int len = packet.length;
+ packet[0] = type;
+ if (useSendto) {
+ assertEquals(len, Os.sendto(fd, packet, 0, len, 0, to, 0));
+ } else {
+ Os.connect(fd, to, 0);
+ assertEquals(len, Os.sendto(fd, packet, 0, len, 0, null, 0));
+ }
+
+ int icmpId = ((InetSocketAddress) Os.getsockname(fd)).getPort();
+ byte[] received = new byte[4096];
+ InetSocketAddress srcAddress = new InetSocketAddress();
+ assertEquals(len, Os.recvfrom(fd, received, 0, received.length, 0, srcAddress));
+ assertEquals(to, srcAddress.getAddress());
+ assertEquals(responseType, received[0]);
+ assertEquals(received[4], (byte) (icmpId >> 8));
+ assertEquals(received[5], (byte) (icmpId & 0xff));
+
+ received = Arrays.copyOf(received, len);
+ received[0] = (byte) type;
+ received[2] = received[3] = 0; // Checksum.
+ received[4] = received[5] = 0; // ICMP ID.
+ assertArrayEquals(packet, received);
+ }
+
+ public void test_socketPing() throws Exception {
+ final byte ICMP_ECHO = 8, ICMP_ECHOREPLY = 0;
+ final byte ICMPV6_ECHO_REQUEST = (byte) 128, ICMPV6_ECHO_REPLY = (byte) 129;
+ final byte[] packet = ("\000\000\000\000" + // ICMP type, code.
+ "\000\000\000\003" + // ICMP ID (== port), sequence number.
+ "Hello myself").getBytes(StandardCharsets.US_ASCII);
+
+ FileDescriptor fd = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
+ InetAddress ipv6Loopback = InetAddress.getByName("::1");
+ checkSocketPing(fd, ipv6Loopback, packet, ICMPV6_ECHO_REQUEST, ICMPV6_ECHO_REPLY, true);
+ checkSocketPing(fd, ipv6Loopback, packet, ICMPV6_ECHO_REQUEST, ICMPV6_ECHO_REPLY, false);
+
+ fd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
+ InetAddress ipv4Loopback = InetAddress.getByName("127.0.0.1");
+ checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, true);
+ checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, false);
+ }
+
+ public void test_Ipv4Fallback() throws Exception {
+ // This number of iterations gives a ~60% chance of creating the conditions that caused
+ // http://b/23088314 without making test times too long. On a hammerhead running MRZ37C
+ // using vogar, this test takes about 4s.
+ final int ITERATIONS = 10000;
+ for (int i = 0; i < ITERATIONS; i++) {
+ FileDescriptor mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ try {
+ Os.bind(mUdpSock, Inet4Address.ANY, 0);
+ } catch (ErrnoException e) {
+ fail("ErrnoException after " + i + " iterations: " + e);
+ } finally {
+ Os.close(mUdpSock);
+ }
+ }
+ }
+
+ public void test_unlink() throws Exception {
+ File f = File.createTempFile("OsTest", "tst");
+ assertTrue(f.exists());
+ Os.unlink(f.getAbsolutePath());
+ assertFalse(f.exists());
+
+ try {
+ Os.unlink(f.getAbsolutePath());
+ fail();
+ } catch (ErrnoException e) {
+ assertEquals(OsConstants.ENOENT, e.errno);
+ }
+ }
+
+ // b/27294715
+ public void test_recvfrom_concurrentShutdown() throws Exception {
+ final FileDescriptor serverFd = Os.socket(AF_INET, SOCK_DGRAM, 0);
+ Os.bind(serverFd, InetAddress.getByName("127.0.0.1"), 0);
+ // Set 4s timeout
+ StructTimeval tv = StructTimeval.fromMillis(4000);
+ Os.setsockoptTimeval(serverFd, SOL_SOCKET, SO_RCVTIMEO, tv);
+
+ final AtomicReference<Exception> killerThreadException = new AtomicReference<Exception>(
+ null);
+ final Thread killer = new Thread(new Runnable() {
+ public void run() {
+ try {
+ Thread.sleep(2000);
+ try {
+ Os.shutdown(serverFd, SHUT_RDWR);
+ } catch (ErrnoException expected) {
+ if (OsConstants.ENOTCONN != expected.errno) {
+ killerThreadException.set(expected);
+ }
+ }
+ } catch (Exception ex) {
+ killerThreadException.set(ex);
+ }
+ }
+ });
+ killer.start();
+
+ ByteBuffer buffer = ByteBuffer.allocate(16);
+ InetSocketAddress srcAddress = new InetSocketAddress();
+ int received = Os.recvfrom(serverFd, buffer, 0, srcAddress);
+ assertTrue(received == 0);
+ Os.close(serverFd);
+
+ killer.join();
+ assertNull(killerThreadException.get());
+ }
+
+ public void test_xattr() throws Exception {
+ final String NAME_TEST = "user.meow";
+
+ final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8);
+ final byte[] VALUE_PIE = "pie".getBytes(StandardCharsets.UTF_8);
+
+ File file = File.createTempFile("xattr", "test");
+ String path = file.getAbsolutePath();
+
+ try {
+ try {
+ Os.getxattr(path, NAME_TEST);
+ fail("Expected ENODATA");
+ } catch (ErrnoException e) {
+ assertEquals(OsConstants.ENODATA, e.errno);
+ }
+ assertFalse(Arrays.asList(Os.listxattr(path)).contains(NAME_TEST));
+
+ Os.setxattr(path, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
+ byte[] xattr_create = Os.getxattr(path, NAME_TEST);
+ assertTrue(Arrays.asList(Os.listxattr(path)).contains(NAME_TEST));
+ assertEquals(VALUE_CAKE.length, xattr_create.length);
+ assertStartsWith(VALUE_CAKE, xattr_create);
+
+ try {
+ Os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_CREATE);
+ fail("Expected EEXIST");
+ } catch (ErrnoException e) {
+ assertEquals(OsConstants.EEXIST, e.errno);
+ }
+
+ Os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_REPLACE);
+ byte[] xattr_replace = Os.getxattr(path, NAME_TEST);
+ assertTrue(Arrays.asList(Os.listxattr(path)).contains(NAME_TEST));
+ assertEquals(VALUE_PIE.length, xattr_replace.length);
+ assertStartsWith(VALUE_PIE, xattr_replace);
+
+ Os.removexattr(path, NAME_TEST);
+ try {
+ Os.getxattr(path, NAME_TEST);
+ fail("Expected ENODATA");
+ } catch (ErrnoException e) {
+ assertEquals(OsConstants.ENODATA, e.errno);
+ }
+ assertFalse(Arrays.asList(Os.listxattr(path)).contains(NAME_TEST));
+
+ } finally {
+ file.delete();
+ }
+ }
+
+ public void test_xattr_NPE() throws Exception {
+ File file = File.createTempFile("xattr", "test");
+ final String path = file.getAbsolutePath();
+ final String NAME_TEST = "user.meow";
+ final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8);
+
+ // getxattr
+ try {
+ Os.getxattr(null, NAME_TEST);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ try {
+ Os.getxattr(path, null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ // listxattr
+ try {
+ Os.listxattr(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ // removexattr
+ try {
+ Os.removexattr(null, NAME_TEST);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ try {
+ Os.removexattr(path, null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+
+ // setxattr
+ try {
+ Os.setxattr(null, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ try {
+ Os.setxattr(path, null, VALUE_CAKE, OsConstants.XATTR_CREATE);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ try {
+ Os.setxattr(path, NAME_TEST, null, OsConstants.XATTR_CREATE);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ public void test_xattr_Errno() throws Exception {
+ final String NAME_TEST = "user.meow";
+ final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8);
+
+ // ENOENT, No such file or directory.
+ try {
+ Os.getxattr("", NAME_TEST);
+ fail();
+ } catch (ErrnoException e) {
+ assertEquals(ENOENT, e.errno);
+ }
+ try {
+ Os.listxattr("");
+ fail();
+ } catch (ErrnoException e) {
+ assertEquals(ENOENT, e.errno);
+ }
+ try {
+ Os.removexattr("", NAME_TEST);
+ fail();
+ } catch (ErrnoException e) {
+ assertEquals(ENOENT, e.errno);
+ }
+ try {
+ Os.setxattr("", NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
+ fail();
+ } catch (ErrnoException e) {
+ assertEquals(ENOENT, e.errno);
+ }
+
+ // ENOTSUP, Extended attributes are not supported by the filesystem, or are disabled.
+ // Since kernel version 4.9 (or some other version after 4.4), *xattr() methods
+ // may set errno to EACCESS instead. This behavior change is likely related to
+ // https://patchwork.kernel.org/patch/9294421/ which reimplemented getxattr, setxattr,
+ // and removexattr on top of generic handlers.
+ final String path = "/proc/self/stat";
+ try {
+ Os.setxattr(path, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
+ fail();
+ } catch (ErrnoException e) {
+ assertTrue("Unexpected errno: " + e.errno, e.errno == ENOTSUP || e.errno == EACCES);
+ }
+ try {
+ Os.getxattr(path, NAME_TEST);
+ fail();
+ } catch (ErrnoException e) {
+ assertEquals(ENOTSUP, e.errno);
+ }
+ try {
+ // Linux listxattr does not set errno.
+ Os.listxattr(path);
+ } catch (ErrnoException e) {
+ fail();
+ }
+ try {
+ Os.removexattr(path, NAME_TEST);
+ fail();
+ } catch (ErrnoException e) {
+ assertTrue("Unexpected errno: " + e.errno, e.errno == ENOTSUP || e.errno == EACCES);
+ }
+ }
+
+ public void test_realpath() throws Exception {
+ File tmpDir = new File(System.getProperty("java.io.tmpdir"));
+ // This is a chicken and egg problem. We have no way of knowing whether
+ // the temporary directory or one of its path elements were symlinked, so
+ // we'll need this call to realpath.
+ String canonicalTmpDir = Os.realpath(tmpDir.getAbsolutePath());
+
+ // Test that "." and ".." are resolved correctly.
+ assertEquals(canonicalTmpDir,
+ Os.realpath(canonicalTmpDir + "/./../" + tmpDir.getName()));
+
+ // Test that symlinks are resolved correctly.
+ File target = new File(tmpDir, "target");
+ File link = new File(tmpDir, "link");
+ try {
+ assertTrue(target.createNewFile());
+ Os.symlink(target.getAbsolutePath(), link.getAbsolutePath());
+
+ assertEquals(canonicalTmpDir + "/target",
+ Os.realpath(canonicalTmpDir + "/link"));
+ } finally {
+ boolean deletedTarget = target.delete();
+ boolean deletedLink = link.delete();
+ // Asserting this here to provide a definitive reason for
+ // a subsequent failure on the same run.
+ assertTrue("deletedTarget = " + deletedTarget + ", deletedLink =" + deletedLink,
+ deletedTarget && deletedLink);
+ }
+ }
+
+ /**
+ * Tests that TCP_USER_TIMEOUT can be set on a TCP socket, but doesn't test
+ * that it behaves as expected.
+ */
+ public void test_socket_tcpUserTimeout_setAndGet() throws Exception {
+ final FileDescriptor fd = Os.socket(AF_INET, SOCK_STREAM, 0);
+ try {
+ int v = Os.getsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_USER_TIMEOUT);
+ assertEquals(0, v); // system default value
+ int newValue = 3000;
+ Os.setsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_USER_TIMEOUT,
+ newValue);
+ int actualValue = Os.getsockoptInt(fd, OsConstants.IPPROTO_TCP,
+ OsConstants.TCP_USER_TIMEOUT);
+ // The kernel can round the requested value based on the HZ setting. We allow up to 10ms
+ // difference.
+ assertTrue("Returned incorrect timeout:" + actualValue,
+ Math.abs(newValue - actualValue) <= 10);
+ // No need to reset the value to 0, since we're throwing the socket away
+ } finally {
+ Os.close(fd);
+ }
+ }
+
+ public void test_socket_tcpUserTimeout_doesNotWorkOnDatagramSocket() throws Exception {
+ final FileDescriptor fd = Os.socket(AF_INET, SOCK_DGRAM, 0);
+ try {
+ Os.setsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_USER_TIMEOUT,
+ 3000);
+ fail("datagram (connectionless) sockets shouldn't support TCP_USER_TIMEOUT");
+ } catch (ErrnoException expected) {
+ // expected
+ } finally {
+ Os.close(fd);
+ }
+ }
+
+ public void test_socket_sockoptTimeval_readWrite() throws Exception {
+ FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
+ try {
+ StructTimeval v = Os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO);
+ assertEquals(0, v.toMillis()); // system default value
+
+ StructTimeval newValue = StructTimeval.fromMillis(3000);
+ Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, newValue);
+
+ StructTimeval actualValue = Os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO);
+
+ // The kernel can round the requested value based on the HZ setting. We allow up to 10ms
+ // difference.
+ assertTrue("Returned incorrect timeout:" + actualValue,
+ Math.abs(newValue.toMillis() - actualValue.toMillis()) <= 10);
+ // No need to reset the value to 0, since we're throwing the socket away
+ } finally {
+ Os.close(fd);
+ }
+ }
+
+ public void test_socket_setSockoptTimeval_effective() throws Exception {
+ int timeoutValueMillis = 50;
+ int allowedTimeoutMillis = 500;
+
+ FileDescriptor fd = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ try {
+ StructTimeval tv = StructTimeval.fromMillis(timeoutValueMillis);
+ Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, tv);
+ Os.bind(fd, InetAddress.getByName("::1"), 0);
+
+ byte[] request = new byte[1];
+ long startTime = System.nanoTime();
+ expectException(() -> Os.read(fd, request, 0, request.length),
+ ErrnoException.class, EAGAIN, "Expected timeout");
+ long endTime = System.nanoTime();
+ assertTrue(Duration.ofNanos(endTime - startTime).toMillis() < allowedTimeoutMillis);
+ } finally {
+ Os.close(fd);
+ }
+ }
+
+ public void test_socket_setSockoptTimeval_nullFd() throws Exception {
+ StructTimeval tv = StructTimeval.fromMillis(500);
+ expectException(
+ () -> Os.setsockoptTimeval(null, SOL_SOCKET, SO_RCVTIMEO, tv),
+ ErrnoException.class, EBADF, "setsockoptTimeval(null, ...)");
+ }
+
+ public void test_socket_setSockoptTimeval_fileFd() throws Exception {
+ File testFile = createTempFile("test_socket_setSockoptTimeval_invalidFd", "");
+ try (FileInputStream fis = new FileInputStream(testFile)) {
+ final FileDescriptor fd = fis.getFD();
+
+ StructTimeval tv = StructTimeval.fromMillis(500);
+ expectException(
+ () -> Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, tv),
+ ErrnoException.class, ENOTSOCK, "setsockoptTimeval(<file fd>, ...)");
+ }
+ }
+
+ public void test_socket_setSockoptTimeval_badFd() throws Exception {
+ StructTimeval tv = StructTimeval.fromMillis(500);
+ FileDescriptor invalidFd = Os.socket(AF_INET6, SOCK_STREAM, 0);
+ Os.close(invalidFd);
+
+ expectException(
+ () -> Os.setsockoptTimeval(invalidFd, SOL_SOCKET, SO_RCVTIMEO, tv),
+ ErrnoException.class, EBADF, "setsockoptTimeval(<closed fd>, ...)");
+ }
+
+ public void test_socket_setSockoptTimeval_invalidLevel() throws Exception {
+ StructTimeval tv = StructTimeval.fromMillis(500);
+ FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
+ try {
+ expectException(
+ () -> Os.setsockoptTimeval(fd, -1, SO_RCVTIMEO, tv),
+ ErrnoException.class, ENOPROTOOPT,
+ "setsockoptTimeval(fd, <invalid level>, ...)");
+ } finally {
+ Os.close(fd);
+ }
+ }
+
+ public void test_socket_setSockoptTimeval_invalidOpt() throws Exception {
+ StructTimeval tv = StructTimeval.fromMillis(500);
+ FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
+ try {
+ expectException(
+ () -> Os.setsockoptTimeval(fd, SOL_SOCKET, -1, tv),
+ ErrnoException.class, ENOPROTOOPT,
+ "setsockoptTimeval(fd, <invalid level>, ...)");
+ } finally {
+ Os.close(fd);
+ }
+ }
+
+ public void test_socket_setSockoptTimeval_nullTimeVal() throws Exception {
+ FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
+ try {
+ expectException(
+ () -> Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, null),
+ NullPointerException.class, null, "setsockoptTimeval(..., null)");
+ } finally {
+ Os.close(fd);
+ }
+ }
+
+ public void test_socket_getSockoptTimeval_invalidOption() throws Exception {
+ FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
+ try {
+ expectException(
+ () -> Os.getsockoptTimeval(fd, SOL_SOCKET, SO_DEBUG),
+ IllegalArgumentException.class, null,
+ "getsockoptTimeval(..., <non-timeval option>)");
+ } finally {
+ Os.close(fd);
+ }
+ }
+
+ public void test_if_nametoindex_if_indextoname() throws Exception {
+ List<NetworkInterface> nis = Collections.list(NetworkInterface.getNetworkInterfaces());
+
+ assertTrue(nis.size() > 0);
+ for (NetworkInterface ni : nis) {
+ int index = ni.getIndex();
+ String name = ni.getName();
+ assertEquals(index, Os.if_nametoindex(name));
+ assertTrue(Os.if_indextoname(index).equals(name));
+ }
+
+ assertEquals(0, Os.if_nametoindex("this-interface-does-not-exist"));
+ assertEquals(null, Os.if_indextoname(-1000));
+
+ try {
+ Os.if_nametoindex(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ private static void assertStartsWith(byte[] expectedContents, byte[] container) {
+ for (int i = 0; i < expectedContents.length; i++) {
+ if (expectedContents[i] != container[i]) {
+ fail("Expected " + Arrays.toString(expectedContents) + " but found "
+ + Arrays.toString(expectedContents));
+ }
+ }
+ }
+
+ public void test_readlink() throws Exception {
+ File path = new File(TestIoUtils.createTemporaryDirectory("test_readlink"), "symlink");
+
+ // ext2 and ext4 have PAGE_SIZE limits on symlink targets.
+ // If file encryption is enabled, there's extra overhead to store the
+ // size of the encrypted symlink target. There's also an off-by-one
+ // in current kernels (and marlin/sailfish where we're seeing this
+ // failure are still on 3.18, far from current). Given that we don't
+ // really care here, just use 2048 instead. http://b/33306057.
+ int size = 2048;
+ String xs = "";
+ for (int i = 0; i < size - 1; ++i) {
+ xs += "x";
+ }
+
+ Os.symlink(xs, path.getPath());
+
+ assertEquals(xs, Os.readlink(path.getPath()));
+ }
+
+ // Address should be correctly set for empty packets. http://b/33481605
+ public void test_recvfrom_EmptyPacket() throws Exception {
+ try (DatagramSocket ds = new DatagramSocket();
+ DatagramSocket srcSock = new DatagramSocket()) {
+ srcSock.send(new DatagramPacket(new byte[0], 0, ds.getLocalSocketAddress()));
+
+ byte[] recvBuf = new byte[16];
+ InetSocketAddress address = new InetSocketAddress();
+ int recvCount =
+ android.system.Os.recvfrom(ds.getFileDescriptor$(), recvBuf, 0, 16, 0, address);
+ assertEquals(0, recvCount);
+ assertTrue(address.getAddress().isLoopbackAddress());
+ assertEquals(srcSock.getLocalPort(), address.getPort());
+ }
+ }
+
+ public void test_fstat_times() throws Exception {
+ File file = File.createTempFile("OsTest", "fstattest");
+ FileOutputStream fos = new FileOutputStream(file);
+ StructStat structStat1 = Os.fstat(fos.getFD());
+ assertEquals(structStat1.st_mtim.tv_sec, structStat1.st_mtime);
+ assertEquals(structStat1.st_ctim.tv_sec, structStat1.st_ctime);
+ assertEquals(structStat1.st_atim.tv_sec, structStat1.st_atime);
+ Thread.sleep(100);
+ fos.write(new byte[] { 1, 2, 3 });
+ fos.flush();
+ StructStat structStat2 = Os.fstat(fos.getFD());
+ fos.close();
+
+ assertEquals(-1, structStat1.st_mtim.compareTo(structStat2.st_mtim));
+ assertEquals(-1, structStat1.st_ctim.compareTo(structStat2.st_ctim));
+ assertEquals(0, structStat1.st_atim.compareTo(structStat2.st_atim));
+ }
+
+ public void test_getrlimit() throws Exception {
+ StructRlimit rlimit = Os.getrlimit(OsConstants.RLIMIT_NOFILE);
+ // We can't really make any assertions about these values since they might vary from
+ // device to device and even process to process. We do know that they will be greater
+ // than zero, though.
+ assertTrue(rlimit.rlim_cur > 0);
+ assertTrue(rlimit.rlim_max > 0);
+ }
+
+ // http://b/65051835
+ public void test_pipe2_errno() throws Exception {
+ try {
+ // flag=-1 is not a valid value for pip2, will EINVAL
+ Os.pipe2(-1);
+ fail();
+ } catch (ErrnoException expected) {
+ }
+ }
+
+ // http://b/65051835
+ public void test_sendfile_errno() throws Exception {
+ try {
+ // FileDescriptor.out is not open for input, will cause EBADF
+ Int64Ref offset = new Int64Ref(10);
+ Os.sendfile(FileDescriptor.out, FileDescriptor.out, offset, 10);
+ fail();
+ } catch (ErrnoException expected) {
+ }
+ }
+
+ public void test_sendfile_null() throws Exception {
+ File in = createTempFile("test_sendfile_null", "Hello, world!");
+ try {
+ int len = "Hello".length();
+ assertEquals("Hello", checkSendfile(in, null, len, null));
+ } finally {
+ in.delete();
+ }
+ }
+
+ public void test_sendfile_offset() throws Exception {
+ File in = createTempFile("test_sendfile_offset", "Hello, world!");
+ try {
+ // checkSendfile(sendFileImplToUse, in, startOffset, maxBytes, expectedEndOffset)
+ assertEquals("Hello", checkSendfile(in, 0L, 5, 5L));
+ assertEquals("ello,", checkSendfile(in, 1L, 5, 6L));
+ // At offset 9, only 4 bytes/chars available, even though we're asking for 5.
+ assertEquals("rld!", checkSendfile(in, 9L, 5, 13L));
+ assertEquals("", checkSendfile(in, 1L, 0, 1L));
+ } finally {
+ in.delete();
+ }
+ }
+
+ private static String checkSendfile(File in, Long startOffset,
+ int maxBytes, Long expectedEndOffset) throws IOException, ErrnoException {
+ File out = File.createTempFile(OsTest.class.getSimpleName() + "_checkSendFile", ".out");
+ try (FileInputStream inStream = new FileInputStream(in)) {
+ FileDescriptor inFd = inStream.getFD();
+ try (FileOutputStream outStream = new FileOutputStream(out)) {
+ FileDescriptor outFd = outStream.getFD();
+ Int64Ref offset = (startOffset == null) ? null : new Int64Ref(startOffset);
+ android.system.Os.sendfile(outFd, inFd, offset, maxBytes);
+ assertEquals(expectedEndOffset, offset == null ? null : offset.value);
+ }
+ return TestIoUtils.readFileAsString(out.getPath());
+ } finally {
+ out.delete();
+ }
+ }
+
+ private static File createTempFile(String namePart, String contents) throws IOException {
+ File f = File.createTempFile(OsTest.class.getSimpleName() + namePart, ".in");
+ try (FileWriter writer = new FileWriter(f)) {
+ writer.write(contents);
+ }
+ return f;
+ }
+
+ public void test_odirect() throws Exception {
+ File testFile = createTempFile("test_odirect", "");
+ try {
+ FileDescriptor fd =
+ Os.open(testFile.toString(), O_WRONLY | O_DIRECT, S_IRUSR | S_IWUSR);
+ assertNotNull(fd);
+ assertTrue(fd.valid());
+ int flags = Os.fcntlVoid(fd, F_GETFL);
+ assertTrue("Expected file flags to include " + O_DIRECT + ", actual value: " + flags,
+ 0 != (flags & O_DIRECT));
+ Os.close(fd);
+ } finally {
+ testFile.delete();
+ }
+ }
+
+ public void test_splice() throws Exception {
+ FileDescriptor[] pipe = Os.pipe2(0);
+ File in = createTempFile("splice1", "foobar");
+ File out = createTempFile("splice2", "");
+
+ Int64Ref offIn = new Int64Ref(1);
+ Int64Ref offOut = new Int64Ref(0);
+
+ // Splice into pipe
+ try (FileInputStream streamIn = new FileInputStream(in)) {
+ FileDescriptor fdIn = streamIn.getFD();
+ long result = Os
+ .splice(fdIn, offIn, pipe[1], null /* offOut */, 10 /* len */, 0 /* flags */);
+ assertEquals(5, result);
+ assertEquals(6, offIn.value);
+ }
+
+ // Splice from pipe
+ try (FileOutputStream streamOut = new FileOutputStream(out)) {
+ FileDescriptor fdOut = streamOut.getFD();
+ long result = Os
+ .splice(pipe[0], null /* offIn */, fdOut, offOut, 10 /* len */, 0 /* flags */);
+ assertEquals(5, result);
+ assertEquals(5, offOut.value);
+ }
+
+ assertEquals("oobar", TestIoUtils.readFileAsString(out.getPath()));
+
+ Os.close(pipe[0]);
+ Os.close(pipe[1]);
+ }
+
+ public void test_splice_errors() throws Exception {
+ File in = createTempFile("splice3", "");
+ File out = createTempFile("splice4", "");
+ FileDescriptor[] pipe = Os.pipe2(0);
+
+ //.fdIn == null
+ try {
+ Os.splice(null /* fdIn */, null /* offIn */, pipe[1],
+ null /*offOut*/, 10 /* len */, 0 /* flags */);
+ fail();
+ } catch (ErrnoException expected) {
+ assertEquals(EBADF, expected.errno);
+ }
+
+ //.fdOut == null
+ try {
+ Os.splice(pipe[0] /* fdIn */, null /* offIn */, null /* fdOut */,
+ null /*offOut*/, 10 /* len */, 0 /* flags */);
+ fail();
+ } catch (ErrnoException expected) {
+ assertEquals(EBADF, expected.errno);
+ }
+
+ // No pipe fd
+ try (FileOutputStream streamOut = new FileOutputStream(out)) {
+ try (FileInputStream streamIn = new FileInputStream(in)) {
+ FileDescriptor fdIn = streamIn.getFD();
+ FileDescriptor fdOut = streamOut.getFD();
+ Os.splice(fdIn, null /* offIn */, fdOut, null /* offOut */, 10 /* len */,
+ 0 /* flags */);
+ fail();
+ } catch (ErrnoException expected) {
+ assertEquals(EINVAL, expected.errno);
+ }
+ }
+
+ Os.close(pipe[0]);
+ Os.close(pipe[1]);
+ }
+
+ public void testCloseNullFileDescriptor() throws Exception {
+ try {
+ Os.close(null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ public void testSocketpairNullFileDescriptor1() throws Exception {
+ try {
+ Os.socketpair(AF_UNIX, SOCK_STREAM, 0, null, new FileDescriptor());
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ public void testSocketpairNullFileDescriptor2() throws Exception {
+ try {
+ Os.socketpair(AF_UNIX, SOCK_STREAM, 0, new FileDescriptor(), null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ public void testSocketpairNullFileDescriptorBoth() throws Exception {
+ try {
+ Os.socketpair(AF_UNIX, SOCK_STREAM, 0, null, null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ public void testInetPtonIpv4() {
+ String srcAddress = "127.0.0.1";
+ InetAddress inetAddress = Os.inet_pton(AF_INET, srcAddress);
+ assertEquals(srcAddress, inetAddress.getHostAddress());
+ }
+
+ public void testInetPtonIpv6() {
+ String srcAddress = "1123:4567:89ab:cdef:fedc:ba98:7654:3210";
+ InetAddress inetAddress = Os.inet_pton(AF_INET6, srcAddress);
+ assertEquals(srcAddress, inetAddress.getHostAddress());
+ }
+
+ public void testInetPtonInvalidFamily() {
+ String srcAddress = "127.0.0.1";
+ InetAddress inetAddress = Os.inet_pton(AF_UNIX, srcAddress);
+ assertNull(inetAddress);
+ }
+
+ public void testInetPtonWrongFamily() {
+ String srcAddress = "127.0.0.1";
+ InetAddress inetAddress = Os.inet_pton(AF_INET6, srcAddress);
+ assertNull(inetAddress);
+ }
+
+ public void testInetPtonInvalidData() {
+ String srcAddress = "10.1";
+ InetAddress inetAddress = Os.inet_pton(AF_INET, srcAddress);
+ assertNull(inetAddress);
+ }
+}
diff --git a/luni/src/test/java/libcore/java/net/InetAddressTest.java b/luni/src/test/java/libcore/java/net/InetAddressTest.java
index 307cd1d..2ce79fd 100644
--- a/luni/src/test/java/libcore/java/net/InetAddressTest.java
+++ b/luni/src/test/java/libcore/java/net/InetAddressTest.java
@@ -36,6 +36,7 @@
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import libcore.libcore.util.SerializationTester;
+import libcore.net.InetAddressUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -312,8 +313,11 @@
public void test_getByName_invalid(String invalid) throws Exception {
try {
InetAddress.getByName(invalid);
- fail("Invalid IP address incorrectly recognized as valid: "
- + invalid);
+ String failMessage = "Invalid IP address incorrectly recognized as valid: " + invalid;
+ if (InetAddressUtils.parseNumericAddressNoThrowStripOptionalBrackets(invalid) == null) {
+ failMessage += " (it was probably unexpectedly resolved by this network's DNS)";
+ }
+ fail(failMessage);
} catch (UnknownHostException expected) {
}
diff --git a/luni/src/test/java/libcore/java/util/DateTest.java b/luni/src/test/java/libcore/java/util/DateTest.java
index df86a38..1fd8193 100644
--- a/luni/src/test/java/libcore/java/util/DateTest.java
+++ b/luni/src/test/java/libcore/java/util/DateTest.java
@@ -50,10 +50,19 @@
c.clear();
c.set(Calendar.YEAR, 21);
assertEquals("Wed Jan 01 00:00:00 PST 21", c.getTime().toString());
- assertEquals("1 Jan 21 08:00:00 GMT", c.getTime().toGMTString());
+ String actual21GmtString = c.getTime().toGMTString();
+ // zic <= 2014b data gives -08:00:00, later ones gives -07:52:58 instead. http://b/73719425
+ assertTrue("Actual: " + actual21GmtString,
+ "1 Jan 21 07:52:58 GMT".equals(actual21GmtString)
+ || "1 Jan 21 08:00:00 GMT".equals(actual21GmtString));
+
c.set(Calendar.YEAR, 321);
assertEquals("Sun Jan 01 00:00:00 PST 321", c.getTime().toString());
- assertEquals("1 Jan 321 08:00:00 GMT", c.getTime().toGMTString());
+ String actual321GmtString = c.getTime().toGMTString();
+ // zic <= 2014b data gives -08:00:00, later ones gives -07:52:58 instead. http://b/73719425
+ assertTrue("Actual: " + actual321GmtString,
+ "1 Jan 321 07:52:58 GMT".equals(actual321GmtString)
+ || "1 Jan 321 08:00:00 GMT".equals(actual321GmtString));
}
public void test_toGMTString_nonUs() throws Exception {
@@ -64,10 +73,19 @@
c.clear();
c.set(Calendar.YEAR, 21);
assertEquals("Wed Jan 01 00:00:00 PST 21", c.getTime().toString());
- assertEquals("1 Jan 21 08:00:00 GMT", c.getTime().toGMTString());
+ String actual21GmtString = c.getTime().toGMTString();
+ // zic <= 2014b data gives -08:00:00, later ones gives -07:52:58 instead. http://b/73719425
+ assertTrue("Actual: " + actual21GmtString,
+ "1 Jan 21 07:52:58 GMT".equals(actual21GmtString)
+ || "1 Jan 21 08:00:00 GMT".equals(actual21GmtString));
+
c.set(Calendar.YEAR, 321);
assertEquals("Sun Jan 01 00:00:00 PST 321", c.getTime().toString());
- assertEquals("1 Jan 321 08:00:00 GMT", c.getTime().toGMTString());
+ String actual321GmtString = c.getTime().toGMTString();
+ // zic <= 2014b data gives -08:00:00, later ones gives -07:52:58 instead. http://b/73719425
+ assertTrue("Actual: " + actual321GmtString,
+ "1 Jan 321 07:52:58 GMT".equals(actual321GmtString)
+ || "1 Jan 321 08:00:00 GMT".equals(actual321GmtString));
}
public void test_parse_timezones() {
diff --git a/luni/src/test/java/libcore/java/util/TimeZoneTest.java b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
index 68b6a54..6297080 100644
--- a/luni/src/test/java/libcore/java/util/TimeZoneTest.java
+++ b/luni/src/test/java/libcore/java/util/TimeZoneTest.java
@@ -58,7 +58,30 @@
}
// http://code.google.com/p/android/issues/detail?id=14395
- public void testPreHistoricInDaylightTime() throws Exception {
+ public void testPreHistoricInDaylightTime() {
+ // A replacement for testPreHistoricInDaylightTime_old() using a zone that still lacks an
+ // explicit transition at Integer.MIN_VALUE with zic 2019a and 2019a data.
+ TimeZone tz = TimeZone.getTimeZone("CET");
+
+ long firstTransitionTimeMillis = -1693706400000L; // Apr 30, 1916 22:00:00 GMT
+ assertEquals(7200000L, tz.getOffset(firstTransitionTimeMillis));
+ assertTrue(tz.inDaylightTime(new Date(firstTransitionTimeMillis)));
+
+ long beforeFirstTransitionTimeMillis = firstTransitionTimeMillis - 1;
+ assertEquals(3600000L, tz.getOffset(beforeFirstTransitionTimeMillis));
+ assertFalse(tz.inDaylightTime(new Date(beforeFirstTransitionTimeMillis)));
+ }
+
+ // http://code.google.com/p/android/issues/detail?id=14395
+ public void testPreHistoricInDaylightTime_old() throws Exception {
+ // Originally this test was intended to assert what happens when the first transition for a
+ // time zone was a "to DST" transition. i.e. that the (implicit) offset / DST state before
+ // the first was treated as a non-DST state. Since zic version 2014c some zones have an
+ // explicit non-DST transition at time -2^31 seconds so it is no longer possible to test
+ // this with America/Los_Angeles.
+ // This regression test has been kept in case that changes again in future and to prove the
+ // behavior has remained consistent.
+
Locale.setDefault(Locale.US);
TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
TimeZone.setDefault(tz);
@@ -69,7 +92,7 @@
assertFalse(tz.inDaylightTime(date));
assertEquals("Fri Oct 31 08:00:00 PST 1902", date.toString());
assertEquals("31 Oct 1902 16:00:00 GMT", date.toGMTString());
- // Any time before we have transition data is considered non-daylight, even in summer.
+ // For zic versions <= 2014b, this would be before the first transition.
date = sdf.parse("1902-06-01T00:00:00.000+0800");
assertEquals(-28800000, tz.getOffset(date.getTime()));
assertFalse(tz.inDaylightTime(date));
@@ -84,17 +107,20 @@
public void testPreHistoricOffsets() throws Exception {
// Note: This test changed after P to account for previously incorrect handling of
// prehistoric offsets. http://b/118835133
- // "Africa/Bissau" has just a few transitions:
- // Date, Offset, IsDst
- // 1901-12-13 20:45:52,-3740,0 (Integer.MIN_VALUE, implicit with zic <= 2014b)
- // 1912-01-01 01:00:00,-3600,0
- // 1975-01-01 01:00:00,0,0
+ // "Africa/Bissau" has just a few known transitions:
+ // Transition time : Offset : DST / non-DST
+ // <Integer.MIN_VALUE secs>[1] : -01:02:20 : non-DST
+ // 1912-01-01 01:00:00 GMT : -01:00:00 : non-DST
+ // 1975-01-01 01:00:00 GMT : 00:00:00 : non-DST
+ //
+ // [1] This transition can be implicit or explicit depending on the version of zic used to
+ // generate the data. When implicit, the first non-DST type defn should be used.
TimeZone tz = TimeZone.getTimeZone("Africa/Bissau");
- // Before Integer.MIN_VALUE.
+ // Times before Integer.MIN_VALUE should assume we're using the first non-DST type.
assertNonDaylightOffset(-3740, parseIsoTime("1900-01-01T00:00:00.0+0000"), tz);
- // Times before 1912-01-01 01:00:00
+ // Time before 1912-01-01 01:00:00 but after Integer.MIN_VALUE.
assertNonDaylightOffset(-3740, parseIsoTime("1911-01-01T00:00:00.0+0000"), tz);
// Times after 1912-01-01 01:00:00 should use that transition.
@@ -104,19 +130,22 @@
assertNonDaylightOffset(0, parseIsoTime("1980-01-01T00:00:00.0+0000"), tz);
}
- private static void assertNonDaylightOffset(int expectedOffsetSeconds, long epochSeconds, TimeZone tz) {
- assertEquals(expectedOffsetSeconds, tz.getOffset(epochSeconds * 1000) / 1000);
- assertFalse(tz.inDaylightTime(new Date(epochSeconds * 1000)));
+ private static void assertNonDaylightOffset(
+ int expectedOffsetSeconds, long epochMillis, TimeZone tz) {
+ assertEquals(expectedOffsetSeconds, tz.getOffset(epochMillis) / 1000);
+ assertFalse(tz.inDaylightTime(new Date(epochMillis)));
}
+ /** Returns the millis elapsed since the beginning of the Unix epoch. */
private static long parseIsoTime(String isoTime) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
Date date = sdf.parse(isoTime);
- return date.getTime() / 1000;
+ return date.getTime();
}
- public void testZeroTransitionZones() throws Exception {
- // Zones with no transitions historical or future seem ideal for testing.
+ public void testMinimalTransitionZones() throws Exception {
+ // Zones with minimal transitions, historical or future, seem ideal for testing.
+ // UTC is also included, although it may be implemented differently from the others.
String[] ids = new String[] { "Africa/Bujumbura", "Indian/Cocos", "Pacific/Wake", "UTC" };
for (String id : ids) {
TimeZone tz = TimeZone.getTimeZone(id);
@@ -338,8 +367,7 @@
final long lowerTimeMillis = beforeInt32Seconds * 1000L;
final long upperTimeMillis = afterInt32Seconds * 1000L;
- // This timezone didn't have any daylight savings prior to 1917 and this
- // date is in 1900.
+ // This timezone didn't have any daylight savings prior to 1917 and this date is in 1900.
assertFalse(tz.inDaylightTime(new Date(lowerTimeMillis)));
// http://b/118835133:
@@ -347,7 +375,7 @@
// 07:00:00 GMT) the offset was -18000000.
// zic > 2014b produces data that suggests before Integer.MIN_VALUE seconds the offset was
// -17762000 and between Integer.MIN_VALUE and -1633280400 it was -18000000. Once Android
- // moves to zic > 2014b the -18000000 can be removed.
+ // moves to zic > 2014b the -18000000 can be removed. http://b/73719425
int actualOffset = tz.getOffset(lowerTimeMillis);
assertTrue(-18000000 == actualOffset || -17762000 == actualOffset);
diff --git a/luni/src/test/java/libcore/libcore/io/BlockGuardOsTest.java b/luni/src/test/java/libcore/libcore/io/BlockGuardOsTest.java
index 91d7c3a..471906f 100644
--- a/luni/src/test/java/libcore/libcore/io/BlockGuardOsTest.java
+++ b/luni/src/test/java/libcore/libcore/io/BlockGuardOsTest.java
@@ -24,6 +24,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import android.system.ErrnoException;
import android.system.OsConstants;
import android.system.StructAddrinfo;
@@ -58,22 +59,42 @@
@Mock private Os mockOsDelegate;
@Mock private BlockGuard.Policy mockThreadPolicy;
+ @Mock private BlockGuard.VmPolicy mockVmPolicy;
private BlockGuard.Policy savedThreadPolicy;
+ private BlockGuard.VmPolicy savedVmPolicy;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
savedThreadPolicy = BlockGuard.getThreadPolicy();
+ savedVmPolicy = BlockGuard.getVmPolicy();
BlockGuard.setThreadPolicy(mockThreadPolicy);
+ BlockGuard.setVmPolicy(mockVmPolicy);
}
@After
public void tearDown() {
+ BlockGuard.setVmPolicy(savedVmPolicy);
BlockGuard.setThreadPolicy(savedThreadPolicy);
}
@Test
+ public void test_blockguardOsIsNotifiedByDefault_rename() {
+ String oldPath = "BlockGuardOsTest/missing/old/path";
+ String newPath = "BlockGuardOsTest/missing/new/path";
+ try {
+ // We try not to be prescriptive about the exact default Os implementation.
+ // Whatever default Os is installed, we do expect BlockGuard to be called.
+ Os.getDefault().rename(oldPath, newPath);
+ } catch (ErrnoException ignored) {
+ }
+ verify(mockThreadPolicy).onWriteToDisk();
+ verify(mockVmPolicy).onPathAccess(oldPath);
+ verify(mockVmPolicy).onPathAccess(newPath);
+ }
+
+ @Test
public void test_android_getaddrinfo_networkPolicy() {
InetAddress[] addresses = new InetAddress[] { InetAddress.getLoopbackAddress() };
when(mockOsDelegate.android_getaddrinfo(anyString(), any(), anyInt()))
diff --git a/luni/src/test/java/libcore/libcore/io/OsTest.java b/luni/src/test/java/libcore/libcore/io/OsTest.java
index 1bda0e6..6359f87 100644
--- a/luni/src/test/java/libcore/libcore/io/OsTest.java
+++ b/luni/src/test/java/libcore/libcore/io/OsTest.java
@@ -16,1451 +16,54 @@
package libcore.libcore.io;
-import android.system.ErrnoException;
-import android.system.Int64Ref;
-import android.system.NetlinkSocketAddress;
-import android.system.OsConstants;
-import android.system.PacketSocketAddress;
-import android.system.StructRlimit;
-import android.system.StructStat;
-import android.system.StructTimeval;
-import android.system.StructUcred;
-import android.system.UnixSocketAddress;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.net.DatagramPacket;
-import java.net.DatagramSocket;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.NetworkInterface;
-import java.net.ServerSocket;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.time.Duration;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.atomic.AtomicReference;
import junit.framework.TestCase;
-import libcore.io.BlockGuardOs;
import libcore.io.ForwardingOs;
-import libcore.io.IoUtils;
-import libcore.io.Libcore;
import libcore.io.Os;
import org.mockito.Mockito;
-import static android.system.OsConstants.*;
-import static libcore.libcore.io.OsTest.SendFileImpl.ANDROID_SYSTEM_OS_INT64_REF;
-import static libcore.libcore.io.OsTest.SendFileImpl.LIBCORE_OS;
-
public class OsTest extends TestCase {
- public void testIsSocket() throws Exception {
- File f = new File("/dev/null");
- FileInputStream fis = new FileInputStream(f);
- assertFalse(S_ISSOCK(Libcore.os.fstat(fis.getFD()).st_mode));
- fis.close();
- ServerSocket s = new ServerSocket();
- assertTrue(S_ISSOCK(Libcore.os.fstat(s.getImpl().getFD$()).st_mode));
- s.close();
- }
-
- public void testFcntlInt() throws Exception {
- File f = File.createTempFile("OsTest", "tst");
- FileInputStream fis = null;
- try {
- fis = new FileInputStream(f);
- Libcore.os.fcntlInt(fis.getFD(), F_SETFD, FD_CLOEXEC);
- int flags = Libcore.os.fcntlVoid(fis.getFD(), F_GETFD);
- assertTrue((flags & FD_CLOEXEC) != 0);
- } finally {
- IoUtils.closeQuietly(fis);
- f.delete();
- }
- }
-
- public void testUnixDomainSockets_in_file_system() throws Exception {
- String path = System.getProperty("java.io.tmpdir") + "/test_unix_socket";
- new File(path).delete();
- checkUnixDomainSocket(UnixSocketAddress.createFileSystem(path), false);
- }
-
- public void testUnixDomainSocket_abstract_name() throws Exception {
- // Linux treats a sun_path starting with a NUL byte as an abstract name. See unix(7).
- checkUnixDomainSocket(UnixSocketAddress.createAbstract("/abstract_name_unix_socket"), true);
- }
-
- public void testUnixDomainSocket_unnamed() throws Exception {
- final FileDescriptor fd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
- // unix(7) says an unbound socket is unnamed.
- checkNoSockName(fd);
- Libcore.os.close(fd);
- }
-
- private void checkUnixDomainSocket(final UnixSocketAddress address, final boolean isAbstract)
- throws Exception {
- final FileDescriptor serverFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
- Libcore.os.bind(serverFd, address);
- Libcore.os.listen(serverFd, 5);
-
- checkSockName(serverFd, isAbstract, address);
-
- Thread server = new Thread(new Runnable() {
- public void run() {
+ public void testCompareAndSetDefault_success() throws Exception {
+ Os defaultOs = Os.getDefault();
+ Os mockOs = Mockito.mock(Os.class);
try {
- UnixSocketAddress peerAddress = UnixSocketAddress.createUnnamed();
- FileDescriptor clientFd = Libcore.os.accept(serverFd, peerAddress);
- checkSockName(clientFd, isAbstract, address);
- checkNoName(peerAddress);
+ // There shouldn't be any concurrent threads replacing the default Os.
+ assertTrue(Os.compareAndSetDefault(defaultOs, mockOs));
+ assertSame(mockOs, Os.getDefault());
- checkNoPeerName(clientFd);
-
- StructUcred credentials = Libcore.os.getsockoptUcred(clientFd, SOL_SOCKET, SO_PEERCRED);
- assertEquals(Libcore.os.getpid(), credentials.pid);
- assertEquals(Libcore.os.getuid(), credentials.uid);
- assertEquals(Libcore.os.getgid(), credentials.gid);
-
- byte[] request = new byte[256];
- Libcore.os.read(clientFd, request, 0, request.length);
-
- String s = new String(request, "UTF-8");
- byte[] response = s.toUpperCase(Locale.ROOT).getBytes("UTF-8");
- Libcore.os.write(clientFd, response, 0, response.length);
-
- Libcore.os.close(clientFd);
- } catch (Exception ex) {
- throw new RuntimeException(ex);
+ // Calls to android.system.Os should now reach our custom Os instance.
+ android.system.Os.rename("/old/path", "/new/path");
+ Mockito.verify(mockOs).rename("/old/path", "/new/path");
+ } finally {
+ assertTrue(Os.compareAndSetDefault(mockOs, defaultOs));
+ assertSame(defaultOs, Os.getDefault());
}
- }
- });
- server.start();
-
- FileDescriptor clientFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
-
- Libcore.os.connect(clientFd, address);
- checkNoSockName(clientFd);
-
- String string = "hello, world!";
-
- byte[] request = string.getBytes("UTF-8");
- assertEquals(request.length, Libcore.os.write(clientFd, request, 0, request.length));
-
- byte[] response = new byte[request.length];
- assertEquals(response.length, Libcore.os.read(clientFd, response, 0, response.length));
-
- assertEquals(string.toUpperCase(Locale.ROOT), new String(response, "UTF-8"));
-
- Libcore.os.close(clientFd);
- }
-
- private static void checkSockName(FileDescriptor fd, boolean isAbstract,
- UnixSocketAddress address) throws Exception {
- UnixSocketAddress isa = (UnixSocketAddress) Libcore.os.getsockname(fd);
- assertEquals(address, isa);
- if (isAbstract) {
- assertEquals(0, isa.getSunPath()[0]);
}
- }
- private void checkNoName(UnixSocketAddress usa) {
- assertEquals(0, usa.getSunPath().length);
- }
-
- private void checkNoPeerName(FileDescriptor fd) throws Exception {
- checkNoName((UnixSocketAddress) Libcore.os.getpeername(fd));
- }
-
- private void checkNoSockName(FileDescriptor fd) throws Exception {
- checkNoName((UnixSocketAddress) Libcore.os.getsockname(fd));
- }
-
- public void test_strsignal() throws Exception {
- assertEquals("Killed", Libcore.os.strsignal(9));
- assertEquals("Unknown signal -1", Libcore.os.strsignal(-1));
- }
-
- public void test_byteBufferPositions_write_pwrite() throws Exception {
- FileOutputStream fos = new FileOutputStream(new File("/dev/null"));
- FileDescriptor fd = fos.getFD();
- final byte[] contents = new String("goodbye, cruel world").getBytes(StandardCharsets.US_ASCII);
- ByteBuffer byteBuffer = ByteBuffer.wrap(contents);
-
- byteBuffer.position(0);
- int written = Libcore.os.write(fd, byteBuffer);
- assertTrue(written > 0);
- assertEquals(written, byteBuffer.position());
-
- byteBuffer.position(4);
- written = Libcore.os.write(fd, byteBuffer);
- assertTrue(written > 0);
- assertEquals(written + 4, byteBuffer.position());
-
- byteBuffer.position(0);
- written = Libcore.os.pwrite(fd, byteBuffer, 64 /* offset */);
- assertTrue(written > 0);
- assertEquals(written, byteBuffer.position());
-
- byteBuffer.position(4);
- written = Libcore.os.pwrite(fd, byteBuffer, 64 /* offset */);
- assertTrue(written > 0);
- assertEquals(written + 4, byteBuffer.position());
-
- fos.close();
- }
-
- public void test_byteBufferPositions_read_pread() throws Exception {
- FileInputStream fis = new FileInputStream(new File("/dev/zero"));
- FileDescriptor fd = fis.getFD();
- ByteBuffer byteBuffer = ByteBuffer.allocate(64);
-
- byteBuffer.position(0);
- int read = Libcore.os.read(fd, byteBuffer);
- assertTrue(read > 0);
- assertEquals(read, byteBuffer.position());
-
- byteBuffer.position(4);
- read = Libcore.os.read(fd, byteBuffer);
- assertTrue(read > 0);
- assertEquals(read + 4, byteBuffer.position());
-
- byteBuffer.position(0);
- read = Libcore.os.pread(fd, byteBuffer, 64 /* offset */);
- assertTrue(read > 0);
- assertEquals(read, byteBuffer.position());
-
- byteBuffer.position(4);
- read = Libcore.os.pread(fd, byteBuffer, 64 /* offset */);
- assertTrue(read > 0);
- assertEquals(read + 4, byteBuffer.position());
-
- fis.close();
- }
-
- static void checkByteBufferPositions_sendto_recvfrom(
- int family, InetAddress loopback) throws Exception {
- final FileDescriptor serverFd = Libcore.os.socket(family, SOCK_STREAM, 0);
- Libcore.os.bind(serverFd, loopback, 0);
- Libcore.os.listen(serverFd, 5);
-
- InetSocketAddress address = (InetSocketAddress) Libcore.os.getsockname(serverFd);
-
- final Thread server = new Thread(new Runnable() {
- public void run() {
+ public void testCompareandSetDefault_null() {
+ Os defaultOs = Os.getDefault();
+ // update == null is not allowed
try {
- InetSocketAddress peerAddress = new InetSocketAddress();
- FileDescriptor clientFd = Libcore.os.accept(serverFd, peerAddress);
-
- // Attempt to receive a maximum of 24 bytes from the client, and then
- // close the connection.
- ByteBuffer buffer = ByteBuffer.allocate(16);
- int received = Libcore.os.recvfrom(clientFd, buffer, 0, null);
- assertTrue(received > 0);
- assertEquals(received, buffer.position());
-
- ByteBuffer buffer2 = ByteBuffer.allocate(16);
- buffer2.position(8);
- received = Libcore.os.recvfrom(clientFd, buffer2, 0, null);
- assertTrue(received > 0);
- assertEquals(received + 8, buffer.position());
-
- Libcore.os.close(clientFd);
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
- });
-
- server.start();
-
- FileDescriptor clientFd = Libcore.os.socket(family, SOCK_STREAM, 0);
- Libcore.os.connect(clientFd, address.getAddress(), address.getPort());
-
- final byte[] bytes = "good bye, cruel black hole with fancy distortion"
- .getBytes(StandardCharsets.US_ASCII);
- assertTrue(bytes.length > 24);
-
- ByteBuffer input = ByteBuffer.wrap(bytes);
- input.position(0);
- input.limit(16);
-
- int sent = Libcore.os.sendto(clientFd, input, 0, address.getAddress(), address.getPort());
- assertTrue(sent > 0);
- assertEquals(sent, input.position());
-
- input.position(16);
- input.limit(24);
- sent = Libcore.os.sendto(clientFd, input, 0, address.getAddress(), address.getPort());
- assertTrue(sent > 0);
- assertEquals(sent + 16, input.position());
-
- Libcore.os.close(clientFd);
- }
-
- interface ExceptionalRunnable {
- public void run() throws Exception;
- }
-
- /**
- * Expects that the given Runnable will throw an exception of the specified class. If the class is
- * ErrnoException, and expectedErrno is non-null, also checks that the errno is equal to
- * expectedErrno.
- */
- private static void expectException(ExceptionalRunnable r, Class<? extends Exception> exClass,
- Integer expectedErrno, String msg) {
- try {
- r.run();
- fail(msg + " did not throw exception");
- } catch (Exception e) {
- assertEquals(msg + " threw unexpected exception", exClass, e.getClass());
-
- if (expectedErrno != null) {
- if (e instanceof ErrnoException) {
- assertEquals(msg + "threw ErrnoException with unexpected error number",
- (int) expectedErrno, ((ErrnoException) e).errno);
- } else {
- fail("Can only pass expectedErrno when expecting ErrnoException");
- }
- }
-
- }
- }
-
- private static void expectBindException(FileDescriptor socket, SocketAddress addr,
- Class exClass, Integer expectedErrno) {
- String msg = String.format("bind(%s, %s)", socket, addr);
- expectException(() -> { Libcore.os.bind(socket, addr); }, exClass, expectedErrno, msg);
- }
-
- private static void expectConnectException(FileDescriptor socket, SocketAddress addr,
- Class exClass, Integer expectedErrno) {
- String msg = String.format("connect(%s, %s)", socket, addr);
- expectException(() -> { Libcore.os.connect(socket, addr); }, exClass, expectedErrno, msg);
- }
-
- private static void expectSendtoException(FileDescriptor socket, SocketAddress addr,
- Class exClass, Integer expectedErrno) {
- String msg = String.format("sendto(%s, %s)", socket, addr);
- byte[] packet = new byte[42];
- expectException(() -> { Libcore.os.sendto(socket, packet, 0, packet.length, 0, addr); },
- exClass, expectedErrno, msg);
- }
-
- private static void expectBindConnectSendtoSuccess(FileDescriptor socket, String socketDesc,
- SocketAddress addr) {
- String msg = socketDesc + " socket to " + addr.toString();
-
- try {
- try {
- // Expect that bind throws when any of its arguments are null.
- expectBindException(null, addr, ErrnoException.class, EBADF);
- expectBindException(socket, null, NullPointerException.class, null);
- expectBindException(null, null, NullPointerException.class, null);
-
- // Expect bind to succeed.
- Libcore.os.bind(socket, addr);
-
- // Find out which port we're actually bound to, and use that in subsequent connect() and
- // send() calls. We can't send to addr because that has a port of 0.
- if (addr instanceof InetSocketAddress) {
- InetSocketAddress addrISA = (InetSocketAddress) addr;
- InetSocketAddress socknameISA = (InetSocketAddress) Libcore.os.getsockname(socket);
-
- assertEquals(addrISA.getAddress(), socknameISA.getAddress());
- assertEquals(0, addrISA.getPort());
- assertFalse(0 == socknameISA.getPort());
- addr = socknameISA;
- }
-
- // Expect sendto with a null address to throw because the socket is not connected, but to
- // succeed with a non-null address.
- byte[] packet = new byte[42];
- Libcore.os.sendto(socket, packet, 0, packet.length, 0, addr);
- // UNIX and IP sockets return different errors for this operation, so we can't check errno.
- expectSendtoException(socket, null, ErrnoException.class, null);
- expectSendtoException(null, null, ErrnoException.class, EBADF);
-
- // Expect that connect throws when any of its arguments are null.
- expectConnectException(null, addr, ErrnoException.class, EBADF);
- expectConnectException(socket, null, NullPointerException.class, null);
- expectConnectException(null, null, NullPointerException.class, null);
-
- // Expect connect to succeed.
- Libcore.os.connect(socket, addr);
- assertEquals(Libcore.os.getsockname(socket), Libcore.os.getpeername(socket));
-
- // Expect sendto to succeed both when given an explicit address and a null address.
- Libcore.os.sendto(socket, packet, 0, packet.length, 0, addr);
- Libcore.os.sendto(socket, packet, 0, packet.length, 0, null);
- } catch (SocketException | ErrnoException e) {
- fail("Expected success for " + msg + ", but got: " + e);
- }
-
- } finally {
- IoUtils.closeQuietly(socket);
- }
- }
-
- private static void expectBindConnectSendtoErrno(int bindErrno, int connectErrno, int sendtoErrno,
- FileDescriptor socket, String socketDesc,
- SocketAddress addr) {
- try {
-
- // Expect bind to fail with bindErrno.
- String msg = "bind " + socketDesc + " socket to " + addr.toString();
- try {
- Libcore.os.bind(socket, addr);
- fail("Expected to fail " + msg);
- } catch (ErrnoException e) {
- assertEquals("Expected errno " + bindErrno + " " + msg, bindErrno, e.errno);
- } catch (SocketException e) {
- fail("Unexpected SocketException " + msg);
- }
-
- // Expect connect to fail with connectErrno.
- msg = "connect " + socketDesc + " socket to " + addr.toString();
- try {
- Libcore.os.connect(socket, addr);
- fail("Expected to fail " + msg);
- } catch (ErrnoException e) {
- assertEquals("Expected errno " + connectErrno + " " + msg, connectErrno, e.errno);
- } catch (SocketException e) {
- fail("Unexpected SocketException " + msg);
- }
-
- // Expect sendto to fail with sendtoErrno.
- byte[] packet = new byte[42];
- msg = "sendto " + socketDesc + " socket to " + addr.toString();
- try {
- Libcore.os.sendto(socket, packet, 0, packet.length, 0, addr);
- fail("Expected to fail " + msg);
- } catch (ErrnoException e) {
- assertEquals("Expected errno " + sendtoErrno + " " + msg, sendtoErrno, e.errno);
- } catch (SocketException e) {
- fail("Unexpected SocketException " + msg);
- }
-
- } finally {
- // No matter what happened, close the socket.
- IoUtils.closeQuietly(socket);
- }
- }
-
- private FileDescriptor makeIpv4Socket() throws Exception {
- return Libcore.os.socket(AF_INET, SOCK_DGRAM, 0);
- }
-
- private FileDescriptor makeIpv6Socket() throws Exception {
- return Libcore.os.socket(AF_INET6, SOCK_DGRAM, 0);
- }
-
- private FileDescriptor makeUnixSocket() throws Exception {
- return Libcore.os.socket(AF_UNIX, SOCK_DGRAM, 0);
- }
-
- public void testCrossFamilyBindConnectSendto() throws Exception {
- SocketAddress addrIpv4 = new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 0);
- SocketAddress addrIpv6 = new InetSocketAddress(InetAddress.getByName("::1"), 0);
- SocketAddress addrUnix = UnixSocketAddress.createAbstract("/abstract_name_unix_socket");
-
- expectBindConnectSendtoSuccess(makeIpv4Socket(), "ipv4", addrIpv4);
- expectBindConnectSendtoErrno(EAFNOSUPPORT, EAFNOSUPPORT, EAFNOSUPPORT,
- makeIpv4Socket(), "ipv4", addrIpv6);
- expectBindConnectSendtoErrno(EAFNOSUPPORT, EAFNOSUPPORT, EAFNOSUPPORT,
- makeIpv4Socket(), "ipv4", addrUnix);
-
- // This succeeds because Java always uses dual-stack sockets and all InetAddress and
- // InetSocketAddress objects represent IPv4 addresses using IPv4-mapped IPv6 addresses.
- expectBindConnectSendtoSuccess(makeIpv6Socket(), "ipv6", addrIpv4);
- expectBindConnectSendtoSuccess(makeIpv6Socket(), "ipv6", addrIpv6);
- expectBindConnectSendtoErrno(EAFNOSUPPORT, EAFNOSUPPORT, EINVAL,
- makeIpv6Socket(), "ipv6", addrUnix);
-
- expectBindConnectSendtoErrno(EINVAL, EINVAL, EINVAL,
- makeUnixSocket(), "unix", addrIpv4);
- expectBindConnectSendtoErrno(EINVAL, EINVAL, EINVAL,
- makeUnixSocket(), "unix", addrIpv6);
- expectBindConnectSendtoSuccess(makeUnixSocket(), "unix", addrUnix);
- }
-
- public void testUnknownSocketAddressSubclass() throws Exception {
- class MySocketAddress extends SocketAddress {}
- MySocketAddress myaddr = new MySocketAddress();
-
- for (int family : new int[]{AF_INET, AF_INET6, AF_NETLINK}) {
- FileDescriptor s = Libcore.os.socket(family, SOCK_DGRAM, 0);
- try {
-
- try {
- Libcore.os.bind(s, myaddr);
- fail("bind socket family " + family + " to unknown SocketAddress subclass succeeded");
- } catch (UnsupportedOperationException expected) {}
-
- try {
- Libcore.os.connect(s, myaddr);
- fail("connect socket family " + family + " to unknown SocketAddress subclass succeeded");
- } catch (UnsupportedOperationException expected) {}
-
- byte[] msg = new byte[42];
- try {
- Libcore.os.sendto(s, msg, 0, msg.length, 0, myaddr);
- fail("sendto socket family " + family + " to unknown SocketAddress subclass succeeded");
- } catch (UnsupportedOperationException expected) {}
-
- } finally {
- Libcore.os.close(s);
- }
- }
- }
-
- public void test_NetlinkSocket() throws Exception {
- FileDescriptor nlSocket = Libcore.os.socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
- Libcore.os.bind(nlSocket, new NetlinkSocketAddress());
- NetlinkSocketAddress address = (NetlinkSocketAddress) Libcore.os.getsockname(nlSocket);
- assertTrue(address.getPortId() > 0);
- assertEquals(0, address.getGroupsMask());
-
- NetlinkSocketAddress nlKernel = new NetlinkSocketAddress();
- Libcore.os.connect(nlSocket, nlKernel);
- NetlinkSocketAddress nlPeer = (NetlinkSocketAddress) Libcore.os.getpeername(nlSocket);
- assertEquals(0, nlPeer.getPortId());
- assertEquals(0, nlPeer.getGroupsMask());
- Libcore.os.close(nlSocket);
- }
-
- public void test_PacketSocketAddress() throws Exception {
- NetworkInterface lo = NetworkInterface.getByName("lo");
- FileDescriptor fd = Libcore.os.socket(AF_PACKET, SOCK_DGRAM, ETH_P_IPV6);
- PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IPV6, lo.getIndex());
- Libcore.os.bind(fd, addr);
-
- PacketSocketAddress bound = (PacketSocketAddress) Libcore.os.getsockname(fd);
- assertEquals((short) ETH_P_IPV6, bound.sll_protocol); // ETH_P_IPV6 is an int.
- assertEquals(lo.getIndex(), bound.sll_ifindex);
- assertEquals(ARPHRD_LOOPBACK, bound.sll_hatype);
- assertEquals(0, bound.sll_pkttype);
-
- // The loopback address is ETH_ALEN bytes long and is all zeros.
- // http://lxr.free-electrons.com/source/drivers/net/loopback.c?v=3.10#L167
- assertEquals(6, bound.sll_addr.length);
- for (int i = 0; i < 6; i++) {
- assertEquals(0, bound.sll_addr[i]);
- }
- }
-
- public void test_byteBufferPositions_sendto_recvfrom_af_inet() throws Exception {
- checkByteBufferPositions_sendto_recvfrom(AF_INET, InetAddress.getByName("127.0.0.1"));
- }
-
- public void test_byteBufferPositions_sendto_recvfrom_af_inet6() throws Exception {
- checkByteBufferPositions_sendto_recvfrom(AF_INET6, InetAddress.getByName("::1"));
- }
-
- private void checkSendToSocketAddress(int family, InetAddress loopback) throws Exception {
- FileDescriptor recvFd = Libcore.os.socket(family, SOCK_DGRAM, 0);
- Libcore.os.bind(recvFd, loopback, 0);
- StructTimeval tv = StructTimeval.fromMillis(20);
- Libcore.os.setsockoptTimeval(recvFd, SOL_SOCKET, SO_RCVTIMEO, tv);
-
- InetSocketAddress to = ((InetSocketAddress) Libcore.os.getsockname(recvFd));
- FileDescriptor sendFd = Libcore.os.socket(family, SOCK_DGRAM, 0);
- byte[] msg = ("Hello, I'm going to a socket address: " + to.toString()).getBytes("UTF-8");
- int len = msg.length;
-
- assertEquals(len, Libcore.os.sendto(sendFd, msg, 0, len, 0, to));
- byte[] received = new byte[msg.length + 42];
- InetSocketAddress from = new InetSocketAddress();
- assertEquals(len, Libcore.os.recvfrom(recvFd, received, 0, received.length, 0, from));
- assertEquals(loopback, from.getAddress());
- }
-
- public void test_sendtoSocketAddress_af_inet() throws Exception {
- checkSendToSocketAddress(AF_INET, InetAddress.getByName("127.0.0.1"));
- }
-
- public void test_sendtoSocketAddress_af_inet6() throws Exception {
- checkSendToSocketAddress(AF_INET6, InetAddress.getByName("::1"));
- }
-
- public void test_socketFamilies() throws Exception {
- FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
- Libcore.os.bind(fd, InetAddress.getByName("::"), 0);
- InetSocketAddress localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd);
- assertEquals(Inet6Address.ANY, localSocketAddress.getAddress());
-
- fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
- Libcore.os.bind(fd, InetAddress.getByName("0.0.0.0"), 0);
- localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd);
- assertEquals(Inet6Address.ANY, localSocketAddress.getAddress());
-
- fd = Libcore.os.socket(AF_INET, SOCK_STREAM, 0);
- Libcore.os.bind(fd, InetAddress.getByName("0.0.0.0"), 0);
- localSocketAddress = (InetSocketAddress) Libcore.os.getsockname(fd);
- assertEquals(Inet4Address.ANY, localSocketAddress.getAddress());
- try {
- Libcore.os.bind(fd, InetAddress.getByName("::"), 0);
- fail("Expected ErrnoException binding IPv4 socket to ::");
- } catch (ErrnoException expected) {
- assertEquals("Expected EAFNOSUPPORT binding IPv4 socket to ::", EAFNOSUPPORT, expected.errno);
- }
- }
-
- private static void assertArrayEquals(byte[] expected, byte[] actual) {
- assertTrue("Expected=" + Arrays.toString(expected) + ", actual=" + Arrays.toString(actual),
- Arrays.equals(expected, actual));
- }
-
- private static void checkSocketPing(FileDescriptor fd, InetAddress to, byte[] packet,
- byte type, byte responseType, boolean useSendto) throws Exception {
- int len = packet.length;
- packet[0] = type;
- if (useSendto) {
- assertEquals(len, Libcore.os.sendto(fd, packet, 0, len, 0, to, 0));
- } else {
- Libcore.os.connect(fd, to, 0);
- assertEquals(len, Libcore.os.sendto(fd, packet, 0, len, 0, null, 0));
- }
-
- int icmpId = ((InetSocketAddress) Libcore.os.getsockname(fd)).getPort();
- byte[] received = new byte[4096];
- InetSocketAddress srcAddress = new InetSocketAddress();
- assertEquals(len, Libcore.os.recvfrom(fd, received, 0, received.length, 0, srcAddress));
- assertEquals(to, srcAddress.getAddress());
- assertEquals(responseType, received[0]);
- assertEquals(received[4], (byte) (icmpId >> 8));
- assertEquals(received[5], (byte) (icmpId & 0xff));
-
- received = Arrays.copyOf(received, len);
- received[0] = (byte) type;
- received[2] = received[3] = 0; // Checksum.
- received[4] = received[5] = 0; // ICMP ID.
- assertArrayEquals(packet, received);
- }
-
- public void test_socketPing() throws Exception {
- final byte ICMP_ECHO = 8, ICMP_ECHOREPLY = 0;
- final byte ICMPV6_ECHO_REQUEST = (byte) 128, ICMPV6_ECHO_REPLY = (byte) 129;
- final byte[] packet = ("\000\000\000\000" + // ICMP type, code.
- "\000\000\000\003" + // ICMP ID (== port), sequence number.
- "Hello myself").getBytes(StandardCharsets.US_ASCII);
-
- FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
- InetAddress ipv6Loopback = InetAddress.getByName("::1");
- checkSocketPing(fd, ipv6Loopback, packet, ICMPV6_ECHO_REQUEST, ICMPV6_ECHO_REPLY, true);
- checkSocketPing(fd, ipv6Loopback, packet, ICMPV6_ECHO_REQUEST, ICMPV6_ECHO_REPLY, false);
-
- fd = Libcore.os.socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
- InetAddress ipv4Loopback = InetAddress.getByName("127.0.0.1");
- checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, true);
- checkSocketPing(fd, ipv4Loopback, packet, ICMP_ECHO, ICMP_ECHOREPLY, false);
- }
-
- public void test_Ipv4Fallback() throws Exception {
- // This number of iterations gives a ~60% chance of creating the conditions that caused
- // http://b/23088314 without making test times too long. On a hammerhead running MRZ37C using
- // vogar, this test takes about 4s.
- final int ITERATIONS = 10000;
- for (int i = 0; i < ITERATIONS; i++) {
- FileDescriptor mUdpSock = Libcore.os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
- try {
- Libcore.os.bind(mUdpSock, Inet4Address.ANY, 0);
- } catch(ErrnoException e) {
- fail("ErrnoException after " + i + " iterations: " + e);
- } finally {
- Libcore.os.close(mUdpSock);
- }
- }
- }
-
- public void test_unlink() throws Exception {
- File f = File.createTempFile("OsTest", "tst");
- assertTrue(f.exists());
- Libcore.os.unlink(f.getAbsolutePath());
- assertFalse(f.exists());
-
- try {
- Libcore.os.unlink(f.getAbsolutePath());
- fail();
- } catch (ErrnoException e) {
- assertEquals(OsConstants.ENOENT, e.errno);
- }
- }
-
- // b/27294715
- public void test_recvfrom_concurrentShutdown() throws Exception {
- final FileDescriptor serverFd = Libcore.os.socket(AF_INET, SOCK_DGRAM, 0);
- Libcore.os.bind(serverFd, InetAddress.getByName("127.0.0.1"), 0);
- // Set 4s timeout
- StructTimeval tv = StructTimeval.fromMillis(4000);
- Libcore.os.setsockoptTimeval(serverFd, SOL_SOCKET, SO_RCVTIMEO, tv);
-
- final AtomicReference<Exception> killerThreadException = new AtomicReference<Exception>(null);
- final Thread killer = new Thread(new Runnable() {
- public void run() {
- try {
- Thread.sleep(2000);
- try {
- Libcore.os.shutdown(serverFd, SHUT_RDWR);
- } catch (ErrnoException expected) {
- if (OsConstants.ENOTCONN != expected.errno) {
- killerThreadException.set(expected);
- }
- }
- } catch (Exception ex) {
- killerThreadException.set(ex);
- }
- }
- });
- killer.start();
-
- ByteBuffer buffer = ByteBuffer.allocate(16);
- InetSocketAddress srcAddress = new InetSocketAddress();
- int received = Libcore.os.recvfrom(serverFd, buffer, 0, srcAddress);
- assertTrue(received == 0);
- Libcore.os.close(serverFd);
-
- killer.join();
- assertNull(killerThreadException.get());
- }
-
- public void test_xattr() throws Exception {
- final String NAME_TEST = "user.meow";
-
- final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8);
- final byte[] VALUE_PIE = "pie".getBytes(StandardCharsets.UTF_8);
-
- File file = File.createTempFile("xattr", "test");
- String path = file.getAbsolutePath();
-
- try {
- try {
- Libcore.os.getxattr(path, NAME_TEST);
- fail("Expected ENODATA");
- } catch (ErrnoException e) {
- assertEquals(OsConstants.ENODATA, e.errno);
- }
- assertFalse(Arrays.asList(Libcore.os.listxattr(path)).contains(NAME_TEST));
-
- Libcore.os.setxattr(path, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
- byte[] xattr_create = Libcore.os.getxattr(path, NAME_TEST);
- assertTrue(Arrays.asList(Libcore.os.listxattr(path)).contains(NAME_TEST));
- assertEquals(VALUE_CAKE.length, xattr_create.length);
- assertStartsWith(VALUE_CAKE, xattr_create);
-
- try {
- Libcore.os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_CREATE);
- fail("Expected EEXIST");
- } catch (ErrnoException e) {
- assertEquals(OsConstants.EEXIST, e.errno);
- }
-
- Libcore.os.setxattr(path, NAME_TEST, VALUE_PIE, OsConstants.XATTR_REPLACE);
- byte[] xattr_replace = Libcore.os.getxattr(path, NAME_TEST);
- assertTrue(Arrays.asList(Libcore.os.listxattr(path)).contains(NAME_TEST));
- assertEquals(VALUE_PIE.length, xattr_replace.length);
- assertStartsWith(VALUE_PIE, xattr_replace);
-
- Libcore.os.removexattr(path, NAME_TEST);
- try {
- Libcore.os.getxattr(path, NAME_TEST);
- fail("Expected ENODATA");
- } catch (ErrnoException e) {
- assertEquals(OsConstants.ENODATA, e.errno);
- }
- assertFalse(Arrays.asList(Libcore.os.listxattr(path)).contains(NAME_TEST));
-
- } finally {
- file.delete();
- }
- }
-
- public void test_xattr_NPE() throws Exception {
- File file = File.createTempFile("xattr", "test");
- final String path = file.getAbsolutePath();
- final String NAME_TEST = "user.meow";
- final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8);
-
- // getxattr
- try {
- Libcore.os.getxattr(null, NAME_TEST);
- fail();
- } catch (NullPointerException expected) { }
- try {
- Libcore.os.getxattr(path, null);
- fail();
- } catch (NullPointerException expected) { }
-
- // listxattr
- try {
- Libcore.os.listxattr(null);
- fail();
- } catch (NullPointerException expected) { }
-
- // removexattr
- try {
- Libcore.os.removexattr(null, NAME_TEST);
- fail();
- } catch (NullPointerException expected) { }
- try {
- Libcore.os.removexattr(path, null);
- fail();
- } catch (NullPointerException expected) { }
-
- // setxattr
- try {
- Libcore.os.setxattr(null, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
- fail();
- } catch (NullPointerException expected) { }
- try {
- Libcore.os.setxattr(path, null, VALUE_CAKE, OsConstants.XATTR_CREATE);
- fail();
- } catch (NullPointerException expected) { }
- try {
- Libcore.os.setxattr(path, NAME_TEST, null, OsConstants.XATTR_CREATE);
- fail();
- } catch (NullPointerException expected) { }
- }
-
- public void test_xattr_Errno() throws Exception {
- final String NAME_TEST = "user.meow";
- final byte[] VALUE_CAKE = "cake cake cake".getBytes(StandardCharsets.UTF_8);
-
- // ENOENT, No such file or directory.
- try {
- Libcore.os.getxattr("", NAME_TEST);
- fail();
- } catch (ErrnoException e) {
- assertEquals(ENOENT, e.errno);
- }
- try {
- Libcore.os.listxattr("");
- fail();
- } catch (ErrnoException e) {
- assertEquals(ENOENT, e.errno);
- }
- try {
- Libcore.os.removexattr("", NAME_TEST);
- fail();
- } catch (ErrnoException e) {
- assertEquals(ENOENT, e.errno);
- }
- try {
- Libcore.os.setxattr("", NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
- fail();
- } catch (ErrnoException e) {
- assertEquals(ENOENT, e.errno);
- }
-
- // ENOTSUP, Extended attributes are not supported by the filesystem, or are disabled.
- // Since kernel version 4.9 (or some other version after 4.4), *xattr() methods
- // may set errno to EACCESS instead. This behavior change is likely related to
- // https://patchwork.kernel.org/patch/9294421/ which reimplemented getxattr, setxattr,
- // and removexattr on top of generic handlers.
- final String path = "/proc/self/stat";
- try {
- Libcore.os.setxattr(path, NAME_TEST, VALUE_CAKE, OsConstants.XATTR_CREATE);
- fail();
- } catch (ErrnoException e) {
- assertTrue("Unexpected errno: " + e.errno, e.errno == ENOTSUP || e.errno == EACCES);
- }
- try {
- Libcore.os.getxattr(path, NAME_TEST);
- fail();
- } catch (ErrnoException e) {
- assertEquals(ENOTSUP, e.errno);
- }
- try {
- // Linux listxattr does not set errno.
- Libcore.os.listxattr(path);
- } catch (ErrnoException e) {
- fail();
- }
- try {
- Libcore.os.removexattr(path, NAME_TEST);
- fail();
- } catch (ErrnoException e) {
- assertTrue("Unexpected errno: " + e.errno, e.errno == ENOTSUP || e.errno == EACCES);
- }
- }
-
- public void test_realpath() throws Exception {
- File tmpDir = new File(System.getProperty("java.io.tmpdir"));
- // This is a chicken and egg problem. We have no way of knowing whether
- // the temporary directory or one of its path elements were symlinked, so
- // we'll need this call to realpath.
- String canonicalTmpDir = Libcore.os.realpath(tmpDir.getAbsolutePath());
-
- // Test that "." and ".." are resolved correctly.
- assertEquals(canonicalTmpDir,
- Libcore.os.realpath(canonicalTmpDir + "/./../" + tmpDir.getName()));
-
- // Test that symlinks are resolved correctly.
- File target = new File(tmpDir, "target");
- File link = new File(tmpDir, "link");
- try {
- assertTrue(target.createNewFile());
- Libcore.os.symlink(target.getAbsolutePath(), link.getAbsolutePath());
-
- assertEquals(canonicalTmpDir + "/target",
- Libcore.os.realpath(canonicalTmpDir + "/link"));
- } finally {
- boolean deletedTarget = target.delete();
- boolean deletedLink = link.delete();
- // Asserting this here to provide a definitive reason for
- // a subsequent failure on the same run.
- assertTrue("deletedTarget = " + deletedTarget + ", deletedLink =" + deletedLink,
- deletedTarget && deletedLink);
- }
- }
-
- /**
- * Tests that TCP_USER_TIMEOUT can be set on a TCP socket, but doesn't test
- * that it behaves as expected.
- */
- public void test_socket_tcpUserTimeout_setAndGet() throws Exception {
- final FileDescriptor fd = Libcore.os.socket(AF_INET, SOCK_STREAM, 0);
- try {
- int v = Libcore.os.getsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_USER_TIMEOUT);
- assertEquals(0, v); // system default value
- int newValue = 3000;
- Libcore.os.setsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_USER_TIMEOUT,
- newValue);
- int actualValue = Libcore.os.getsockoptInt(fd, OsConstants.IPPROTO_TCP,
- OsConstants.TCP_USER_TIMEOUT);
- // The kernel can round the requested value based on the HZ setting. We allow up to 10ms
- // difference.
- assertTrue("Returned incorrect timeout:" + actualValue,
- Math.abs(newValue - actualValue) <= 10);
- // No need to reset the value to 0, since we're throwing the socket away
- } finally {
- Libcore.os.close(fd);
- }
- }
-
- public void test_socket_tcpUserTimeout_doesNotWorkOnDatagramSocket() throws Exception {
- final FileDescriptor fd = Libcore.os.socket(AF_INET, SOCK_DGRAM, 0);
- try {
- Libcore.os.setsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_USER_TIMEOUT,
- 3000);
- fail("datagram (connectionless) sockets shouldn't support TCP_USER_TIMEOUT");
- } catch (ErrnoException expected) {
- // expected
- } finally {
- Libcore.os.close(fd);
- }
- }
-
- public void test_socket_sockoptTimeval_readWrite() throws Exception {
- FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
- try {
- StructTimeval v = Libcore.os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO);
- assertEquals(0, v.toMillis()); // system default value
-
- StructTimeval newValue = StructTimeval.fromMillis(3000);
- Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, newValue);
-
- StructTimeval actualValue = Libcore.os.getsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO);
-
- // The kernel can round the requested value based on the HZ setting. We allow up to 10ms
- // difference.
- assertTrue("Returned incorrect timeout:" + actualValue,
- Math.abs(newValue.toMillis() - actualValue.toMillis()) <= 10);
- // No need to reset the value to 0, since we're throwing the socket away
- } finally {
- Libcore.os.close(fd);
- }
- }
-
- public void test_socket_setSockoptTimeval_effective() throws Exception {
- int timeoutValueMillis = 50;
- int allowedTimeoutMillis = 500;
-
- FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
- try {
- StructTimeval tv = StructTimeval.fromMillis(timeoutValueMillis);
- Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, tv);
- Libcore.os.bind(fd, InetAddress.getByName("::1"), 0);
-
- byte[] request = new byte[1];
- long startTime = System.nanoTime();
- expectException(() -> Libcore.os.read(fd, request, 0, request.length),
- ErrnoException.class, EAGAIN, "Expected timeout");
- long endTime = System.nanoTime();
- assertTrue(Duration.ofNanos(endTime - startTime).toMillis() < allowedTimeoutMillis);
- } finally {
- Libcore.os.close(fd);
- }
- }
-
- public void test_socket_setSockoptTimeval_nullFd() throws Exception {
- StructTimeval tv = StructTimeval.fromMillis(500);
- expectException(
- () -> Libcore.os.setsockoptTimeval(null, SOL_SOCKET, SO_RCVTIMEO, tv),
- ErrnoException.class, EBADF, "setsockoptTimeval(null, ...)");
- }
-
- public void test_socket_setSockoptTimeval_fileFd() throws Exception {
- File testFile = createTempFile("test_socket_setSockoptTimeval_invalidFd", "");
- try (FileInputStream fis = new FileInputStream(testFile)) {
- final FileDescriptor fd = fis.getFD();
-
- StructTimeval tv = StructTimeval.fromMillis(500);
- expectException(
- () -> Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, tv),
- ErrnoException.class, ENOTSOCK, "setsockoptTimeval(<file fd>, ...)");
- }
- }
-
- public void test_socket_setSockoptTimeval_badFd() throws Exception {
- StructTimeval tv = StructTimeval.fromMillis(500);
- FileDescriptor invalidFd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
- Libcore.os.close(invalidFd);
-
- expectException(
- () -> Libcore.os.setsockoptTimeval(invalidFd, SOL_SOCKET, SO_RCVTIMEO, tv),
- ErrnoException.class, EBADF, "setsockoptTimeval(<closed fd>, ...)");
- }
-
- public void test_socket_setSockoptTimeval_invalidLevel() throws Exception {
- StructTimeval tv = StructTimeval.fromMillis(500);
- FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
- try {
- expectException(
- () -> Libcore.os.setsockoptTimeval(fd, -1, SO_RCVTIMEO, tv),
- ErrnoException.class, ENOPROTOOPT, "setsockoptTimeval(fd, <invalid level>, ...)");
- } finally {
- Libcore.os.close(fd);
- }
- }
-
- public void test_socket_setSockoptTimeval_invalidOpt() throws Exception {
- StructTimeval tv = StructTimeval.fromMillis(500);
- FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
- try {
- expectException(
- () -> Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, -1, tv),
- ErrnoException.class, ENOPROTOOPT, "setsockoptTimeval(fd, <invalid level>, ...)");
- } finally {
- Libcore.os.close(fd);
- }
- }
-
- public void test_socket_setSockoptTimeval_nullTimeVal() throws Exception {
- FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
- try {
- expectException(
- () -> Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, null),
- NullPointerException.class, null, "setsockoptTimeval(..., null)");
- } finally {
- Libcore.os.close(fd);
- }
- }
-
- public void test_socket_getSockoptTimeval_invalidOption() throws Exception {
- FileDescriptor fd = Libcore.os.socket(AF_INET6, SOCK_STREAM, 0);
- try {
- expectException(
- () -> Libcore.os.getsockoptTimeval(fd, SOL_SOCKET, SO_DEBUG),
- IllegalArgumentException.class, null, "getsockoptTimeval(..., <non-timeval option>)");
- } finally {
- Libcore.os.close(fd);
- }
- }
-
- public void test_if_nametoindex_if_indextoname() throws Exception {
- List<NetworkInterface> nis = Collections.list(NetworkInterface.getNetworkInterfaces());
-
- assertTrue(nis.size() > 0);
- for (NetworkInterface ni : nis) {
- int index = ni.getIndex();
- String name = ni.getName();
- assertEquals(index, Libcore.os.if_nametoindex(name));
- assertTrue(Libcore.os.if_indextoname(index).equals(name));
- }
-
- assertEquals(0, Libcore.os.if_nametoindex("this-interface-does-not-exist"));
- assertEquals(null, Libcore.os.if_indextoname(-1000));
-
- try {
- Libcore.os.if_nametoindex(null);
- fail();
- } catch (NullPointerException expected) { }
- }
-
- private static void assertStartsWith(byte[] expectedContents, byte[] container) {
- for (int i = 0; i < expectedContents.length; i++) {
- if (expectedContents[i] != container[i]) {
- fail("Expected " + Arrays.toString(expectedContents) + " but found "
- + Arrays.toString(expectedContents));
- }
- }
- }
-
- public void test_readlink() throws Exception {
- File path = new File(IoUtils.createTemporaryDirectory("test_readlink"), "symlink");
-
- // ext2 and ext4 have PAGE_SIZE limits on symlink targets.
- // If file encryption is enabled, there's extra overhead to store the
- // size of the encrypted symlink target. There's also an off-by-one
- // in current kernels (and marlin/sailfish where we're seeing this
- // failure are still on 3.18, far from current). Given that we don't
- // really care here, just use 2048 instead. http://b/33306057.
- int size = 2048;
- String xs = "";
- for (int i = 0; i < size - 1; ++i) xs += "x";
-
- Libcore.os.symlink(xs, path.getPath());
-
- assertEquals(xs, Libcore.os.readlink(path.getPath()));
- }
-
- // Address should be correctly set for empty packets. http://b/33481605
- public void test_recvfrom_EmptyPacket() throws Exception {
- try (DatagramSocket ds = new DatagramSocket();
- DatagramSocket srcSock = new DatagramSocket()) {
- srcSock.send(new DatagramPacket(new byte[0], 0, ds.getLocalSocketAddress()));
-
- byte[] recvBuf = new byte[16];
- InetSocketAddress address = new InetSocketAddress();
- int recvCount =
- android.system.Os.recvfrom(ds.getFileDescriptor$(), recvBuf, 0, 16, 0, address);
- assertEquals(0, recvCount);
- assertTrue(address.getAddress().isLoopbackAddress());
- assertEquals(srcSock.getLocalPort(), address.getPort());
- }
- }
-
- public void test_fstat_times() throws Exception {
- File file = File.createTempFile("OsTest", "fstattest");
- FileOutputStream fos = new FileOutputStream(file);
- StructStat structStat1 = Libcore.os.fstat(fos.getFD());
- assertEquals(structStat1.st_mtim.tv_sec, structStat1.st_mtime);
- assertEquals(structStat1.st_ctim.tv_sec, structStat1.st_ctime);
- assertEquals(structStat1.st_atim.tv_sec, structStat1.st_atime);
- Thread.sleep(100);
- fos.write(new byte[]{1,2,3});
- fos.flush();
- StructStat structStat2 = Libcore.os.fstat(fos.getFD());
- fos.close();
-
- assertEquals(-1, structStat1.st_mtim.compareTo(structStat2.st_mtim));
- assertEquals(-1, structStat1.st_ctim.compareTo(structStat2.st_ctim));
- assertEquals(0, structStat1.st_atim.compareTo(structStat2.st_atim));
- }
-
- public void test_getrlimit() throws Exception {
- StructRlimit rlimit = Libcore.os.getrlimit(OsConstants.RLIMIT_NOFILE);
- // We can't really make any assertions about these values since they might vary from
- // device to device and even process to process. We do know that they will be greater
- // than zero, though.
- assertTrue(rlimit.rlim_cur > 0);
- assertTrue(rlimit.rlim_max > 0);
- }
-
- // http://b/65051835
- public void test_pipe2_errno() throws Exception {
- try {
- // flag=-1 is not a valid value for pip2, will EINVAL
- Libcore.os.pipe2(-1);
- fail();
- } catch(ErrnoException expected) {
- }
- }
-
- // http://b/65051835
- public void test_sendfile_errno() throws Exception {
- try {
- // FileDescriptor.out is not open for input, will cause EBADF
- Int64Ref offset = new Int64Ref(10);
- Libcore.os.sendfile(FileDescriptor.out, FileDescriptor.out, offset, 10);
- fail();
- } catch(ErrnoException expected) {
- }
- }
-
- public void test_sendfile_null() throws Exception {
- File in = createTempFile("test_sendfile_null", "Hello, world!");
- try {
- int len = "Hello".length();
- assertEquals("Hello", checkSendfile(ANDROID_SYSTEM_OS_INT64_REF, in, null, len, null));
- assertEquals("Hello", checkSendfile(LIBCORE_OS, in, null, len, null));
- } finally {
- in.delete();
- }
- }
-
- public void test_sendfile_offset() throws Exception {
- File in = createTempFile("test_sendfile_offset", "Hello, world!");
- try {
- // checkSendfile(sendFileImplToUse, in, startOffset, maxBytes, expectedEndOffset)
-
- assertEquals("Hello", checkSendfile(ANDROID_SYSTEM_OS_INT64_REF, in, 0L, 5, 5L));
- assertEquals("Hello", checkSendfile(LIBCORE_OS, in, 0L, 5, 5L));
-
- assertEquals("ello,", checkSendfile(ANDROID_SYSTEM_OS_INT64_REF, in, 1L, 5, 6L));
- assertEquals("ello,", checkSendfile(LIBCORE_OS, in, 1L, 5, 6L));
-
- // At offset 9, only 4 bytes/chars available, even though we're asking for 5.
- assertEquals("rld!", checkSendfile(ANDROID_SYSTEM_OS_INT64_REF, in, 9L, 5, 13L));
- assertEquals("rld!", checkSendfile(LIBCORE_OS, in, 9L, 5, 13L));
-
- assertEquals("", checkSendfile(ANDROID_SYSTEM_OS_INT64_REF, in, 1L, 0, 1L));
- assertEquals("", checkSendfile(LIBCORE_OS, in, 1L, 0, 1L));
- } finally {
- in.delete();
- }
- }
-
- /** Which of the {@code sendfile()} implementations to use. */
- enum SendFileImpl {
- ANDROID_SYSTEM_OS_INT64_REF,
- LIBCORE_OS
- }
-
- private static String checkSendfile(SendFileImpl sendFileImplToUse, File in, Long startOffset,
- int maxBytes, Long expectedEndOffset) throws IOException, ErrnoException {
- File out = File.createTempFile(OsTest.class.getSimpleName() + "_checkSendFile_" +
- sendFileImplToUse, ".out");
- try (FileInputStream inStream = new FileInputStream(in)) {
- FileDescriptor inFd = inStream.getFD();
- try (FileOutputStream outStream = new FileOutputStream(out)) {
- FileDescriptor outFd = outStream.getFD();
- switch (sendFileImplToUse) {
- case ANDROID_SYSTEM_OS_INT64_REF: {
- Int64Ref offset = (startOffset == null) ? null : new Int64Ref(startOffset);
- android.system.Os.sendfile(outFd, inFd, offset, maxBytes);
- assertEquals(expectedEndOffset, offset == null ? null : offset.value);
- break;
- }
- case LIBCORE_OS: {
- Int64Ref offset = (startOffset == null) ? null : new Int64Ref(startOffset);
- libcore.io.Libcore.os.sendfile(outFd, inFd, offset, maxBytes);
- assertEquals(expectedEndOffset, offset == null ? null : offset.value);
- break;
- }
- default: {
+ Os.compareAndSetDefault(defaultOs, null);
fail();
- break;
- }
+ } catch (NullPointerException expected) {
}
- }
- return IoUtils.readFileAsString(out.getPath());
- } finally {
- out.delete();
- }
- }
-
- private static File createTempFile(String namePart, String contents) throws IOException {
- File f = File.createTempFile(OsTest.class.getSimpleName() + namePart, ".in");
- try (FileWriter writer = new FileWriter(f)) {
- writer.write(contents);
- }
- return f;
- }
-
- public void test_odirect() throws Exception {
- File testFile = createTempFile("test_odirect", "");
- try {
- FileDescriptor fd =
- Libcore.os.open(testFile.toString(), O_WRONLY | O_DIRECT, S_IRUSR | S_IWUSR);
- assertNotNull(fd);
- assertTrue(fd.valid());
- int flags = Libcore.os.fcntlVoid(fd, F_GETFL);
- assertTrue("Expected file flags to include " + O_DIRECT + ", actual value: " + flags,
- 0 != (flags & O_DIRECT));
- Libcore.os.close(fd);
- } finally {
- testFile.delete();
- }
- }
-
- public void test_splice() throws Exception {
- FileDescriptor[] pipe = Libcore.os.pipe2(0);
- File in = createTempFile("splice1", "foobar");
- File out = createTempFile("splice2", "");
-
- Int64Ref offIn = new Int64Ref(1);
- Int64Ref offOut = new Int64Ref(0);
-
- // Splice into pipe
- try (FileInputStream streamIn = new FileInputStream(in)) {
- FileDescriptor fdIn = streamIn.getFD();
- long result = Libcore.os.splice(fdIn, offIn, pipe[1], null /* offOut */ , 10 /* len */, 0 /* flags */);
- assertEquals(5, result);
- assertEquals(6, offIn.value);
+ // value hasn't changed
+ assertSame(defaultOs, Os.getDefault());
}
- // Splice from pipe
- try (FileOutputStream streamOut = new FileOutputStream(out)) {
- FileDescriptor fdOut = streamOut.getFD();
- long result = Libcore.os.splice(pipe[0], null /* offIn */, fdOut, offOut, 10 /* len */, 0 /* flags */);
- assertEquals(5, result);
- assertEquals(5, offOut.value);
+ public void testCompareAndSetDefault_comparisonFailure() throws Exception {
+ Os defaultOs = Os.getDefault();
+ Os otherOs = new ForwardingOs(defaultOs) { };
+
+ // current default is non-null, but expect is null
+ assertFalse(Os.compareAndSetDefault(null, otherOs));
+ assertSame(defaultOs, Os.getDefault());
+
+ // current default != expect (both non-null)
+ assertFalse(Os.compareAndSetDefault(otherOs, otherOs));
+ assertSame(defaultOs, Os.getDefault());
}
-
- assertEquals("oobar", IoUtils.readFileAsString(out.getPath()));
-
- Libcore.os.close(pipe[0]);
- Libcore.os.close(pipe[1]);
- }
-
- public void test_splice_errors() throws Exception {
- File in = createTempFile("splice3", "");
- File out = createTempFile("splice4", "");
- FileDescriptor[] pipe = Libcore.os.pipe2(0);
-
- //.fdIn == null
- try {
- Libcore.os.splice(null /* fdIn */, null /* offIn */, pipe[1],
- null /*offOut*/, 10 /* len */, 0 /* flags */);
- fail();
- } catch(ErrnoException expected) {
- assertEquals(EBADF, expected.errno);
- }
-
- //.fdOut == null
- try {
- Libcore.os.splice(pipe[0] /* fdIn */, null /* offIn */, null /* fdOut */,
- null /*offOut*/, 10 /* len */, 0 /* flags */);
- fail();
- } catch(ErrnoException expected) {
- assertEquals(EBADF, expected.errno);
- }
-
- // No pipe fd
- try (FileOutputStream streamOut = new FileOutputStream(out)) {
- try (FileInputStream streamIn = new FileInputStream(in)) {
- FileDescriptor fdIn = streamIn.getFD();
- FileDescriptor fdOut = streamOut.getFD();
- Libcore.os.splice(fdIn, null /* offIn */, fdOut, null /* offOut */, 10 /* len */, 0 /* flags */);
- fail();
- } catch(ErrnoException expected) {
- assertEquals(EINVAL, expected.errno);
- }
- }
-
- Libcore.os.close(pipe[0]);
- Libcore.os.close(pipe[1]);
- }
-
- public void testCloseNullFileDescriptor() throws Exception {
- try {
- Libcore.rawOs.close(null);
- fail();
- } catch (NullPointerException expected) {
- }
- }
-
- public void testSocketpairNullFileDescriptor1() throws Exception {
- try {
- Libcore.rawOs.socketpair(AF_UNIX, SOCK_STREAM, 0, null, new FileDescriptor());
- fail();
- } catch (NullPointerException expected) {
- }
- }
-
- public void testSocketpairNullFileDescriptor2() throws Exception {
- try {
- Libcore.rawOs.socketpair(AF_UNIX, SOCK_STREAM, 0, new FileDescriptor(), null);
- fail();
- } catch (NullPointerException expected) {
- }
- }
-
- public void testSocketpairNullFileDescriptorBoth() throws Exception {
- try {
- Libcore.rawOs.socketpair(AF_UNIX, SOCK_STREAM, 0, null, null);
- fail();
- } catch (NullPointerException expected) {
- }
- }
-
- public void testGetDefault_instanceofBlockguardOs() {
- Os os = Os.getDefault();
- assertTrue(os.getClass().toString(), os instanceof BlockGuardOs);
- }
-
- public void testCompareAndSetDefault_success() throws Exception {
- Os defaultOs = Os.getDefault();
- Os mockOs = Mockito.mock(Os.class);
- try {
- // There shouldn't be any concurrent threads replacing the default Os.
- assertTrue(Os.compareAndSetDefault(defaultOs, mockOs));
- assertSame(mockOs, Os.getDefault());
-
- // Calls to android.system.Os should now reach our custom Os instance.
- android.system.Os.rename("/old/path", "/new/path");
- Mockito.verify(mockOs).rename("/old/path", "/new/path");
- } finally {
- assertTrue(Os.compareAndSetDefault(mockOs, defaultOs));
- assertSame(defaultOs, Os.getDefault());
- }
- }
-
- public void testCompareandSetDefault_null() {
- Os defaultOs = Os.getDefault();
- // update == null is not allowed
- try {
- Os.compareAndSetDefault(defaultOs, null);
- fail();
- } catch (NullPointerException expected) {
- }
- // value hasn't changed
- assertSame(defaultOs, Os.getDefault());
-
- }
-
- public void testCompareAndSetDefault_comparisonFailure() throws Exception {
- Os defaultOs = Os.getDefault();
- Os otherOs = new ForwardingOs(defaultOs) { };
-
- // current default is non-null, but expect is null
- assertFalse(Os.compareAndSetDefault(null, otherOs));
- assertSame(defaultOs, Os.getDefault());
-
- // current default != expect (both non-null)
- assertFalse(Os.compareAndSetDefault(otherOs, otherOs));
- assertSame(defaultOs, Os.getDefault());
- }
-
- public void testInetPtonIpv4() {
- String srcAddress = "127.0.0.1";
- InetAddress inetAddress = Libcore.rawOs.inet_pton(AF_INET, srcAddress);
- assertEquals(srcAddress, inetAddress.getHostAddress());
- }
-
- public void testInetPtonIpv6() {
- String srcAddress = "1123:4567:89ab:cdef:fedc:ba98:7654:3210";
- InetAddress inetAddress = Libcore.rawOs.inet_pton(AF_INET6, srcAddress);
- assertEquals(srcAddress, inetAddress.getHostAddress());
- }
-
- public void testInetPtonInvalidFamily() {
- String srcAddress = "127.0.0.1";
- InetAddress inetAddress = Libcore.rawOs.inet_pton(AF_UNIX, srcAddress);
- assertNull(inetAddress);
- }
-
- public void testInetPtonWrongFamily() {
- String srcAddress = "127.0.0.1";
- InetAddress inetAddress = Libcore.rawOs.inet_pton(AF_INET6, srcAddress);
- assertNull(inetAddress);
- }
-
- public void testInetPtonInvalidData() {
- String srcAddress = "10.1";
- InetAddress inetAddress = Libcore.rawOs.inet_pton(AF_INET, srcAddress);
- assertNull(inetAddress);
- }
}
diff --git a/luni/src/test/java/libcore/libcore/net/MimeUtilsTest.java b/luni/src/test/java/libcore/libcore/net/MimeUtilsTest.java
index cf1e233..26be32b 100644
--- a/luni/src/test/java/libcore/libcore/net/MimeUtilsTest.java
+++ b/luni/src/test/java/libcore/libcore/net/MimeUtilsTest.java
@@ -90,8 +90,13 @@
assertEquals("video/ogg", MimeUtils.guessMimeTypeFromExtension("ogv"));
}
- public void test_70851634() {
- assertEquals("application/vnd.youtube.yt", MimeUtils.guessMimeTypeFromExtension("yt"));
+ public void test_70851634_mimeTypeFromExtension() {
+ assertEquals("video/vnd.youtube.yt", MimeUtils.guessMimeTypeFromExtension("yt"));
+ }
+
+ public void test_70851634_extensionFromMimeType() {
+ assertEquals("yt", MimeUtils.guessExtensionFromMimeType("video/vnd.youtube.yt"));
+ assertEquals("yt", MimeUtils.guessExtensionFromMimeType("application/vnd.youtube.yt"));
}
public void test_112162449_audio() {
diff --git a/luni/src/test/java/libcore/libcore/util/HexEncodingTest.java b/luni/src/test/java/libcore/libcore/util/HexEncodingTest.java
index 4de1a01..800928f 100644
--- a/luni/src/test/java/libcore/libcore/util/HexEncodingTest.java
+++ b/luni/src/test/java/libcore/libcore/util/HexEncodingTest.java
@@ -18,28 +18,69 @@
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.Locale;
+
import junit.framework.TestCase;
import static libcore.util.HexEncoding.decode;
import static libcore.util.HexEncoding.encode;
+import static libcore.util.HexEncoding.encodeToString;
public class HexEncodingTest extends TestCase {
- public void testEncode() {
- final byte[] avocados = "avocados".getBytes(StandardCharsets.UTF_8);
- assertArraysEqual("61766F6361646F73".toCharArray(), encode(avocados));
- assertArraysEqual(avocados, decode(encode(avocados), false));
- // Make sure we can handle lower case hex encodings as well.
- assertArraysEqual(avocados, decode("61766f6361646f73".toCharArray(), false));
+ public void testEncodeByte() {
+ Object[][] testCases = new Object[][] {
+ { 0x01, "01" },
+ { 0x09, "09" },
+ { 0x0A, "0A" },
+ { 0x0F, "0F" },
+ { 0x10, "10" },
+ { 0x1F, "1F" },
+ { 0x20, "20" },
+ { 0x7F, "7F" },
+ { 0x80, "80" },
+ { 0xFF, "FF" },
+ };
+ for (Object[] testCase : testCases) {
+ Number toEncode = (Number) testCase[0];
+ String expected = (String) testCase[1];
+
+ String actualUpper = encodeToString(toEncode.byteValue(), true /* upperCase */);
+ assertEquals(upper(expected), actualUpper);
+
+ String actualLower = encodeToString(toEncode.byteValue(), false /* upperCase */);
+ assertEquals(lower(expected), actualLower);
+ }
+ }
+
+ public void testEncodeBytes() {
+ Object[][] testCases = new Object[][] {
+ { "avocados".getBytes(StandardCharsets.UTF_8), "61766F6361646F73" },
+ };
+
+ for (Object[] testCase : testCases) {
+ byte[] bytes = (byte[]) testCase[0];
+ String encodedLower = lower((String) testCase[1]);
+ String encodedUpper = upper((String) testCase[1]);
+
+ assertArraysEqual(encodedUpper.toCharArray(), encode(bytes));
+ assertArraysEqual(encodedUpper.toCharArray(), encode(bytes, true /* upperCase */));
+ assertArraysEqual(encodedLower.toCharArray(), encode(bytes, false /* upperCase */));
+
+ assertArraysEqual(bytes, decode(encode(bytes), false /* allowSingleChar */));
+
+ // Make sure we can handle lower case hex encodings as well.
+ assertArraysEqual(bytes, decode(encodedLower.toCharArray(), false /* allowSingleChar */));
+ }
}
public void testDecode_allow4Bit() {
assertArraysEqual(new byte[] { 6 }, decode("6".toCharArray(), true));
- assertArraysEqual(new byte[] { 6, 'v' }, decode("676".toCharArray(), true));
+ assertArraysEqual(new byte[] { 6, 0x76 }, decode("676".toCharArray(), true));
}
public void testDecode_disallow4Bit() {
try {
- decode("676".toCharArray(), false);
+ decode("676".toCharArray(), false /* allowSingleChar */);
fail();
} catch (IllegalArgumentException expected) {
}
@@ -47,7 +88,7 @@
public void testDecode_invalid() {
try {
- decode("DEADBARD".toCharArray(), false);
+ decode("DEADBARD".toCharArray(), false /* allowSingleChar */);
fail();
} catch (IllegalArgumentException expected) {
}
@@ -56,13 +97,13 @@
// commons uses Character.isDigit and would successfully decode a string with
// arabic and devanagari characters.
try {
- decode("६१٧٥٥F6361646F73".toCharArray(), false);
+ decode("६१٧٥٥F6361646F73".toCharArray(), false /* allowSingleChar */);
fail();
} catch (IllegalArgumentException expected) {
}
try {
- decode("#%6361646F73".toCharArray(), false);
+ decode("#%6361646F73".toCharArray(), false /* allowSingleChar */);
fail();
} catch (IllegalArgumentException expected) {
}
@@ -75,4 +116,12 @@
private static void assertArraysEqual(byte[] lhs, byte[] rhs) {
assertEquals(Arrays.toString(lhs), Arrays.toString(rhs));
}
+
+ private static String lower(String string) {
+ return string.toLowerCase(Locale.ROOT);
+ }
+
+ private static String upper(String string) {
+ return string.toUpperCase(Locale.ROOT);
+ }
}
diff --git a/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java b/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java
index a151ee4..cddf41b 100644
--- a/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java
+++ b/luni/src/test/java/libcore/libcore/util/ZoneInfoTest.java
@@ -322,6 +322,7 @@
* <p>Newer versions of zic after 2014b introduce an explicit transition at the earliest
* representable time, which is Integer.MIN_VALUE for TZif version 1 files. Previously the type
* used was left implicit and readers were expected to use the first non-DST type in the file.
+ * This extra transition mostly went away again with zic 2018f.
*
* <p>Testing newer zic versions demonstrated that Android had been mishandling the lookup of
* offset for times before the first transition. The logic has been corrected. This test would
@@ -357,8 +358,8 @@
{ offsetToSeconds(type2Offset), 0 },
};
- // Creates a simulation of zic version <= 2014b where there is usually no explicit transition at
- // Integer.MIN_VALUE seconds in TZif version 1 data.
+ // Creates a simulation of zic version <= 2014b or zic version >= 2018f where there is often
+ // no explicit transition at Integer.MIN_VALUE seconds in TZif version 1 data.
{
int[][] transitions = {
{ timeToSeconds(firstRealTransitionTime), 2 /* type 2 */ },
@@ -374,8 +375,8 @@
assertOffsetAt(oldZoneInfo, type2Offset, afterFirstRealTransitionTimes);
}
- // Creates a simulation of zic version > 2014b where there is usually an explicit transition at
- // Integer.MIN_VALUE seconds for TZif version 1 data.
+ // Creates a simulation of zic version > 2014b and zic version < 2018f where there is usually an
+ // explicit transition at Integer.MIN_VALUE seconds for TZif version 1 data.
{
int[][] transitions = {
{ Integer.MIN_VALUE, 1 /* type 1 */ }, // The extra transition added by zic.
diff --git a/mmodules/core_platform_api/api/platform/current-api.txt b/mmodules/core_platform_api/api/platform/current-api.txt
index ccfec23..ec28ddf 100644
--- a/mmodules/core_platform_api/api/platform/current-api.txt
+++ b/mmodules/core_platform_api/api/platform/current-api.txt
@@ -743,6 +743,7 @@
method public void setHiddenApiExemptions(String[]);
method public static void setHiddenApiUsageLogger(dalvik.system.VMRuntime.HiddenApiUsageLogger);
method public static void setNonSdkApiUsageConsumer(java.util.function.Consumer<java.lang.String>);
+ method public static void setProcessDataDirectory(String);
method public static void setProcessPackageName(String);
method @dalvik.annotation.compat.UnsupportedAppUsage public float setTargetHeapUtilization(float);
method public void setTargetSdkVersion(int);
@@ -1225,8 +1226,11 @@
method public static byte[] decode(char[]) throws java.lang.IllegalArgumentException;
method public static byte[] decode(char[], boolean) throws java.lang.IllegalArgumentException;
method public static char[] encode(byte[]);
+ method public static char[] encode(byte[], boolean);
method public static char[] encode(byte[], int, int);
+ method public static String encodeToString(byte, boolean);
method public static String encodeToString(byte[]);
+ method public static String encodeToString(byte[], boolean);
}
public class NativeAllocationRegistry {
diff --git a/ojluni/src/main/java/java/lang/System.java b/ojluni/src/main/java/java/lang/System.java
index 8235bf7..d698446 100644
--- a/ojluni/src/main/java/java/lang/System.java
+++ b/ojluni/src/main/java/java/lang/System.java
@@ -509,13 +509,9 @@
* The byte[] specialized version of arraycopy().
* Note: This method is required for runtime ART compiler optimizations.
* Do not remove or change the signature.
- * Note: Unlike the others, this variant is public due to a dependency we
- * are working on removing. b/74103559
- *
- * @hide
*/
@SuppressWarnings("unused")
- public static void arraycopy(byte[] src, int srcPos, byte[] dst, int dstPos, int length) {
+ private static void arraycopy(byte[] src, int srcPos, byte[] dst, int dstPos, int length) {
if (src == null) {
throw new NullPointerException("src == null");
}
diff --git a/ojluni/src/main/java/java/net/Inet6AddressImpl.java b/ojluni/src/main/java/java/net/Inet6AddressImpl.java
index 6b22f8c..bb722f3 100644
--- a/ojluni/src/main/java/java/net/Inet6AddressImpl.java
+++ b/ojluni/src/main/java/java/net/Inet6AddressImpl.java
@@ -44,6 +44,7 @@
import static android.system.OsConstants.AI_ADDRCONFIG;
import static android.system.OsConstants.EACCES;
import static android.system.OsConstants.ECONNREFUSED;
+import static android.system.OsConstants.EPERM;
import static android.system.OsConstants.NI_NAMEREQD;
import static android.system.OsConstants.ICMP6_ECHO_REPLY;
import static android.system.OsConstants.ICMP_ECHOREPLY;
@@ -144,7 +145,8 @@
// SecurityException to aid in debugging this common mistake.
// http://code.google.com/p/android/issues/detail?id=15722
if (gaiException.getCause() instanceof ErrnoException) {
- if (((ErrnoException) gaiException.getCause()).errno == EACCES) {
+ int errno = ((ErrnoException) gaiException.getCause()).errno;
+ if (errno == EACCES || errno == EPERM) {
throw new SecurityException("Permission denied (missing INTERNET permission?)", gaiException);
}
}
diff --git a/ojluni/src/main/java/java/util/JapaneseImperialCalendar.java b/ojluni/src/main/java/java/util/JapaneseImperialCalendar.java
index c569981..67507bc 100644
--- a/ojluni/src/main/java/java/util/JapaneseImperialCalendar.java
+++ b/ojluni/src/main/java/java/util/JapaneseImperialCalendar.java
@@ -1574,14 +1574,17 @@
zoneOffsets = new int[2];
}
if (tzMask != (ZONE_OFFSET_MASK|DST_OFFSET_MASK)) {
- // Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
+ // BEGIN Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
// if (tz instanceof ZoneInfo) {
// zoneOffset = ((ZoneInfo)tz).getOffsets(time, zoneOffsets);
- // } else {
+ if (tz instanceof libcore.util.ZoneInfo) {
+ zoneOffset = ((libcore.util.ZoneInfo)tz).getOffsetsByUtcTime(time, zoneOffsets);
+ // END Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
+ } else {
zoneOffset = tz.getOffset(time);
zoneOffsets[0] = tz.getRawOffset();
zoneOffsets[1] = zoneOffset - zoneOffsets[0];
- // }
+ }
}
if (tzMask != 0) {
if (isFieldSet(tzMask, ZONE_OFFSET)) {
diff --git a/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java b/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java
index 143fa5e..b71d11a 100644
--- a/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java
+++ b/ojluni/src/main/java/javax/net/ssl/HttpsURLConnection.java
@@ -188,7 +188,6 @@
*/
private static class NoPreloadHolder {
public static HostnameVerifier defaultHostnameVerifier;
- public static final Class<? extends HostnameVerifier> originalDefaultHostnameVerifierClass;
static {
try {
/**
@@ -200,7 +199,6 @@
defaultHostnameVerifier = (HostnameVerifier)
Class.forName("com.android.okhttp.internal.tls.OkHostnameVerifier")
.getField("INSTANCE").get(null);
- originalDefaultHostnameVerifierClass = defaultHostnameVerifier.getClass();
} catch (Exception e) {
throw new AssertionError("Failed to obtain okhttp HostnameVerifier", e);
}
@@ -210,7 +208,7 @@
/**
* The <code>hostnameVerifier</code> for this object.
*/
- protected HostnameVerifier hostnameVerifier;
+ protected HostnameVerifier hostnameVerifier = NoPreloadHolder.defaultHostnameVerifier;
// END Android-changed: Use holder class idiom for a lazily-created OkHttp hostname verifier.
// Android-changed: Modified the documentation to explain side effects / discourage method use.
@@ -329,15 +327,6 @@
* @see #setDefaultHostnameVerifier(HostnameVerifier)
*/
public HostnameVerifier getHostnameVerifier() {
- // Android-added: Use the default verifier if none is set.
- // Note that this also has the side effect of *setting* (if unset)
- // hostnameVerifier to be the default one. It's not clear why this
- // was done (commit abd00f0eaa46f71f98e75a631c268c812d1ec7c1) but
- // we're keeping this behavior for lack of a strong reason to do
- // otherwise.
- if (hostnameVerifier == null) {
- hostnameVerifier = NoPreloadHolder.defaultHostnameVerifier;
- }
return hostnameVerifier;
}
diff --git a/ojluni/src/main/java/sun/util/calendar/AbstractCalendar.java b/ojluni/src/main/java/sun/util/calendar/AbstractCalendar.java
index ead18de..28b10c8 100644
--- a/ojluni/src/main/java/sun/util/calendar/AbstractCalendar.java
+++ b/ojluni/src/main/java/sun/util/calendar/AbstractCalendar.java
@@ -126,12 +126,14 @@
// BEGIN Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
// if (zi instanceof ZoneInfo) {
// zoneOffset = ((ZoneInfo)zi).getOffsets(millis, offsets);
- // } else {
+ if (zi instanceof libcore.util.ZoneInfo) {
+ zoneOffset = ((libcore.util.ZoneInfo) zi).getOffsetsByUtcTime(millis, offsets);
+ // END Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
+ } else {
zoneOffset = zi.getOffset(millis);
offsets[0] = zi.getRawOffset();
offsets[1] = zoneOffset - offsets[0];
- // }
- // END Android-changed: Android doesn't have sun.util.calendar.ZoneInfo.
+ }
// We need to calculate the given millis and time zone
// offset separately for java.util.GregorianCalendar
diff --git a/support/src/test/java/libcore/testing/io/TestIoUtils.java b/support/src/test/java/libcore/testing/io/TestIoUtils.java
index 8e241df..34a2cf7 100644
--- a/support/src/test/java/libcore/testing/io/TestIoUtils.java
+++ b/support/src/test/java/libcore/testing/io/TestIoUtils.java
@@ -17,6 +17,10 @@
package libcore.testing.io;
import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.Random;
public class TestIoUtils {
@@ -25,6 +29,13 @@
private TestIoUtils() {}
/**
+ * Returns the contents of 'path' as a string. The contents are assumed to be UTF-8.
+ */
+ public static String readFileAsString(String absolutePath) throws IOException {
+ return new String(Files.readAllBytes(Paths.get(absolutePath)), StandardCharsets.UTF_8);
+ }
+
+ /**
* Creates a unique new temporary directory under "java.io.tmpdir".
*/
public static File createTemporaryDirectory(String prefix) {