Add GSO and GRO Support Via recvmsg and sendmsg

Create new wrappers for sendmsg and recvmsg so that apps
that know about UDP GRO can call these directly.

Bug: 172945027
Test: atest libcore.android.system.OsTest#test_sendmsg_af_inet_4K
libcore.android.system.OsTest#test_sendmsg_af_inet6_4K
libcore.android.system.OsTest#test_sendmsg_af_inet_16K_recvparts
libcore.android.system.OsTest#test_sendmsg_af_inet_16K_reciveall
libcore.android.system.OsTest#test_sendmsg_af_inet_16K_reciveall_without_recv_msgname
libcore.android.system.OsTest#test_sendmsg_af_inet_16K_without_send_msgcontrl
libcore.android.system.OsTest#test_sendmsg_af_inet_abnormal

Change-Id: Ib65bfba7361139d49049c5f2cae9f2b0da6e27cb
diff --git a/api/current.txt b/api/current.txt
old mode 100644
new mode 100755
index c3e758c..4abf0c5
--- a/api/current.txt
+++ b/api/current.txt
@@ -85,10 +85,12 @@
     method public static int readv(java.io.FileDescriptor, Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
     method public static int recvfrom(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
     method public static int recvfrom(java.io.FileDescriptor, byte[], int, int, int, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
+    method public static int recvmsg(@NonNull java.io.FileDescriptor, @NonNull android.system.StructMsghdr, int) throws android.system.ErrnoException, java.net.SocketException;
     method public static void remove(String) throws android.system.ErrnoException;
     method public static void removexattr(String, String) throws android.system.ErrnoException;
     method public static void rename(String, String) throws android.system.ErrnoException;
     method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
+    method public static int sendmsg(@NonNull java.io.FileDescriptor, @NonNull android.system.StructMsghdr, int) throws android.system.ErrnoException, java.net.SocketException;
     method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
     method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
     method public static int sendto(@NonNull java.io.FileDescriptor, @NonNull byte[], int, int, int, @Nullable java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException;
@@ -636,6 +638,22 @@
     field public static final int _SC_XOPEN_XCU_VERSION;
   }
 
+  public final class StructCmsghdr {
+    ctor public StructCmsghdr(int, int, short);
+    ctor public StructCmsghdr(int, int, @NonNull byte[]);
+    field @NonNull public final byte[] cmsg_data;
+    field public final int cmsg_level;
+    field public final int cmsg_type;
+  }
+
+  public final class StructMsghdr {
+    ctor public StructMsghdr(@Nullable java.net.SocketAddress, @NonNull java.nio.ByteBuffer[], @Nullable android.system.StructCmsghdr[], int);
+    field @Nullable public android.system.StructCmsghdr[] msg_control;
+    field public int msg_flags;
+    field @NonNull public final java.nio.ByteBuffer[] msg_iov;
+    field @Nullable public java.net.SocketAddress msg_name;
+  }
+
   public final class StructPollfd {
     ctor public StructPollfd();
     field public short events;
diff --git a/luni/src/main/java/android/system/Os.java b/luni/src/main/java/android/system/Os.java
old mode 100644
new mode 100755
index 80a700c..d999ad7
--- a/luni/src/main/java/android/system/Os.java
+++ b/luni/src/main/java/android/system/Os.java
@@ -510,6 +510,11 @@
     public static int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return Libcore.os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); }
 
     /**
+     * See <a href="http://man7.org/linux/man-pages/man2/recvmsg.2.html">recvmsg(2)</a>.
+     */
+    public static int recvmsg(@NonNull FileDescriptor fd, @NonNull StructMsghdr msg, int flags) throws ErrnoException, SocketException { return Libcore.os.recvmsg(fd, msg, flags); }
+
+    /**
      * See <a href="http://man7.org/linux/man-pages/man3/remove.3.html">remove(3)</a>.
      */
     public static void remove(String path) throws ErrnoException { Libcore.os.remove(path); }
@@ -532,6 +537,13 @@
     }
 
     /**
+     * See <a href="http://man7.org/linux/man-pages/man2/sendmsg.2.html">sendmsg(2)</a>.
+     */
+    public static int sendmsg(@NonNull FileDescriptor fd, @NonNull StructMsghdr msg, int flags) throws ErrnoException, SocketException {
+        return Libcore.os.sendmsg(fd, msg, flags);
+    }
+
+    /**
      * See <a href="http://man7.org/linux/man-pages/man2/sendto.2.html">sendto(2)</a>.
      */
     public static int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return Libcore.os.sendto(fd, buffer, flags, inetAddress, port); }
diff --git a/luni/src/main/java/android/system/StructCmsghdr.java b/luni/src/main/java/android/system/StructCmsghdr.java
new file mode 100755
index 0000000..be77481
--- /dev/null
+++ b/luni/src/main/java/android/system/StructCmsghdr.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *   *Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *   *Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials provided
+ *    with the distribution.
+ *   *Neither the name of The Linux Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package android.system;
+
+import libcore.util.NonNull;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Corresponds to C's {@code struct cmsghdr}.
+ *
+ */
+public final class StructCmsghdr {
+    /** Originating protocol */
+    public final int cmsg_level;
+
+    /** Protocol-specific type */
+    public final int cmsg_type;
+
+    /** message data sent/received */
+    @NonNull public final byte[] cmsg_data;
+
+    public StructCmsghdr(int cmsg_level, int cmsg_type, short value) {
+        // Short.Size unit is bits, ByteBuffer data unit is bytes
+        ByteBuffer buf = ByteBuffer.allocate(Short.SIZE / 8);
+        buf.order(ByteOrder.nativeOrder());
+        buf.putShort(value);
+
+        this.cmsg_level = cmsg_level;
+        this.cmsg_type = cmsg_type;
+        this.cmsg_data = buf.array();
+    }
+
+    public StructCmsghdr(int cmsg_level, int cmsg_type, @NonNull byte[] value) {
+        this.cmsg_level = cmsg_level;
+        this.cmsg_type = cmsg_type;
+        this.cmsg_data = value;
+    }
+
+}
diff --git a/luni/src/main/java/android/system/StructMsghdr.java b/luni/src/main/java/android/system/StructMsghdr.java
new file mode 100755
index 0000000..60a4d97
--- /dev/null
+++ b/luni/src/main/java/android/system/StructMsghdr.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *    *Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *    *Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following
+ *     disclaimer in the documentation and/or other materials provided
+ *     with the distribution.
+ *    *Neither the name of The Linux Foundation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+package android.system;
+
+import libcore.util.NonNull;
+import libcore.util.Nullable;
+
+import java.net.SocketAddress;
+import java.nio.ByteBuffer;
+
+/**
+ * Corresponds to C's {@code struct msghdr}
+ *
+ */
+public final class StructMsghdr{
+    /**
+     * Optional address.
+     * <p>Sendmsg: Caller must populate to specify the target address for a datagram, or pass
+     * {@code null} to send to the destination of an already-connected socket.
+     * Recvmsg: Populated by the system to specify the source address.
+     */
+    @Nullable public SocketAddress msg_name;
+
+    /** Scatter/gather array */
+    @NonNull public final ByteBuffer[] msg_iov;
+
+    /** Ancillary data */
+    @Nullable public StructCmsghdr[] msg_control;
+
+    /** Flags on received message. */
+    public int msg_flags;
+
+    /**
+     * Constructs an instance with the given field values
+     */
+    public StructMsghdr(@Nullable SocketAddress msg_name, @NonNull ByteBuffer[] msg_iov,
+                        @Nullable StructCmsghdr[] msg_control, int msg_flags) {
+        this.msg_name = msg_name;
+        this.msg_iov = msg_iov;
+        this.msg_control = msg_control;
+        this.msg_flags = msg_flags;
+    }
+}
diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java
old mode 100644
new mode 100755
index 3112c16..5b331b1
--- a/luni/src/main/java/libcore/io/BlockGuardOs.java
+++ b/luni/src/main/java/libcore/io/BlockGuardOs.java
@@ -23,6 +23,7 @@
 import android.system.OsConstants;
 import android.system.StructAddrinfo;
 import android.system.StructLinger;
