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 } finally { 344 if (isBlocking()) { 345 end(readCount > 0); 346 } 347 } 348 return readCount; 349 } 350 } 351 352 @Override 353 public int write(ByteBuffer src) throws IOException { 354 if (src == null) { 355 throw new NullPointerException("src == null"); 356 } 357 checkOpenConnected(); 358 if (!src.hasRemaining()) { 359 return 0; 360 } 361 return writeImpl(src); 362 } 363 364 @Override 365 public long write(ByteBuffer[] sources, int offset, int length) throws IOException { 366 Arrays.checkOffsetAndCount(sources.length, offset, length); 367 checkOpenConnected(); 368 int count = FileChannelImpl.calculateTotalRemaining(sources, offset, length, false); 369 if (count == 0) { 370 return 0; 371 } 372 ByteBuffer writeBuf = ByteBuffer.allocate(count); 373 for (int val = offset; val < length + offset; val++) { 374 ByteBuffer source = sources[val]; 375 int oldPosition = source.position(); 376 writeBuf.put(source); 377 source.position(oldPosition); 378 } 379 writeBuf.flip(); 380 int result = writeImpl(writeBuf); 381 int val = offset; 382 int written = result; 383 while (result > 0) { 384 ByteBuffer source = sources[val]; 385 int gap = Math.min(result, source.remaining()); 386 source.position(source.position() + gap); 387 val++; 388 result -= gap; 389 } 390 return written; 391 } 392 393 private int writeImpl(ByteBuffer src) throws IOException { 394 synchronized (writeLock) { 395 if (!src.hasRemaining()) { 396 return 0; 397 } 398 int writeCount = 0; 399 try { 400 if (isBlocking()) { 401 begin(); 402 } 403 writeCount = IoBridge.sendto(fd, src, 0, null, 0); 404 } finally { 405 if (isBlocking()) { 406 end(writeCount >= 0); 407 } 408 } 409 return writeCount; 410 } 411 } 412 413 /* 414 * Status check, open and "connected", when read and write. 415 */ 416 synchronized private void checkOpenConnected() throws ClosedChannelException { 417 if (!isOpen()) { 418 throw new ClosedChannelException(); 419 } 420 if (!isConnected()) { 421 throw new NotYetConnectedException(); 422 } 423 } 424 425 /* 426 * Status check, open and "unconnected", before connection. 427 */ 428 synchronized private void checkUnconnected() throws IOException { 429 if (!isOpen()) { 430 throw new ClosedChannelException(); 431 } 432 if (status == SOCKET_STATUS_CONNECTED) { 433 throw new AlreadyConnectedException(); 434 } 435 if (status == SOCKET_STATUS_PENDING) { 436 throw new ConnectionPendingException(); 437 } 438 } 439 440 /* 441 * Shared by this class and DatagramChannelImpl, to do the address transfer 442 * and check. 443 */ 444 static InetSocketAddress validateAddress(SocketAddress socketAddress) { 445 if (socketAddress == null) { 446 throw new IllegalArgumentException("socketAddress == null"); 447 } 448 if (!(socketAddress instanceof InetSocketAddress)) { 449 throw new UnsupportedAddressTypeException(); 450 } 451 InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress; 452 if (inetSocketAddress.isUnresolved()) { 453 throw new UnresolvedAddressException(); 454 } 455 return inetSocketAddress; 456 } 457 458 /* 459 * Do really closing action here. 460 */ 461 @Override 462 protected synchronized void implCloseSelectableChannel() throws IOException { 463 if (status != SOCKET_STATUS_CLOSED) { 464 status = SOCKET_STATUS_CLOSED; 465 // IoBridge.closeAndSignalBlockedThreads(fd) is idempotent: It is safe to call on an 466 // already-closed file descriptor. 467 IoBridge.closeAndSignalBlockedThreads(fd); 468 if (socket != null && !socket.isClosed()) { 469 socket.onClose(); 470 } 471 } 472 } 473 474 @Override protected void implConfigureBlocking(boolean blocking) throws IOException { 475 IoUtils.setBlocking(fd, blocking); 476 } 477 478 /* 479 * Get the fd. 480 */ 481 public FileDescriptor getFD() { 482 return fd; 483 } 484 485 /* @hide used by ServerSocketChannelImpl to sync channel state during accept() */ 486 public void onAccept(InetSocketAddress remoteAddress, boolean updateSocketState) { 487 onBind(updateSocketState); 488 onConnectStatusChanged(remoteAddress, SOCKET_STATUS_CONNECTED, updateSocketState); 489 } 490 491 /* 492 * Adapter classes for internal socket. 493 */ 494 private static class SocketAdapter extends Socket { 495 private final SocketChannelImpl channel; 496 private final PlainSocketImpl socketImpl; 497 498 SocketAdapter(PlainSocketImpl socketImpl, SocketChannelImpl channel) 499 throws SocketException { 500 super(socketImpl); 501 this.socketImpl = socketImpl; 502 this.channel = channel; 503 SocketUtils.setCreated(this); 504 505 // Sync state socket state with the channel it is being created from 506 if (channel.isBound) { 507 onBind(channel.localAddress, channel.localPort); 508 } 509 if (channel.isConnected()) { 510 onConnect(channel.connectAddress.getAddress(), channel.connectAddress.getPort()); 511 } 512 if (!channel.isOpen()) { 513 onClose(); 514 } 515 516 } 517 518 @Override 519 public SocketChannel getChannel() { 520 return channel; 521 } 522 523 @Override 524 public void connect(SocketAddress remoteAddr, int timeout) throws IOException { 525 if (!channel.isBlocking()) { 526 throw new IllegalBlockingModeException(); 527 } 528 if (isConnected()) { 529 throw new AlreadyConnectedException(); 530 } 531 super.connect(remoteAddr, timeout); 532 channel.onBind(false); 533 if (super.isConnected()) { 534 InetSocketAddress remoteInetAddress = (InetSocketAddress) remoteAddr; 535 channel.onConnectStatusChanged( 536 remoteInetAddress, SOCKET_STATUS_CONNECTED, false /* updateSocketState */); 537 } 538 } 539 540 @Override 541 public void bind(SocketAddress localAddr) throws IOException { 542 if (channel.isConnected()) { 543 throw new AlreadyConnectedException(); 544 } 545 if (SocketChannelImpl.SOCKET_STATUS_PENDING == channel.status) { 546 throw new ConnectionPendingException(); 547 } 548 super.bind(localAddr); 549 channel.onBind(false); 550 } 551 552 @Override 553 public void close() throws IOException { 554 synchronized (channel) { 555 super.close(); 556 if (channel.isOpen()) { 557 // channel.close() recognizes the socket is closed and avoids recursion. There 558 // is no channel.onClose() because the "closed" field is private. 559 channel.close(); 560 } 561 } 562 } 563 564 @Override 565 public OutputStream getOutputStream() throws IOException { 566 return new BlockingCheckOutputStream(super.getOutputStream(), channel); 567 } 568 569 @Override 570 public InputStream getInputStream() throws IOException { 571 return new BlockingCheckInputStream(super.getInputStream(), channel); 572 } 573 574 @Override 575 public FileDescriptor getFileDescriptor$() { 576 return socketImpl.getFD$(); 577 } 578 } 579 580 /* 581 * Throws an IllegalBlockingModeException if the channel is in non-blocking 582 * mode when performing write operations. 583 */ 584 private static class BlockingCheckOutputStream extends FilterOutputStream { 585 private final SocketChannel channel; 586 587 public BlockingCheckOutputStream(OutputStream out, SocketChannel channel) { 588 super(out); 589 this.channel = channel; 590 } 591 592 @Override 593 public void write(byte[] buffer, int offset, int byteCount) throws IOException { 594 checkBlocking(); 595 out.write(buffer, offset, byteCount); 596 } 597 598 @Override 599 public void write(int oneByte) throws IOException { 600 checkBlocking(); 601 out.write(oneByte); 602 } 603 604 @Override 605 public void write(byte[] buffer) throws IOException { 606 checkBlocking(); 607 out.write(buffer); 608 } 609 610 @Override 611 public void close() throws IOException { 612 super.close(); 613 // channel.close() recognizes the socket is closed and avoids recursion. There is no 614 // channel.onClose() because the "closed" field is private. 615 channel.close(); 616 } 617 618 private void checkBlocking() { 619 if (!channel.isBlocking()) { 620 throw new IllegalBlockingModeException(); 621 } 622 } 623 } 624 625 /* 626 * Throws an IllegalBlockingModeException if the channel is in non-blocking 627 * mode when performing read operations. 628 */ 629 private static class BlockingCheckInputStream extends FilterInputStream { 630 private final SocketChannel channel; 631 632 public BlockingCheckInputStream(InputStream in, SocketChannel channel) { 633 super(in); 634 this.channel = channel; 635 } 636 637 @Override 638 public int read() throws IOException { 639 checkBlocking(); 640 return in.read(); 641 } 642 643 @Override 644 public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { 645 checkBlocking(); 646 return in.read(buffer, byteOffset, byteCount); 647 } 648 649 @Override 650 public int read(byte[] buffer) throws IOException { 651 checkBlocking(); 652 return in.read(buffer); 653 } 654 655 @Override 656 public void close() throws IOException { 657 super.close(); 658 // channel.close() recognizes the socket is closed and avoids recursion. There is no 659 // channel.onClose() because the "closed" field is private. 660 channel.close(); 661 } 662 663 private void checkBlocking() { 664 if (!channel.isBlocking()) { 665 throw new IllegalBlockingModeException(); 666 } 667 } 668 } 669} 670