DatagramSocket.java revision dd828f42a5c83b4270d4fbf6fce2da1878f1e84a
1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.net;
19
20import java.io.IOException;
21import java.nio.channels.DatagramChannel;
22
23import org.apache.harmony.luni.net.SocketImplProvider;
24import org.apache.harmony.luni.platform.Platform;
25
26import org.apache.harmony.luni.util.Msg;
27
28/**
29 * This class implements a UDP socket for sending and receiving {@code
30 * DatagramPacket}. A {@code DatagramSocket} object can be used for both
31 * endpoints of a connection for a packet delivery service.
32 *
33 * @see DatagramPacket
34 * @see DatagramSocketImplFactory
35 * @since Android 1.0
36 */
37public class DatagramSocket {
38
39    DatagramSocketImpl impl;
40
41    InetAddress address;
42
43    int port = -1;
44
45    static DatagramSocketImplFactory factory;
46
47    boolean isBound = false;
48
49    private boolean isConnected = false;
50
51    private boolean isClosed = false;
52
53    private static class Lock {
54    }
55
56    static {
57        Platform.getNetworkSystem().oneTimeInitialization(true);
58    }
59
60    private Object lock = new Lock();
61
62    /**
63     * Constructs a UDP datagram socket which is bound to any available port on
64     * the localhost.
65     *
66     * @throws SocketException
67     *             if an error occurs while creating or binding the socket.
68     * @since Android 1.0
69     */
70    public DatagramSocket() throws SocketException {
71        this(0);
72    }
73
74    /**
75     * Constructs a UDP datagram socket which is bound to the specific port
76     * {@code aPort} on the localhost. Valid values for {@code aPort} are
77     * between 0 and 65535 inclusive.
78     *
79     * @param aPort
80     *            the port to bind on the localhost.
81     * @throws SocketException
82     *             if an error occurs while creating or binding the socket.
83     * @since Android 1.0
84     */
85    public DatagramSocket(int aPort) throws SocketException {
86        super();
87        checkListen(aPort);
88        createSocket(aPort, InetAddress.ANY);
89    }
90
91    /**
92     * Constructs a UDP datagram socket which is bound to the specific local
93     * address {@code addr} on port {@code aPort}. Valid values for {@code
94     * aPort} are between 0 and 65535 inclusive.
95     *
96     * @param aPort
97     *            the port to bind on the localhost.
98     * @param addr
99     *            the address to bind on the localhost.
100     * @throws SocketException
101     *             if an error occurs while creating or binding the socket.
102     * @since Android 1.0
103     */
104    public DatagramSocket(int aPort, InetAddress addr) throws SocketException {
105        super();
106        checkListen(aPort);
107        createSocket(aPort, null == addr ? InetAddress.ANY : addr);
108    }
109
110    /**
111     * Sends prior to attempting to bind the socket, checks whether the port is
112     * within the valid port range and verifies with the security manager that
113     * the port may be bound by the current context.
114     *
115     * @param aPort
116     *            the port on the localhost that is to be bound.
117     * @since Android 1.0
118     */
119    void checkListen(int aPort) {
120        if (aPort < 0 || aPort > 65535) {
121            throw new IllegalArgumentException(Msg.getString("K0325", aPort)); //$NON-NLS-1$
122        }
123        SecurityManager security = System.getSecurityManager();
124        if (security != null) {
125            security.checkListen(aPort);
126        }
127    }
128
129    /**
130     * Closes this UDP datagram socket and all possibly associated channels.
131     *
132     * @since Android 1.0
133     */
134    // In the documentation jdk1.1.7a/guide/net/miscNet.html, this method is
135    // noted as not being synchronized.
136    public void close() {
137        isClosed = true;
138        impl.close();
139    }
140
141    /**
142     * Connects this UDP datagram socket to the specific target host with the
143     * address {@code anAdress} on port {@code aPort}. The host and port are
144     * validated, thereafter the only validation on {@code send()} and {@code
145     * receive()} is to check whether the packet address/port matches the
146     * connected target.
147     *
148     * @param anAddress
149     *            the target address of this socket.
150     * @param aPort
151     *            the target port of this socket.
152     * @since Android 1.0
153     */
154    public void connect(InetAddress anAddress, int aPort) {
155        if (anAddress == null || aPort < 0 || aPort > 65535) {
156            throw new IllegalArgumentException(Msg.getString("K0032")); //$NON-NLS-1$
157        }
158
159        synchronized (lock) {
160            if (isClosed()) {
161                return;
162            }
163            try {
164                checkClosedAndBind(true);
165            } catch (SocketException e) {
166                // Ignored
167            }
168
169            SecurityManager security = System.getSecurityManager();
170            if (security != null) {
171                if (anAddress.isMulticastAddress()) {
172                    security.checkMulticast(anAddress);
173                } else {
174                    security.checkConnect(anAddress.getHostName(), aPort);
175                }
176            }
177
178            try {
179                impl.connect(anAddress, aPort);
180            } catch (SocketException e) {
181                // not connected at the native level just do what we did before
182            }
183            address = anAddress;
184            port = aPort;
185            isConnected = true;
186        }
187    }
188
189    /**
190     * Disconnects this UDP datagram socket from the remote host. This method
191     * called on an unconnected socket does nothing.
192     *
193     * @since Android 1.0
194     */
195    public void disconnect() {
196        if (isClosed() || !isConnected()) {
197            return;
198        }
199        impl.disconnect();
200        address = null;
201        port = -1;
202        isConnected = false;
203    }
204
205    synchronized void createSocket(int aPort, InetAddress addr)
206            throws SocketException {
207        impl = factory != null ? factory.createDatagramSocketImpl()
208                : SocketImplProvider.getDatagramSocketImpl();
209        impl.create();
210        try {
211            impl.bind(aPort, addr);
212            isBound = true;
213        } catch (SocketException e) {
214            close();
215            throw e;
216        }
217    }
218
219    /**
220     * Gets the {@code InetAddress} instance representing the remote address to
221     * which this UDP datagram socket is connected.
222     *
223     * @return the remote address this socket is connected to or {@code null} if
224     *         this socket is not connected.
225     * @since Android 1.0
226     */
227    public InetAddress getInetAddress() {
228        return address;
229    }
230
231    /**
232     * Gets the {@code InetAddress} instance representing the bound local
233     * address of this UDP datagram socket.
234     *
235     * @return the local address to which this socket is bound to or {@code
236     *         null} if this socket is closed.
237     * @since Android 1.0
238     */
239    public InetAddress getLocalAddress() {
240        if (isClosed()) {
241            return null;
242        }
243        if (!isBound()) {
244            return InetAddress.ANY;
245        }
246        InetAddress anAddr = impl.getLocalAddress();
247        try {
248            SecurityManager security = System.getSecurityManager();
249            if (security != null) {
250                security.checkConnect(anAddr.getHostName(), -1);
251            }
252        } catch (SecurityException e) {
253            return InetAddress.ANY;
254        }
255        return anAddr;
256    }
257
258    /**
259     * Gets the local port which this socket is bound to.
260     *
261     * @return the local port of this socket or {@code -1} if this socket is
262     *         closed and {@code 0} if it is unbound.
263     * @since Android 1.0
264     */
265    public int getLocalPort() {
266        if (isClosed()) {
267            return -1;
268        }
269        if (!isBound()) {
270            return 0;
271        }
272        return impl.getLocalPort();
273    }
274
275    /**
276     * Gets the remote port which this socket is connected to.
277     *
278     * @return the remote port of this socket. The return value {@code -1}
279     *         indicates that this socket is not connected.
280     * @since Android 1.0
281     */
282    public int getPort() {
283        return port;
284    }
285
286    /**
287     * Indicates whether this socket is multicast or not.
288     *
289     * @return the return value is always {@code false}.
290     * @since Android 1.0
291     */
292    boolean isMulticastSocket() {
293        return false;
294    }
295
296    /**
297     * Gets the socket receive buffer size. ( {@code SocketOptions.SO_RCVBUF} )
298     *
299     * @return the input buffer size.
300     * @exception SocketException
301     *                if an error occurs while getting the option value.
302     * @since Android 1.0
303     */
304    public synchronized int getReceiveBufferSize() throws SocketException {
305        checkClosedAndBind(false);
306        return ((Integer) impl.getOption(SocketOptions.SO_RCVBUF)).intValue();
307    }
308
309    /**
310     * Gets the socket send buffer size. ( {@code SocketOptions.SO_SNDBUF} )
311     *
312     * @return the output buffer size.
313     * @exception SocketException
314     *                if an error occurs while getting the option value.
315     * @since Android 1.0
316     */
317    public synchronized int getSendBufferSize() throws SocketException {
318        checkClosedAndBind(false);
319        return ((Integer) impl.getOption(SocketOptions.SO_SNDBUF)).intValue();
320    }
321
322    /**
323     * Gets the socket receive timeout in milliseconds. The return value {@code
324     * 0} implies the timeout is disabled/infinitive. ( {@code
325     * SocketOptions.SO_TIMEOUT} )
326     *
327     * @return the socket receive timeout.
328     * @exception SocketException
329     *                if an error occurs while getting the option value.
330     * @since Android 1.0
331     */
332    public synchronized int getSoTimeout() throws SocketException {
333        checkClosedAndBind(false);
334        return ((Integer) impl.getOption(SocketOptions.SO_TIMEOUT)).intValue();
335    }
336
337    /**
338     * Receives a packet from this socket and stores it in the argument {@code
339     * pack}. All fields of {@code pack} must be set according to the data
340     * received. If the received data is longer than the packet buffer size it
341     * is truncated. This method blocks until a packet is received or a timeout
342     * has expired. If a security manager exists, its {@code checkAccept} method
343     * determines whether or not a packet is discarded. Any packets from
344     * unacceptable origins are silently discarded.
345     *
346     * @param pack
347     *            the {@code DatagramPacket} to store the received data.
348     * @exception IOException
349     *                if an error occurs while receiving the packet.
350     * @since Android 1.0
351     */
352    public synchronized void receive(DatagramPacket pack) throws IOException {
353        checkClosedAndBind(true);
354
355        boolean secure = true;
356
357        InetAddress senderAddr = null;
358
359        int senderPort = 0;
360        DatagramPacket tempPack = new DatagramPacket(new byte[1], 1);
361        boolean copy = false;
362
363        SecurityManager security = System.getSecurityManager();
364        if (address != null || security != null) { // The socket is connected
365            // Check pack before peeking
366            if (pack == null) {
367                throw new NullPointerException();
368            }
369            secure = false;
370            while (!secure) {
371                copy = false;
372                try {
373                    senderPort = impl.peekData(tempPack);
374                    senderAddr = tempPack.getAddress();
375                } catch (SocketException e) {
376                    if (e.getMessage().equals(
377                            "The socket does not support the operation")) { //$NON-NLS-1$
378                        tempPack = new DatagramPacket(new byte[pack.length],
379                                pack.getLength());
380                        impl.receive(tempPack);
381                        senderAddr = tempPack.getAddress();
382                        senderPort = tempPack.getPort();
383                        copy = true;
384                    } else {
385                        throw e;
386                    }
387                }
388                if (address == null) {
389                    try {
390                        security.checkAccept(senderAddr.getHostName(),
391                                senderPort);
392                        if (!copy) {
393                            secure = true;
394                        }
395                        break;
396                    } catch (SecurityException e) {
397                        if (!copy) {
398                            if (tempPack == null) {
399                                tempPack = new DatagramPacket(
400                                        new byte[pack.length], pack.length);
401                            }
402                            impl.receive(tempPack);
403                        }
404                    }
405                } else if (port == senderPort && address.equals(senderAddr)) {
406                    if (!copy) {
407                        secure = true;
408                    }
409                    break;
410                } else if (!copy) {
411                    if (tempPack == null) {
412                        tempPack = new DatagramPacket(new byte[pack.length],
413                                pack.length);
414                    }
415                    impl.receive(tempPack);
416                }
417            }
418        }
419        if (copy) {
420            System.arraycopy(tempPack.getData(), 0, pack.getData(), pack
421                    .getOffset(), tempPack.getLength());
422            pack.setLength(tempPack.getLength());
423            pack.setAddress(tempPack.getAddress());
424            pack.setPort(tempPack.getPort());
425        }
426        if (secure) {
427            impl.receive(pack);
428        }
429    }
430
431    /**
432     * Sends a packet over this socket. The packet must satisfy the security
433     * policy before it may be sent. If a security manager is installed, this
434     * method checks whether it is allowed to send this packet to the specified
435     * address.
436     *
437     * @param pack
438     *            the {@code DatagramPacket} which has to be sent.
439     * @exception IOException
440     *                if an error occurs while sending the packet.
441     * @since Android 1.0
442     */
443    public void send(DatagramPacket pack) throws IOException {
444        checkClosedAndBind(true);
445
446        InetAddress packAddr = pack.getAddress();
447        if (address != null) { // The socket is connected
448            if (packAddr != null) {
449                if (!address.equals(packAddr) || port != pack.getPort()) {
450                    throw new IllegalArgumentException(Msg.getString("K0034")); //$NON-NLS-1$
451                }
452            } else {
453                pack.setAddress(address);
454                pack.setPort(port);
455            }
456        } else {
457            // not connected so the target address is not allowed to be null
458            if (packAddr == null) {
459                if (pack.port == -1) {
460                    // KA019 Destination address is null
461                    throw new NullPointerException(Msg.getString("KA019")); //$NON-NLS-1$
462                }
463                return;
464            }
465            SecurityManager security = System.getSecurityManager();
466            if (security != null) {
467                if (packAddr.isMulticastAddress()) {
468                    security.checkMulticast(packAddr);
469                } else {
470                    security.checkConnect(packAddr.getHostName(), pack
471                            .getPort());
472                }
473            }
474        }
475        impl.send(pack);
476    }
477
478    /**
479     * Sets the socket send buffer size. This buffer size determines which the
480     * maximum packet size is that can be sent over this socket. It depends on
481     * the network implementation what will happen if the packet is bigger than
482     * the buffer size. ( {@code SocketOptions.SO_SNDBUF} )
483     *
484     * @param size
485     *            the buffer size in bytes. The size must be at least one byte.
486     * @exception SocketException
487     *                if an error occurs while setting the option.
488     * @since Android 1.0
489     */
490    public synchronized void setSendBufferSize(int size) throws SocketException {
491        if (size < 1) {
492            throw new IllegalArgumentException(Msg.getString("K0035")); //$NON-NLS-1$
493        }
494        checkClosedAndBind(false);
495        impl.setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(size));
496    }
497
498    /**
499     * Sets the socket receive buffer size. This buffer size determines which
500     * the maximum packet size is that can be received over this socket. It
501     * depends on the network implementation what will happen if the packet is
502     * bigger than the buffer size. ( {@code SocketOptions.SO_RCVBUF} )
503     *
504     * @param size
505     *            the buffer size in bytes. The size must be at least one byte.
506     * @exception SocketException
507     *                if an error occurs while setting the option.
508     * @since Android 1.0
509     */
510    public synchronized void setReceiveBufferSize(int size)
511            throws SocketException {
512        if (size < 1) {
513            throw new IllegalArgumentException(Msg.getString("K0035")); //$NON-NLS-1$
514        }
515        checkClosedAndBind(false);
516        impl.setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size));
517    }
518
519    /**
520     * Sets the timeout period in milliseconds for the {@code receive()} method.
521     * This receive timeout defines the period the socket will block waiting to
522     * receive data before throwing an {@code InterruptedIOException}. The value
523     * {@code 0} (default) is used to set an infinite timeout. To have effect
524     * this option must be set before the blocking method was called. ( {@code
525     * SocketOptions.SO_TIMEOUT} )
526     *
527     * @param timeout
528     *            the timeout period in milliseconds or {@code 0} for infinite.
529     * @exception SocketException
530     *                if an error occurs while setting the option.
531     * @since Android 1.0
532     */
533    public synchronized void setSoTimeout(int timeout) throws SocketException {
534        if (timeout < 0) {
535            throw new IllegalArgumentException(Msg.getString("K0036")); //$NON-NLS-1$
536        }
537        checkClosedAndBind(false);
538        impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(timeout));
539    }
540
541    /**
542     * Sets the socket implementation factory. This may only be invoked once
543     * over the lifetime of the application. This factory is used to create
544     * a new datagram socket implementation. If a security manager is set its
545     * method {@code checkSetFactory()} is called to check if the operation is
546     * allowed. A {@code SecurityException} is thrown if the operation is not
547     * allowed.
548     *
549     * @param fac
550     *            the socket factory to use.
551     * @exception IOException
552     *                if the factory has already been set.
553     * @see DatagramSocketImplFactory
554     * @since Android 1.0
555     */
556    public static synchronized void setDatagramSocketImplFactory(
557            DatagramSocketImplFactory fac) throws IOException {
558        SecurityManager security = System.getSecurityManager();
559        if (security != null) {
560            security.checkSetFactory();
561        }
562        if (factory != null) {
563            throw new SocketException(Msg.getString("K0044")); //$NON-NLS-1$
564        }
565        factory = fac;
566    }
567
568    /**
569     * Constructs a new {@code DatagramSocket} using the specific datagram
570     * socket implementation {@code socketImpl}. The created {@code
571     * DatagramSocket} will not be bound.
572     *
573     * @param socketImpl
574     *            the DatagramSocketImpl to use.
575     * @since Android 1.0
576     */
577    protected DatagramSocket(DatagramSocketImpl socketImpl) {
578        if (socketImpl == null) {
579            throw new NullPointerException();
580        }
581        impl = socketImpl;
582    }
583
584    /**
585     * Constructs a new {@code DatagramSocket} bound to the host/port specified
586     * by the {@code SocketAddress} {@code localAddr} or an unbound {@code
587     * DatagramSocket} if the {@code SocketAddress} is {@code null}.
588     *
589     * @param localAddr
590     *            the local machine address and port to bind to.
591     * @throws SocketException
592     *             if a problem occurs creating or binding the socket.
593     * @since Android 1.0
594     */
595    public DatagramSocket(SocketAddress localAddr) throws SocketException {
596        if (localAddr != null) {
597            if (!(localAddr instanceof InetSocketAddress)) {
598                throw new IllegalArgumentException(Msg.getString(
599                        "K0316", localAddr.getClass())); //$NON-NLS-1$
600            }
601            checkListen(((InetSocketAddress) localAddr).getPort());
602        }
603        impl = factory != null ? factory.createDatagramSocketImpl()
604                : SocketImplProvider.getDatagramSocketImpl();
605        impl.create();
606        if (localAddr != null) {
607            try {
608                bind(localAddr);
609            } catch (SocketException e) {
610                close();
611                throw e;
612            }
613        }
614        // SocketOptions.SO_BROADCAST is set by default for DatagramSocket
615        setBroadcast(true);
616    }
617
618    void checkClosedAndBind(boolean bind) throws SocketException {
619        if (isClosed()) {
620            throw new SocketException(Msg.getString("K003d")); //$NON-NLS-1$
621        }
622        if (bind && !isBound()) {
623            checkListen(0);
624            impl.bind(0, InetAddress.ANY);
625            isBound = true;
626        }
627    }
628
629    /**
630     * Binds this socket to the local address and port specified by {@code
631     * localAddr}. If this value is {@code null} any free port on a valid local
632     * address is used.
633     *
634     * @param localAddr
635     *            the local machine address and port to bind on.
636     * @throws SocketException
637     *             if the socket is already bound or a problem occurs during
638     *             binding.
639     * @since Android 1.0
640     */
641    public void bind(SocketAddress localAddr) throws SocketException {
642        checkClosedAndBind(false);
643        int localPort = 0;
644        InetAddress addr = InetAddress.ANY;
645        if (localAddr != null) {
646            if (!(localAddr instanceof InetSocketAddress)) {
647                throw new IllegalArgumentException(Msg.getString(
648                        "K0316", localAddr.getClass())); //$NON-NLS-1$
649            }
650            InetSocketAddress inetAddr = (InetSocketAddress) localAddr;
651            addr = inetAddr.getAddress();
652            if (addr == null) {
653                throw new SocketException(Msg.getString(
654                        "K0317", inetAddr.getHostName())); //$NON-NLS-1$
655            }
656            localPort = inetAddr.getPort();
657            checkListen(localPort);
658        }
659        impl.bind(localPort, addr);
660        isBound = true;
661    }
662
663    /**
664     * Connects this datagram socket to the remote host and port specified by
665     * {@code remoteAddr}. The host and port are validated, thereafter the only
666     * validation on {@code send()} and {@code receive()} is that the packet
667     * address/port matches the connected target.
668     *
669     * @param remoteAddr
670     *            the address and port of the target host.
671     * @exception SocketException
672     *                if an error occurs during connecting.
673     * @since Android 1.0
674     */
675    public void connect(SocketAddress remoteAddr) throws SocketException {
676        if (remoteAddr == null) {
677            throw new IllegalArgumentException(Msg.getString("K0318")); //$NON-NLS-1$
678        }
679
680        if (!(remoteAddr instanceof InetSocketAddress)) {
681            throw new IllegalArgumentException(Msg.getString(
682                    "K0316", remoteAddr.getClass())); //$NON-NLS-1$
683        }
684
685        InetSocketAddress inetAddr = (InetSocketAddress) remoteAddr;
686        if (inetAddr.getAddress() == null) {
687            throw new SocketException(Msg.getString(
688                    "K0317", inetAddr.getHostName())); //$NON-NLS-1$
689        }
690
691        synchronized (lock) {
692            // make sure the socket is open
693            checkClosedAndBind(true);
694
695            SecurityManager security = System.getSecurityManager();
696            if (security != null) {
697                if (inetAddr.getAddress().isMulticastAddress()) {
698                    security.checkMulticast(inetAddr.getAddress());
699                } else {
700                    security.checkConnect(inetAddr.getAddress().getHostName(),
701                            inetAddr.getPort());
702                }
703            }
704
705            // now try to do the connection at the native level. To be
706            // compatible for the case when the address is inaddr_any we just
707            // eat the exception an act as if we are connected at the java level
708            try {
709                impl.connect(inetAddr.getAddress(), inetAddr.getPort());
710            } catch (Exception e) {
711                // not connected at the native level just do what we did before
712            }
713
714            // if we get here then we connected ok
715            address = inetAddr.getAddress();
716            port = inetAddr.getPort();
717            isConnected = true;
718        }
719    }
720
721    /**
722     * Determines whether the socket is bound to an address or not.
723     *
724     * @return {@code true} if the socket is bound, {@code false} otherwise.
725     * @since Android 1.0
726     */
727    public boolean isBound() {
728        return isBound;
729    }
730
731    /**
732     * Determines whether the socket is connected to a target host.
733     *
734     * @return {@code true} if the socket is connected, {@code false} otherwise.
735     * @since Android 1.0
736     */
737    public boolean isConnected() {
738        return isConnected;
739    }
740
741    /**
742     * Gets the address and port of the connected remote host. If this socket is
743     * not connected yet, {@code null} is returned.
744     *
745     * @return the remote socket address.
746     * @since Android 1.0
747     */
748    public SocketAddress getRemoteSocketAddress() {
749        if (!isConnected()) {
750            return null;
751        }
752        return new InetSocketAddress(getInetAddress(), getPort());
753    }
754
755    /**
756     * Gets the bound local address and port of this socket. If the socket is
757     * unbound, {@code null} is returned.
758     *
759     * @return the local socket address.
760     * @since Android 1.0
761     */
762    public SocketAddress getLocalSocketAddress() {
763        if (!isBound()) {
764            return null;
765        }
766        return new InetSocketAddress(getLocalAddress(), getLocalPort());
767    }
768
769    /**
770     * Sets the socket option {@code SocketOptions.SO_REUSEADDR}. This option
771     * has to be enabled if more than one UDP socket wants to be bound to the
772     * same address. That could be needed for receiving multicast packets.
773     * <p>
774     * There is an undefined behavior if this option is set after the socket is
775     * already bound.
776     * </p>
777     *
778     * @param reuse
779     *            the socket option value to enable or disable this option.
780     * @throws SocketException
781     *             if the socket is closed or the option could not be set.
782     * @since Android 1.0
783     */
784    public void setReuseAddress(boolean reuse) throws SocketException {
785        checkClosedAndBind(false);
786        impl.setOption(SocketOptions.SO_REUSEADDR, reuse ? Boolean.TRUE
787                : Boolean.FALSE);
788    }
789
790    /**
791     * Gets the state of the socket option {@code SocketOptions.SO_REUSEADDR}.
792     *
793     * @return {@code true} if the option is enabled, {@code false} otherwise.
794     * @throws SocketException
795     *             if the socket is closed or the option is invalid.
796     * @since Android 1.0
797     */
798    public boolean getReuseAddress() throws SocketException {
799        checkClosedAndBind(false);
800        return ((Boolean) impl.getOption(SocketOptions.SO_REUSEADDR))
801                .booleanValue();
802    }
803
804    /**
805     * Sets the socket option {@code SocketOptions.SO_BROADCAST}. This option
806     * must be enabled to send broadcast messages.
807     *
808     * @param broadcast
809     *            the socket option value to enable or disable this option.
810     * @throws SocketException
811     *             if the socket is closed or the option could not be set.
812     * @since Android 1.0
813     */
814    public void setBroadcast(boolean broadcast) throws SocketException {
815        checkClosedAndBind(false);
816        impl.setOption(SocketOptions.SO_BROADCAST, broadcast ? Boolean.TRUE
817                : Boolean.FALSE);
818    }
819
820    /**
821     * Gets the state of the socket option {@code SocketOptions.SO_BROADCAST}.
822     *
823     * @return {@code true} if the option is enabled, {@code false} otherwise.
824     * @throws SocketException
825     *             if the socket is closed or the option is invalid.
826     * @since Android 1.0
827     */
828    public boolean getBroadcast() throws SocketException {
829        checkClosedAndBind(false);
830        return ((Boolean) impl.getOption(SocketOptions.SO_BROADCAST))
831                .booleanValue();
832    }
833
834    /**
835     * Sets the socket option {@code SocketOptions.IP_TOS}. This option defines
836     * the value of the type-of-service field of the IP-header for every packet
837     * sent by this socket. The value could be ignored by the underlying network
838     * implementation.
839     * <p>
840     * Values between {@code 0} and {@code 255} inclusive are valid for this
841     * option.
842     * </p>
843     *
844     * @param value
845     *            the socket option value to be set as type-of-service.
846     * @throws SocketException
847     *             if the socket is closed or the option could not be set.
848     * @since Android 1.0
849     */
850    public void setTrafficClass(int value) throws SocketException {
851        checkClosedAndBind(false);
852        if (value < 0 || value > 255) {
853            throw new IllegalArgumentException();
854        }
855        impl.setOption(SocketOptions.IP_TOS, Integer.valueOf(value));
856    }
857
858    /**
859     * Gets the value of the type-of-service socket option {@code
860     * SocketOptions.IP_TOS}.
861     *
862     * @return the type-of-service socket option value.
863     * @throws SocketException
864     *             if the socket is closed or the option is invalid.
865     * @since Android 1.0
866     */
867    public int getTrafficClass() throws SocketException {
868        checkClosedAndBind(false);
869        return ((Number) impl.getOption(SocketOptions.IP_TOS)).intValue();
870    }
871
872    /**
873     * Gets the state of this socket.
874     *
875     * @return {@code true} if the socket is closed, {@code false} otherwise.
876     * @since Android 1.0
877     */
878    public boolean isClosed() {
879        return isClosed;
880    }
881
882    /**
883     * Gets the related DatagramChannel of this socket. This implementation
884     * returns always {@code null}.
885     *
886     * @return the related DatagramChannel or {@code null} if this socket was
887     *         not created by a {@code DatagramChannel} object.
888     * @since Android 1.0
889     */
890    public DatagramChannel getChannel() {
891        return null;
892    }
893}
894