+import android.system.StructMsghdr;
 import android.system.StructPollfd;
 import android.system.StructStat;
 import android.system.StructStatVfs;
@@ -353,6 +354,11 @@
         return super.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress);
     }
 
+    @Override public int recvmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException {
+        BlockGuard.getThreadPolicy().onNetwork();
+        return super.recvmsg(fd, msg, flags);
+    }
+
     @UnsupportedAppUsage
     @Override public void remove(String path) throws ErrnoException {
         BlockGuard.getThreadPolicy().onWriteToDisk();
@@ -373,6 +379,11 @@
         return super.sendfile(outFd, inFd, offset, byteCount);
     }
 
+    @Override public int sendmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException {
+        BlockGuard.getThreadPolicy().onNetwork();
+        return super.sendmsg(fd, msg, flags);
+    }
+
     @Override public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException {
         BlockGuard.getThreadPolicy().onNetwork();
         return super.sendto(fd, buffer, flags, inetAddress, port);
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
old mode 100644
new mode 100755
index 7e8a40e..346a199
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -26,6 +26,7 @@
 import android.system.StructGroupReq;
 import android.system.StructIfaddrs;
 import android.system.StructLinger;
+import android.system.StructMsghdr;
 import android.system.StructPasswd;
 import android.system.StructPollfd;
 import android.system.StructRlimit;
@@ -180,6 +181,7 @@
     public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException { return os.readv(fd, buffers, offsets, byteCounts); }
     public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return os.recvfrom(fd, buffer, flags, srcAddress); }
     public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); }
+    public int recvmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException { return os.recvmsg(fd, msg, flags); }
     @UnsupportedAppUsage
     @libcore.api.CorePlatformApi
     public void remove(String path) throws ErrnoException { os.remove(path); }
@@ -189,6 +191,7 @@
     @libcore.api.CorePlatformApi
     public void rename(String oldPath, String newPath) throws ErrnoException { os.rename(oldPath, newPath); }
     public long sendfile(FileDescriptor outFd, FileDescriptor inFd, Int64Ref offset, long byteCount) throws ErrnoException { return os.sendfile(outFd, inFd, offset, byteCount); }
+    public int sendmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException { return os.sendmsg(fd, msg, flags); }
     public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return os.sendto(fd, buffer, flags, inetAddress, port); }
     public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException { return os.sendto(fd, bytes, byteOffset, byteCount, flags, inetAddress, port); }
     public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException { return os.sendto(fd, bytes, byteOffset, byteCount, flags, address); }
diff --git a/luni/src/main/java/libcore/io/Linux.java b/luni/src/main/java/libcore/io/Linux.java
old mode 100644
new mode 100755
index 37e5f5e..4ecb785
--- a/luni/src/main/java/libcore/io/Linux.java
+++ b/luni/src/main/java/libcore/io/Linux.java
@@ -26,6 +26,7 @@
 import android.system.StructGroupReq;
 import android.system.StructIfaddrs;
 import android.system.StructLinger;
+import android.system.StructMsghdr;
 import android.system.StructPasswd;
 import android.system.StructPollfd;
 import android.system.StructRlimit;
@@ -210,10 +211,12 @@
         return recvfromBytes(fd, bytes, byteOffset, byteCount, flags, srcAddress);
     }
     private native int recvfromBytes(FileDescriptor fd, Object buffer, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
+    public native int recvmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException;
     public native void remove(String path) throws ErrnoException;
     public native void removexattr(String path, String name) throws ErrnoException;
     public native void rename(String oldPath, String newPath) throws ErrnoException;
     public native long sendfile(FileDescriptor outFd, FileDescriptor inFd, Int64Ref offset, long byteCount) throws ErrnoException;
+    public native int sendmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException;
     public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException {
         final int bytesSent;
         final int position = buffer.position();
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
old mode 100644
new mode 100755
index 87e5d78..341e917
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -26,6 +26,7 @@
 import android.system.StructGroupReq;
 import android.system.StructIfaddrs;
 import android.system.StructLinger;
+import android.system.StructMsghdr;
 import android.system.StructPasswd;
 import android.system.StructPollfd;
 import android.system.StructRlimit;
@@ -153,10 +154,12 @@
     public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException;
     public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
     public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
+    public int recvmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException;
     @UnsupportedAppUsage
     public void remove(String path) throws ErrnoException;
     public void removexattr(String path, String name) throws ErrnoException;
     public void rename(String oldPath, String newPath) throws ErrnoException;
+    public int sendmsg(FileDescriptor fd, StructMsghdr msg, int flags) throws ErrnoException, SocketException;
     public int sendto(FileDescriptor fd, ByteBuffer buffer, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
     public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetAddress inetAddress, int port) throws ErrnoException, SocketException;
     public int sendto(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, SocketAddress address) throws ErrnoException, SocketException;
diff --git a/luni/src/main/native/JniConstants.cpp b/luni/src/main/native/JniConstants.cpp
old mode 100644
new mode 100755
index 8e0a0f6..fc1607f
--- a/luni/src/main/native/JniConstants.cpp
+++ b/luni/src/main/native/JniConstants.cpp
@@ -45,6 +45,7 @@
 
 // Constants
 jclass booleanClass;
+jclass byteBufferClass;
 jclass doubleClass;
 jclass errnoExceptionClass;
 jclass fileDescriptorClass;
@@ -63,9 +64,11 @@
 jclass primitiveByteArrayClass;
 jclass stringClass;
 jclass structAddrinfoClass;
+jclass structCmsghdrClass;
 jclass structGroupReqClass;
 jclass structIfaddrsClass;
 jclass structLingerClass;
+jclass structMsghdrClass;
 jclass structPasswdClass;
 jclass structPollfdClass;
 jclass structStatClass;
@@ -91,6 +94,7 @@
     }
 
     booleanClass = findClass(env, "java/lang/Boolean");
+    byteBufferClass = findClass(env, "java/nio/ByteBuffer");
     doubleClass = findClass(env, "java/lang/Double");
     errnoExceptionClass = findClass(env, "android/system/ErrnoException");
     fileDescriptorClass = findClass(env, "java/io/FileDescriptor");
@@ -109,9 +113,11 @@
     primitiveByteArrayClass = findClass(env, "[B");
     stringClass = findClass(env, "java/lang/String");
     structAddrinfoClass = findClass(env, "android/system/StructAddrinfo");
+    structCmsghdrClass = findClass(env, "android/system/StructCmsghdr");
     structGroupReqClass = findClass(env, "android/system/StructGroupReq");
     structIfaddrsClass = findClass(env, "android/system/StructIfaddrs");
     structLingerClass = findClass(env, "android/system/StructLinger");
+    structMsghdrClass = findClass(env, "android/system/StructMsghdr");
     structPasswdClass = findClass(env, "android/system/StructPasswd");
     structPollfdClass = findClass(env, "android/system/StructPollfd");
     structStatClass = findClass(env, "android/system/StructStat");
@@ -150,6 +156,11 @@
     return booleanClass;
 }
 
