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]) {