changeset 59319:d652d9aaf286

8241305: Add protocol specific factory creation methods to SocketChannel and ServerSocketChannel Reviewed-by: alanb, chegar, dfuchs
author michaelm
date Sun, 17 May 2020 21:15:33 +0100
parents 179d3a7a5d10
children a95911422005
files src/java.base/share/classes/java/nio/channels/DatagramChannel.java src/java.base/share/classes/java/nio/channels/ServerSocketChannel.java src/java.base/share/classes/java/nio/channels/SocketChannel.java src/java.base/share/classes/java/nio/channels/spi/SelectorProvider.java src/java.base/share/classes/sun/nio/ch/Net.java src/java.base/share/classes/sun/nio/ch/SelectorProviderImpl.java src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java src/java.base/unix/classes/sun/nio/ch/InheritedChannel.java test/jdk/java/nio/channels/etc/LocalSocketAddressType.java test/jdk/java/nio/channels/etc/OpenAndConnect.java test/jdk/java/nio/channels/etc/ProtocolFamilies.java
diffstat 12 files changed, 1044 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/nio/channels/DatagramChannel.java	Sun May 17 11:34:32 2020 -0700
+++ b/src/java.base/share/classes/java/nio/channels/DatagramChannel.java	Sun May 17 21:15:33 2020 +0100
@@ -33,6 +33,7 @@
 import java.nio.ByteBuffer;
 import java.nio.channels.spi.AbstractSelectableChannel;
 import java.nio.channels.spi.SelectorProvider;
+import static java.util.Objects.requireNonNull;
 
 /**
  * A selectable channel for datagram-oriented sockets.
@@ -184,7 +185,7 @@
      * @since   1.7
      */
     public static DatagramChannel open(ProtocolFamily family) throws IOException {
-        return SelectorProvider.provider().openDatagramChannel(family);
+        return SelectorProvider.provider().openDatagramChannel(requireNonNull(family));
     }
 
     /**
--- a/src/java.base/share/classes/java/nio/channels/ServerSocketChannel.java	Sun May 17 11:34:32 2020 -0700
+++ b/src/java.base/share/classes/java/nio/channels/ServerSocketChannel.java	Sun May 17 21:15:33 2020 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,11 +26,13 @@
 package java.nio.channels;
 
 import java.io.IOException;
+import java.net.ProtocolFamily;
 import java.net.ServerSocket;
 import java.net.SocketOption;
 import java.net.SocketAddress;
 import java.nio.channels.spi.AbstractSelectableChannel;
 import java.nio.channels.spi.SelectorProvider;
+import static java.util.Objects.requireNonNull;
 
 /**
  * A selectable channel for stream-oriented listening sockets.
@@ -114,6 +116,34 @@
     }
 
     /**
+     * Opens a server-socket channel.The {@code family} parameter specifies the
+     * {@link ProtocolFamily protocol family} of the channel's socket.
+     *
+     * <p> The new channel is created by invoking the {@link
+     * java.nio.channels.spi.SelectorProvider#openServerSocketChannel(ProtocolFamily)
+     * openServerSocketChannel(ProtocolFamily)} method of the system-wide default {@link
+     * java.nio.channels.spi.SelectorProvider} object. </p>
+     *
+     * @param   family
+     *          The protocol family
+     *
+     * @return  A new socket channel
+     *
+     * @throws  UnsupportedOperationException
+     *          If the specified protocol family is not supported. For example,
+     *          suppose the parameter is specified as {@link
+     *          java.net.StandardProtocolFamily#INET6 StandardProtocolFamily.INET6}
+     *          but IPv6 is not enabled on the platform.
+     * @throws  IOException
+     *          If an I/O error occurs
+     *
+     * @since 15
+     */
+    public static ServerSocketChannel open(ProtocolFamily family) throws IOException {
+        return SelectorProvider.provider().openServerSocketChannel(requireNonNull(family));
+    }
+
+    /**
      * Returns an operation set identifying this channel's supported
      * operations.
      *
--- a/src/java.base/share/classes/java/nio/channels/SocketChannel.java	Sun May 17 11:34:32 2020 -0700
+++ b/src/java.base/share/classes/java/nio/channels/SocketChannel.java	Sun May 17 21:15:33 2020 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,12 +26,14 @@
 package java.nio.channels;
 
 import java.io.IOException;
+import java.net.ProtocolFamily;
 import java.net.Socket;
 import java.net.SocketOption;
 import java.net.SocketAddress;
 import java.nio.ByteBuffer;
 import java.nio.channels.spi.AbstractSelectableChannel;
 import java.nio.channels.spi.SelectorProvider;
+import static java.util.Objects.requireNonNull;
 
 /**
  * A selectable channel for stream-oriented connecting sockets.
@@ -151,6 +153,34 @@
     }
 
     /**
+     * Opens a socket channel. The {@code family} parameter specifies the
+     * {@link ProtocolFamily protocol family} of the channel's socket.
+     *
+     * <p> The new channel is created by invoking the {@link
+     * java.nio.channels.spi.SelectorProvider#openSocketChannel(ProtocolFamily)
+     * openSocketChannel(ProtocolFamily)} method of the system-wide default.
+     * {@link java.nio.channels.spi.SelectorProvider} object.</p>
+     *
+     * @param   family
+     *          The protocol family
+     *
+     * @return  A new socket channel
+     *
+     * @throws  UnsupportedOperationException
+     *          If the specified protocol family is not supported. For example,
+     *          suppose the parameter is specified as {@link
+     *          java.net.StandardProtocolFamily#INET6 StandardProtocolFamily.INET6}
+     *          but IPv6 is not enabled on the platform.
+     * @throws  IOException
+     *          If an I/O error occurs
+     *
+     * @since 15
+     */
+    public static SocketChannel open(ProtocolFamily family) throws IOException {
+        return SelectorProvider.provider().openSocketChannel(requireNonNull(family));
+    }
+
+    /**
      * Opens a socket channel and connects it to a remote address.
      *
      * <p> This convenience method works as if by invoking the {@link #open()}
--- a/src/java.base/share/classes/java/nio/channels/spi/SelectorProvider.java	Sun May 17 11:34:32 2020 -0700
+++ b/src/java.base/share/classes/java/nio/channels/spi/SelectorProvider.java	Sun May 17 21:15:33 2020 +0100
@@ -36,6 +36,7 @@
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.Iterator;
+import java.util.Objects;
 import java.util.ServiceLoader;
 import java.util.ServiceConfigurationError;
 
@@ -315,4 +316,51 @@
         return null;
     }
 
+    /**
+     * Opens a socket channel.
+     *
+     * @implSpec The default implementation of this method first checks that
+     * the given protocol {@code family} is not {@code null},
+     * then throws {@link UnsupportedOperationException}.
+     *
+     * @param   family
+     *          The protocol family
+     *
+     * @return  The new channel
+     *
+     * @throws  UnsupportedOperationException
+     *          If the specified protocol family is not supported
+     * @throws  IOException
+     *          If an I/O error occurs
+     *
+     * @since 15
+     */
+    public SocketChannel openSocketChannel(ProtocolFamily family) throws IOException {
+        Objects.requireNonNull(family);
+        throw new UnsupportedOperationException("Protocol family not supported");
+    }
+
+    /**
+     * Opens a server-socket channel.
+     *
+     * @implSpec The default implementation of this method first checks that
+     * the given protocol {@code family} is not {@code null},
+     * then throws {@link UnsupportedOperationException}.
+     *
+     * @param   family
+     *          The protocol family
+     *
+     * @return  The new channel
+     *
+     * @throws  UnsupportedOperationException
+     *          If the specified protocol family is not supported
+     * @throws  IOException
+     *          If an I/O error occurs
+     *
+     * @since 15
+     */
+    public ServerSocketChannel openServerSocketChannel(ProtocolFamily family) throws IOException {
+        Objects.requireNonNull(family);
+        throw new UnsupportedOperationException("Protocol family not supported");
+    }
 }