+jclass JniConstants::GetByteBufferClass(JNIEnv* env) {
+    EnsureJniConstantsInitialized(env);
+    return byteBufferClass;
+}
+
 jclass JniConstants::GetDoubleClass(JNIEnv* env) {
     EnsureJniConstantsInitialized(env);
     return doubleClass;
@@ -240,6 +251,11 @@
     return structAddrinfoClass;
 }
 
+jclass JniConstants::GetStructCmsghdrClass(JNIEnv* env) {
+    EnsureJniConstantsInitialized(env);
+    return structCmsghdrClass;
+}
+
 jclass JniConstants::GetStructGroupReqClass(JNIEnv* env) {
     EnsureJniConstantsInitialized(env);
     return structGroupReqClass;
@@ -255,6 +271,11 @@
     return structLingerClass;
 }
 
+jclass JniConstants::GetStructMsghdrClass(JNIEnv* env) {
+    EnsureJniConstantsInitialized(env);
+    return structMsghdrClass;
+}
+
 jclass JniConstants::GetStructPasswdClass(JNIEnv* env) {
     EnsureJniConstantsInitialized(env);
     return structPasswdClass;
diff --git a/luni/src/main/native/JniConstants.h b/luni/src/main/native/JniConstants.h
old mode 100644
new mode 100755
index f77095e..1aa801e
--- a/luni/src/main/native/JniConstants.h
+++ b/luni/src/main/native/JniConstants.h
@@ -30,6 +30,7 @@
     static void Invalidate();
 
     static jclass GetBooleanClass(JNIEnv* env);
+    static jclass GetByteBufferClass(JNIEnv* env);
     static jclass GetDoubleClass(JNIEnv* env);
     static jclass GetErrnoExceptionClass(JNIEnv* env);
     static jclass GetFileDescriptorClass(JNIEnv* env);
@@ -48,10 +49,12 @@
     static jclass GetPrimitiveByteArrayClass(JNIEnv* env);
     static jclass GetStringClass(JNIEnv* env);
     static jclass GetStructAddrinfoClass(JNIEnv* env);
+    static jclass GetStructCmsghdrClass(JNIEnv* env);
     static jclass GetStructFlockClass(JNIEnv* env);
     static jclass GetStructGroupReqClass(JNIEnv* env);
     static jclass GetStructIfaddrsClass(JNIEnv* env);
     static jclass GetStructLingerClass(JNIEnv* env);
+    static jclass GetStructMsghdrClass(JNIEnv* env);
     static jclass GetStructPasswdClass(JNIEnv* env);
     static jclass GetStructPollfdClass(JNIEnv* env);
     static jclass GetStructStatClass(JNIEnv* env);
diff --git a/luni/src/main/native/NetworkUtilities.cpp b/luni/src/main/native/NetworkUtilities.cpp
old mode 100644
new mode 100755
index b765c4e..cd695b7
--- a/luni/src/main/native/NetworkUtilities.cpp
+++ b/luni/src/main/native/NetworkUtilities.cpp
@@ -21,6 +21,7 @@
 #include <arpa/inet.h>
 #include <fcntl.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <sys/socket.h>
 #include <sys/un.h>
@@ -30,6 +31,9 @@
 
 #include "JniConstants.h"
 
+#include <log/log.h>
+
+
 jobject sockaddrToInetAddress(JNIEnv* env, const sockaddr_storage& ss, jint* port) {
     // Convert IPv4-mapped IPv6 addresses to IPv4 addresses.
     // The RI states "Java will never return an IPv4-mapped address".
@@ -199,6 +203,325 @@
     return inetAddressToSockaddr(env, inetAddress, port, ss, sa_len, true);
 }
 
+/*
+ * Fill msg_contrl data from structCmsghdr[]
+ */
+bool structCmsghdrArrayToMsgcontrol(JNIEnv* env, jobjectArray cmsgArray, struct msghdr& mhdr) {
+    struct cmsghdr *cm = NULL;
+    int i = 0;
+    jclass structCmsghdrClass = JniConstants::GetStructCmsghdrClass(env);
+    static jfieldID cmsgDataFid = env->GetFieldID(structCmsghdrClass, "cmsg_data", "[B");
+    if (!cmsgDataFid) {
+        return false;
+    }
+    static jfieldID cmsgLevelFid = env->GetFieldID(structCmsghdrClass, "cmsg_level", "I");
+    if (!cmsgLevelFid) {
+        return false;
+    }
+    static jfieldID cmsgTypeFid = env->GetFieldID(structCmsghdrClass, "cmsg_type", "I");
+    if (!cmsgTypeFid) {
+        return false;
+    }
+
+    int cmsgArrayize = env->GetArrayLength(cmsgArray);
+    if (!cmsgArrayize) {
+        // Return true since msg_control is optional parameter.
+        return true;
+    }
+
+    for (int i = 0; i < cmsgArrayize; ++i) {
+        ScopedLocalRef<jobject> cmsg(env, env->GetObjectArrayElement(cmsgArray, i));
+        ScopedLocalRef<jbyteArray> cmsgData(env, reinterpret_cast<jbyteArray>(
+                env->GetObjectField(cmsg.get(), cmsgDataFid)));
+
+        mhdr.msg_controllen += CMSG_SPACE(env->GetArrayLength(cmsgData.get()));
+    }
+
+    mhdr.msg_control = (unsigned char*)malloc(mhdr.msg_controllen);
+    if (mhdr.msg_control == NULL) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", "Out of memory");
+        return false;
+    }
+    memset(mhdr.msg_control, 0, mhdr.msg_controllen);
+
+    // Loop over each cmsghdr header and set data.
+    for (cm = CMSG_FIRSTHDR(&mhdr), i = 0; (cm != NULL); cm = CMSG_NXTHDR(&mhdr, cm), ++i)
+    {
+        size_t data_len = 0;
+        ScopedLocalRef<jobject> cmsg(env, env->GetObjectArrayElement(cmsgArray, i));
+        ScopedLocalRef<jbyteArray> cmsgData(env, reinterpret_cast<jbyteArray>(
+                env->GetObjectField(cmsg.get(), cmsgDataFid)));
+
+        cm->cmsg_level = env->GetIntField(cmsg.get(), cmsgLevelFid);
+        cm->cmsg_type  = env->GetIntField(cmsg.get(), cmsgTypeFid);
+        data_len = env->GetArrayLength(cmsgData.get());
+        cm->cmsg_len   = CMSG_LEN(data_len);
+        env->GetByteArrayRegion(cmsgData.get(), 0,
+                data_len, reinterpret_cast<jbyte*>CMSG_DATA(cm));
+    }
+    return true;
+}
+
+/*
+ * Fill structCmsghdr[] data per msgcontrol data, used when recvmsg
+ */
+bool msgcontrolToStructCmsghdrArray(JNIEnv* env, jobject structMsghdr, struct msghdr& mhdr) {
+    struct cmsghdr *cm = NULL;
+    int i = 0;
+
+    static jfieldID msgControlFid = env->GetFieldID(JniConstants::GetStructMsghdrClass(env),
+                                                 "msg_control", "[Landroid/system/StructCmsghdr;");
+    if (!msgControlFid) {
+        return false;
+    }
+
+    static jmethodID cmsgInitMid = env->GetMethodID(JniConstants::GetStructCmsghdrClass(env),
+                                                    "<init>", "(II[B)V");
+    if (!cmsgInitMid) {
+        return false;
+    }
+
+    int cmsghdrNumber = 0;
+    for (cm = CMSG_FIRSTHDR(&mhdr); (cm != NULL); cm = CMSG_NXTHDR(&mhdr, cm)) {
+        cmsghdrNumber++;
+    }
+    if (!cmsghdrNumber)
+        return true;
+
+    jobjectArray structCmsghdrArray = env->NewObjectArray(cmsghdrNumber,
+                                          JniConstants::GetStructCmsghdrClass(env), NULL);
+    if (!structCmsghdrArray) {
+        return false;
+    }
+
+    // Loop over each cmsghdr header and set data.
+    for (cm = CMSG_FIRSTHDR(&mhdr),i=0; (cm!=NULL); cm = CMSG_NXTHDR(&mhdr, cm),i++) {
+        // copy out cmsg_data
+        ScopedLocalRef<jbyteArray> msgData(env,
+            env->NewByteArray(cm->cmsg_len - sizeof(struct cmsghdr)));
+        env->SetByteArrayRegion(msgData.get(),
+                                0,
+                                env->GetArrayLength(msgData.get()),
+                                reinterpret_cast<jbyte*>CMSG_DATA(cm));
+
+        ScopedLocalRef<jobject> objItem(env, env->NewObject(
+                JniConstants::GetStructCmsghdrClass(env),
+                cmsgInitMid, cm->cmsg_level, cm->cmsg_type, msgData.get()));
+
+        env->SetObjectArrayElement(structCmsghdrArray, i, objItem.get());
+    }
+
+    env->SetObjectField(structMsghdr, msgControlFid, structCmsghdrArray);
+
+    return true;
+}
+
+/*
+ * generate ScopedBytes object per ByteBuffer.isDirect
+ * if ByteBuffer.isDirect, generate ScopedBytes object by ByteBuffer itself;
+ * else,  generate ScopedBytes object by ByteBuffer.array;
+ *
+ * Input:  ByteBuffer object, isRW(R only or RW)
+ * Output: byte_len, length of the byte data per ByteBuffer.remaining;
+ * return value: pointer of new ScopedBytesRW or ScopedBytesRO
+ */
+static void* getScopedBytesFromByteBuffer(JNIEnv* env,
+                                          jobject byteBuffer, int& byteLen, bool isRW) {
+
+    jclass byteBufferClass = JniConstants::GetByteBufferClass(env);
+    static jmethodID isDirectMid = env->GetMethodID(byteBufferClass, "isDirect", "()Z");
+    static jmethodID remainingMid = env->GetMethodID(byteBufferClass, "remaining", "()I");
+    static jmethodID arrayMid = env->GetMethodID(byteBufferClass, "array", "()[B");
+
+    if (!isDirectMid || !remainingMid || !arrayMid) {
+        return NULL;
+    }
+
+    byteLen = env->CallIntMethod(byteBuffer, remainingMid);
+    bool isDirect = env->CallBooleanMethod(byteBuffer, isDirectMid);
+    jobject objBuff;
+    if (isDirect == true) {
+        objBuff = env->NewLocalRef(byteBuffer); // Add LocalRef to align with CallObjectMethod
+    } else {
+        // return array
+        objBuff = env->CallObjectMethod(byteBuffer, arrayMid);
+    }
+
+    if (isRW) {
+        return (void*)(new ScopedBytesRW(env, objBuff));
+    } else {
+        return (void*)(new ScopedBytesRO(env, objBuff));
+    }
+
+}
+
+/*
+ *  Convert ByteBuffer[] to mhdr.msg_iov/msg_iovlen
+ */
+bool byteBufferArrayToIOV(JNIEnv* env, jobjectArray msgiovArray, struct msghdr& mhdr,
+                         ScopedByteBufferArray& scopeBufArray) {
+    int msgIovArraySize = env->GetArrayLength(msgiovArray);
+    if (!msgIovArraySize) {
+        /* would not happen since msg_iov is marked as NonNull */
+        mhdr.msg_iov = NULL;
+        mhdr.msg_iovlen = 0;
+    }
+
+    struct iovec* iovarr = (struct iovec*)malloc(sizeof(iovec)*msgIovArraySize);
+    if (!iovarr) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", "Out of memory");
+        return false;
+    }
+
+    if (scopeBufArray.initArray(msgIovArraySize) == false) {
+        jniThrowException(env, "java/lang/OutOfMemoryError", "Out of memory");
+        return false;
+    }
+
+    // Set memory of each msg_iov item by the original bytes address.
+    for (int i=0; i<msgIovArraySize; i++)
+    {
+        jobject msgiovItem = env->GetObjectArrayElement(msgiovArray, i);
+        int byteLen = 0;
+        void* ptr = getScopedBytesFromByteBuffer(env, msgiovItem, byteLen, scopeBufArray.isRW());
+        if (!ptr) {
+            jniThrowException(env, "java/lang/OutOfMemoryError", "Out of memory");
+            return false;
+        }
+
+        scopeBufArray.setArrayItem(i, ptr);
+
+        if (scopeBufArray.isRW()) {
+            iovarr[i].iov_base = (unsigned char*)(((ScopedBytesRW*)ptr)->get());
+        }
+        else {
+            iovarr[i].iov_base = (unsigned char*)(((ScopedBytesRO*)ptr)->get());
+        }
+
+        iovarr[i].iov_len  = byteLen;
+    }
+
+    mhdr.msg_iov = iovarr;
+    mhdr.msg_iovlen = msgIovArraySize;
+
+    return true;
+}
+
+/*
+ * Function: convertStructMsghdrAndmsghdr
+ * Description: convert between Java#StructMsghdr and C#msghdr for sendmsg/recvmsg
+ *
+ * Function Parameters:
+ *   StructMsghdr, input, StructMsghdr
+ *                 for sendmsg,
+ *                   StructMsghdr.msg_name       input(mandatory),
+ *                   StructMsghdr.msg_iov        iput(mandatory)
+ *                   StructMsghdr.msg_control    input(optional)
+ *                   StructMsghdr.msg_flags      input(mandatory)
+ *                 for recvmsg,
+ *                   StructMsghdr.msg_name       input/output(optional),
+ *                   StructMsghdr.msg_iov        input/output(mandatory)
+ *                   StructMsghdr.msg_control    input/output(optional)
+ *                   StructMsghdr.msg_flags      input
+ *   mhdr, input, struct msghdr
+ *   scopeBufArray, output, store buffer array of ScopedBytesRW or ScopedBytesRO
+ *   isFromStructCMsghdrTomsghdr, input,  indicate StructMsghdr->msghdr or msghdr->StructMsghdr
+ *
+ * then in sendmsg scenario, call sequence will be:
+ *             1. convert(StructMsg->msghdr)
+ *             2. sendmsg
+ *      in recvmsg scenario, call sequence will be:
+ *             1. convert(StructMsg->msghdr)
+ *             2. recvmsg
+ *             3. convert again(msghdr->StructMsg)
+ */
+bool convertStructMsghdrAndmsghdr(JNIEnv* env, jobject structMsghdr, struct msghdr& mhdr,
+                                  ScopedByteBufferArray& scopeBufArray,
+                                  bool isFromStructCMsghdrTomsghdr) {
+    if (!structMsghdr) {
+        jniThrowNullPointerException(env, "missing structMsghdr");
+        return false;
+    }
+
+    jclass StructMsghdrClass = JniConstants::GetStructMsghdrClass(env);
+
+    // Get fieldID of each item in StructMsghdr.
+    static jfieldID msgIovFid = env->GetFieldID(StructMsghdrClass,
+                                                 "msg_iov",
+                                                 "[Ljava/nio/ByteBuffer;");
+    if (!msgIovFid) {
+        return false;
+    }
+    static jfieldID msgControlFid = env->GetFieldID(StructMsghdrClass,
+                                                    "msg_control",
+                                                    "[Landroid/system/StructCmsghdr;");
+    if (!msgControlFid) {
+        return false;
+    }
+    static jfieldID msgFlagsFid = env->GetFieldID(StructMsghdrClass,
+                                                  "msg_flags",
+                                                  "I");
+    if (!msgFlagsFid) {
+        return false;
+    }
+
+    if (isFromStructCMsghdrTomsghdr) {
+        // Pick StructMsghdr.msg_iov[].
+        jobjectArray msgIovArray = reinterpret_cast<jobjectArray>(
+                                        env->GetObjectField(structMsghdr, msgIovFid));
+        if (!msgIovArray) {
+            jniThrowNullPointerException(env, "null StructMsghdr.msg_iov");
+            return false;
+        }
+        // In case sendmsg, IOV buffer are RO to send data,
+        // in case recvmsg, IOV buffer are RW to store received data.
+        if (byteBufferArrayToIOV(env, msgIovArray, mhdr, scopeBufArray) == false) {
+            return false;
+        }
+
+        if (!scopeBufArray.isRW()) {
+            jobjectArray structCmsghdrObjArray = reinterpret_cast<jobjectArray>(
+                                               env->GetObjectField(structMsghdr, msgControlFid));
+            if (structCmsghdrObjArray != NULL) {
+                // convert StrucCmsg[] <-> msghdr.msgcontrl
+                if (structCmsghdrArrayToMsgcontrol(env, structCmsghdrObjArray, mhdr) == false) {
+                    return false;
+                }
+            }
+        } else {
+            // hardcode 512 for recvmsg/msg_controllen, it should be enough for recvmsg
+            mhdr.msg_controllen = 512;
+            mhdr.msg_control = (unsigned char*)malloc(mhdr.msg_controllen);
+        }
+
+        mhdr.msg_flags = env->GetIntField(structMsghdr, msgFlagsFid);
+    } else {
+        // StructMsghdr.msg_iov[]/msg_control[] are output paramenter.
+        // StructMsghdr.msg_iov[] data are already updated by recvmsg syscall directly.
+        // StructMsghdr.msg_control[] are set below.
+        if (msgcontrolToStructCmsghdrArray(env, structMsghdr, mhdr) == false)
+            return false;
+        env->SetIntField(structMsghdr, msgFlagsFid, mhdr.msg_flags);
+    }
+
+    return true;
+
+}
+
+// Convert Java StructMsghdr to C msghdr.
+bool msghdrJavaToC(JNIEnv* env, jobject structMsghdr, struct msghdr& mhdr,
+                          ScopedByteBufferArray& scopedBufArray) {
+    return convertStructMsghdrAndmsghdr(env, structMsghdr, mhdr,
+                                             scopedBufArray, true);
+}
+
+// Convert C msghdr to Java StructMsghdr.
+bool msghdrCToJava(JNIEnv* env, jobject structMsghdr, struct msghdr& mhdr,
+                          ScopedByteBufferArray& scopedBufArray) {
+    return convertStructMsghdrAndmsghdr(env, structMsghdr, mhdr,
+                                             scopedBufArray, false);
+}
+
 bool setBlocking(int fd, bool blocking) {
     int flags = fcntl(fd, F_GETFL);
     if (flags == -1) {
diff --git a/luni/src/main/native/NetworkUtilities.h b/luni/src/main/native/NetworkUtilities.h
old mode 100644
new mode 100755
index db1ad1f..da35036
--- a/luni/src/main/native/NetworkUtilities.h
+++ b/luni/src/main/native/NetworkUtilities.h
@@ -16,6 +16,7 @@
 
 #include "jni.h"
 #include <sys/socket.h>
+#include "ScopedByteBufferArray.h"
 
 // Convert from sockaddr_storage to Inet4Address (AF_INET) or Inet6Address (AF_INET6).
 // If 'port' is non-NULL and the address family includes a notion
@@ -38,7 +39,15 @@
 bool inetAddressToSockaddrVerbatim(JNIEnv* env, jobject inetAddress, int port,
                                    sockaddr_storage& ss, socklen_t& sa_len);
 
+// Convert from StructMsghdr to msghdr,
+// set all fields except msg_name which would be set outside since IPv4 fallback handled outside
+bool msghdrJavaToC(JNIEnv* env, jobject structMsghdr, struct msghdr& mhdr,
+                   ScopedByteBufferArray& scopedBufArray);
 
+// Convert from msghdr to StructMsghdr,
+// msg_iov/msg_control need to be set since they are output parameters;
+bool msghdrCToJava(JNIEnv* env, jobject structMsghdr, struct msghdr& mhdr,
+                   ScopedByteBufferArray& scopedBufArray);
 
 // Changes 'fd' to be blocking/non-blocking. Returns false and sets errno on failure.
 // @Deprecated - use IoUtils.setBlocking
diff --git a/luni/src/main/native/ScopedByteBufferArray.h b/luni/src/main/native/ScopedByteBufferArray.h
new file mode 100755
index 0000000..7b21845
--- /dev/null
+++ b/luni/src/main/native/ScopedByteBufferArray.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *    *Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *    *Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following
+ *     disclaimer in the documentation and/or other materials provided
+ *     with the distribution.
+ *    *Neither the name of The Linux Foundation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+
+#include <nativehelper/ScopedLocalRef.h>
+#include "ScopedBytes.h"
+
+/**
+ * ScopedByteBufferArray manages the dynamic buffer array of ScopedBytesRW/ScopedBytesRO.
+ */
+class ScopedByteBufferArray {
+public:
+    ScopedByteBufferArray(JNIEnv* env, int isRW)
+    : mEnv(env), mIsRW(isRW)
+    {
+        mArrayPtr = NULL;
+        mArraySize = 0;
+    }
+
+    ~ScopedByteBufferArray() {
+        if(!mArrayPtr) {
+            return;
+        }
+
+        // Loop over arrary and release memory.
+        for (int i = 0; i < mArraySize; ++i) {
+            if (!mArrayPtr[i])
+                continue;
+
+            if (mIsRW) {
+                jobject tmp = ((ScopedBytesRW*)mArrayPtr[i])->getObject();
+                delete (ScopedBytesRW*)mArrayPtr[i];
+                mEnv->DeleteLocalRef(tmp);
+            } else {
+                jobject tmp = ((ScopedBytesRO*)mArrayPtr[i])->getObject();
+                delete (ScopedBytesRO*)mArrayPtr[i];
+                mEnv->DeleteLocalRef(tmp);
+            }
+        }
+        delete[] mArrayPtr;
+    }
+
+    bool initArray(int size) {
+        if (mArrayPtr) {
+            return false;
+        }
+
+        mArraySize = size;
+
+        if (mIsRW) {
+            mArrayPtr = (void**)(new ScopedBytesRW*[size]);
+        }
+        else {
+            mArrayPtr = (void**)(new ScopedBytesRO*[size]);
+        }
+
+        if (!mArrayPtr) {
+            return false;
+        }
+
+        for (int i=0; i<size; ++i) {
+            mArrayPtr[i] = 0;
+        }
+
+        return true;
+    }
+
+    bool isRW() const {
+        return mIsRW;
+    }
+
+    bool setArrayItem(int itemNo, void* item) {
+        if (itemNo >= mArraySize || itemNo < 0) {
+            return false;
+        }
+        mArrayPtr[itemNo] = item;
+        return true;
+    }
+
+private:
+    JNIEnv* const mEnv;
+    int mIsRW;
+    int mArraySize;
+    void** mArrayPtr;
+};
diff --git a/luni/src/main/native/ScopedBytes.h b/luni/src/main/native/ScopedBytes.h
index 5258a57..951ef4b 100644
--- a/luni/src/main/native/ScopedBytes.h
+++ b/luni/src/main/native/ScopedBytes.h
@@ -51,6 +51,10 @@
         }
     }
 
