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.FilterInputStream; 23import java.io.FilterOutputStream; 24import java.io.InputStream; 25import java.io.IOException; 26import java.io.OutputStream; 27import java.net.ConnectException; 28import java.net.Inet4Address; 29import java.net.InetAddress; 30import java.net.InetSocketAddress; 31import java.net.PlainSocketImpl; 32import java.net.Socket; 33import java.net.SocketAddress; 34import java.net.SocketException; 35import java.net.SocketUtils; 36import java.nio.channels.AlreadyConnectedException; 37import java.nio.channels.ClosedChannelException; 38import java.nio.channels.ConnectionPendingException; 39import java.nio.channels.IllegalBlockingModeException; 40import java.nio.channels.NoConnectionPendingException; 41import java.nio.channels.NotYetConnectedException; 42import java.nio.channels.SocketChannel; 43import java.nio.channels.spi.SelectorProvider; 44import java.nio.channels.UnresolvedAddressException; 45import java.nio.channels.UnsupportedAddressTypeException; 46import java.util.Arrays; 47import java.util.Set; 48import libcore.io.IoBridge; 49import libcore.io.IoUtils; 50import libcore.io.Libcore; 51import static android.system.OsConstants.*; 52 53/* 54 * The default implementation class of java.nio.channels.SocketChannel. 55 */ 56class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel { 57 private static final int SOCKET_STATUS_UNINITIALIZED = -1; 58 59 // Status before connect. 60 private static final int SOCKET_STATUS_UNCONNECTED = 0; 61 62 // Status connection pending. 63 private static final int SOCKET_STATUS_PENDING = 1; 64 65 // Status after connection success. 66 private static final int SOCKET_STATUS_CONNECTED = 2; 67 68 // Status closed. 69 private static final int SOCKET_STATUS_CLOSED = 3; 70 71 private final FileDescriptor fd; 72 73 // Our internal Socket. 74 private SocketAdapter socket = null; 75 76 // The address to be connected. 77 private InetSocketAddress connectAddress = null; 78 79 // The local address the socket is bound to. 80 private InetAddress localAddress = null; 81 private int localPort; 82 83 private int status = SOCKET_STATUS_UNINITIALIZED; 84 85 // Whether the socket is bound. 86 private volatile boolean isBound = false; 87 88 private final Object readLock = new Object(); 89 90 private final Object writeLock = new Object(); 91 92 /* 93 * Constructor for creating a connected socket channel. 94 */ 95 public SocketChannelImpl(SelectorProvider selectorProvider) throws IOException { 96 this(selectorProvider, true); 97 } 98 99 /* 100 * Constructor for creating an optionally connected socket channel. 101 */ 102 public SocketChannelImpl(SelectorProvider selectorProvider, boolean connect) throws IOException { 103 super(selectorProvider); 104 status = SOCKET_STATUS_UNCONNECTED; 105 fd = (connect ? IoBridge.socket(true) : new FileDescriptor()); 106 } 107 108 /* 109 * Constructor for use by Pipe.SinkChannel and Pipe.SourceChannel. 110 */ 111 public SocketChannelImpl(SelectorProvider selectorProvider, FileDescriptor existingFd) throws IOException { 112 super(selectorProvider); 113 status = SOCKET_STATUS_CONNECTED; 114 fd = existingFd; 115 } 116 117 /* 118 * Getting the internal Socket If we have not the socket, we create a new 119 * one. 120 */ 121 @Override 122 synchronized public Socket socket() { 123 if (socket == null) { 124 try { 125 InetAddress addr = null; 126 int port = 0; 127 if (connectAddress != null) { 128 addr = connectAddress.getAddress(); 129 port = connectAddress.getPort(); 130 } 131 socket = new SocketAdapter(new PlainSocketImpl(fd, localPort, addr, port), this); 132 } catch (SocketException e) { 133 return null; 134 } 135 } 136 return socket; 137 } 138 139 /** 140 * Initialise the isBound, localAddress and localPort state from the file descriptor. Used when 141 * some or all of the bound state has been left to the OS to decide, or when the Socket handled 142 * bind() or connect(). 143 * 144 * @param updateSocketState 145 * if the associated socket (if present) needs to be updated 146 * @hide package visible for other nio classes 147 */ 148 void onBind(boolean updateSocketState) { 149 SocketAddress sa; 150 try { 151 sa = Libcore.os.getsockname(fd); 152 } catch (ErrnoException errnoException) { 153 throw new AssertionError(errnoException); 154 } 155 isBound = true; 156 InetSocketAddress localSocketAddress = (InetSocketAddress) sa; 157 localAddress = localSocketAddress.getAddress(); 158 localPort = localSocketAddress.getPort(); 159 if (updateSocketState && socket != null) { 160 socket.onBind(localAddress, localPort); 161 } 162 } 163 164 @Override 165 synchronized public boolean isConnected() { 166 return status == SOCKET_STATUS_CONNECTED; 167 } 168 169 @Override 170 synchronized public boolean isConnectionPending() { 171 return status == SOCKET_STATUS_PENDING; 172 } 173 174 @Override 175 public boolean connect(SocketAddress socketAddress) throws IOException { 176 // status must be open and unconnected 177 checkUnconnected(); 178 179 // check the address 180 InetSocketAddress inetSocketAddress = validateAddress(socketAddress); 181 InetAddress normalAddr = inetSocketAddress.getAddress(); 182 int port = inetSocketAddress.getPort(); 183 184 // When connecting, map ANY address to localhost 185 if (normalAddr.isAnyLocalAddress()) { 186 normalAddr = InetAddress.getLocalHost(); 187 } 188 189 boolean isBlocking = isBlocking(); 190 boolean finished = false; 191 int newStatus; 192 try { 193 if (isBlocking) { 194 begin(); 195 } 196 // When in blocking mode, IoBridge.connect() will return without an exception when the 197 // socket is connected. When in non-blocking mode it will return without an exception 198 // without knowing the result of the connection attempt, which could still be going on. 199 IoBridge.connect(fd, normalAddr, port); 200 newStatus = isBlocking ? SOCKET_STATUS_CONNECTED : SOCKET_STATUS_PENDING; 201 finished = true; 202 } catch (IOException e) { 203 if (isEINPROGRESS(e)) { 204 newStatus = SOCKET_STATUS_PENDING; 205 } else { 206 if (isOpen()) { 207 close(); 208 finished = true; 209 } 210 throw e; 211 } 212 } finally { 213 if (isBlocking) { 214 end(finished); 215 } 216 } 217 218 // If the channel was not bound, a connection attempt will have caused an implicit bind() to 219 // take place. Keep the local address state held by the channel and the socket up to date. 220 if (!isBound) { 221 onBind(true /* updateSocketState */); 222 } 223 224 // Keep the connected state held by the channel and the socket up to date. 225 onConnectStatusChanged(inetSocketAddress, newStatus, true /* updateSocketState */); 226 227 return status == SOCKET_STATUS_CONNECTED; 228 } 229 230 /** 231 * Initialise the connect() state with the supplied information. 232 * 233 * @param updateSocketState 234 * if the associated socket (if present) needs to be updated 235 * @hide package visible for other nio classes 236 */ 237 void onConnectStatusChanged(InetSocketAddress address, int status, boolean updateSocketState) { 238 this.status = status; 239 connectAddress = address; 240 if (status == SOCKET_STATUS_CONNECTED && updateSocketState && socket != null) { 241 socket.onConnect(connectAddress.getAddress(), connectAddress.getPort()); 242 } 243 } 244 245 private boolean isEINPROGRESS(IOException e) { 246 if (isBlocking()) { 247 return false; 248 } 249 if (e instanceof ConnectException) { 250 Throwable cause = e.getCause(); 251 if (cause instanceof ErrnoException) { 252 return ((ErrnoException) cause).errno == EINPROGRESS; 253 } 254 } 255 return false; 256 } 257 258 @Override 259 public boolean finishConnect() throws IOException { 260 synchronized (this) { 261 if (!isOpen()) { 262 throw new ClosedChannelException(); 263 } 264 if (status == SOCKET_STATUS_CONNECTED) { 265 return true; 266 } 267 if (status != SOCKET_STATUS_PENDING) { 268 throw new NoConnectionPendingException(); 269 } 270 } 271 272 boolean finished = false; 273 try { 274 begin(); 275 InetAddress inetAddress = connectAddress.getAddress(); 276 int port = connectAddress.getPort(); 277 finished = IoBridge.isConnected(fd, inetAddress, port, 0, 0); // Return immediately. 278 } catch (ConnectException e) { 279 if (isOpen()) { 280 close(); 281 finished = true; 282 } 283 throw e; 284 } finally { 285 end(finished); 286 } 287 288 synchronized (this) { 289 status = (finished ? SOCKET_STATUS_CONNECTED : status); 290 if (finished && socket != null) { 291 socket.onConnect(connectAddress.getAddress(), connectAddress.getPort()); 292 } 293 } 294 return finished; 295 } 296 297 @Override 298 public int read(ByteBuffer dst) throws IOException { 299 dst.checkWritable(); 300 checkOpenConnected(); 301 if (!dst.hasRemaining()) { 302 return 0; 303 } 304 return readImpl(dst); 305 } 306 307 @Override 308 public long read(ByteBuffer[] targets, int offset, int length) throws IOException { 309 Arrays.checkOffsetAndCount(targets.length, offset, length); 310 checkOpenConnected(); 311 int totalCount = FileChannelImpl.calculateTotalRemaining(targets, offset, length, true); 312 if (totalCount == 0) { 313 return 0; 314 } 315 byte[] readArray = new byte[totalCount]; 316 ByteBuffer readBuffer = ByteBuffer.wrap(readArray); 317 int readCount; 318 // read data to readBuffer, and then transfer data from readBuffer to targets. 319 readCount = readImpl(readBuffer); 320 readBuffer.flip(); 321 if (readCount > 0) { 322 int left = readCount; 323 int index = offset; 324 // transfer data from readArray to targets 325 while (left > 0) { 326 int putLength = Math.min(targets[index].remaining(), left); 327 targets[index].put(readArray, readCount - left, putLength); 328 index++; 329 left -= putLength; 330 } 331 } 332 return readCount; 333 } 334 335 private int readImpl(ByteBuffer dst) throws IOException { 336 synchronized (readLock) { 337 int readCount = 0; 338 try { 339 if (isBlocking()) { 340 begin(); 341 } 342 readCount = IoBridge.recvfrom(true, fd, dst, 0, null, false); 343 if (readCount > 0) { 344 dst.position(dst.position() + readCount); 345 } 346 } finally { 347 if (isBlocking()) { 348 end(readCount > 0); 349 } 350 } 351 return readCount; 352 } 353 } 354 355 @Override 356 public int write(ByteBuffer src) throws IOException { 357 if (src == null) { 358 throw new NullPointerException("src == null"); 359 } 360 checkOpenConnected(); 361 if (!src.hasRemaining()) { 362 return 0; 363 } 364 return writeImpl(src); 365 } 366 367 @Override 368 public long write(ByteBuffer[] sources, int offset, int length) throws IOException { 369 Arrays.checkOffsetAndCount(sources.length, offset, length); 370 checkOpenConnected(); 371 int count = FileChannelImpl.calculateTotalRemaining(sources, offset, length, false); 372 if (count == 0) { 373 return 0; 374 } 375 ByteBuffer writeBuf = ByteBuffer.allocate(count); 376 for (int val = offset; val < length + offset; val++) { 377 ByteBuffer source = sources[val]; 378 int oldPosition = source.position(); 379 writeBuf.put(source); 380 source.position(oldPosition); 381 } 382 writeBuf.flip(); 383 int result = writeImpl(writeBuf); 384 int val = offset; 385 int written = result; 386 while (result > 0) { 387 ByteBuffer source = sources[val]; 388 int gap = Math.min(result, source.remaining()); 389 source.position(source.position() + gap); 390 val++; 391 result -= gap; 392 } 393 return written; 394 } 395 396 private int writeImpl(ByteBuffer src) throws IOException { 397 synchronized (writeLock) { 398 if (!src.hasRemaining()) { 399 return 0; 400 } 401 int writeCount = 0; 402 try { 403 if (isBlocking()) { 404 begin(); 405 } 406 writeCount = IoBridge.sendto(fd, src, 0, null, 0); 407 if (writeCount > 0) { 408 src.position(src.position() + writeCount); 409 } 410 } finally { 411 if (isBlocking()) { 412 end(writeCount >= 0); 413 } 414 } 415 return writeCount; 416 } 417 } 418 419 /* 420 * Status check, open and "connected", when read and write. 421 */ 422 synchronized private void checkOpenConnected() throws ClosedChannelException { 423 if (!isOpen()) { 424 throw new ClosedChannelException(); 425 } 426 if (!isConnected()) { 427 throw new NotYetConnectedException(); 428 } 429 } 430 431 /* 432 * Status check, open and "unconnected", before connection. 433 */ 434 synchronized private void checkUnconnected() throws IOException { 435 if (!isOpen()) { 436 throw new ClosedChannelException(); 437 } 438 if (status == SOCKET_STATUS_CONNECTED) { 439 throw new AlreadyConnectedException(); 440 } 441 if (status == SOCKET_STATUS_PENDING) { 442 throw new ConnectionPendingException(); 443 } 444 } 445 446 /* 447 * Shared by this class and DatagramChannelImpl, to do the address transfer 448 * and check. 449 */ 450 static InetSocketAddress validateAddress(SocketAddress socketAddress) { 451 if (socketAddress == null) { 452 throw new IllegalArgumentException("socketAddress == null"); 453 } 454 if (!(socketAddress instanceof InetSocketAddress)) { 455 throw new UnsupportedAddressTypeException(); 456 } 457 InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress; 458 if (inetSocketAddress.isUnresolved()) { 459 throw new UnresolvedAddressException(); 460 } 461 return inetSocketAddress; 462 } 463 464 /* 465 * Do really closing action here. 466 */ 467 @Override 468 protected synchronized void implCloseSelectableChannel() throws IOException { 469 if (status != SOCKET_STATUS_CLOSED) { 470 status = SOCKET_STATUS_CLOSED; 471 // IoBridge.closeAndSignalBlockedThreads(fd) is idempotent: It is safe to call on an 472 // already-closed file descriptor. 473 IoBridge.closeAndSignalBlockedThreads(fd); 474 if (socket != null && !socket.isClosed()) { 475 socket.onClose(); 476 } 477 } 478 } 479 480 @Override protected void implConfigureBlocking(boolean blocking) throws IOException { 481 IoUtils.setBlocking(fd, blocking); 482 } 483 484 /* 485 * Get the fd. 486 */ 487 public FileDescriptor getFD() { 488 return fd; 489 } 490 491 /* @hide used by ServerSocketChannelImpl to sync channel state during accept() */ 492 public void onAccept(InetSocketAddress remoteAddress, boolean updateSocketState) { 493 onBind(updateSocketState); 494 onConnectStatusChanged(remoteAddress, SOCKET_STATUS_CONNECTED, updateSocketState); 495 } 496 497 /* 498 * Adapter classes for internal socket. 499 */ 500 private static class SocketAdapter extends Socket { 501 private final SocketChannelImpl channel; 502 private final PlainSocketImpl socketImpl; 503 504 SocketAdapter(PlainSocketImpl socketImpl, SocketChannelImpl channel) 505 throws SocketException { 506 super(socketImpl); 507 this.socketImpl = socketImpl; 508 this.channel = channel; 509 SocketUtils.setCreated(this); 510 511 // Sync state socket state with the channel it is being created from 512 if (channel.isBound) { 513 onBind(channel.localAddress, channel.localPort); 514 } 515 if (channel.isConnected()) { 516 onConnect(channel.connectAddress.getAddress(), channel.connectAddress.getPort()); 517 } 518 if (!channel.isOpen()) { 519 onClose(); 520 } 521 522 } 523 524 @Override 525 public SocketChannel getChannel() { 526 return channel; 527 } 528 529 @Override 530 public void connect(SocketAddress remoteAddr, int timeout) throws IOException { 531 if (!channel.isBlocking()) { 532 throw new IllegalBlockingModeException(); 533 } 534 if (isConnected()) { 535 throw new AlreadyConnectedException(); 536 } 537 super.connect(remoteAddr, timeout); 538 channel.onBind(false); 539 if (super.isConnected()) { 540 InetSocketAddress remoteInetAddress = (InetSocketAddress) remoteAddr; 541 channel.onConnectStatusChanged( 542 remoteInetAddress, SOCKET_STATUS_CONNECTED, false /* updateSocketState */); 543 } 544 } 545 546 @Override 547 public void bind(SocketAddress localAddr) throws IOException { 548 if (channel.isConnected()) { 549 throw new AlreadyConnectedException(); 550 } 551 if (SocketChannelImpl.SOCKET_STATUS_PENDING == channel.status) { 552 throw new ConnectionPendingException(); 553 } 554 super.bind(localAddr); 555 channel.onBind(false); 556 } 557 558 @Override 559 public void close() throws IOException { 560 synchronized (channel) { 561 super.close(); 562 if (channel.isOpen()) { 563 // channel.close() recognizes the socket is closed and avoids recursion. There 564 // is no channel.onClose() because the "closed" field is private. 565 channel.close(); 566 } 567 } 568 } 569 570 @Override 571 public OutputStream getOutputStream() throws IOException { 572 return new BlockingCheckOutputStream(super.getOutputStream(), channel); 573 } 574 575 @Override 576 public InputStream getInputStream() throws IOException { 577 return new BlockingCheckInputStream(super.getInputStream(), channel); 578 } 579 580 @Override 581 public FileDescriptor getFileDescriptor$() { 582 return socketImpl.getFD$(); 583 } 584 } 585 586 /* 587 * Throws an IllegalBlockingModeException if the channel is in non-blocking 588 * mode when performing write operations. 589 */ 590 private static class BlockingCheckOutputStream extends FilterOutputStream { 591 private final SocketChannel channel; 592 593 public BlockingCheckOutputStream(OutputStream out, SocketChannel channel) { 594 super(out); 595 this.channel = channel; 596 } 597 598 @Override 599 public void write(byte[] buffer, int offset, int byteCount) throws IOException { 600 checkBlocking(); 601 out.write(buffer, offset, byteCount); 602 } 603 604 @Override 605 public void write(int oneByte) throws IOException { 606 checkBlocking(); 607 out.write(oneByte); 608 } 609 610 @Override 611 public void write(byte[] buffer) throws IOException { 612 checkBlocking(); 613 out.write(buffer); 614 } 615 616 @Override 617 public void close() throws IOException { 618 super.close(); 619 // channel.close() recognizes the socket is closed and avoids recursion. There is no 620 // channel.onClose() because the "closed" field is private. 621 channel.close(); 622 } 623 624 private void checkBlocking() { 625 if (!channel.isBlocking()) { 626 throw new IllegalBlockingModeException(); 627 } 628 } 629 } 630 631 /* 632 * Throws an IllegalBlockingModeException if the channel is in non-blocking 633 * mode when performing read operations. 634 */ 635 private static class BlockingCheckInputStream extends FilterInputStream { 636 private final SocketChannel channel; 637 638 public BlockingCheckInputStream(InputStream in, SocketChannel channel) { 639 super(in); 640 this.channel = channel; 641 } 642 643 @Override 644 public int read() throws IOException { 645 checkBlocking(); 646 return in.read(); 647 } 648 649 @Override 650 public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { 651 checkBlocking(); 652 return in.read(buffer, byteOffset, byteCount); 653 } 654 655 @Override 656 public int read(byte[] buffer) throws IOException { 657 checkBlocking(); 658 return in.read(buffer); 659 } 660 661 @Override 662 public void close() throws IOException { 663 super.close(); 664 // channel.close() recognizes the socket is closed and avoids recursion. There is no 665 // channel.onClose() because the "closed" field is private. 666 channel.close(); 667 } 668 669 private void checkBlocking() { 670 if (!channel.isBlocking()) { 671 throw new IllegalBlockingModeException(); 672 } 673 } 674 } 675} 676