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.nio; 19 20import android.system.ErrnoException; 21import java.io.FileDescriptor; 22import java.io.InterruptedIOException; 23import java.io.IOException; 24import java.net.ConnectException; 25import java.net.DatagramPacket; 26import java.net.DatagramSocket; 27import java.net.DatagramSocketImpl; 28import java.net.Inet4Address; 29import java.net.InetAddress; 30import java.net.InetSocketAddress; 31import java.net.NetworkInterface; 32import java.net.PlainDatagramSocketImpl; 33import java.net.SocketAddress; 34import java.net.SocketException; 35import java.nio.channels.AlreadyConnectedException; 36import java.nio.channels.ClosedChannelException; 37import java.nio.channels.DatagramChannel; 38import java.nio.channels.IllegalBlockingModeException; 39import java.nio.channels.NotYetConnectedException; 40import java.nio.channels.spi.SelectorProvider; 41import java.nio.channels.UnresolvedAddressException; 42import java.nio.channels.UnsupportedAddressTypeException; 43import java.util.Arrays; 44import java.util.Set; 45import libcore.io.IoBridge; 46import libcore.io.IoUtils; 47import libcore.io.Libcore; 48import libcore.util.EmptyArray; 49 50/* 51 * The default implementation class of java.nio.channels.DatagramChannel. 52 */ 53class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChannel { 54 // The fd to interact with native code 55 private final FileDescriptor fd; 56 57 // Our internal DatagramSocket. 58 private DatagramSocket socket; 59 60 // The remote address to be connected. 61 InetSocketAddress connectAddress; 62 63 // The local address. 64 InetAddress localAddress; 65 66 // local port 67 private int localPort; 68 69 // At first, uninitialized. 70 boolean connected = false; 71 72 // whether the socket is bound 73 boolean isBound = false; 74 75 private final Object readLock = new Object(); 76 private final Object writeLock = new Object(); 77 78 /* 79 * Constructor 80 */ 81 protected DatagramChannelImpl(SelectorProvider selectorProvider) throws IOException { 82 super(selectorProvider); 83 fd = IoBridge.socket(false); 84 } 85 86 /* 87 * for native call 88 */ 89 @SuppressWarnings("unused") 90 private DatagramChannelImpl() { 91 super(SelectorProvider.provider()); 92 fd = new FileDescriptor(); 93 connectAddress = new InetSocketAddress(0); 94 } 95 96 /* 97 * Getting the internal DatagramSocket If we have not the socket, we create 98 * a new one. 99 */ 100 @Override 101 synchronized public DatagramSocket socket() { 102 if (socket == null) { 103 socket = new DatagramSocketAdapter(new PlainDatagramSocketImpl(fd, localPort), this); 104 } 105 return socket; 106 } 107 108 /** 109 * Initialise the isBound, localAddress and localPort state from the file descriptor. Used when 110 * some or all of the bound state has been left to the OS to decide, or when the Socket handled 111 * bind() or connect(). 112 * 113 * @param updateSocketState 114 * if the associated socket (if present) needs to be updated 115 * @hide used to sync state, non-private to avoid synthetic method 116 */ 117 void onBind(boolean updateSocketState) { 118 SocketAddress sa; 119 try { 120 sa = Libcore.os.getsockname(fd); 121 } catch (ErrnoException errnoException) { 122 throw new AssertionError(errnoException); 123 } 124 isBound = true; 125 InetSocketAddress localSocketAddress = (InetSocketAddress) sa; 126 localAddress = localSocketAddress.getAddress(); 127 localPort = localSocketAddress.getPort(); 128 if (updateSocketState && socket != null) { 129 socket.onBind(localAddress, localPort); 130 } 131 } 132 133 @Override 134 synchronized public boolean isConnected() { 135 return connected; 136 } 137 138 @Override 139 synchronized public DatagramChannel connect(SocketAddress address) throws IOException { 140 // must be open 141 checkOpen(); 142 // status must be un-connected. 143 if (connected) { 144 throw new IllegalStateException(); 145 } 146 147 // check the address 148 InetSocketAddress inetSocketAddress = SocketChannelImpl.validateAddress(address); 149 InetAddress remoteAddress = inetSocketAddress.getAddress(); 150 int remotePort = inetSocketAddress.getPort(); 151 try { 152 begin(); 153 IoBridge.connect(fd, remoteAddress, remotePort); 154 } catch (ConnectException e) { 155 // ConnectException means connect fail, not exception 156 } finally { 157 end(true); 158 } 159 160 // connect() performs a bind() if an explicit bind() was not performed. Keep the local 161 // address state held by the channel and the socket up to date. 162 if (!isBound) { 163 onBind(true /* updateSocketState */); 164 } 165 166 // Keep the connected state held by the channel and the socket up to date. 167 onConnect(remoteAddress, remotePort, true /* updateSocketState */); 168 return this; 169 } 170 171 /** 172 * Initialize the state associated with being connected, optionally syncing the socket if there 173 * is one. 174 * @hide used to sync state, non-private to avoid synthetic method 175 */ 176 void onConnect(InetAddress remoteAddress, int remotePort, boolean updateSocketState) { 177 connected = true; 178 connectAddress = new InetSocketAddress(remoteAddress, remotePort); 179 if (updateSocketState && socket != null) { 180 socket.onConnect(remoteAddress, remotePort); 181 } 182 } 183 184 @Override 185 synchronized public DatagramChannel disconnect() throws IOException { 186 if (!isConnected() || !isOpen()) { 187 return this; 188 } 189 190 // Keep the disconnected state held by the channel and the socket up to date. 191 onDisconnect(true /* updateSocketState */); 192 193 try { 194 Libcore.os.connect(fd, InetAddress.UNSPECIFIED, 0); 195 } catch (ErrnoException errnoException) { 196 throw errnoException.rethrowAsIOException(); 197 } 198 return this; 199 } 200 201 /** 202 * Initialize the state associated with being disconnected, optionally syncing the socket if 203 * there is one. 204 * @hide used to sync state, non-private to avoid synthetic method 205 */ 206 void onDisconnect(boolean updateSocketState) { 207 connected = false; 208 connectAddress = null; 209 if (updateSocketState && socket != null && socket.isConnected()) { 210 socket.onDisconnect(); 211 } 212 } 213 214 @Override 215 public SocketAddress receive(ByteBuffer target) throws IOException { 216 target.checkWritable(); 217 checkOpen(); 218 219 if (!isBound) { 220 return null; 221 } 222 223 SocketAddress retAddr = null; 224 try { 225 begin(); 226 227 // receive real data packet, (not peek) 228 synchronized (readLock) { 229 boolean loop = isBlocking(); 230 if (!target.isDirect()) { 231 retAddr = receiveImpl(target, loop); 232 } else { 233 retAddr = receiveDirectImpl(target, loop); 234 } 235 } 236 } catch (InterruptedIOException e) { 237 // this line used in Linux 238 return null; 239 } finally { 240 end(retAddr != null); 241 } 242 return retAddr; 243 } 244 245 private SocketAddress receiveImpl(ByteBuffer target, boolean loop) throws IOException { 246 SocketAddress retAddr = null; 247 DatagramPacket receivePacket; 248 int oldposition = target.position(); 249 int received; 250 // TODO: disallow mapped buffers and lose this conditional? 251 if (target.hasArray()) { 252 receivePacket = new DatagramPacket(target.array(), target.position() + target.arrayOffset(), target.remaining()); 253 } else { 254 receivePacket = new DatagramPacket(new byte[target.remaining()], target.remaining()); 255 } 256 do { 257 received = IoBridge.recvfrom(false, fd, receivePacket.getData(), receivePacket.getOffset(), receivePacket.getLength(), 0, receivePacket, isConnected()); 258 if (receivePacket.getAddress() != null) { 259 if (received > 0) { 260 if (target.hasArray()) { 261 target.position(oldposition + received); 262 } else { 263 // copy the data of received packet 264 target.put(receivePacket.getData(), 0, received); 265 } 266 } 267 retAddr = receivePacket.getSocketAddress(); 268 break; 269 } 270 } while (loop); 271 return retAddr; 272 } 273 274 private SocketAddress receiveDirectImpl(ByteBuffer target, boolean loop) throws IOException { 275 SocketAddress retAddr = null; 276 DatagramPacket receivePacket = new DatagramPacket(EmptyArray.BYTE, 0); 277 do { 278 IoBridge.recvfrom(false, fd, target, 0, receivePacket, isConnected()); 279 if (receivePacket.getAddress() != null) { 280 retAddr = receivePacket.getSocketAddress(); 281 break; 282 } 283 } while (loop); 284 return retAddr; 285 } 286 287 @Override 288 public int send(ByteBuffer source, SocketAddress socketAddress) throws IOException { 289 checkNotNull(source); 290 checkOpen(); 291 292 InetSocketAddress isa = (InetSocketAddress) socketAddress; 293 if (isa.getAddress() == null) { 294 throw new IOException(); 295 } 296 297 if (isConnected() && !connectAddress.equals(isa)) { 298 throw new IllegalArgumentException("Connected to " + connectAddress + 299 ", not " + socketAddress); 300 } 301 302 synchronized (writeLock) { 303 int sendCount = 0; 304 try { 305 begin(); 306 sendCount = IoBridge.sendto(fd, source, 0, isa.getAddress(), isa.getPort()); 307 if (!isBound) { 308 onBind(true /* updateSocketState */); 309 } 310 } finally { 311 end(sendCount >= 0); 312 } 313 return sendCount; 314 } 315 } 316 317 @Override 318 public int read(ByteBuffer target) throws IOException { 319 target.checkWritable(); 320 checkOpenConnected(); 321 322 if (!target.hasRemaining()) { 323 return 0; 324 } 325 326 int readCount; 327 if (target.isDirect() || target.hasArray()) { 328 readCount = readImpl(target); 329 } else { 330 byte[] readArray = new byte[target.remaining()]; 331 ByteBuffer readBuffer = ByteBuffer.wrap(readArray); 332 readCount = readImpl(readBuffer); 333 if (readCount > 0) { 334 target.put(readArray, 0, readCount); 335 } 336 } 337 return readCount; 338 } 339 340 @Override 341 public long read(ByteBuffer[] targets, int offset, int length) throws IOException { 342 Arrays.checkOffsetAndCount(targets.length, offset, length); 343 344 // status must be open and connected 345 checkOpenConnected(); 346 int totalCount = FileChannelImpl.calculateTotalRemaining(targets, offset, length, true); 347 if (totalCount == 0) { 348 return 0; 349 } 350 351 // read data to readBuffer, and then transfer data from readBuffer to 352 // targets. 353 ByteBuffer readBuffer = ByteBuffer.allocate(totalCount); 354 int readCount; 355 readCount = readImpl(readBuffer); 356 int left = readCount; 357 int index = offset; 358 // transfer data from readBuffer to targets 359 byte[] readArray = readBuffer.array(); 360 while (left > 0) { 361 int putLength = Math.min(targets[index].remaining(), left); 362 targets[index].put(readArray, readCount - left, putLength); 363 index++; 364 left -= putLength; 365 } 366 return readCount; 367 } 368 369 /* 370 * read from channel, and store the result in the target. 371 */ 372 private int readImpl(ByteBuffer dst) throws IOException { 373 synchronized (readLock) { 374 int readCount = 0; 375 try { 376 begin(); 377 readCount = IoBridge.recvfrom(false, fd, dst, 0, null, isConnected()); 378 } catch (InterruptedIOException e) { 379 // InterruptedIOException will be thrown when timeout. 380 return 0; 381 } finally { 382 end(readCount > 0); 383 } 384 return readCount; 385 } 386 } 387 388 @Override public int write(ByteBuffer src) throws IOException { 389 checkNotNull(src); 390 checkOpenConnected(); 391 if (!src.hasRemaining()) { 392 return 0; 393 } 394 395 return writeImpl(src); 396 } 397 398 /** 399 * @see java.nio.channels.DatagramChannel#write(java.nio.ByteBuffer[], int, 400 * int) 401 */ 402 @Override 403 public long write(ByteBuffer[] sources, int offset, int length) throws IOException { 404 Arrays.checkOffsetAndCount(sources.length, offset, length); 405 406 // status must be open and connected 407 checkOpenConnected(); 408 int count = FileChannelImpl.calculateTotalRemaining(sources, offset, length, false); 409 if (count == 0) { 410 return 0; 411 } 412 ByteBuffer writeBuf = ByteBuffer.allocate(count); 413 for (int val = offset; val < length + offset; val++) { 414 ByteBuffer source = sources[val]; 415 int oldPosition = source.position(); 416 writeBuf.put(source); 417 source.position(oldPosition); 418 } 419 writeBuf.flip(); 420 int result = writeImpl(writeBuf); 421 int val = offset; 422 int written = result; 423 while (result > 0) { 424 ByteBuffer source = sources[val]; 425 int gap = Math.min(result, source.remaining()); 426 source.position(source.position() + gap); 427 val++; 428 result -= gap; 429 } 430 return written; 431 } 432 433 private int writeImpl(ByteBuffer buf) throws IOException { 434 synchronized (writeLock) { 435 int result = 0; 436 try { 437 begin(); 438 result = IoBridge.sendto(fd, buf, 0, null, 0); 439 } finally { 440 end(result > 0); 441 } 442 return result; 443 } 444 } 445 446 @Override protected synchronized void implCloseSelectableChannel() throws IOException { 447 // A closed channel is not connected. 448 onDisconnect(true /* updateSocketState */); 449 IoBridge.closeAndSignalBlockedThreads(fd); 450 451 if (socket != null && !socket.isClosed()) { 452 socket.onClose(); 453 } 454 } 455 456 @Override protected void implConfigureBlocking(boolean blocking) throws IOException { 457 IoUtils.setBlocking(fd, blocking); 458 } 459 460 /* 461 * Status check, must be open. 462 */ 463 private void checkOpen() throws ClosedChannelException { 464 if (!isOpen()) { 465 throw new ClosedChannelException(); 466 } 467 } 468 469 /* 470 * Status check, must be open and connected, for read and write. 471 */ 472 private void checkOpenConnected() throws IOException { 473 checkOpen(); 474 if (!isConnected()) { 475 throw new NotYetConnectedException(); 476 } 477 } 478 479 /* 480 * Buffer check, must not null 481 */ 482 private void checkNotNull(ByteBuffer source) { 483 if (source == null) { 484 throw new NullPointerException("source == null"); 485 } 486 } 487 488 /* 489 * Get the fd for internal use. 490 */ 491 public FileDescriptor getFD() { 492 return fd; 493 } 494 495 /* 496 * The adapter class of DatagramSocket 497 */ 498 private static class DatagramSocketAdapter extends DatagramSocket { 499 500 /* 501 * The internal datagramChannelImpl. 502 */ 503 private final DatagramChannelImpl channelImpl; 504 505 /* 506 * Constructor initialize the datagramSocketImpl and datagramChannelImpl 507 */ 508 DatagramSocketAdapter(DatagramSocketImpl socketimpl, DatagramChannelImpl channelImpl) { 509 super(socketimpl); 510 this.channelImpl = channelImpl; 511 512 // Sync state socket state with the channel it is being created from 513 if (channelImpl.isBound) { 514 onBind(channelImpl.localAddress, channelImpl.localPort); 515 } 516 if (channelImpl.connected) { 517 onConnect( 518 channelImpl.connectAddress.getAddress(), 519 channelImpl.connectAddress.getPort()); 520 } else { 521 onDisconnect(); 522 } 523 if (!channelImpl.isOpen()) { 524 onClose(); 525 } 526 } 527 528 /* 529 * Get the internal datagramChannelImpl 530 */ 531 @Override 532 public DatagramChannel getChannel() { 533 return channelImpl; 534 } 535 536 @Override 537 public void bind(SocketAddress localAddr) throws SocketException { 538 if (channelImpl.isConnected()) { 539 throw new AlreadyConnectedException(); 540 } 541 super.bind(localAddr); 542 channelImpl.onBind(false /* updateSocketState */); 543 } 544 545 @Override 546 public void connect(SocketAddress peer) throws SocketException { 547 if (isConnected()) { 548 // RI compatibility: If the socket is already connected this fails. 549 throw new IllegalStateException("Socket is already connected."); 550 } 551 super.connect(peer); 552 // Connect may have performed an implicit bind(). Sync up here. 553 channelImpl.onBind(false /* updateSocketState */); 554 555 InetSocketAddress inetSocketAddress = (InetSocketAddress) peer; 556 channelImpl.onConnect( 557 inetSocketAddress.getAddress(), inetSocketAddress.getPort(), 558 false /* updateSocketState */); 559 } 560 561 @Override 562 public void connect(InetAddress address, int port) { 563 // To avoid implementing connect() twice call this.connect(SocketAddress) in preference 564 // to super.connect(). 565 try { 566 connect(new InetSocketAddress(address, port)); 567 } catch (SocketException e) { 568 // Ignored - there is nothing we can report here. 569 } 570 } 571 572 @Override 573 public void receive(DatagramPacket packet) throws IOException { 574 if (!channelImpl.isBlocking()) { 575 throw new IllegalBlockingModeException(); 576 } 577 578 boolean wasBound = isBound(); 579 super.receive(packet); 580 if (!wasBound) { 581 // DatagramSocket.receive() will implicitly bind if it hasn't been done explicitly. 582 // Sync the channel state with the socket. 583 channelImpl.onBind(false /* updateSocketState */); 584 } 585 } 586 587 @Override 588 public void send(DatagramPacket packet) throws IOException { 589 if (!channelImpl.isBlocking()) { 590 throw new IllegalBlockingModeException(); 591 } 592 593 // DatagramSocket.send() will implicitly bind if it hasn't been done explicitly. Force 594 // bind() here so that the channel state stays in sync with the socket. 595 boolean wasBound = isBound(); 596 super.send(packet); 597 if (!wasBound) { 598 // DatagramSocket.send() will implicitly bind if it hasn't been done explicitly. 599 // Sync the channel state with the socket. 600 channelImpl.onBind(false /* updateSocketState */); 601 } 602 } 603 604 @Override 605 public void close() { 606 synchronized (channelImpl) { 607 super.close(); 608 if (channelImpl.isOpen()) { 609 try { 610 channelImpl.close(); 611 } catch (IOException e) { 612 // Ignore 613 } 614 } 615 } 616 } 617 618 @Override 619 public void disconnect() { 620 super.disconnect(); 621 channelImpl.onDisconnect(false /* updateSocketState */); 622 } 623 } 624} 625