+    jobject getObject() {
+        return mObject;
+    }
+
 private:
     JNIEnv* const mEnv;
     const jobject mObject;
diff --git a/luni/src/main/native/ScopedMsghdr.h b/luni/src/main/native/ScopedMsghdr.h
new file mode 100755
index 0000000..e64077f
--- /dev/null
+++ b/luni/src/main/native/ScopedMsghdr.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *    *Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *    *Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following
+ *     disclaimer in the documentation and/or other materials provided
+ *     with the distribution.
+ *    *Neither the name of The Linux Foundation nor the names of its
+ *     contributors may be used to endorse or promote products derived
+ *     from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#pragma once
+
+#include <nativehelper/JNIHelp.h>
+
+#include "JniConstants.h"
+#include "ScopedBytes.h"
+
+/**
+ * Wrapper for managing the name and length of msghdr.
+ */
+class ScopedMsghdr {
+public:
+    ScopedMsghdr() {
+    }
+
+    ~ScopedMsghdr() {
+        if (mMsghdrValue.msg_iov)
+            free(mMsghdrValue.msg_iov);
+        if (mMsghdrValue.msg_control)
+            free(mMsghdrValue.msg_control);
+    }
+
+    struct msghdr& getObject() {
+        return mMsghdrValue;
+    }
+
+    void setMsgNameAndLen(sockaddr* ss, socklen_t sa_len) {
+        mMsghdrValue.msg_name = ss;
+        mMsghdrValue.msg_namelen = sa_len;
+    }
+
+    bool isNameLenValid() const {
+        if(mMsghdrValue.msg_name == NULL) {
+            return false;
+        }
+        if ((mMsghdrValue.msg_namelen != sizeof(sockaddr_in6)) &&
+            (mMsghdrValue.msg_namelen != sizeof(sockaddr_in))) {
+            return false;
+        }
+        return true;
+    }
+
+private:
+    struct msghdr mMsghdrValue = {};
+
+};
+
+
diff --git a/luni/src/main/native/libcore_io_Linux.cpp b/luni/src/main/native/libcore_io_Linux.cpp
old mode 100644
new mode 100755
index 9b82b48..23c1742
--- a/luni/src/main/native/libcore_io_Linux.cpp
+++ b/luni/src/main/native/libcore_io_Linux.cpp
@@ -73,6 +73,9 @@
 #include "NetworkUtilities.h"
 #include "Portability.h"
 #include "ScopedBytes.h"
