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