--- a/src/java.base/share/classes/sun/nio/ch/Net.java	Sun May 17 11:34:32 2020 -0700
+++ b/src/java.base/share/classes/sun/nio/ch/Net.java	Sun May 17 21:15:33 2020 +0100
@@ -249,6 +249,57 @@
                                      port);
     }
 
+    private static final InetAddress anyLocalInet4Address;
+    private static final InetAddress anyLocalInet6Address;
+    private static final InetAddress inet4LoopbackAddress;
+    private static final InetAddress inet6LoopbackAddress;
+    static {
+        try {
+            anyLocalInet4Address = inet4FromInt(0);
+            assert anyLocalInet4Address instanceof Inet4Address
+                    && anyLocalInet4Address.isAnyLocalAddress();
+
+            anyLocalInet6Address = InetAddress.getByAddress(new byte[16]);
+            assert anyLocalInet6Address instanceof Inet6Address
+                    && anyLocalInet6Address.isAnyLocalAddress();
+
+            inet4LoopbackAddress = inet4FromInt(0x7f000001);
+            assert inet4LoopbackAddress instanceof Inet4Address
+                    && inet4LoopbackAddress.isLoopbackAddress();
+
+            byte[] bytes = new byte[16];
+            bytes[15] = 0x01;
+            inet6LoopbackAddress = InetAddress.getByAddress(bytes);
+            assert inet6LoopbackAddress instanceof Inet6Address
+                    && inet6LoopbackAddress.isLoopbackAddress();
+        } catch (Exception e) {
+            throw new InternalError(e);
+        }
+    }
+
+    static InetAddress inet4LoopbackAddress() {
+        return inet4LoopbackAddress;
+    }
+
+    static InetAddress inet6LoopbackAddress() {
+        return inet6LoopbackAddress;
+    }
+
+    /**
+     * Returns the wildcard address that corresponds to the given protocol family.
+     *
+     * @see InetAddress#isAnyLocalAddress()
+     */
+    static InetAddress anyLocalAddress(ProtocolFamily family) {
+        if (family == StandardProtocolFamily.INET) {
+            return anyLocalInet4Address;
+        } else if (family == StandardProtocolFamily.INET6) {
+            return anyLocalInet6Address;
+        } else {
+            throw new IllegalArgumentException();
+        }
+    }
+
     /**
      * Returns any IPv4 address of the given network interface, or
      * null if the interface does not have any IPv4 addresses.
@@ -467,7 +518,13 @@
     }
 
     static FileDescriptor serverSocket(boolean stream) {
-        return IOUtil.newFD(socket0(isIPv6Available(), stream, true, fastLoopback));
+        return serverSocket(UNSPEC, stream);
+    }
+
+    static FileDescriptor serverSocket(ProtocolFamily family, boolean stream) {
+        boolean preferIPv6 = isIPv6Available() &&
+            (family != StandardProtocolFamily.INET);
+        return IOUtil.newFD(socket0(preferIPv6, stream, true, fastLoopback));
     }
 
     // Due to oddities SO_REUSEADDR on windows reuse is ignored
--- a/src/java.base/share/classes/sun/nio/ch/SelectorProviderImpl.java	Sun May 17 11:34:32 2020 -0700
+++ b/src/java.base/share/classes/sun/nio/ch/SelectorProviderImpl.java	Sun May 17 21:15:33 2020 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -72,4 +72,14 @@
     public SocketChannel openSocketChannel() throws IOException {
         return new SocketChannelImpl(this);
     }
+
+    @Override
+    public SocketChannel openSocketChannel(ProtocolFamily family) throws IOException {
+        return new SocketChannelImpl(this, family);
+    }
+
+    @Override
+    public ServerSocketChannel openServerSocketChannel(ProtocolFamily family) {
+        return new ServerSocketChannelImpl(this, family);
+    }
 }
--- a/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	Sun May 17 11:34:32 2020 -0700
+++ b/src/java.base/share/classes/sun/nio/ch/ServerSocketChannelImpl.java	Sun May 17 21:15:33 2020 +0100
@@ -28,10 +28,12 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.net.InetSocketAddress;
+import java.net.ProtocolFamily;
 import java.net.ServerSocket;
 import java.net.SocketAddress;
 import java.net.SocketOption;
 import java.net.SocketTimeoutException;
+import java.net.StandardProtocolFamily;
 import java.net.StandardSocketOptions;
 import java.nio.channels.AlreadyBoundException;
 import java.nio.channels.AsynchronousCloseException;
@@ -62,6 +64,9 @@
     // Used to make native close and configure calls
     private static final NativeDispatcher nd = new SocketDispatcher();
 
+    // The protocol family of the socket
+    private final ProtocolFamily family;
+
     // Our file descriptor
     private final FileDescriptor fd;
     private final int fdVal;
@@ -95,10 +100,26 @@
 
     // -- End of fields protected by stateLock
 
+    ServerSocketChannelImpl(SelectorProvider sp) {
+        this(sp, Net.isIPv6Available()
+                ? StandardProtocolFamily.INET6
+                : StandardProtocolFamily.INET);
+    }
 
-    ServerSocketChannelImpl(SelectorProvider sp) {
+    ServerSocketChannelImpl(SelectorProvider sp, ProtocolFamily family) {
         super(sp);
-        this.fd = Net.serverSocket(true);
+        Objects.requireNonNull(family, "'family' is null");
+
+        if ((family != StandardProtocolFamily.INET) &&
+                (family != StandardProtocolFamily.INET6)) {
+            throw new UnsupportedOperationException("Protocol family not supported");
+        }
+        if (family == StandardProtocolFamily.INET6 && !Net.isIPv6Available()) {
+            throw new UnsupportedOperationException("IPv6 not available");
+        }
+
+        this.family = family;
+        this.fd = Net.serverSocket(family, true);
         this.fdVal = IOUtil.fdVal(fd);
     }
 
@@ -106,8 +127,13 @@
         throws IOException
     {
         super(sp);
+
+        this.family = Net.isIPv6Available()
+                ? StandardProtocolFamily.INET6
+                : StandardProtocolFamily.INET;
         this.fd =  fd;
         this.fdVal = IOUtil.fdVal(fd);
+
         if (bound) {
             synchronized (stateLock) {
                 localAddress = Net.localAddress(fd);
@@ -210,14 +236,17 @@
             ensureOpen();
             if (localAddress != null)
                 throw new AlreadyBoundException();
-            InetSocketAddress isa = (local == null)
-                                    ? new InetSocketAddress(0)
-                                    : Net.checkAddress(local);
+            InetSocketAddress isa;
+            if (local == null) {
+                isa = new InetSocketAddress(Net.anyLocalAddress(family), 0);
+            } else {
+                isa = Net.checkAddress(local, family);
+            }
             SecurityManager sm = System.getSecurityManager();
             if (sm != null)
                 sm.checkListen(isa.getPort());
             NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
-            Net.bind(fd, isa.getAddress(), isa.getPort());
+            Net.bind(family, fd, isa.getAddress(), isa.getPort());
             Net.listen(fd, backlog < 1 ? 50 : backlog);
             localAddress = Net.localAddress(fd);
         }
@@ -358,7 +387,7 @@
             if (sm != null) {
                 sm.checkAccept(isa.getAddress().getHostAddress(), isa.getPort());
             }
-            return new SocketChannelImpl(provider(), newfd, isa);
+            return new SocketChannelImpl(provider(), family, newfd, isa);
         } catch (Exception e) {
             nd.close(newfd);
             throw e;
--- a/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java	Sun May 17 11:34:32 2020 -0700
+++ b/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java	Sun May 17 21:15:33 2020 +0100
@@ -28,6 +28,7 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.net.InetAddress;
+import java.net.Inet4Address;
 import java.net.InetSocketAddress;
 import java.net.ProtocolFamily;
 import java.net.Socket;
@@ -71,6 +72,9 @@
     // Used to make native read and write calls
     private static final NativeDispatcher nd = new SocketDispatcher();
 
+    // The protocol family of the socket
+    private final ProtocolFamily family;
+
     // Our file descriptor object
     private final FileDescriptor fd;
     private final int fdVal;
@@ -118,12 +122,26 @@
 
     // -- End of fields protected by stateLock
 
-
     // Constructor for normal connecting sockets
     //
     SocketChannelImpl(SelectorProvider sp) throws IOException {
+        this(sp, Net.isIPv6Available()
+                ? StandardProtocolFamily.INET6
+                : StandardProtocolFamily.INET);
+    }
+
+    SocketChannelImpl(SelectorProvider sp, ProtocolFamily family) throws IOException {
         super(sp);
-        this.fd = Net.socket(true);
+        Objects.requireNonNull(family, "'family' is null");
+        if ((family != StandardProtocolFamily.INET) &&
+                (family != StandardProtocolFamily.INET6)) {
+            throw new UnsupportedOperationException("Protocol family not supported");
+        }
+        if (family == StandardProtocolFamily.INET6 && !Net.isIPv6Available()) {
+            throw new UnsupportedOperationException("IPv6 not available");
+        }
+        this.family = family;
+        this.fd = Net.socket(family, true);
         this.fdVal = IOUtil.fdVal(fd);
     }
 
@@ -131,8 +149,12 @@
         throws IOException
     {
         super(sp);
+        this.family = Net.isIPv6Available()
+                ? StandardProtocolFamily.INET6
+                : StandardProtocolFamily.INET;
         this.fd = fd;
         this.fdVal = IOUtil.fdVal(fd);
+
         if (bound) {
             synchronized (stateLock) {
                 this.localAddress = Net.localAddress(fd);
@@ -142,10 +164,14 @@
 
     // Constructor for sockets obtained from server sockets
     //
-    SocketChannelImpl(SelectorProvider sp, FileDescriptor fd, InetSocketAddress isa)
+    SocketChannelImpl(SelectorProvider sp,
+                      ProtocolFamily family,
+                      FileDescriptor fd,
+                      InetSocketAddress isa)
         throws IOException
     {
         super(sp);
+        this.family = family;
         this.fd = fd;
         this.fdVal = IOUtil.fdVal(fd);
         synchronized (stateLock) {
@@ -225,8 +251,6 @@
             ensureOpen();
 
             if (name == StandardSocketOptions.IP_TOS) {
-                ProtocolFamily family = Net.isIPv6Available() ?
-                    StandardProtocolFamily.INET6 : StandardProtocolFamily.INET;
                 Net.setSocketOption(fd, family, name, value);
                 return this;
             }
@@ -260,10 +284,8 @@
                 return (T)Boolean.valueOf(isReuseAddress);
             }
 
-            // special handling for IP_TOS: always return 0 when IPv6
+            // special handling for IP_TOS
             if (name == StandardSocketOptions.IP_TOS) {
-                ProtocolFamily family = Net.isIPv6Available() ?
-                    StandardProtocolFamily.INET6 : StandardProtocolFamily.INET;
                 return (T) Net.getSocketOption(fd, family, name);
             }
 
@@ -632,14 +654,18 @@
                         throw new ConnectionPendingException();
                     if (localAddress != null)
                         throw new AlreadyBoundException();
-                    InetSocketAddress isa = (local == null) ?
-                        new InetSocketAddress(0) : Net.checkAddress(local);
+                    InetSocketAddress isa;
+                    if (local == null) {
+                        isa = new InetSocketAddress(Net.anyLocalAddress(family), 0);
+                    } else {
+                        isa = Net.checkAddress(local, family);
+                    }
                     SecurityManager sm = System.getSecurityManager();
                     if (sm != null) {
                         sm.checkListen(isa.getPort());
                     }
                     NetHooks.beforeTcpBind(fd, isa.getAddress(), isa.getPort());
-                    Net.bind(fd, isa.getAddress(), isa.getPort());
+                    Net.bind(family, fd, isa.getAddress(), isa.getPort());
                     localAddress = Net.localAddress(fd);
                 }
             } finally {
@@ -723,14 +749,21 @@
     /**
      * Checks the remote address to which this channel is to be connected.
      */
