diff --git a/framework/jni/android_net_NetworkUtils.cpp b/framework/jni/android_net_NetworkUtils.cpp index ca297e5a8b..5403be79f0 100644 --- a/framework/jni/android_net_NetworkUtils.cpp +++ b/framework/jni/android_net_NetworkUtils.cpp @@ -26,6 +26,7 @@ #include #include // NETID_USE_LOCAL_NAMESERVERS #include +#include #include #include "jni.h" @@ -240,6 +241,15 @@ static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jclass clazz, j trw.rcv_wnd, trw.rcv_wup, tcpinfo.tcpi_rcv_wscale); } +static void android_net_utils_setsockoptBytes(JNIEnv *env, jclass clazz, jobject javaFd, + jint level, jint option, jbyteArray javaBytes) { + int sock = AFileDescriptor_getFd(env, javaFd); + ScopedByteArrayRO value(env, javaBytes); + if (setsockopt(sock, level, option, value.get(), value.size()) != 0) { + jniThrowErrnoException(env, "setsockoptBytes", errno); + } +} + // ---------------------------------------------------------------------------- /* @@ -260,6 +270,8 @@ static const JNINativeMethod gNetworkUtilMethods[] = { { "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult }, { "resNetworkCancel", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_resNetworkCancel }, { "getDnsNetwork", "()Landroid/net/Network;", (void*) android_net_utils_getDnsNetwork }, + { "setsockoptBytes", "(Ljava/io/FileDescriptor;II[B)V", + (void*) android_net_utils_setsockoptBytes}, }; // clang-format on diff --git a/framework/src/android/net/NetworkUtils.java b/framework/src/android/net/NetworkUtils.java index 2679b6218c..fbdc024340 100644 --- a/framework/src/android/net/NetworkUtils.java +++ b/framework/src/android/net/NetworkUtils.java @@ -426,4 +426,16 @@ public class NetworkUtils { return routedIPCount; } + /** + * Sets a socket option with byte array + * + * @param fd The socket file descriptor + * @param level The level at which the option is defined + * @param option The socket option for which the value is to be set + * @param value The option value to be set in byte array + * @throws ErrnoException if setsockopt fails + */ + public static native void setsockoptBytes(FileDescriptor fd, int level, int option, + byte[] value) throws ErrnoException; + } diff --git a/tests/unit/java/android/net/NetworkUtilsTest.java b/tests/unit/java/android/net/NetworkUtilsTest.java index a28245d4b0..5d789b4cb6 100644 --- a/tests/unit/java/android/net/NetworkUtilsTest.java +++ b/tests/unit/java/android/net/NetworkUtilsTest.java @@ -16,19 +16,32 @@ package android.net; +import static android.system.OsConstants.AF_INET6; +import static android.system.OsConstants.IPPROTO_ICMPV6; +import static android.system.OsConstants.SOCK_DGRAM; +import static android.system.OsConstants.SOL_SOCKET; +import static android.system.OsConstants.SO_RCVTIMEO; import static junit.framework.Assert.assertEquals; import android.os.Build; +import android.system.ErrnoException; +import android.system.Os; +import android.system.StructTimeval; import androidx.test.filters.SmallTest; +import com.android.net.module.util.SocketUtils; import com.android.testutils.DevSdkIgnoreRule; import com.android.testutils.DevSdkIgnoreRunner; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.FileDescriptor; +import java.io.IOException; import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.TreeSet; @RunWith(DevSdkIgnoreRunner.class) @@ -131,4 +144,27 @@ public class NetworkUtilsTest { assertEquals(BigInteger.valueOf(7l - 4 + 4 + 16 + 65536), NetworkUtils.routedIPv6AddressCount(set)); } + + private byte[] getTimevalBytes(StructTimeval tv) { + byte[] timeval = new byte[16]; + ByteBuffer buf = ByteBuffer.wrap(timeval); + buf.order(ByteOrder.nativeOrder()); + buf.putLong(tv.tv_sec); + buf.putLong(tv.tv_usec); + return timeval; + } + + @Test + public void testSetSockOptBytes() throws ErrnoException { + final FileDescriptor sock = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6); + final StructTimeval writeTimeval = StructTimeval.fromMillis(1200); + byte[] timeval = getTimevalBytes(writeTimeval); + final StructTimeval readTimeval; + + NetworkUtils.setsockoptBytes(sock, SOL_SOCKET, SO_RCVTIMEO, timeval); + readTimeval = Os.getsockoptTimeval(sock, SOL_SOCKET, SO_RCVTIMEO); + + assertEquals(writeTimeval, readTimeval); + SocketUtils.closeSocketQuietly(sock); + } }