1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.  Oracle designates this
9 * particular file as subject to the "Classpath" exception as provided
10 * by Oracle in the LICENSE file that accompanied this code.
11 *
12 * This code is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 * version 2 for more details (a copy is included in the LICENSE file that
16 * accompanied this code).
17 *
18 * You should have received a copy of the GNU General Public License version
19 * 2 along with this work; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23 * or visit www.oracle.com if you need additional information or have any
24 * questions.
25 */
26
27package sun.nio.ch;
28
29import java.io.FileDescriptor;
30import java.io.IOException;
31import java.net.*;
32import java.nio.ByteBuffer;
33import java.nio.channels.*;
34import java.nio.channels.spi.*;
35import java.util.*;
36
37import dalvik.system.BlockGuard;
38import sun.net.ResourceManager;
39
40
41/**
42 * An implementation of DatagramChannels.
43 */
44
45class DatagramChannelImpl
46    extends DatagramChannel
47    implements SelChImpl
48{
49
50    // Used to make native read and write calls
51    private static NativeDispatcher nd = new DatagramDispatcher();
52
53    // Our file descriptor
54    // Android-changed: Make the fd package visible so that we can expose it through DatagramSocketAdaptor.
55    final FileDescriptor fd;
56
57    // fd value needed for dev/poll. This value will remain valid
58    // even after the value in the file descriptor object has been set to -1
59    private final int fdVal;
60
61    // The protocol family of the socket
62    private final ProtocolFamily family;
63
64    // IDs of native threads doing reads and writes, for signalling
65    private volatile long readerThread = 0;
66    private volatile long writerThread = 0;
67
68    // Cached InetAddress and port for unconnected DatagramChannels
69    // used by receive0
70    private InetAddress cachedSenderInetAddress;
71    private int cachedSenderPort;
72
73    // Lock held by current reading or connecting thread
74    private final Object readLock = new Object();
75
76    // Lock held by current writing or connecting thread
77    private final Object writeLock = new Object();
78
79    // Lock held by any thread that modifies the state fields declared below
80    // DO NOT invoke a blocking I/O operation while holding this lock!
81    private final Object stateLock = new Object();
82
83    // -- The following fields are protected by stateLock
84
85    // State (does not necessarily increase monotonically)
86    private static final int ST_UNINITIALIZED = -1;
87    private static final int ST_UNCONNECTED = 0;
88    private static final int ST_CONNECTED = 1;
89    private static final int ST_KILLED = 2;
90    private int state = ST_UNINITIALIZED;
91
92    // Binding
93    private InetSocketAddress localAddress;
94    private InetSocketAddress remoteAddress;
95
96    // Our socket adaptor, if any
97    private DatagramSocket socket;
98
99    // set true when socket is bound and SO_REUSEADDRESS is emulated
100    private boolean reuseAddressEmulated;
101
102    // set true/false when socket is already bound and SO_REUSEADDR is emulated
103    private boolean isReuseAddress;
104
105    // -- End of fields protected by stateLock
106
107
108    public DatagramChannelImpl(SelectorProvider sp)
109        throws IOException
110    {
111        super(sp);
112        ResourceManager.beforeUdpCreate();
113        try {
114            this.family = Net.isIPv6Available() ?
115                StandardProtocolFamily.INET6 : StandardProtocolFamily.INET;
116            this.fd = Net.socket(family, false);
117            this.fdVal = IOUtil.fdVal(fd);
118            this.state = ST_UNCONNECTED;
119        } catch (IOException ioe) {
120            ResourceManager.afterUdpClose();
121            throw ioe;
122        }
123    }
124
125    public DatagramChannelImpl(SelectorProvider sp, ProtocolFamily family)
126        throws IOException
127    {
128        super(sp);
129        if ((family != StandardProtocolFamily.INET) &&
130            (family != StandardProtocolFamily.INET6))
131        {
132            if (family == null)
133                throw new NullPointerException("'family' is null");
134            else
135                throw new UnsupportedOperationException("Protocol family not supported");
136        }
137        if (family == StandardProtocolFamily.INET6) {
138            if (!Net.isIPv6Available()) {
139                throw new UnsupportedOperationException("IPv6 not available");
140            }
141        }
142        this.family = family;
143        this.fd = Net.socket(family, false);
144        this.fdVal = IOUtil.fdVal(fd);
145        this.state = ST_UNCONNECTED;
146    }
147
148    public DatagramChannelImpl(SelectorProvider sp, FileDescriptor fd)
149        throws IOException
150    {
151        super(sp);
152        this.family = Net.isIPv6Available() ?
153            StandardProtocolFamily.INET6 : StandardProtocolFamily.INET;
154        this.fd = fd;
155        this.fdVal = IOUtil.fdVal(fd);
156        this.state = ST_UNCONNECTED;
157        this.localAddress = Net.localAddress(fd);
158    }
159
160    public DatagramSocket socket() {
161        synchronized (stateLock) {
162            if (socket == null)
163                socket = DatagramSocketAdaptor.create(this);
164            return socket;
165        }
166    }
167
168    public SocketAddress getLocalAddress() throws IOException {
169        synchronized (stateLock) {
170            if (!isOpen())
171                throw new ClosedChannelException();
172            return Net.getRevealedLocalAddress(localAddress);
173        }
174    }
175
176    @Override
177    public SocketAddress getRemoteAddress() throws IOException {
178        synchronized (stateLock) {
179            if (!isOpen())
180                throw new ClosedChannelException();
181            return remoteAddress;
182        }
183    }
184
185    @Override
186    public <T> DatagramChannel setOption(SocketOption<T> name, T value)
187        throws IOException
188    {
189        if (name == null)
190            throw new NullPointerException();
191        if (!supportedOptions().contains(name))
192            throw new UnsupportedOperationException("'" + name + "' not supported");
193
194        synchronized (stateLock) {
195            ensureOpen();
196
197            if (name == StandardSocketOptions.IP_TOS) {
198                // IPv4 only; no-op for IPv6
199                if (family == StandardProtocolFamily.INET) {
200                    Net.setSocketOption(fd, family, name, value);
201                }
202                return this;
203            }
204
205            if (name == StandardSocketOptions.IP_MULTICAST_TTL ||
206                name == StandardSocketOptions.IP_MULTICAST_LOOP)
207            {
208                // options are protocol dependent
209                Net.setSocketOption(fd, family, name, value);
210                return this;
211            }
212
213            if (name == StandardSocketOptions.IP_MULTICAST_IF) {
214                if (value == null)
215                    throw new IllegalArgumentException("Cannot set IP_MULTICAST_IF to 'null'");
216                NetworkInterface interf = (NetworkInterface)value;
217                if (family == StandardProtocolFamily.INET6) {
218                    int index = interf.getIndex();
219                    if (index == -1)
220                        throw new IOException("Network interface cannot be identified");
221                    Net.setInterface6(fd, index);
222                } else {
223                    // need IPv4 address to identify interface
224                    Inet4Address target = Net.anyInet4Address(interf);
225                    if (target == null)
226                        throw new IOException("Network interface not configured for IPv4");
227                    int targetAddress = Net.inet4AsInt(target);
228                    Net.setInterface4(fd, targetAddress);
229                }
230                return this;
231            }
232            if (name == StandardSocketOptions.SO_REUSEADDR &&
233                    Net.useExclusiveBind() && localAddress != null)
234            {
235                reuseAddressEmulated = true;
236                this.isReuseAddress = (Boolean)value;
237            }
238
239            // remaining options don't need any special handling
240            Net.setSocketOption(fd, Net.UNSPEC, name, value);
241            return this;
242        }
243    }
244
245    @SuppressWarnings("unchecked")
246    public <T> T getOption(SocketOption<T> name)
247        throws IOException
248    {
249        if (name == null)
250            throw new NullPointerException();
251        if (!supportedOptions().contains(name))
252            throw new UnsupportedOperationException("'" + name + "' not supported");
253
254        synchronized (stateLock) {
255            ensureOpen();
256
257            if (name == StandardSocketOptions.IP_TOS) {
258                // IPv4 only; always return 0 on IPv6
259                if (family == StandardProtocolFamily.INET) {
260                    return (T) Net.getSocketOption(fd, family, name);
261                } else {
262                    return (T) Integer.valueOf(0);
263                }
264            }
265
266            if (name == StandardSocketOptions.IP_MULTICAST_TTL ||
267                name == StandardSocketOptions.IP_MULTICAST_LOOP)
268            {
269                return (T) Net.getSocketOption(fd, family, name);
270            }
271
272            if (name == StandardSocketOptions.IP_MULTICAST_IF) {
273                if (family == StandardProtocolFamily.INET) {
274                    int address = Net.getInterface4(fd);
275                    if (address == 0)
276                        return null;    // default interface
277
278                    InetAddress ia = Net.inet4FromInt(address);
279                    NetworkInterface ni = NetworkInterface.getByInetAddress(ia);
280                    if (ni == null)
281                        throw new IOException("Unable to map address to interface");
282                    return (T) ni;
283                } else {
284                    int index = Net.getInterface6(fd);
285                    if (index == 0)
286                        return null;    // default interface
287
288                    NetworkInterface ni = NetworkInterface.getByIndex(index);
289                    if (ni == null)
290                        throw new IOException("Unable to map index to interface");
291                    return (T) ni;
292                }
293            }
294
295            if (name == StandardSocketOptions.SO_REUSEADDR &&
296                    reuseAddressEmulated)
297            {
298                return (T)Boolean.valueOf(isReuseAddress);
299            }
300
301            // no special handling
302            return (T) Net.getSocketOption(fd, Net.UNSPEC, name);
303        }
304    }
305
306    private static class DefaultOptionsHolder {
307        static final Set<SocketOption<?>> defaultOptions = defaultOptions();
308
309        private static Set<SocketOption<?>> defaultOptions() {
310            HashSet<SocketOption<?>> set = new HashSet<SocketOption<?>>(8);
311            set.add(StandardSocketOptions.SO_SNDBUF);
312            set.add(StandardSocketOptions.SO_RCVBUF);
313            set.add(StandardSocketOptions.SO_REUSEADDR);
314            set.add(StandardSocketOptions.SO_BROADCAST);
315            set.add(StandardSocketOptions.IP_TOS);
316            set.add(StandardSocketOptions.IP_MULTICAST_IF);
317            set.add(StandardSocketOptions.IP_MULTICAST_TTL);
318            set.add(StandardSocketOptions.IP_MULTICAST_LOOP);
319            return Collections.unmodifiableSet(set);
320        }
321    }
322
323    public final Set<SocketOption<?>> supportedOptions() {
324        return DefaultOptionsHolder.defaultOptions;
325    }
326
327    private void ensureOpen() throws ClosedChannelException {
328        if (!isOpen())
329            throw new ClosedChannelException();
330    }
331
332    private SocketAddress sender;       // Set by receive0 (## ugh)
333
334    public SocketAddress receive(ByteBuffer dst) throws IOException {
335        if (dst.isReadOnly())
336            throw new IllegalArgumentException("Read-only buffer");
337        if (dst == null)
338            throw new NullPointerException();
339        // Android-changed : Do not attempt to bind to 0 (or 0.0.0.0) if there hasn't been
340        // an explicit call to bind() yet. Fail fast and return null.
341        if (localAddress == null)
342            return null;
343        synchronized (readLock) {
344            ensureOpen();
345            // Socket was not bound before attempting receive
346            // if (localAddress() == null)
347            //     bind(null);
348            int n = 0;
349            ByteBuffer bb = null;
350            try {
351                begin();
352                if (!isOpen())
353                    return null;
354                SecurityManager security = System.getSecurityManager();
355                readerThread = NativeThread.current();
356                if (isConnected() || (security == null)) {
357                    do {
358                        n = receive(fd, dst);
359                    } while ((n == IOStatus.INTERRUPTED) && isOpen());
360                    if (n == IOStatus.UNAVAILABLE)
361                        return null;
362                } else {
363                    bb = Util.getTemporaryDirectBuffer(dst.remaining());
364                    for (;;) {
365                        do {
366                            n = receive(fd, bb);
367                        } while ((n == IOStatus.INTERRUPTED) && isOpen());
368                        if (n == IOStatus.UNAVAILABLE)
369                            return null;
370                        InetSocketAddress isa = (InetSocketAddress)sender;
371                        try {
372                            security.checkAccept(
373                                isa.getAddress().getHostAddress(),
374                                isa.getPort());
375                        } catch (SecurityException se) {
376                            // Ignore packet
377                            bb.clear();
378                            n = 0;
379                            continue;
380                        }
381                        bb.flip();
382                        dst.put(bb);
383                        break;
384                    }
385                }
386                return sender;
387            } finally {
388                if (bb != null)
389                    Util.releaseTemporaryDirectBuffer(bb);
390                readerThread = 0;
391                end((n > 0) || (n == IOStatus.UNAVAILABLE));
392                assert IOStatus.check(n);
393            }
394        }
395    }
396
397    private int receive(FileDescriptor fd, ByteBuffer dst)
398        throws IOException
399    {
400        int pos = dst.position();
401        int lim = dst.limit();
402        assert (pos <= lim);
403        int rem = (pos <= lim ? lim - pos : 0);
404        if (dst instanceof DirectBuffer && rem > 0)
405            return receiveIntoNativeBuffer(fd, dst, rem, pos);
406
407        // Substitute a native buffer. If the supplied buffer is empty
408        // we must instead use a nonempty buffer, otherwise the call
409        // will not block waiting for a datagram on some platforms.
410        int newSize = Math.max(rem, 1);
411        ByteBuffer bb = Util.getTemporaryDirectBuffer(newSize);
412        try {
413            BlockGuard.getThreadPolicy().onNetwork();
414
415            int n = receiveIntoNativeBuffer(fd, bb, newSize, 0);
416            bb.flip();
417            if (n > 0 && rem > 0)
418                dst.put(bb);
419            return n;
420        } finally {
421            Util.releaseTemporaryDirectBuffer(bb);
422        }
423    }
424
425    private int receiveIntoNativeBuffer(FileDescriptor fd, ByteBuffer bb,
426                                        int rem, int pos)
427        throws IOException
428    {
429        int n = receive0(fd, ((DirectBuffer)bb).address() + pos, rem,
430                         isConnected());
431        if (n > 0)
432            bb.position(pos + n);
433        return n;
434    }
435
436    public int send(ByteBuffer src, SocketAddress target)
437        throws IOException
438    {
439        if (src == null)
440            throw new NullPointerException();
441
442        synchronized (writeLock) {
443            ensureOpen();
444            InetSocketAddress isa = Net.checkAddress(target);
445            InetAddress ia = isa.getAddress();
446            if (ia == null)
447                throw new IOException("Target address not resolved");
448            synchronized (stateLock) {
449                if (!isConnected()) {
450                    if (target == null)
451                        throw new NullPointerException();
452                    SecurityManager sm = System.getSecurityManager();
453                    if (sm != null) {
454                        if (ia.isMulticastAddress()) {
455                            sm.checkMulticast(ia);
456                        } else {
457                            sm.checkConnect(ia.getHostAddress(),
458                                            isa.getPort());
459                        }
460                    }
461                } else { // Connected case; Check address then write
462                    if (!target.equals(remoteAddress)) {
463                        throw new IllegalArgumentException(
464                            "Connected address not equal to target address");
465                    }
466                    return write(src);
467                }
468            }
469
470            int n = 0;
471            try {
472                begin();
473                if (!isOpen())
474                    return 0;
475                writerThread = NativeThread.current();
476                BlockGuard.getThreadPolicy().onNetwork();
477
478                do {
479                    n = send(fd, src, isa);
480                } while ((n == IOStatus.INTERRUPTED) && isOpen());
481
482                synchronized (stateLock) {
483                    if (isOpen() && (localAddress == null)) {
484                        localAddress = Net.localAddress(fd);
485                    }
486                }
487                return IOStatus.normalize(n);
488            } finally {
489                writerThread = 0;
490                end((n > 0) || (n == IOStatus.UNAVAILABLE));
491                assert IOStatus.check(n);
492            }
493        }
494    }
495
496    private int send(FileDescriptor fd, ByteBuffer src, InetSocketAddress target)
497        throws IOException
498    {
499        if (src instanceof DirectBuffer)
500            return sendFromNativeBuffer(fd, src, target);
501
502        // Substitute a native buffer
503        int pos = src.position();
504        int lim = src.limit();
505        assert (pos <= lim);
506        int rem = (pos <= lim ? lim - pos : 0);
507
508        ByteBuffer bb = Util.getTemporaryDirectBuffer(rem);
509        try {
510            bb.put(src);
511            bb.flip();
512            // Do not update src until we see how many bytes were written
513            src.position(pos);
514
515            int n = sendFromNativeBuffer(fd, bb, target);
516            if (n > 0) {
517                // now update src
518                src.position(pos + n);
519            }
520            return n;
521        } finally {
522            Util.releaseTemporaryDirectBuffer(bb);
523        }
524    }
525
526    private int sendFromNativeBuffer(FileDescriptor fd, ByteBuffer bb,
527                                     InetSocketAddress target)
528        throws IOException
529    {
530        int pos = bb.position();
531        int lim = bb.limit();
532        assert (pos <= lim);
533        int rem = (pos <= lim ? lim - pos : 0);
534
535        boolean preferIPv6 = (family != StandardProtocolFamily.INET);
536        int written;
537        try {
538            written = send0(preferIPv6, fd, ((DirectBuffer)bb).address() + pos,
539                            rem, target.getAddress(), target.getPort());
540        } catch (PortUnreachableException pue) {
541            if (isConnected())
542                throw pue;
543            written = rem;
544        }
545        if (written > 0)
546            bb.position(pos + written);
547        return written;
548    }
549
550    public int read(ByteBuffer buf) throws IOException {
551        if (buf == null)
552            throw new NullPointerException();
553        synchronized (readLock) {
554            synchronized (stateLock) {
555                ensureOpen();
556                if (!isConnected())
557                    throw new NotYetConnectedException();
558            }
559            int n = 0;
560            try {
561                begin();
562                if (!isOpen())
563                    return 0;
564                readerThread = NativeThread.current();
565                do {
566                    n = IOUtil.read(fd, buf, -1, nd);
567                } while ((n == IOStatus.INTERRUPTED) && isOpen());
568                return IOStatus.normalize(n);
569            } finally {
570                readerThread = 0;
571                end((n > 0) || (n == IOStatus.UNAVAILABLE));
572                assert IOStatus.check(n);
573            }
574        }
575    }
576
577    public long read(ByteBuffer[] dsts, int offset, int length)
578        throws IOException
579    {
580        if ((offset < 0) || (length < 0) || (offset > dsts.length - length))
581            throw new IndexOutOfBoundsException();
582        synchronized (readLock) {
583            synchronized (stateLock) {
584                ensureOpen();
585                if (!isConnected())
586                    throw new NotYetConnectedException();
587            }
588            long n = 0;
589            try {
590                begin();
591                if (!isOpen())
592                    return 0;
593                readerThread = NativeThread.current();
594                do {
595                    n = IOUtil.read(fd, dsts, offset, length, nd);
596                } while ((n == IOStatus.INTERRUPTED) && isOpen());
597                return IOStatus.normalize(n);
598            } finally {
599                readerThread = 0;
600                end((n > 0) || (n == IOStatus.UNAVAILABLE));
601                assert IOStatus.check(n);
602            }
603        }
604    }
605
606    public int write(ByteBuffer buf) throws IOException {
607        if (buf == null)
608            throw new NullPointerException();
609        synchronized (writeLock) {
610            synchronized (stateLock) {
611                ensureOpen();
612                if (!isConnected())
613                    throw new NotYetConnectedException();
614            }
615            int n = 0;
616            try {
617                begin();
618                if (!isOpen())
619                    return 0;
620                writerThread = NativeThread.current();
621                do {
622                    n = IOUtil.write(fd, buf, -1, nd);
623                } while ((n == IOStatus.INTERRUPTED) && isOpen());
624                return IOStatus.normalize(n);
625            } finally {
626                writerThread = 0;
627                end((n > 0) || (n == IOStatus.UNAVAILABLE));
628                assert IOStatus.check(n);
629            }
630        }
631    }
632
633    public long write(ByteBuffer[] srcs, int offset, int length)
634        throws IOException
635    {
636        if ((offset < 0) || (length < 0) || (offset > srcs.length - length))
637            throw new IndexOutOfBoundsException();
638        synchronized (writeLock) {
639            synchronized (stateLock) {
640                ensureOpen();
641                if (!isConnected())
642                    throw new NotYetConnectedException();
643            }
644            long n = 0;
645            try {
646                begin();
647                if (!isOpen())
648                    return 0;
649                writerThread = NativeThread.current();
650                do {
651                    n = IOUtil.write(fd, srcs, offset, length, nd);
652                } while ((n == IOStatus.INTERRUPTED) && isOpen());
653                return IOStatus.normalize(n);
654            } finally {
655                writerThread = 0;
656                end((n > 0) || (n == IOStatus.UNAVAILABLE));
657                assert IOStatus.check(n);
658            }
659        }
660    }
661
662    protected void implConfigureBlocking(boolean block) throws IOException {
663        IOUtil.configureBlocking(fd, block);
664    }
665
666    public SocketAddress localAddress() {
667        synchronized (stateLock) {
668            return localAddress;
669        }
670    }
671
672    public SocketAddress remoteAddress() {
673        synchronized (stateLock) {
674            return remoteAddress;
675        }
676    }
677
678    @Override
679    public DatagramChannel bind(SocketAddress local) throws IOException {
680        synchronized (readLock) {
681            synchronized (writeLock) {
682                synchronized (stateLock) {
683                    ensureOpen();
684                    if (localAddress != null)
685                        throw new AlreadyBoundException();
686                    InetSocketAddress isa;
687                    if (local == null) {
688                        // only Inet4Address allowed with IPv4 socket
689                        if (family == StandardProtocolFamily.INET) {
690                            isa = new InetSocketAddress(InetAddress.getByName("0.0.0.0"), 0);
691                        } else {
692                            isa = new InetSocketAddress(0);
693                        }
694                    } else {
695                        isa = Net.checkAddress(local);
696
697                        // only Inet4Address allowed with IPv4 socket
698                        if (family == StandardProtocolFamily.INET) {
699                            InetAddress addr = isa.getAddress();
700                            if (!(addr instanceof Inet4Address))
701                                throw new UnsupportedAddressTypeException();
702                        }
703                    }
704                    SecurityManager sm = System.getSecurityManager();
705                    if (sm != null) {
706                        sm.checkListen(isa.getPort());
707                    }
708                    Net.bind(family, fd, isa.getAddress(), isa.getPort());
709                    localAddress = Net.localAddress(fd);
710                }
711            }
712        }
713        return this;
714    }
715
716    public boolean isConnected() {
717        synchronized (stateLock) {
718            return (state == ST_CONNECTED);
719        }
720    }
721
722    void ensureOpenAndUnconnected() throws IOException { // package-private
723        synchronized (stateLock) {
724            if (!isOpen())
725                throw new ClosedChannelException();
726            if (state != ST_UNCONNECTED)
727                throw new IllegalStateException("Connect already invoked");
728        }
729    }
730
731    @Override
732    public DatagramChannel connect(SocketAddress sa) throws IOException {
733        int localPort = 0;
734
735        synchronized(readLock) {
736            synchronized(writeLock) {
737                synchronized (stateLock) {
738                    ensureOpenAndUnconnected();
739                    InetSocketAddress isa = Net.checkAddress(sa);
740                    SecurityManager sm = System.getSecurityManager();
741                    if (sm != null)
742                        sm.checkConnect(isa.getAddress().getHostAddress(),
743                                        isa.getPort());
744                    int n = Net.connect(family,
745                                        fd,
746                                        isa.getAddress(),
747                                        isa.getPort());
748                    if (n <= 0)
749                        throw new Error();      // Can't happen
750
751                    // Connection succeeded; disallow further invocation
752                    state = ST_CONNECTED;
753                    remoteAddress = isa;
754                    sender = isa;
755                    cachedSenderInetAddress = isa.getAddress();
756                    cachedSenderPort = isa.getPort();
757
758                    // set or refresh local address
759                    localAddress = Net.localAddress(fd);
760                }
761            }
762        }
763        return this;
764    }
765
766    public DatagramChannel disconnect() throws IOException {
767        synchronized(readLock) {
768            synchronized(writeLock) {
769                synchronized (stateLock) {
770                    if (!isConnected() || !isOpen())
771                        return this;
772                    InetSocketAddress isa = remoteAddress;
773                    SecurityManager sm = System.getSecurityManager();
774                    if (sm != null)
775                        sm.checkConnect(isa.getAddress().getHostAddress(),
776                                        isa.getPort());
777                    boolean isIPv6 = (family == StandardProtocolFamily.INET6);
778                    disconnect0(fd, isIPv6);
779                    remoteAddress = null;
780                    state = ST_UNCONNECTED;
781
782                    // refresh local address
783                    localAddress = Net.localAddress(fd);
784                }
785            }
786        }
787        return this;
788    }
789
790    protected void implCloseSelectableChannel() throws IOException {
791        synchronized (stateLock) {
792            if (state != ST_KILLED)
793                nd.preClose(fd);
794            ResourceManager.afterUdpClose();
795
796            long th;
797            if ((th = readerThread) != 0)
798                NativeThread.signal(th);
799            if ((th = writerThread) != 0)
800                NativeThread.signal(th);
801            if (!isRegistered())
802                kill();
803        }
804    }
805
806    public void kill() throws IOException {
807        synchronized (stateLock) {
808            if (state == ST_KILLED)
809                return;
810            if (state == ST_UNINITIALIZED) {
811                state = ST_KILLED;
812                return;
813            }
814            assert !isOpen() && !isRegistered();
815            nd.close(fd);
816            state = ST_KILLED;
817        }
818    }
819
820    protected void finalize() throws IOException {
821        // fd is null if constructor threw exception
822        if (fd != null)
823            close();
824    }
825
826    /**
827     * Translates native poll revent set into a ready operation set
828     */
829    public boolean translateReadyOps(int ops, int initialOps,
830                                     SelectionKeyImpl sk) {
831        int intOps = sk.nioInterestOps(); // Do this just once, it synchronizes
832        int oldOps = sk.nioReadyOps();
833        int newOps = initialOps;
834
835        if ((ops & PollArrayWrapper.POLLNVAL) != 0) {
836            // This should only happen if this channel is pre-closed while a
837            // selection operation is in progress
838            // ## Throw an error if this channel has not been pre-closed
839            return false;
840        }
841
842        if ((ops & (PollArrayWrapper.POLLERR
843                    | PollArrayWrapper.POLLHUP)) != 0) {
844            newOps = intOps;
845            sk.nioReadyOps(newOps);
846            return (newOps & ~oldOps) != 0;
847        }
848
849        if (((ops & PollArrayWrapper.POLLIN) != 0) &&
850            ((intOps & SelectionKey.OP_READ) != 0))
851            newOps |= SelectionKey.OP_READ;
852
853        if (((ops & PollArrayWrapper.POLLOUT) != 0) &&
854            ((intOps & SelectionKey.OP_WRITE) != 0))
855            newOps |= SelectionKey.OP_WRITE;
856
857        sk.nioReadyOps(newOps);
858        return (newOps & ~oldOps) != 0;
859    }
860
861    public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) {
862        return translateReadyOps(ops, sk.nioReadyOps(), sk);
863    }
864
865    public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) {
866        return translateReadyOps(ops, 0, sk);
867    }
868
869    /**
870     * Translates an interest operation set into a native poll event set
871     */
872    public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) {
873        int newOps = 0;
874
875        if ((ops & SelectionKey.OP_READ) != 0)
876            newOps |= PollArrayWrapper.POLLIN;
877        if ((ops & SelectionKey.OP_WRITE) != 0)
878            newOps |= PollArrayWrapper.POLLOUT;
879        if ((ops & SelectionKey.OP_CONNECT) != 0)
880            newOps |= PollArrayWrapper.POLLIN;
881        sk.selector.putEventOps(sk, newOps);
882    }
883
884    public FileDescriptor getFD() {
885        return fd;
886    }
887
888    public int getFDVal() {
889        return fdVal;
890    }
891
892
893    // -- Native methods --
894
895    private static native void initIDs();
896
897    private static native void disconnect0(FileDescriptor fd, boolean isIPv6)
898        throws IOException;
899
900    private native int receive0(FileDescriptor fd, long address, int len,
901                                boolean connected)
902        throws IOException;
903
904    private native int send0(boolean preferIPv6, FileDescriptor fd, long address,
905                             int len, InetAddress addr, int port)
906        throws IOException;
907
908    static {
909        initIDs();
910    }
911
912}
913