+#include "ScopedByteBufferArray.h"
+#include "ScopedMsghdr.h"
+
 
 #ifndef __unused
 #define __unused __attribute__((__unused__))
@@ -2134,6 +2137,62 @@
     return recvCount;
 }
 
+static jint Linux_recvmsg(JNIEnv* env, jobject, jobject javaFd, jobject structMsghdr, jint flags) {
+    ssize_t rc = -1;
+    ScopedMsghdr scopedMsghdrValue;
+    ScopedByteBufferArray scopedBytesArray(env, true);
+    sockaddr_storage ss = {};
+
+    static jfieldID msgNameFid = env->GetFieldID(JniConstants::GetStructMsghdrClass(env),
+                                                  "msg_name", "Ljava/net/SocketAddress;");
+    if (!msgNameFid) {
+        return -1;
+    }
+
+    // Initialize msghdr with everything from StructCMsghdr except msg_name.
+    if (msghdrJavaToC(env, structMsghdr, scopedMsghdrValue.getObject(),
+                           scopedBytesArray) == false) {
+        return -1;
+    }
+
+    jobject javaSocketAddress = env->GetObjectField(structMsghdr, msgNameFid);
+    if (javaSocketAddress) {
+        // client want to get source address, then set msg_name and msg_namelen.
+        scopedMsghdrValue.setMsgNameAndLen(reinterpret_cast<sockaddr*>(&ss),
+                                              sizeof(sockaddr_in6));
+    }
+
+    rc = NET_FAILURE_RETRY(env, ssize_t, recvmsg, javaFd, \
+                               &scopedMsghdrValue.getObject(), flags);
+
+    if (rc < 0) {
+        return rc;
+    }
+
+    if (javaSocketAddress) {
+        sockaddr_storage* interfaceAddr = NULL;
+        if (!scopedMsghdrValue.isNameLenValid()) {
+            jniThrowException(env, "java/net/SocketException",
+                                   "unknown socket address type");
+                return -1;
+        }
+
+        interfaceAddr = reinterpret_cast<sockaddr_storage*>(scopedMsghdrValue.getObject().msg_name);
+        if (fillSocketAddress(env, javaSocketAddress, *interfaceAddr,
+                scopedMsghdrValue.getObject().msg_namelen) == false) {
+            return -1;
+        }
+    }
+
+    if (msghdrCToJava(env, structMsghdr, scopedMsghdrValue.getObject(),
+                            scopedBytesArray) == false) {
+        return -1;
+    }
+
+    return rc;
+}
+
+
 static void Linux_remove(JNIEnv* env, jobject, jstring javaPath) {
     ScopedUtfChars path(env, javaPath);
     if (path.c_str() == NULL) {
@@ -2190,6 +2249,66 @@
     return result;
 }
 
+static jint Linux_sendmsg(JNIEnv* env, jobject, jobject javaFd, jobject structMsghdr, jint flags) {
+
+    ssize_t rc = -1;
+    ScopedMsghdr scopedMsghdrValue = {};
+    ScopedByteBufferArray scopedBytesArray(env, false);
+    static jfieldID msgNameFid = env->GetFieldID(JniConstants::GetStructMsghdrClass(env),
+                                                  "msg_name",
+                                                  "Ljava/net/SocketAddress;");
+    if (!msgNameFid) {
+        return -1;
+    }
+
+    jobject sockAddrObj = env->GetObjectField(structMsghdr, msgNameFid);
+
+    // Initialize structMsghdr with everything from C msghdr except msg_name.
+    if (msghdrJavaToC(env, structMsghdr, scopedMsghdrValue.getObject(),
+                           scopedBytesArray) == false) {
+        return -1;
+    }
+
+    sockaddr_storage ss = {};
+    socklen_t sa_len = 0;
+    if (sockAddrObj && javaSocketAddressToSockaddr(env, sockAddrObj, ss, sa_len) == false) {
+        return -1;
+    }
+
+    sockaddr* _sa = sa_len ? reinterpret_cast<sockaddr*>(&ss) : NULL;
+    scopedMsghdrValue.setMsgNameAndLen(_sa, sa_len);
+    rc  = NET_FAILURE_RETRY(env, ssize_t, sendmsg, javaFd, \
+                                 &(scopedMsghdrValue.getObject()), flags);
+
+    if (sockAddrObj &&
+        !env->IsInstanceOf(sockAddrObj, JniConstants::GetInetSocketAddressClass(env))) {
+        // non InetSockAddress case, return now;
+        return rc;
+    }
+
+    // InetSocket and IPv6 didn't work, fallback to IPv4.
+    if (rc  == -1 && errno == EAFNOSUPPORT && sa_len && isIPv4MappedAddress(_sa)) {
+        env->ExceptionClear();
+
+        jobject javaInetAddress;
+        jint port = 0;
+        javaInetSocketAddressToInetAddressAndPort(env, sockAddrObj, javaInetAddress, port);
+
+        // Get IPv4 sockaddr.
+        if (!inetAddressToSockaddrVerbatim(env, javaInetAddress, port, ss, sa_len)) {
+            return rc;
+        }
+
+        // Use IPv4 msghdr.msg_name.
+        _sa = sa_len ? reinterpret_cast<sockaddr*>(&ss) : NULL;
+        scopedMsghdrValue.setMsgNameAndLen(_sa, sa_len);
+        rc = NET_FAILURE_RETRY(env, ssize_t, sendmsg, javaFd, \
+                                    &(scopedMsghdrValue.getObject()), flags);
+    }
+
+    return rc;
+}
+
 static jint Linux_sendtoBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jint flags, jobject javaInetAddress, jint port) {
     ScopedBytesRO bytes(env, javaBytes);
     if (bytes.get() == NULL) {
@@ -2669,10 +2788,12 @@
     NATIVE_METHOD(Linux, realpath, "(Ljava/lang/String;)Ljava/lang/String;"),
     NATIVE_METHOD(Linux, readv, "(Ljava/io/FileDescriptor;[Ljava/lang/Object;[I[I)I"),
     NATIVE_METHOD(Linux, recvfromBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/InetSocketAddress;)I"),
+    NATIVE_METHOD(Linux, recvmsg, "(Ljava/io/FileDescriptor;Landroid/system/StructMsghdr;I)I"),
     NATIVE_METHOD(Linux, remove, "(Ljava/lang/String;)V"),
     NATIVE_METHOD(Linux, removexattr, "(Ljava/lang/String;Ljava/lang/String;)V"),
     NATIVE_METHOD(Linux, rename, "(Ljava/lang/String;Ljava/lang/String;)V"),
     NATIVE_METHOD(Linux, sendfile, "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;Landroid/system/Int64Ref;J)J"),
+    NATIVE_METHOD(Linux, sendmsg, "(Ljava/io/FileDescriptor;Landroid/system/StructMsghdr;I)I"),
     NATIVE_METHOD(Linux, sendtoBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/InetAddress;I)I"),
     NATIVE_METHOD_OVERLOAD(Linux, sendtoBytes, "(Ljava/io/FileDescriptor;Ljava/lang/Object;IIILjava/net/SocketAddress;)I", SocketAddress),
     NATIVE_METHOD(Linux, setegid, "(I)V"),
diff --git a/luni/src/test/java/libcore/android/system/OsTest.java b/luni/src/test/java/libcore/android/system/OsTest.java
old mode 100644
new mode 100755
index 6d1a135..67b92f8
--- a/luni/src/test/java/libcore/android/system/OsTest.java
+++ b/luni/src/test/java/libcore/android/system/OsTest.java
@@ -22,6 +22,8 @@
 import android.system.Os;
 import android.system.OsConstants;
 import android.system.PacketSocketAddress;
+import android.system.StructCmsghdr;
+import android.system.StructMsghdr;
 import android.system.StructRlimit;
 import android.system.StructStat;
 import android.system.StructTimeval;
@@ -45,6 +47,7 @@
 import java.net.SocketAddress;
 import java.net.SocketException;
 import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 import java.nio.charset.StandardCharsets;
 import java.time.Duration;
 import java.util.Arrays;
@@ -734,6 +737,241 @@
         checkSendToSocketAddress(AF_INET6, InetAddress.getByName("::1"));
     }
 
+    /*
+     * Test case for sendmsg with/without GSO in loopback iface,
+     * recvmsg/gro would not happen since in loopback
+     */
+    private void checkSendmsgSocketAddress(int family, InetSocketAddress loopbackAddr,
+            StructMsghdr sendmsgHdr, StructMsghdr recvmsgHdr, int sendSize) throws Exception {
+
+        FileDescriptor sendFd = Os.socket(family, SOCK_DGRAM, 0);
+        FileDescriptor recvFd = Os.socket(family, SOCK_DGRAM, 0);
+        int rc = 0;
+
+        //recvmsg cleanup data
+        if (loopbackAddr.getAddress() instanceof Inet6Address) {
+            Os.bind(recvFd, Inet6Address.ANY, loopbackAddr.getPort());
+        } else {
+            Os.bind(recvFd, Inet4Address.ANY, loopbackAddr.getPort());
+        }
+
+        StructTimeval tv = StructTimeval.fromMillis(20);
+        Os.setsockoptTimeval(recvFd, SOL_SOCKET, SO_RCVTIMEO, tv);
+        Os.setsockoptInt(recvFd, IPPROTO_UDP, UDP_GRO, 1); //enable GRO
+        Os.setsockoptInt(recvFd, SOL_SOCKET, SO_RCVBUF, 1024 * 1024);
+
+        try {
+            assertEquals(sendSize, Os.sendmsg(sendFd, sendmsgHdr, 0));
+            rc = 0;
+            do {
+                int temp_rc = Os.recvmsg(recvFd, recvmsgHdr, OsConstants.MSG_TRUNC);
+                rc += temp_rc;
+                if (recvmsgHdr.msg_control != null && recvmsgHdr.msg_control.length > 0) {
+                    byte[] sendCmsgByte = sendmsgHdr.msg_control[0].cmsg_data;
+                    byte[] recvCmsgByte = recvmsgHdr.msg_control[0].cmsg_data;
+                    /* Note:
+                     * GSO: is set with Short(2Byte) values;
+                     * GRO: IP stack return with Int(4Bytes) value;
+                     */
+                    assertEquals(
+                            ByteBuffer.wrap(sendCmsgByte).order(
+                                    ByteOrder.nativeOrder()).getShort(0),
+                            ByteBuffer.wrap(recvCmsgByte).order(
+                                    ByteOrder.nativeOrder()).getInt(0));
+                }
+
+                recvmsgHdr = new StructMsghdr(recvmsgHdr.msg_name, recvmsgHdr.msg_iov,
+                                              null,
+                                              recvmsgHdr.msg_flags);
+            }while(rc < sendSize);
+        } finally {
+            Os.close(sendFd);
+            Os.close(recvFd);
+        }
+    }
+
+    @Test
+    public void test_sendmsg_af_inet_4K() throws Exception {
+        InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+                10234);
+        StructCmsghdr[] cmsg = new StructCmsghdr[1];
+        cmsg[0] = new StructCmsghdr(SOL_UDP, UDP_SEGMENT, (short) 1400);
+
+        //sendmsg/recvmsg with 1*4K ByteBuffer
+        ByteBuffer[] bufferArray = new ByteBuffer[1];
+        ByteBuffer[] bufferArrayRecv = new ByteBuffer[1];
+        bufferArray[0] = ByteBuffer.allocate(4096);
+        bufferArrayRecv[0] = ByteBuffer.allocate(4096);
+
+        StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr,
+                                                   bufferArray,
+                                                   cmsg, 0);
+        StructMsghdr recvmsgHdr = new StructMsghdr(new InetSocketAddress(),
+                                                   bufferArrayRecv,
+                                                   null, 0);
+
+        checkSendmsgSocketAddress(AF_INET, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096);
+    }
+
+    @Test
+    public void test_sendmsg_af_inet6_4K() throws Exception {
+        InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("::1"), 10234);
+        StructCmsghdr[] cmsg = new StructCmsghdr[1];
+        cmsg[0] = new StructCmsghdr(SOL_UDP, UDP_SEGMENT, (short) 1400);
+
+        //sendmsg/recvmsg with 1*4K ByteBuffer
+        ByteBuffer[] bufferArray = new ByteBuffer[1];
+        ByteBuffer[] bufferArrayRecv = new ByteBuffer[1];
+        bufferArray[0] = ByteBuffer.allocate(4096);
+        bufferArrayRecv[0] = ByteBuffer.allocate(4096);
+
+        StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr,
+                                                   bufferArray,
+                                                   cmsg, 0);
+        StructMsghdr recvmsgHdr = new StructMsghdr(new InetSocketAddress(),
+                                                   bufferArrayRecv,
+                                                   null, 0);
+
+        checkSendmsgSocketAddress(AF_INET6, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096);
+    }
+
+    @Test
+    public void test_sendmsg_af_inet6_4K_directBuffer() throws Exception {
+        InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+                                                               10234);
+        StructCmsghdr[] cmsg = new StructCmsghdr[1];
+        cmsg[0] = new StructCmsghdr(SOL_UDP, UDP_SEGMENT, (short) 1400);
+
+        //sendmsg/recvmsg with 1*4K ByteBuffer
+        ByteBuffer[] bufferArray = new ByteBuffer[1];
+        ByteBuffer[] bufferArrayRecv = new ByteBuffer[1];
+        bufferArray[0] = ByteBuffer.allocateDirect(4096); // DirectBuffer
+        bufferArrayRecv[0] = ByteBuffer.allocateDirect(4096); // DirectBuffer
+
+        StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr,
+                                                   bufferArray,
+                                                   cmsg, 0);
+        StructMsghdr recvmsgHdr = new StructMsghdr(new InetSocketAddress(),
+                                                   bufferArrayRecv,
+                                                   null, 0);
+
+        checkSendmsgSocketAddress(AF_INET6, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096);
+    }
+
+    @Test
+    public void test_sendmsg_af_inet_16K_recvparts() throws Exception {
+        InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+                                                               10234);
+        StructCmsghdr[] cmsg = new StructCmsghdr[1];
+        cmsg[0] = new StructCmsghdr(SOL_UDP, UDP_SEGMENT, (short) 1400);
+
+        //sendmsg with 4*4K ByteBuffer, recv with 1*4K ByteBuffer(already with MSG_TRUNC option)
+        ByteBuffer[] bufferArray = new ByteBuffer[4];
+        ByteBuffer[] bufferArrayRecv = new ByteBuffer[1];
+        bufferArray[0] = ByteBuffer.allocate(4096);
+        bufferArray[1] = ByteBuffer.allocate(4096);
+        bufferArray[2] = ByteBuffer.allocate(4096);
+        bufferArray[3] = ByteBuffer.allocate(4096);
+        bufferArrayRecv[0] = ByteBuffer.allocate(4096); //receive only part of data
+
+        StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr,
+                                                   bufferArray,
+                                                   cmsg, 0);
+        StructMsghdr recvmsgHdr = new StructMsghdr(new InetSocketAddress(),
+                                                   bufferArrayRecv,
+                                                   null, 0);
+
+        checkSendmsgSocketAddress(AF_INET, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096 * 4);
+    }
+
+    @Test
+    public void test_sendmsg_af_inet_16K_reciveall() throws Exception {
+        InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+                                                               10234);
+        StructCmsghdr[] cmsg = new StructCmsghdr[1];
+        cmsg[0] = new StructCmsghdr(SOL_UDP, UDP_SEGMENT, (short) 1400);
+
+        // Create sendmsg/recvmsg with 4*4K ByteBuffer
+        ByteBuffer[] bufferArray = new ByteBuffer[4];
+        bufferArray[0] = ByteBuffer.allocate(4096);
+        bufferArray[1] = ByteBuffer.allocate(4096);
+        bufferArray[2] = ByteBuffer.allocate(4096);
+        bufferArray[3] = ByteBuffer.allocate(4096);
+
+        StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr, bufferArray, cmsg, 0);
+        StructMsghdr recvmsgHdr = new StructMsghdr(new InetSocketAddress(), bufferArray, null, 0);
+
+        checkSendmsgSocketAddress(AF_INET, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096 * 4);
+    }
+
+    @Test
+    public void test_sendmsg_af_inet_16K_reciveall_without_recv_msgname() throws Exception {
+        InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+                                                               10234);
+        StructCmsghdr[] cmsg = new StructCmsghdr[1];
+        cmsg[0] = new StructCmsghdr(SOL_UDP, UDP_SEGMENT, (short) 1400);
+
+        // Create sendmsg/recvmsg with 4*4K ByteBuffer
+        ByteBuffer[] bufferArray = new ByteBuffer[4];
+        bufferArray[0] = ByteBuffer.allocate(4096);
+        bufferArray[1] = ByteBuffer.allocate(4096);
+        bufferArray[2] = ByteBuffer.allocate(4096);
+        bufferArray[3] = ByteBuffer.allocate(4096);
+
+        StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr, bufferArray, cmsg, 0);
+        // msg_name is unnecessary.
+        StructMsghdr recvmsgHdr = new StructMsghdr(null, bufferArray, null, 0);
+
+        checkSendmsgSocketAddress(AF_INET, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096 * 4);
+    }
+
+    @Test
+    public void test_sendmsg_af_inet_16K_without_send_msgcontrl() throws Exception {
+        InetSocketAddress loopbackAddr = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+                                                               10234);
+
+        // Create sendmsg/recvmsg with 4*4K ByteBuffer
+        ByteBuffer[] bufferArray = new ByteBuffer[4];
+        bufferArray[0] = ByteBuffer.allocate(4096);
+        bufferArray[1] = ByteBuffer.allocate(4096);
+        bufferArray[2] = ByteBuffer.allocate(4096);
+        bufferArray[3] = ByteBuffer.allocate(4096);
+
+        // GSO will not happen without msgcontrol.
+        StructMsghdr sendmsgHdr = new StructMsghdr(loopbackAddr, bufferArray, null, 0);
+        StructMsghdr recvmsgHdr = new StructMsghdr(null, bufferArray, null, 0);
+
+        checkSendmsgSocketAddress(AF_INET, loopbackAddr, sendmsgHdr, recvmsgHdr, 4096 * 4);
+    }
+
+    @Test
+    public void test_sendmsg_af_inet_abnormal() throws Exception {
+        //sendmsg socket set
+        InetSocketAddress address = new InetSocketAddress(InetAddress.getByName("127.0.0.1"),
+                                                          10234);
+        FileDescriptor sendFd = Os.socket(AF_INET, SOCK_DGRAM, 0);
+
+        ByteBuffer[] bufferArray = new ByteBuffer[1];
+        bufferArray[0] = ByteBuffer.allocate(8192);
+
+        try {
+            StructMsghdr msgHdr = new StructMsghdr(address, null, null, 0);
+            Os.sendmsg(sendFd, msgHdr, 0);
+            fail("Expected NullPointerException due to invalid StructMsghdr.msg_iov(NULL)");
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            StructMsghdr msgHdr = new StructMsghdr(null, bufferArray, null, 0);
+            Os.sendmsg(sendFd, msgHdr, 0);
+            fail("Expected ErrnoException due to invalid StructMsghdr.msg_name(NULL)");
+        } catch (ErrnoException expected) {
+            assertEquals("Expected EDESTADDRREQ binding IPv4 socket to ::", EDESTADDRREQ,
+                    expected.errno);
+        }
+
+    }
+
     @Test
     public void test_socketFamilies() throws Exception {
         FileDescriptor fd = Os.socket(AF_INET6, SOCK_STREAM, 0);
diff --git a/non_openjdk_java_files.bp b/non_openjdk_java_files.bp
old mode 100644
new mode 100755
index b40556c..7929e62
--- a/non_openjdk_java_files.bp
+++ b/non_openjdk_java_files.bp
@@ -140,9 +140,11 @@
         "luni/src/main/java/android/system/StructAddrinfo.java",
         "luni/src/main/java/android/system/StructCapUserData.java",
         "luni/src/main/java/android/system/StructCapUserHeader.java",
+        "luni/src/main/java/android/system/StructCmsghdr.java",
         "luni/src/main/java/android/system/StructGroupReq.java",
         "luni/src/main/java/android/system/StructIfaddrs.java",
         "luni/src/main/java/android/system/StructLinger.java",
+        "luni/src/main/java/android/system/StructMsghdr.java",
         "luni/src/main/java/android/system/StructPasswd.java",
         "luni/src/main/java/android/system/StructPollfd.java",
         "luni/src/main/java/android/system/StructRlimit.java",