-    private InetSocketAddress checkRemote(SocketAddress sa) throws IOException {
-        InetSocketAddress isa = Net.checkAddress(sa);
+    private InetSocketAddress checkRemote(SocketAddress sa) {
+        InetSocketAddress isa = Net.checkAddress(sa, family);
         SecurityManager sm = System.getSecurityManager();
         if (sm != null) {
             sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort());
         }
-        if (isa.getAddress().isAnyLocalAddress()) {
-            return new InetSocketAddress(InetAddress.getLocalHost(), isa.getPort());
+        InetAddress address = isa.getAddress();
+        if (address.isAnyLocalAddress()) {
+            int port = isa.getPort();
+            if (address instanceof Inet4Address) {
+                return new InetSocketAddress(Net.inet4LoopbackAddress(), port);
+            } else {
+                assert family == StandardProtocolFamily.INET6;
+                return new InetSocketAddress(Net.inet6LoopbackAddress(), port);
+            }
         } else {
             return isa;
         }
@@ -748,7 +781,10 @@
                     boolean connected = false;
                     try {
                         beginConnect(blocking, isa);
-                        int n = Net.connect(fd, isa.getAddress(), isa.getPort());
+                        int n = Net.connect(family,
+                                            fd,
+                                            isa.getAddress(),
+                                            isa.getPort());
                         if (n > 0) {
                             connected = true;
                         } else if (blocking) {
--- a/src/java.base/unix/classes/sun/nio/ch/InheritedChannel.java	Sun May 17 11:34:32 2020 -0700
+++ b/src/java.base/unix/classes/sun/nio/ch/InheritedChannel.java	Sun May 17 21:15:33 2020 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -29,12 +29,13 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.net.InetAddress;
+import java.net.Inet6Address;
 import java.net.InetSocketAddress;
+import java.net.ProtocolFamily;
 import java.nio.channels.Channel;
-import java.nio.channels.SocketChannel;
-import java.nio.channels.ServerSocketChannel;
-import java.nio.channels.DatagramChannel;
 import java.nio.channels.spi.SelectorProvider;
+import static java.net.StandardProtocolFamily.INET6;
+import static java.net.StandardProtocolFamily.INET;
 
 class InheritedChannel {
 
@@ -81,12 +82,16 @@
      */
     public static class InheritedSocketChannelImpl extends SocketChannelImpl {
 
+        static ProtocolFamily family(InetSocketAddress isa) {
+            return (isa.getAddress() instanceof Inet6Address) ? INET6 : INET;
+        }
+
         InheritedSocketChannelImpl(SelectorProvider sp,
                                    FileDescriptor fd,
                                    InetSocketAddress remote)
             throws IOException
         {
-            super(sp, fd, remote);
+            super(sp, family(remote), fd, remote);
         }
 
         protected void implCloseSelectableChannel() throws IOException {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/etc/LocalSocketAddressType.java	Sun May 17 21:15:33 2020 +0100
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @summary Test local address type
+ * @library /test/lib
+ * @build jdk.test.lib.NetworkConfiguration
+ * @run testng/othervm LocalSocketAddressType
+ * @run testng/othervm -Djava.net.preferIPv4Stack=true LocalSocketAddressType
+ */
+
+import jdk.test.lib.NetworkConfiguration;
+import jdk.test.lib.net.IPSupport;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.net.*;
+import java.nio.channels.DatagramChannel;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.Iterator;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static java.lang.Boolean.parseBoolean;
+import static java.lang.System.getProperty;
+import static java.lang.System.out;
+import static jdk.test.lib.Asserts.assertEquals;
+import static jdk.test.lib.Asserts.assertTrue;
+import static jdk.test.lib.net.IPSupport.*;
+
+public class LocalSocketAddressType {
+
+    @BeforeTest()
+    public void setup() {
+        IPSupport.printPlatformSupport(out);
+        throwSkippedExceptionIfNonOperational();
+    }
+
+    @DataProvider(name = "addresses")
+    public static Iterator<Object[]> addresses() throws Exception {
+        NetworkConfiguration nc = NetworkConfiguration.probe();
+        return Stream.concat(nc.ip4Addresses(), nc.ip6Addresses())
+                .map(ia -> new Object[] { new InetSocketAddress(ia, 0) })
+                .iterator();
+    }
+
+    @Test(dataProvider = "addresses")
+    public static void testSocketChannel(InetSocketAddress addr) throws Exception {
+        try (var c = SocketChannel.open()) {
+            Class<? extends InetAddress> cls = addr.getAddress().getClass();
+            InetAddress ia = ((InetSocketAddress)c.bind(addr).getLocalAddress()).getAddress();
+            assertEquals(ia.getClass(), cls);
+            ia = c.socket().getLocalAddress();
+            assertEquals(ia.getClass(), cls);
+        }
+    }
+
+    @Test(dataProvider = "addresses")
+    public static void testServerSocketChannel(InetSocketAddress addr) throws Exception {
+        try (var c = ServerSocketChannel.open()) {
+            Class<? extends InetAddress> cls = addr.getAddress().getClass();
+            InetAddress ia = ((InetSocketAddress)c.bind(addr).getLocalAddress()).getAddress();
+            assertEquals(ia.getClass(), cls);
+            ia = c.socket().getInetAddress();
+            assertEquals(ia.getClass(), cls);
+        }
+    }
+
+    @Test(dataProvider = "addresses")
+    public static void testDatagramChannel(InetSocketAddress addr) throws Exception {
+        try (var c = DatagramChannel.open()) {
+            Class<? extends InetAddress> cls = addr.getAddress().getClass();
+            InetAddress ia = ((InetSocketAddress)c.bind(addr).getLocalAddress()).getAddress();
+            assertEquals(ia.getClass(), cls);
+            ia = c.socket().getLocalAddress();
+            assertEquals(ia.getClass(), cls);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/etc/OpenAndConnect.java	Sun May 17 21:15:33 2020 +0100
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import jdk.test.lib.NetworkConfiguration;
+import jdk.test.lib.net.IPSupport;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.net.*;
+import java.nio.channels.*;
+import java.util.Arrays;
+import java.util.List;
+import java.util.LinkedList;
+
+import static java.lang.System.getProperty;
+import static java.lang.System.out;
+import static java.net.StandardProtocolFamily.INET;
+import static java.net.StandardProtocolFamily.INET6;
+import static jdk.test.lib.net.IPSupport.*;
+
+/*
+ * @test
+ * @summary Test SocketChannel, ServerSocketChannel and DatagramChannel
+ *          open() and connect(), taking into consideration combinations of
+ *          protocol families (INET, INET6, default),
+ *          addresses (Inet4Address, Inet6Address).
+ * @library /test/lib
+ * @build jdk.test.lib.NetworkConfiguration
+ * @run testng/othervm OpenAndConnect
+ */
+
+
+public class OpenAndConnect {
+    static final Inet4Address IA4ANYLOCAL;
+    static final Inet6Address IA6ANYLOCAL;
+    static final Inet4Address IA4LOOPBACK;
+    static final Inet6Address IA6LOOPBACK;
+    static Inet4Address IA4LOCAL = null;
+    static Inet6Address IA6LOCAL = null;
+    static InetAddress DONT_BIND;
+
+    static {
+        try {
+            IA4ANYLOCAL = (Inet4Address) InetAddress.getByName("0.0.0.0");
+            IA6ANYLOCAL = (Inet6Address) InetAddress.getByName("::0");
+            IA4LOOPBACK = (Inet4Address) InetAddress.getByName("127.0.0.1");
+            IA6LOOPBACK = (Inet6Address) InetAddress.getByName("::1");
+
+            // Special value to tell test not to call bind (address is not used)
+            DONT_BIND = (Inet4Address) InetAddress.getByName("127.0.0.3");
+
+            initAddrs();
+        } catch (Exception e) {
+            throw new RuntimeException("Could not initialize addresses", e);
+        }
+    }
+
+    @BeforeTest()
+    public void setup() {
+        NetworkConfiguration.printSystemConfiguration(out);
+        IPSupport.printPlatformSupport(out);
+        throwSkippedExceptionIfNonOperational();
+
+        out.println("IA4LOCAL:    " + IA4LOCAL);
+        out.println("IA6LOCAL:    " + IA6LOCAL);
+        out.println("IA4ANYLOCAL: " + IA4ANYLOCAL);
+        out.println("IA6ANYLOCAL: " + IA6ANYLOCAL);
+        out.println("IA4LOOPBACK: " + IA4LOOPBACK);
+        out.println("IA6LOOPBACK: " + IA6LOOPBACK);
+    }
+
+    @DataProvider(name = "openConnect")
+    public Object[][] openConnect() {
+        LinkedList<Object[]>  l = new LinkedList<>();
+        l.addAll(openConnectGenTests);
+        if (IA4LOCAL != null) {
+            l.addAll(openConnectV4LocalTests);
+        }
+        if (IA6LOCAL != null) {
+            l.addAll(openConnectV6LocalTests);
+        }
+        return l.toArray(new Object[][]{});
+    }
+
+    //            +----- sfam is server/first socket family
+    //            |
+    //            |       +------ saddr is bind address for server/first socket
+    //            |       |
+    //            |       |              +---- cfam is family for client/second socket
+    //            |       |              |
+    //            |       |              |        +---- caddr is address client/second
+    //            |       |              |        |     socket binds to. When the server
+    //            |       |              |        |     has bound to a wildcard address
+    //            |       |              |        |     this is address used for connect
+    //            |       |              |        |     also.
+    //            |       |              |        |
+    //            |       |              |        |
+    //            |       |              |        |
+    //            |       |              |        |
+    //            +       +              +        +
+    //      {   sfam,   saddr,         cfam,    caddr,      }
+
+    public static List<Object[]> openConnectGenTests =
+        Arrays.asList(new Object[][] {
+            {   INET,   IA4LOOPBACK,   INET,    IA4LOOPBACK },
+            {   INET,   IA4LOOPBACK,   null,    IA4LOOPBACK },
+            {   INET,   IA4ANYLOCAL,   null,    IA4LOOPBACK },
+            {   INET,   IA4ANYLOCAL,   INET,    IA4LOOPBACK },
+            {   INET6,  IA6ANYLOCAL,   null,    IA6LOOPBACK },
+            {   INET6,  IA6ANYLOCAL,   INET6,   IA6LOOPBACK },
+            {   INET6,  IA6LOOPBACK,   INET6,   IA6LOOPBACK },
+            {   null,   IA4LOOPBACK,   INET,    IA4ANYLOCAL },
+            {   null,   IA4LOOPBACK,   INET,    IA4LOOPBACK },
+            {   null,   IA4LOOPBACK,   INET,    null        },
+            {   null,   IA4LOOPBACK,   INET6,   IA6ANYLOCAL },
+            {   null,   IA6LOOPBACK,   INET6,   IA6ANYLOCAL },
+            {   null,   IA6LOOPBACK,   INET6,   IA6LOOPBACK },
+            {   null,   IA6LOOPBACK,   INET6,   DONT_BIND   },
+            {   null,   IA4LOOPBACK,   INET6,   DONT_BIND   },
+            {   null,   IA4LOOPBACK,   INET6,   null        },
+            {   null,   IA6LOOPBACK,   INET6,   null        },
+            {   null,   IA4LOOPBACK,   null,    IA6ANYLOCAL },
+            {   null,   IA6LOOPBACK,   null,    IA6ANYLOCAL },
+            {   null,   IA6LOOPBACK,   null,    IA6LOOPBACK },
+            {   null,   IA4LOOPBACK,   null,    null        },
+            {   null,   IA6LOOPBACK,   null,    null        },
+            {   null,   IA6ANYLOCAL,   null,    IA6LOCAL    },
+            {   null,   IA6ANYLOCAL,   null,    IA6LOOPBACK },
+            {   null,   IA6ANYLOCAL,   INET6,   IA6LOCAL    },
+            {   null,   IA6ANYLOCAL,   INET6,   IA6LOOPBACK },
+            {   INET6,  IA6LOOPBACK,   INET6,   IA6LOOPBACK }
+        });
+
+    // Additional tests for when an IPv4 local address or V6
+    // local address is available
+
+    public List<Object[]>  openConnectV4LocalTests =
+        Arrays.asList(new Object[][] {
+            {   INET,   IA4LOCAL,      INET,    IA4LOCAL    },
+            {   INET,   IA4LOCAL,      null,    IA4LOCAL    },
+            {   INET,   IA4LOCAL,      null,    DONT_BIND   },
+            {   INET,   IA4ANYLOCAL,   INET,    IA4LOCAL    },
+            {   INET,   IA4ANYLOCAL,   null,    IA4LOCAL    },
+            {   null,   IA4LOCAL,      INET,    IA4ANYLOCAL },
+            {   null,   IA4LOCAL,      INET,    IA4LOCAL    },
+            {   null,   IA4LOCAL,      INET,    null        },
+            {   null,   IA4LOCAL,      INET6,   IA6ANYLOCAL },
+            {   null,   IA4LOCAL,      INET6,   null        },
+            {   null,   IA4LOCAL,      null,    IA6ANYLOCAL }
+        });
+
+    public List<Object[]> openConnectV6LocalTests =
+        Arrays.asList(new Object[][] {
+            {   INET6,  IA6ANYLOCAL,   null,    IA6LOCAL    },
+            {   INET6,  IA6ANYLOCAL,   INET6,   IA6LOCAL    },
+            {   INET6,  IA6LOCAL,      INET6,   IA6LOCAL    },
+            {   INET6,  IA6LOCAL,      null,    IA6LOCAL    },
+            {   INET6,  IA6LOCAL,      null,    DONT_BIND   },
+            {   null,   IA6LOCAL,      INET6,   IA6LOCAL    },
+            {   null,   IA6LOCAL,      INET6,   IA6ANYLOCAL },
+            {   null,   IA6LOCAL,      null,    IA6ANYLOCAL },
+            {   null,   IA6LOCAL,      null,    IA6LOCAL    },
+            {   null,   IA6LOCAL,      INET6,   null        },
+            {   null,   IA6LOCAL,      null,    null        },
+            {   null,   IA4LOCAL,      null,    null        },
+            {   INET6,  IA6LOCAL,      INET6,   IA6LOCAL    }
+        });
+
+
+    /**
+     * If the destination address is the wildcard, it is replaced by the alternate
+     * using the port number from destination. Otherwise destination is returned.
+     * Only used by dcOpenAndConnect
+     */
+    static InetSocketAddress getDestinationAddress(SocketAddress destination, InetAddress alternate) {
+        InetSocketAddress isa = (InetSocketAddress)destination;
+        if (isa.getAddress().isAnyLocalAddress())
+            return new InetSocketAddress(alternate, isa.getPort());
+        else
+            return isa;
+    }
+
+    @Test(dataProvider = "openConnect")
+    public void scOpenAndConnect(ProtocolFamily sfam,
+                                 InetAddress saddr,
+                                 ProtocolFamily cfam,
+                                 InetAddress caddr) throws IOException
+    {
+        out.printf("scOpenAndConnect: server bind: %s client bind: %s\n", saddr, caddr);
+        try (ServerSocketChannel ssc = openSSC(sfam)) {
+            ssc.bind(getSocketAddress(saddr));
+            InetSocketAddress ssa = (InetSocketAddress)ssc.getLocalAddress();
+            ssa = getDestinationAddress(ssa, caddr);
+            out.println(ssa);
+            try (SocketChannel csc = openSC(cfam)) {
+                if (caddr != DONT_BIND) {
+                    csc.bind(getSocketAddress(caddr));
+                }
+                csc.connect(ssa);
+            }
+        }
+    }
+
+    @Test(dataProvider = "openConnect")
+    public void dcOpenAndConnect(ProtocolFamily sfam,
+                                 InetAddress saddr,
+                                 ProtocolFamily cfam,
+                                 InetAddress caddr) throws IOException
+    {
+        try (DatagramChannel sdc = openDC(sfam)) {
+            sdc.bind(getSocketAddress(saddr));
+            SocketAddress ssa = sdc.socket().getLocalSocketAddress();
+            ssa = getDestinationAddress(ssa, caddr);
+            out.println(ssa);
+            try (DatagramChannel dc = openDC(cfam)) {
+                if (caddr != DONT_BIND) {
+                    dc.bind(getSocketAddress(caddr));
+                }
+                dc.connect(ssa);
+            }
+        }
+    }
+
+    // Helper methods
+
+    private static SocketChannel openSC(ProtocolFamily fam) throws IOException {
+        return fam == null ? SocketChannel.open() : SocketChannel.open(fam);
+    }
+
+    private static ServerSocketChannel openSSC(ProtocolFamily fam)
+            throws IOException {
+        return fam == null ? ServerSocketChannel.open()
+                : ServerSocketChannel.open(fam);
+    }
+
+    private static DatagramChannel openDC(ProtocolFamily fam)
+            throws IOException {
+        return fam == null ? DatagramChannel.open()
+                : DatagramChannel.open(fam);
+    }
+
+    private static SocketAddress getSocketAddress(InetAddress ia) {
+        return ia == null ? null : new InetSocketAddress(ia, 0);
+    }
+
+    private static void initAddrs() throws IOException {
+
+        NetworkConfiguration cfg = NetworkConfiguration.probe();
+
+        IA4LOCAL = cfg.ip4Addresses()
+                .filter(a -> !a.isLoopbackAddress())
+                .findFirst()
+                .orElse(null);
+
+        IA6LOCAL = cfg.ip6Addresses()
+                .filter(a -> !a.isLoopbackAddress())
+                .findFirst()
+                .orElse(null);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/etc/ProtocolFamilies.java	Sun May 17 21:15:33 2020 +0100
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import jdk.test.lib.NetworkConfiguration;
+import jdk.test.lib.Platform;
+import jdk.test.lib.net.IPSupport;
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import org.testng.Assert.ThrowingRunnable;
+import java.io.IOException;
+import java.net.*;
+import java.nio.channels.*;
+import java.nio.channels.spi.AbstractSelector;
+import java.nio.channels.spi.SelectorProvider;
+import static java.lang.System.out;
+import static java.lang.System.getProperty;
+import static java.lang.Boolean.parseBoolean;
+import static java.net.StandardProtocolFamily.INET;
+import static java.net.StandardProtocolFamily.INET6;
+import static jdk.test.lib.net.IPSupport.*;
+import static org.testng.Assert.assertThrows;
+
+/*
+ * @test
+ * @summary Test SocketChannel, ServerSocketChannel and DatagramChannel
+ *          with various ProtocolFamily combinations
+ * @library /test/lib
+ * @build jdk.test.lib.NetworkConfiguration
+ * @run testng ProtocolFamilies
+ * @run testng/othervm -Djava.net.preferIPv4Stack=true ProtocolFamilies
+ */
+
+
+public class ProtocolFamilies {
+    static final boolean hasIPv6 = hasIPv6();
+    static final boolean preferIPv4 = preferIPv4Stack();
+    static Inet4Address ia4;
+    static Inet6Address ia6;
+
+    @BeforeTest()
+    public void setup() throws Exception {
+        NetworkConfiguration.printSystemConfiguration(out);
+        IPSupport.printPlatformSupport(out);
+        throwSkippedExceptionIfNonOperational();
+
+        ia4 = getLocalIPv4Address();
+        ia6 = getLocalIPv6Address();
+        out.println("ia4: " + ia4);
+        out.println("ia6: " + ia6 + "\n");
+    }
+
+    static final Class<UnsupportedAddressTypeException> UATE = UnsupportedAddressTypeException.class;
+    static final Class<UnsupportedOperationException> UOE = UnsupportedOperationException.class;
+
+    @DataProvider(name = "open")
+    public Object[][] open() {
+        if (hasIPv6 && !preferIPv4) {
+            return new Object[][]{
+                    {  INET,   null  },
+                    {  INET6,  null  }
+            };
+        } else {
+            return new Object[][]{
+                    {  INET,   null  },
+                    {  INET6,  UOE   }
+            };
+        }
+    }
+
+    @Test(dataProvider = "open")
+    public void scOpen(StandardProtocolFamily family,
+                       Class<? extends Exception> expectedException)
+        throws Throwable
+    {
+        SocketChannel sc = null;
+        try {
+            if (expectedException == UOE) {
+                try {
+                    sc = openSC(family);
+                } catch (UnsupportedOperationException e) {}
+            } else {
+                sc = openSC(family);
+            }
+        } finally {
+            if (sc != null)
+                sc.close();
+        }
+    }
+
+    @Test(dataProvider = "open")
+    public void sscOpen(StandardProtocolFamily family,
+                        Class<? extends Exception> expectedException)
+        throws Throwable
+    {
+        ServerSocketChannel ssc = null;
+        try {
+            if (expectedException == UOE) {
+                try {
+                    ssc = openSSC(family);
+                } catch (UnsupportedOperationException e) {}
+            } else {
+                openSSC(family);
+            }
+        } finally {
+            if (ssc != null)
+                ssc.close();
+        }
+    }
+
+    @Test(dataProvider = "open")
+    public void dcOpen(StandardProtocolFamily family,
+                       Class<? extends Exception> expectedException)
+        throws Throwable
+    {
+        DatagramChannel dc = null;
+        try {
+            if (expectedException == UOE) {
+                try {
+                    dc = openDC(family);
+                } catch (UnsupportedOperationException e) {}
+            } else {
+                openDC(family);
+            }
+        } finally {
+            if (dc != null)
+                dc.close();
+        }
+    }
+
+    @DataProvider(name = "openBind")
+    public Object[][] openBind() {
+        if (hasIPv6 && !preferIPv4) {
+            return new Object[][]{
+                    {   INET,   INET,   null   },
+                    {   INET,   INET6,  UATE   },
+                    {   INET,   null,   null   },
+                    {   INET6,  INET,   null   },
+                    {   INET6,  INET6,  null   },
+                    {   INET6,  null,   null   },
+                    {   null,   INET,   null   },
+                    {   null,   INET6,  null   },
+                    {   null,   null,   null   }
+            };
+        } else {
+            return new Object[][]{
+                    {   INET,   INET,   null   },
+                    {   INET,   INET6,  UATE   },
+                    {   INET,   null,   null   },
+                    {   null,   INET,   null   },
+                    {   null,   INET6,  UATE   },
+                    {   null,   null,   null   }
+            };
+        }
+    }
+
+    // SocketChannel open - INET, INET6, default
+    // SocketChannel bind - INET, INET6, null
+
+    @Test(dataProvider = "openBind")
+    public void scOpenBind(StandardProtocolFamily ofamily,
+                           StandardProtocolFamily bfamily,
+                           Class<? extends Exception> expectedException)
+        throws Throwable
+    {
+        try (SocketChannel sc = openSC(ofamily)) {
+            SocketAddress addr = getSocketAddress(bfamily);
+            ThrowingRunnable bindOp = () -> sc.bind(addr);
+                if (expectedException == null)
+                    bindOp.run();
+            else
+                assertThrows(expectedException, bindOp);
+        }
+    }
+
+    //  ServerSocketChannel open - INET, INET6, default
+    //  ServerSocketChannel bind - INET, INET6, null
+
+    @Test(dataProvider = "openBind")
+    public void sscOpenBind(StandardProtocolFamily ofamily,
+                            StandardProtocolFamily bfamily,
+                            Class<? extends Exception> expectedException)
+        throws Throwable
+    {
+        try (ServerSocketChannel ssc = openSSC(ofamily)) {
+            SocketAddress addr = getSocketAddress(bfamily);
+            ThrowingRunnable bindOp = () -> ssc.bind(addr);
+            if (expectedException == null)
+                bindOp.run();
+            else
+                assertThrows(expectedException, bindOp);
+        }
+    }
+
+    //  DatagramChannel open - INET, INET6, default
+    //  DatagramChannel bind - INET, INET6, null
+
+    @Test(dataProvider = "openBind")
+    public void dcOpenBind(StandardProtocolFamily ofamily,
+                           StandardProtocolFamily bfamily,
+                           Class<? extends Exception> expectedException)
+        throws Throwable
+    {
+        try (DatagramChannel dc = openDC(ofamily)) {
+            SocketAddress addr = getSocketAddress(bfamily);
+            ThrowingRunnable bindOp = () -> dc.bind(addr);
+            if (expectedException == null)
+                bindOp.run();
+            else
+                assertThrows(expectedException, bindOp);
+        }
+    }
+
+    //  SocketChannel open    - INET, INET6, default
+    //  SocketChannel connect - INET, INET6, default
+
+    @DataProvider(name = "openConnect")
+    public Object[][] openConnect() {
+        if (hasIPv6 && !preferIPv4) {
+            return new Object[][]{
+                    {   INET,   INET,   null   },
+                    {   INET,   INET6,  null   },
+                    {   INET,   null,   null   },
+                    {   INET6,  INET,   UATE   },
+                    {   INET6,  INET6,  null   },
+                    {   INET6,  null,   null   },
+                    {   null,   INET,   UATE   },
+                    {   null,   INET6,  null   },
+                    {   null,   null,   null   }
+            };
+        } else {
+            // INET6 channels cannot be created - UOE - tested elsewhere
+            return new Object[][]{
+                    {   INET,   INET,   null   },
+                    {   INET,   null,   null   },
+                    {   null,   INET,   null   },
+                    {   null,   null,   null   }
+            };
+        }
+    }
+
+    @Test(dataProvider = "openConnect")
+    public void scOpenConnect(StandardProtocolFamily sfamily,
+                              StandardProtocolFamily cfamily,
+                              Class<? extends Exception> expectedException)
+        throws Throwable
+    {
+        try (ServerSocketChannel ssc = openSSC(sfamily)) {
+            ssc.bind(null);
+            SocketAddress saddr = ssc.getLocalAddress();
+            try (SocketChannel sc = openSC(cfamily)) {
+                if (expectedException == null)
+                    sc.connect(saddr);
+                else
+                    assertThrows(expectedException, () -> sc.connect(saddr));
+            }
+        }
+    }
+
+    static final Class<NullPointerException> NPE = NullPointerException.class;
+
+    // Tests null handling
+    @Test
+    public void testNulls() {
+        assertThrows(NPE, () -> SocketChannel.open((ProtocolFamily)null));
+        assertThrows(NPE, () -> ServerSocketChannel.open(null));
+        assertThrows(NPE, () -> DatagramChannel.open(null));
+
+        assertThrows(NPE, () -> SelectorProvider.provider().openSocketChannel(null));
+        assertThrows(NPE, () -> SelectorProvider.provider().openServerSocketChannel(null));
+        assertThrows(NPE, () -> SelectorProvider.provider().openDatagramChannel(null));
+    }
+
+    static final ProtocolFamily BAD_PF = () -> "BAD_PROTOCOL_FAMILY";
+
+    // Tests UOE handling
+    @Test
+    public void testUoe() {
+        assertThrows(UOE, () -> SocketChannel.open(BAD_PF));
+        assertThrows(UOE, () -> ServerSocketChannel.open(BAD_PF));
+        assertThrows(UOE, () -> DatagramChannel.open(BAD_PF));
+
+        assertThrows(UOE, () -> SelectorProvider.provider().openSocketChannel(BAD_PF));
+        assertThrows(UOE, () -> SelectorProvider.provider().openServerSocketChannel(BAD_PF));
+        assertThrows(UOE, () -> SelectorProvider.provider().openDatagramChannel(BAD_PF));
+    }
+
+    // A concrete subclass of SelectorProvider, in order to test implSpec
+    static final SelectorProvider customerSelectorProvider = new SelectorProvider() {
+        @Override public DatagramChannel openDatagramChannel() { return null; }
+        @Override public DatagramChannel openDatagramChannel(ProtocolFamily family) { return null; }
+        @Override public Pipe openPipe() { return null; }
+        @Override public AbstractSelector openSelector() { return null; }
+        @Override public ServerSocketChannel openServerSocketChannel() { return null; }
+        @Override public SocketChannel openSocketChannel() { return null; }
+    };
+
+    // Tests the specified default implementation of SelectorProvider
+    @Test
+    public void testCustomProvider() {
+        assertThrows(NPE, () -> customerSelectorProvider.openSocketChannel(null));
+        assertThrows(NPE, () -> customerSelectorProvider.openServerSocketChannel(null));
+
+        assertThrows(UOE, () -> customerSelectorProvider.openSocketChannel(BAD_PF));
+        assertThrows(UOE, () -> customerSelectorProvider.openServerSocketChannel(BAD_PF));
+    }
+
+    // Helper methods
+
+    private static SocketChannel openSC(StandardProtocolFamily family)
+            throws IOException {
+        return family == null ? SocketChannel.open()
+                : SocketChannel.open(family);
+    }
+
+    private static ServerSocketChannel openSSC(StandardProtocolFamily family)
+            throws IOException {
+        return family == null ? ServerSocketChannel.open()
+                : ServerSocketChannel.open(family);
+    }
+
+    private static DatagramChannel openDC(StandardProtocolFamily family)
+            throws IOException {
+        return family == null ? DatagramChannel.open()
+                : DatagramChannel.open(family);
+    }
+
+    private static SocketAddress getSocketAddress(StandardProtocolFamily family) {
+        return family == null ? null : switch (family) {
+            case INET -> new InetSocketAddress(ia4, 0);
+            case INET6 -> new InetSocketAddress(ia6, 0);
+        };
+    }
+
+    private static SocketAddress getLoopback(StandardProtocolFamily family, int port)
+            throws UnknownHostException {
+        if ((family == null || family == INET6) && hasIPv6) {
+            return new InetSocketAddress(InetAddress.getByName("::1"), port);
+        } else {
+            return new InetSocketAddress(InetAddress.getByName("127.0.0.1"), port);
+        }
+    }
+
+    private static Inet4Address getLocalIPv4Address()
+            throws Exception {
+        return NetworkConfiguration.probe()
+                .ip4Addresses()
+                .filter(a -> !a.isLoopbackAddress())
+                .findFirst()
+                .orElse((Inet4Address)InetAddress.getByName("0.0.0.0"));
+    }
+
+    private static Inet6Address getLocalIPv6Address()
+            throws Exception {
+        return NetworkConfiguration.probe()
+                .ip6Addresses()
+                .filter(a -> !a.isLoopbackAddress())
+                .findFirst()
+                .orElse((Inet6Address) InetAddress.getByName("::0"));
+    }
+}