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 android.system.ErrnoException;
21import android.system.StructGroupReq;
22import dalvik.system.CloseGuard;
23import java.io.FileDescriptor;
24import java.io.IOException;
25import libcore.io.IoBridge;
26import libcore.io.Libcore;
27import libcore.util.EmptyArray;
28import static android.system.OsConstants.*;
29
30/**
31 * @hide used in java.nio.
32 */
33public class PlainDatagramSocketImpl extends DatagramSocketImpl {
34
35    private volatile boolean isNativeConnected;
36
37    private final CloseGuard guard = CloseGuard.get();
38
39    /**
40     * used to keep address to which the socket was connected to at the native
41     * level
42     */
43    private InetAddress connectedAddress;
44
45    private int connectedPort = -1;
46
47    public PlainDatagramSocketImpl(FileDescriptor fd, int localPort) {
48        this.fd = fd;
49        this.localPort = localPort;
50        if (fd.valid()) {
51            guard.open("close");
52        }
53    }
54
55    public PlainDatagramSocketImpl() {
56        fd = new FileDescriptor();
57    }
58
59    @Override public void bind(int port, InetAddress address) throws SocketException {
60        IoBridge.bind(fd, address, port);
61        if (port != 0) {
62            localPort = port;
63        } else {
64            localPort = IoBridge.getSocketLocalPort(fd);
65        }
66        try {
67            setOption(SocketOptions.SO_BROADCAST, Boolean.TRUE);
68        } catch (IOException ignored) {
69        }
70    }
71
72    @Override
73    protected void onBind(InetAddress localAddress, int localPort) {
74        this.localPort = localPort;
75    }
76
77    @Override
78    public synchronized void close() {
79        guard.close();
80        try {
81            IoBridge.closeAndSignalBlockedThreads(fd);
82        } catch (IOException ignored) {
83        }
84    }
85
86    @Override
87    protected void onClose() {
88        guard.close();
89    }
90
91    @Override
92    public void create() throws SocketException {
93        this.fd = IoBridge.socket(false);
94    }
95
96    @Override protected void finalize() throws Throwable {
97        try {
98            if (guard != null) {
99                guard.warnIfOpen();
100            }
101            close();
102        } finally {
103            super.finalize();
104        }
105    }
106
107    @Override public Object getOption(int option) throws SocketException {
108        return IoBridge.getSocketOption(fd, option);
109    }
110
111    @Override
112    public int getTimeToLive() throws IOException {
113        return (Integer) getOption(IoBridge.JAVA_IP_MULTICAST_TTL);
114    }
115
116    @Override
117    public byte getTTL() throws IOException {
118        return (byte) getTimeToLive();
119    }
120
121    private static StructGroupReq makeGroupReq(InetAddress gr_group, NetworkInterface networkInterface) {
122        int gr_interface = (networkInterface != null) ? networkInterface.getIndex() : 0;
123        return new StructGroupReq(gr_interface, gr_group);
124    }
125
126    @Override
127    public void join(InetAddress addr) throws IOException {
128        setOption(IoBridge.JAVA_MCAST_JOIN_GROUP, makeGroupReq(addr, null));
129    }
130
131    @Override
132    public void joinGroup(SocketAddress addr, NetworkInterface netInterface) throws IOException {
133        if (addr instanceof InetSocketAddress) {
134            InetAddress groupAddr = ((InetSocketAddress) addr).getAddress();
135            setOption(IoBridge.JAVA_MCAST_JOIN_GROUP, makeGroupReq(groupAddr, netInterface));
136        }
137    }
138
139    @Override
140    public void leave(InetAddress addr) throws IOException {
141        setOption(IoBridge.JAVA_MCAST_LEAVE_GROUP, makeGroupReq(addr, null));
142    }
143
144    @Override
145    public void leaveGroup(SocketAddress addr, NetworkInterface netInterface) throws IOException {
146        if (addr instanceof InetSocketAddress) {
147            InetAddress groupAddr = ((InetSocketAddress) addr).getAddress();
148            setOption(IoBridge.JAVA_MCAST_LEAVE_GROUP, makeGroupReq(groupAddr, netInterface));
149        }
150    }
151
152    @Override
153    protected int peek(InetAddress sender) throws IOException {
154        // We don't actually want the data: we just want the DatagramPacket's filled-in address.
155        DatagramPacket packet = new DatagramPacket(EmptyArray.BYTE, 0);
156        int result = peekData(packet);
157        // Note: evil side-effect on InetAddress! This method should have returned InetSocketAddress!
158        sender.ipaddress = packet.getAddress().getAddress();
159        return result;
160    }
161
162    private void doRecv(DatagramPacket pack, int flags) throws IOException {
163        IoBridge.recvfrom(false, fd, pack.getData(), pack.getOffset(), pack.getLength(), flags, pack, isNativeConnected);
164        if (isNativeConnected) {
165            updatePacketRecvAddress(pack);
166        }
167    }
168
169    @Override
170    public void receive(DatagramPacket pack) throws IOException {
171        doRecv(pack, 0);
172    }
173
174    @Override
175    public int peekData(DatagramPacket pack) throws IOException {
176        doRecv(pack, MSG_PEEK);
177        return pack.getPort();
178    }
179
180    @Override
181    public void send(DatagramPacket packet) throws IOException {
182        int port = isNativeConnected ? 0 : packet.getPort();
183        InetAddress address = isNativeConnected ? null : packet.getAddress();
184        IoBridge.sendto(fd, packet.getData(), packet.getOffset(), packet.getLength(), 0, address,
185            port);
186    }
187
188    public void setOption(int option, Object value) throws SocketException {
189        IoBridge.setSocketOption(fd, option, value);
190    }
191
192    @Override
193    public void setTimeToLive(int ttl) throws IOException {
194        setOption(IoBridge.JAVA_IP_MULTICAST_TTL, Integer.valueOf(ttl));
195    }
196
197    @Override
198    public void setTTL(byte ttl) throws IOException {
199        setTimeToLive((int) ttl & 0xff); // Avoid sign extension.
200    }
201
202    @Override
203    public void connect(InetAddress inetAddr, int port) throws SocketException {
204        IoBridge.connect(fd, inetAddr, port); // Throws on failure.
205        try {
206            connectedAddress = InetAddress.getByAddress(inetAddr.getAddress());
207        } catch (UnknownHostException e) {
208            // this is never expected to happen as we should not have gotten
209            // here if the address is not resolvable
210            throw new SocketException("Host is unresolved: " + inetAddr.getHostName());
211        }
212        connectedPort = port;
213        isNativeConnected = true;
214    }
215
216    @Override
217    protected void onConnect(InetAddress remoteAddress, int remotePort) {
218        isNativeConnected = true;
219        connectedAddress = remoteAddress;
220        connectedPort = remotePort;
221    }
222
223    @Override
224    public void disconnect() {
225        try {
226            Libcore.os.connect(fd, InetAddress.UNSPECIFIED, 0);
227        } catch (ErrnoException errnoException) {
228            throw new AssertionError(errnoException);
229        } catch (SocketException ignored) {
230            // Thrown if the socket has already been closed, but this method can't throw anything.
231        }
232        connectedPort = -1;
233        connectedAddress = null;
234        isNativeConnected = false;
235    }
236
237    @Override
238    protected void onDisconnect() {
239        connectedPort = -1;
240        connectedAddress = null;
241        isNativeConnected = false;
242    }
243
244    /**
245     * Set the received address and port in the packet. We do this when the
246     * Datagram socket is connected at the native level and the
247     * recvConnnectedDatagramImpl does not update the packet with address from
248     * which the packet was received
249     *
250     * @param packet
251     *            the packet to be updated
252     */
253    private void updatePacketRecvAddress(DatagramPacket packet) {
254        packet.setAddress(connectedAddress);
255        packet.setPort(connectedPort);
256    }
257}
258