Expose TCP_USER_TIMEOUT as a settable socket option.
This is for systems whose tcp.h defines this constant, which is
true for bionic on recent versions of Android but not the prebuilt
glibc-2.15-4.8 currently on host (checked for host/x86_64).
Exposed android.system.Os.setsocketoptInt(). Added a test that
checks that TCP_USER_TIMEOUT can be set and gotten from stream
sockets. Since this is just piping through behavior from the
kernel, added no tests for the semantics of that socket option.
Test: Checked that the newly added tests pass in cts on Nexus6P.
Bug: 30402085
Change-Id: I5fb9e491ce5c856587423d074e9fa3c4b722d956
diff --git a/luni/src/main/java/android/system/Os.java b/luni/src/main/java/android/system/Os.java
index c43c1ba..63246d6 100644
--- a/luni/src/main/java/android/system/Os.java
+++ b/luni/src/main/java/android/system/Os.java
@@ -466,7 +466,12 @@
/** @hide */ public static void setsockoptByte(FileDescriptor fd, int level, int option, int value) throws ErrnoException { Libcore.os.setsockoptByte(fd, level, option, value); }
/** @hide */ public static void setsockoptIfreq(FileDescriptor fd, int level, int option, String value) throws ErrnoException { Libcore.os.setsockoptIfreq(fd, level, option, value); }
- /** @hide */ public static void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException { Libcore.os.setsockoptInt(fd, level, option, value); }
+
+ /**
+ * See <a href="http://man7.org/linux/man-pages/man2/setsockopt.2.html">setsockopt(2)</a>.
+ */
+ public static void setsockoptInt(FileDescriptor fd, int level, int option, int value) throws ErrnoException { Libcore.os.setsockoptInt(fd, level, option, value); }
+
/** @hide */ public static void setsockoptIpMreqn(FileDescriptor fd, int level, int option, int value) throws ErrnoException { Libcore.os.setsockoptIpMreqn(fd, level, option, value); }
/** @hide */ public static void setsockoptGroupReq(FileDescriptor fd, int level, int option, StructGroupReq value) throws ErrnoException { Libcore.os.setsockoptGroupReq(fd, level, option, value); }
/** @hide */ public static void setsockoptGroupSourceReq(FileDescriptor fd, int level, int option, StructGroupSourceReq value) throws ErrnoException { Libcore.os.setsockoptGroupSourceReq(fd, level, option, value); }
diff --git a/luni/src/main/java/android/system/OsConstants.java b/luni/src/main/java/android/system/OsConstants.java
index 31f84e9..140d71d 100644
--- a/luni/src/main/java/android/system/OsConstants.java
+++ b/luni/src/main/java/android/system/OsConstants.java
@@ -495,6 +495,7 @@
public static final int S_IXOTH = placeholder();
public static final int S_IXUSR = placeholder();
public static final int TCP_NODELAY = placeholder();
+ public static final int TCP_USER_TIMEOUT = placeholder();
/** @hide */ public static final int TIOCOUTQ = placeholder();
/** @hide */ public static final int UNIX_PATH_MAX = placeholder();
public static final int WCONTINUED = placeholder();
diff --git a/luni/src/main/native/android_system_OsConstants.cpp b/luni/src/main/native/android_system_OsConstants.cpp
index 1293fe7..e6aff37 100644
--- a/luni/src/main/native/android_system_OsConstants.cpp
+++ b/luni/src/main/native/android_system_OsConstants.cpp
@@ -551,6 +551,9 @@
initConstant(env, c, "S_IXOTH", S_IXOTH);
initConstant(env, c, "S_IXUSR", S_IXUSR);
initConstant(env, c, "TCP_NODELAY", TCP_NODELAY);
+#if defined(TCP_USER_TIMEOUT)
+ initConstant(env, c, "TCP_USER_TIMEOUT", TCP_USER_TIMEOUT);
+#endif
initConstant(env, c, "TIOCOUTQ", TIOCOUTQ);
// UNIX_PATH_MAX is mentioned in some versions of unix(7), but not actually declared.
initConstant(env, c, "UNIX_PATH_MAX", sizeof(sockaddr_un::sun_path));
diff --git a/luni/src/test/java/libcore/android/system/OsConstantsTest.java b/luni/src/test/java/libcore/android/system/OsConstantsTest.java
index 080464d..a98707c 100644
--- a/luni/src/test/java/libcore/android/system/OsConstantsTest.java
+++ b/luni/src/test/java/libcore/android/system/OsConstantsTest.java
@@ -30,4 +30,9 @@
assertTrue(OsConstants.IFA_F_TENTATIVE > 0);
}
+
+ // introduced for http://b/30402085
+ public void testTcpUserTimeoutIsDefined() {
+ assertTrue(OsConstants.TCP_USER_TIMEOUT > 0);
+ }
}
diff --git a/luni/src/test/java/libcore/io/OsTest.java b/luni/src/test/java/libcore/io/OsTest.java
index 5a2b95c..f34d099 100644
--- a/luni/src/test/java/libcore/io/OsTest.java
+++ b/luni/src/test/java/libcore/io/OsTest.java
@@ -571,6 +571,39 @@
}
}
+ /**
+ * 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);
+ assertEquals(newValue, Libcore.os.getsockoptInt(fd, OsConstants.IPPROTO_TCP,
+ OsConstants.TCP_USER_TIMEOUT));
+ // 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);
+ }
+ }
+
private static void assertStartsWith(byte[] expectedContents, byte[] container) {
for (int i = 0; i < expectedContents.length; i++) {
if (expectedContents[i] != container[i]) {