Merge "Add UDP_GRO and UDP_SEGMENT Socket Options"
diff --git a/api/current.txt b/api/current.txt
index ac9f19a..e849e72 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -477,6 +477,7 @@
     field public static final int SOCK_SEQPACKET;
     field public static final int SOCK_STREAM;
     field public static final int SOL_SOCKET;
+    field public static final int SOL_UDP;
     field public static final int SO_BINDTODEVICE;
     field public static final int SO_BROADCAST;
     field public static final int SO_DEBUG;
@@ -532,6 +533,8 @@
     field public static final int S_IXUSR;
     field public static final int TCP_NODELAY;
     field public static final int TCP_USER_TIMEOUT;
+    field public static final int UDP_GRO;
+    field public static final int UDP_SEGMENT;
     field public static final int WCONTINUED;
     field public static final int WEXITED;
     field public static final int WNOHANG;
diff --git a/luni/src/main/java/android/system/OsConstants.java b/luni/src/main/java/android/system/OsConstants.java
index 1b0dc16..7f2182c 100644
--- a/luni/src/main/java/android/system/OsConstants.java
+++ b/luni/src/main/java/android/system/OsConstants.java
@@ -560,6 +560,7 @@
     public static final int SOCK_SEQPACKET = placeholder();
     public static final int SOCK_STREAM = placeholder();
     public static final int SOL_SOCKET = placeholder();
+    public static final int SOL_UDP = placeholder();
     public static final int SO_BINDTODEVICE = placeholder();
     public static final int SO_BROADCAST = placeholder();
     public static final int SO_DEBUG = placeholder();
@@ -632,6 +633,8 @@
     public static final int S_IXUSR = placeholder();
     public static final int TCP_NODELAY = placeholder();
     public static final int TCP_USER_TIMEOUT = placeholder();
+    public static final int UDP_GRO = placeholder();
+    public static final int UDP_SEGMENT = placeholder();
     /** @hide */
     @UnsupportedAppUsage
     @libcore.api.CorePlatformApi
diff --git a/luni/src/main/native/android_system_OsConstants.cpp b/luni/src/main/native/android_system_OsConstants.cpp
index 6cf2c8b..450995b 100644
--- a/luni/src/main/native/android_system_OsConstants.cpp
+++ b/luni/src/main/native/android_system_OsConstants.cpp
@@ -522,6 +522,9 @@
     initConstant(env, c, "SOCK_SEQPACKET", SOCK_SEQPACKET);
     initConstant(env, c, "SOCK_STREAM", SOCK_STREAM);
     initConstant(env, c, "SOL_SOCKET", SOL_SOCKET);
+#if defined(SOL_UDP)
+    initConstant(env, c, "SOL_UDP", SOL_UDP);
+#endif
 #if defined(SO_BINDTODEVICE)
     initConstant(env, c, "SO_BINDTODEVICE", SO_BINDTODEVICE);
 #endif
@@ -598,6 +601,12 @@
     initConstant(env, c, "UDP_ENCAP", UDP_ENCAP);
     initConstant(env, c, "UDP_ENCAP_ESPINUDP_NON_IKE", UDP_ENCAP_ESPINUDP_NON_IKE);
     initConstant(env, c, "UDP_ENCAP_ESPINUDP", UDP_ENCAP_ESPINUDP);
+#if defined(UDP_GRO)
+    initConstant(env, c, "UDP_GRO", UDP_GRO);
+#endif
+#if defined(UDP_SEGMENT)
+    initConstant(env, c, "UDP_SEGMENT", UDP_SEGMENT);
+#endif
     // 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));
     initConstant(env, c, "WCONTINUED", WCONTINUED);
diff --git a/luni/src/test/java/libcore/android/system/OsTest.java b/luni/src/test/java/libcore/android/system/OsTest.java
index 355d340..9cc25e9 100644
--- a/luni/src/test/java/libcore/android/system/OsTest.java
+++ b/luni/src/test/java/libcore/android/system/OsTest.java
@@ -52,6 +52,8 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import junit.framework.TestCase;
 
@@ -1044,6 +1046,50 @@
         }
     }
 
+    private double getKernelVersion() {
+        // Example:
+        // 4.9.29-g958411d --> 4.9
+        String release = Os.uname().release;
+        Matcher m = Pattern.compile("^(\\d+)\\.(\\d+)").matcher(release);
+        assertTrue("No pattern in release string: " + release, m.find());
+        double version = Double.parseDouble(m.group(1) + "." + m.group(2));
+        return version;
+    }
+
+    public void test_socket_udpGro_setAndGet() throws Exception {
+        // UDP GRO not required to be enabled on kernels prior to 5.4
+        double kernelVersion = getKernelVersion();
+        if (kernelVersion < 5.4) return;
+
+        final FileDescriptor fd = Os.socket(AF_INET6, SOCK_DGRAM, 0);
+        try {
+            assertEquals(0, Os.getsockoptInt(fd, IPPROTO_UDP, UDP_GRO));
+
+            final int setValue = 1;
+            Os.setsockoptInt(fd, IPPROTO_UDP, UDP_GRO, setValue);
+            assertEquals(setValue, Os.getsockoptInt(fd, IPPROTO_UDP, UDP_GRO));
+        } finally {
+            Os.close(fd);
+        }
+    }
+
+    public void test_socket_udpGso_setAndGet() throws Exception {
+        // UDP GSO not required to be enabled on kernels prior to 4.19.
+        double kernelVersion = getKernelVersion();
+        if (kernelVersion < 4.19) return;
+
+        final FileDescriptor fd = Os.socket(AF_INET, SOCK_DGRAM, 0);
+        try {
+            assertEquals(0, Os.getsockoptInt(fd, IPPROTO_UDP, UDP_SEGMENT));
+
+            final int setValue = 1452;
+            Os.setsockoptInt(fd, IPPROTO_UDP, UDP_SEGMENT, setValue);
+            assertEquals(setValue, Os.getsockoptInt(fd, IPPROTO_UDP, UDP_SEGMENT));
+        } finally {
+            Os.close(fd);
+        }
+    }
+
     /**
      * Tests that TCP_USER_TIMEOUT can be set on a TCP socket, but doesn't test
      * that it behaves as expected.