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 18 package org.apache.harmony.nio.internal; 19 20 // BEGIN android-note 21 // In this class the address length was changed from long to int. 22 // END android-note 23 24 import java.io.FileDescriptor; 25 import java.io.IOException; 26 import java.io.InputStream; 27 import java.io.OutputStream; 28 import java.net.ConnectException; 29 import java.net.InetAddress; 30 import java.net.InetSocketAddress; 31 import java.net.Socket; 32 import java.net.SocketAddress; 33 import java.net.SocketException; 34 import java.net.SocketUtils; 35 import java.net.UnknownHostException; 36 import java.nio.ByteBuffer; 37 import java.nio.channels.AlreadyConnectedException; 38 import java.nio.channels.ClosedChannelException; 39 import java.nio.channels.ConnectionPendingException; 40 import java.nio.channels.IllegalBlockingModeException; 41 import java.nio.channels.NoConnectionPendingException; 42 import java.nio.channels.NotYetConnectedException; 43 import java.nio.channels.SocketChannel; 44 import java.nio.channels.UnresolvedAddressException; 45 import java.nio.channels.UnsupportedAddressTypeException; 46 import java.nio.channels.spi.SelectorProvider; 47 import libcore.io.IoUtils; 48 import org.apache.harmony.luni.net.PlainSocketImpl; 49 import org.apache.harmony.luni.platform.FileDescriptorHandler; 50 import org.apache.harmony.luni.platform.INetworkSystem; 51 import org.apache.harmony.luni.platform.Platform; 52 import org.apache.harmony.nio.AddressUtil; 53 54 /* 55 * The default implementation class of java.nio.channels.SocketChannel. 56 */ 57 class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler { 58 59 private static final int EOF = -1; 60 61 // The singleton to do the native network operation. 62 static final INetworkSystem networkSystem = Platform.getNetworkSystem(); 63 64 // Status un-init, not initialized. 65 static final int SOCKET_STATUS_UNINIT = EOF; 66 67 // Status before connect. 68 static final int SOCKET_STATUS_UNCONNECTED = 0; 69 70 // Status connection pending. 71 static final int SOCKET_STATUS_PENDING = 1; 72 73 // Status after connection success. 74 static final int SOCKET_STATUS_CONNECTED = 2; 75 76 // Status closed. 77 static final int SOCKET_STATUS_CLOSED = 3; 78 79 // The descriptor to interact with native code. 80 FileDescriptor fd; 81 82 // Our internal Socket. 83 private SocketAdapter socket = null; 84 85 // The address to be connected. 86 InetSocketAddress connectAddress = null; 87 88 // Local address of the this socket (package private for adapter). 89 InetAddress localAddress = null; 90 91 // Local port number. 92 int localPort; 93 94 // At first, uninitialized. 95 int status = SOCKET_STATUS_UNINIT; 96 97 // Whether the socket is bound. 98 volatile boolean isBound = false; 99 100 private static class ReadLock {} 101 private final Object readLock = new ReadLock(); 102 103 private static class WriteLock {} 104 private final Object writeLock = new WriteLock(); 105 106 /* 107 * Constructor for creating a connected socket channel. 108 */ 109 public SocketChannelImpl(SelectorProvider selectorProvider) throws IOException { 110 this(selectorProvider, true); 111 } 112 113 /* 114 * Constructor for creating an optionally connected socket channel. 115 */ 116 public SocketChannelImpl(SelectorProvider selectorProvider, boolean connect) throws IOException { 117 super(selectorProvider); 118 fd = new FileDescriptor(); 119 status = SOCKET_STATUS_UNCONNECTED; 120 if (connect) { 121 networkSystem.socket(fd, true); 122 } 123 } 124 125 /* 126 * Getting the internal Socket If we have not the socket, we create a new 127 * one. 128 */ 129 @Override 130 synchronized public Socket socket() { 131 if (socket == null) { 132 try { 133 InetAddress addr = null; 134 int port = 0; 135 if (connectAddress != null) { 136 addr = connectAddress.getAddress(); 137 port = connectAddress.getPort(); 138 } 139 socket = new SocketAdapter(new PlainSocketImpl(fd, localPort, addr, port), this); 140 } catch (SocketException e) { 141 return null; 142 } 143 } 144 return socket; 145 } 146 147 @Override 148 synchronized public boolean isConnected() { 149 return status == SOCKET_STATUS_CONNECTED; 150 } 151 152 /* 153 * Status setting used by other class. 154 */ 155 synchronized void setConnected() { 156 status = SOCKET_STATUS_CONNECTED; 157 } 158 159 void setBound(boolean flag) { 160 isBound = flag; 161 } 162 163 @Override 164 synchronized public boolean isConnectionPending() { 165 return status == SOCKET_STATUS_PENDING; 166 } 167 168 @Override 169 public boolean connect(SocketAddress socketAddress) throws IOException { 170 // status must be open and unconnected 171 checkUnconnected(); 172 173 // check the address 174 InetSocketAddress inetSocketAddress = validateAddress(socketAddress); 175 InetAddress normalAddr = inetSocketAddress.getAddress(); 176 177 // When connecting, map ANY address to Localhost 178 if (normalAddr.isAnyLocalAddress()) { 179 normalAddr = InetAddress.getLocalHost(); 180 } 181 182 int port = inetSocketAddress.getPort(); 183 String hostName = normalAddr.getHostName(); 184 // security check 185 SecurityManager sm = System.getSecurityManager(); 186 if (sm != null) { 187 sm.checkConnect(hostName, port); 188 } 189 190 // connect result 191 int result = EOF; 192 boolean finished = false; 193 194 try { 195 if (isBlocking()) { 196 begin(); 197 networkSystem.connect(fd, normalAddr, port, 0); 198 finished = true; // Or we'd have thrown an exception. 199 } else { 200 finished = networkSystem.connectNonBlocking(fd, normalAddr, port); 201 // set back to nonblocking to work around with a bug in portlib 202 if (!isBlocking()) { 203 IoUtils.setBlocking(fd, false); 204 } 205 } 206 isBound = finished; 207 } catch (IOException e) { 208 if (e instanceof ConnectException && !isBlocking()) { 209 status = SOCKET_STATUS_PENDING; 210 } else { 211 if (isOpen()) { 212 close(); 213 finished = true; 214 } 215 throw e; 216 } 217 } finally { 218 if (isBlocking()) { 219 end(finished); 220 } 221 } 222 223 initLocalAddressAndPort(); 224 connectAddress = inetSocketAddress; 225 if (socket != null) { 226 socket.socketImpl().initRemoteAddressAndPort(connectAddress.getAddress(), 227 connectAddress.getPort()); 228 } 229 230 synchronized (this) { 231 if (isBlocking()) { 232 status = (finished ? SOCKET_STATUS_CONNECTED : SOCKET_STATUS_UNCONNECTED); 233 } else { 234 status = SOCKET_STATUS_PENDING; 235 } 236 } 237 return finished; 238 } 239 240 private void initLocalAddressAndPort() { 241 localAddress = networkSystem.getSocketLocalAddress(fd); 242 localPort = networkSystem.getSocketLocalPort(fd); 243 if (socket != null) { 244 socket.socketImpl().initLocalPort(localPort); 245 } 246 } 247 248 @Override 249 public boolean finishConnect() throws IOException { 250 // status check 251 synchronized (this) { 252 if (!isOpen()) { 253 throw new ClosedChannelException(); 254 } 255 if (status == SOCKET_STATUS_CONNECTED) { 256 return true; 257 } 258 if (status != SOCKET_STATUS_PENDING) { 259 throw new NoConnectionPendingException(); 260 } 261 } 262 263 boolean finished = false; 264 try { 265 begin(); 266 final int WAIT_FOREVER = -1; 267 final int POLL = 0; 268 finished = networkSystem.isConnected(fd, isBlocking() ? WAIT_FOREVER : POLL); 269 isBound = finished; 270 initLocalAddressAndPort(); 271 } catch (ConnectException e) { 272 if (isOpen()) { 273 close(); 274 finished = true; 275 } 276 throw e; 277 } finally { 278 end(finished); 279 } 280 281 synchronized (this) { 282 status = (finished ? SOCKET_STATUS_CONNECTED : status); 283 isBound = finished; 284 // TPE: Workaround for bug that turns socket back to blocking 285 if (!isBlocking()) implConfigureBlocking(false); 286 } 287 return finished; 288 } 289 290 void finishAccept() { 291 initLocalAddressAndPort(); 292 } 293 294 @Override 295 public int read(ByteBuffer target) throws IOException { 296 FileChannelImpl.checkWritable(target); 297 checkOpenConnected(); 298 if (!target.hasRemaining()) { 299 return 0; 300 } 301 302 int readCount; 303 if (target.isDirect() || target.hasArray()) { 304 readCount = readImpl(target); 305 if (readCount > 0) { 306 target.position(target.position() + readCount); 307 } 308 } else { 309 ByteBuffer readBuffer = null; 310 byte[] readArray = null; 311 readArray = new byte[target.remaining()]; 312 readBuffer = ByteBuffer.wrap(readArray); 313 readCount = readImpl(readBuffer); 314 if (readCount > 0) { 315 target.put(readArray, 0, readCount); 316 } 317 } 318 return readCount; 319 } 320 321 @Override 322 public long read(ByteBuffer[] targets, int offset, int length) throws IOException { 323 if (!isIndexValid(targets, offset, length)) { 324 throw new IndexOutOfBoundsException(); 325 } 326 327 checkOpenConnected(); 328 int totalCount = FileChannelImpl.calculateTotalRemaining(targets, offset, length, true); 329 if (totalCount == 0) { 330 return 0; 331 } 332 byte[] readArray = new byte[totalCount]; 333 ByteBuffer readBuffer = ByteBuffer.wrap(readArray); 334 int readCount; 335 // read data to readBuffer, and then transfer data from readBuffer to 336 // targets. 337 readCount = readImpl(readBuffer); 338 if (readCount > 0) { 339 int left = readCount; 340 int index = offset; 341 // transfer data from readArray to targets 342 while (left > 0) { 343 int putLength = Math.min(targets[index].remaining(), left); 344 targets[index].put(readArray, readCount - left, putLength); 345 index++; 346 left -= putLength; 347 } 348 } 349 return readCount; 350 } 351 352 private boolean isIndexValid(ByteBuffer[] targets, int offset, int length) { 353 return (length >= 0) && (offset >= 0) 354 && ((long) length + (long) offset <= targets.length); 355 } 356 357 /** 358 * Read from channel, and store the result in the target. 359 * 360 * @param target 361 * output parameter 362 */ 363 private int readImpl(ByteBuffer target) throws IOException { 364 synchronized (readLock) { 365 int readCount = 0; 366 try { 367 if (isBlocking()) { 368 begin(); 369 } 370 int offset = target.position(); 371 int length = target.remaining(); 372 if (target.isDirect()) { 373 // BEGIN android-changed 374 // changed address from long to int 375 int address = AddressUtil.getDirectBufferAddress(target); 376 readCount = networkSystem.readDirect(fd, address + offset, length); 377 // END android-changed 378 } else { 379 // target is assured to have array. 380 byte[] array = target.array(); 381 offset += target.arrayOffset(); 382 readCount = networkSystem.read(fd, array, offset, length); 383 } 384 return readCount; 385 } finally { 386 if (isBlocking()) { 387 end(readCount > 0); 388 } 389 } 390 } 391 } 392 393 @Override 394 public int write(ByteBuffer source) throws IOException { 395 if (null == source) { 396 throw new NullPointerException(); 397 } 398 checkOpenConnected(); 399 if (!source.hasRemaining()) { 400 return 0; 401 } 402 return writeImpl(source); 403 } 404 405 @Override 406 public long write(ByteBuffer[] sources, int offset, int length) throws IOException { 407 if (!isIndexValid(sources, offset, length)) { 408 throw new IndexOutOfBoundsException(); 409 } 410 411 checkOpenConnected(); 412 int count = FileChannelImpl.calculateTotalRemaining(sources, offset, length, false); 413 if (count == 0) { 414 return 0; 415 } 416 ByteBuffer writeBuf = ByteBuffer.allocate(count); 417 for (int val = offset; val < length + offset; val++) { 418 ByteBuffer source = sources[val]; 419 int oldPosition = source.position(); 420 writeBuf.put(source); 421 source.position(oldPosition); 422 } 423 writeBuf.flip(); 424 int result = writeImpl(writeBuf); 425 int val = offset; 426 int written = result; 427 while (result > 0) { 428 ByteBuffer source = sources[val]; 429 int gap = Math.min(result, source.remaining()); 430 source.position(source.position() + gap); 431 val++; 432 result -= gap; 433 } 434 return written; 435 } 436 437 /* 438 * Write the source. return the count of bytes written. 439 */ 440 private int writeImpl(ByteBuffer source) throws IOException { 441 synchronized (writeLock) { 442 if (!source.hasRemaining()) { 443 return 0; 444 } 445 int writeCount = 0; 446 try { 447 int pos = source.position(); 448 int length = source.remaining(); 449 if (isBlocking()) { 450 begin(); 451 } 452 if (source.isDirect()) { 453 int address = AddressUtil.getDirectBufferAddress(source); 454 writeCount = networkSystem.writeDirect(fd, address, pos, length); 455 } else if (source.hasArray()) { 456 pos += source.arrayOffset(); 457 writeCount = networkSystem.write(fd, source.array(), pos, length); 458 } else { 459 byte[] array = new byte[length]; 460 source.get(array); 461 writeCount = networkSystem.write(fd, array, 0, length); 462 } 463 source.position(pos + writeCount); 464 } finally { 465 if (isBlocking()) { 466 end(writeCount >= 0); 467 } 468 } 469 return writeCount; 470 } 471 } 472 473 /* 474 * Status check, open and "connected", when read and write. 475 */ 476 synchronized private void checkOpenConnected() throws ClosedChannelException { 477 if (!isOpen()) { 478 throw new ClosedChannelException(); 479 } 480 if (!isConnected()) { 481 throw new NotYetConnectedException(); 482 } 483 } 484 485 /* 486 * Status check, open and "unconnected", before connection. 487 */ 488 synchronized private void checkUnconnected() throws IOException { 489 if (!isOpen()) { 490 throw new ClosedChannelException(); 491 } 492 if (status == SOCKET_STATUS_CONNECTED) { 493 throw new AlreadyConnectedException(); 494 } 495 if (status == SOCKET_STATUS_PENDING) { 496 throw new ConnectionPendingException(); 497 } 498 } 499 500 /* 501 * Shared by this class and DatagramChannelImpl, to do the address transfer 502 * and check. 503 */ 504 static InetSocketAddress validateAddress(SocketAddress socketAddress) { 505 if (null == socketAddress) { 506 throw new IllegalArgumentException(); 507 } 508 if (!(socketAddress instanceof InetSocketAddress)) { 509 throw new UnsupportedAddressTypeException(); 510 } 511 InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress; 512 if (inetSocketAddress.isUnresolved()) { 513 throw new UnresolvedAddressException(); 514 } 515 return inetSocketAddress; 516 } 517 518 /* 519 * Get local address. 520 */ 521 public InetAddress getLocalAddress() throws UnknownHostException { 522 if (!isBound) { 523 byte[] any_bytes = { 0, 0, 0, 0 }; 524 return InetAddress.getByAddress(any_bytes); 525 } 526 return localAddress; 527 } 528 529 /* 530 * Do really closing action here. 531 */ 532 @Override 533 synchronized protected void implCloseSelectableChannel() throws IOException { 534 if (SOCKET_STATUS_CLOSED != status) { 535 status = SOCKET_STATUS_CLOSED; 536 if (null != socket && !socket.isClosed()) { 537 socket.close(); 538 } else { 539 networkSystem.close(fd); 540 } 541 } 542 } 543 544 @Override 545 protected void implConfigureBlocking(boolean blockMode) throws IOException { 546 synchronized (blockingLock()) { 547 IoUtils.setBlocking(fd, blockMode); 548 } 549 } 550 551 /* 552 * Get the fd. 553 */ 554 public FileDescriptor getFD() { 555 return fd; 556 } 557 558 /* 559 * Adapter classes for internal socket. 560 */ 561 private static class SocketAdapter extends Socket { 562 private final SocketChannelImpl channel; 563 private final PlainSocketImpl socketImpl; 564 565 SocketAdapter(PlainSocketImpl socketImpl, SocketChannelImpl channel) throws SocketException { 566 super(socketImpl); 567 this.socketImpl = socketImpl; 568 this.channel = channel; 569 SocketUtils.setCreated(this); 570 } 571 572 PlainSocketImpl socketImpl() { 573 return socketImpl; 574 } 575 576 @Override 577 public SocketChannel getChannel() { 578 return channel; 579 } 580 581 @Override 582 public boolean isBound() { 583 return channel.isBound; 584 } 585 586 @Override 587 public boolean isConnected() { 588 return channel.isConnected(); 589 } 590 591 @Override 592 public InetAddress getLocalAddress() { 593 try { 594 return channel.getLocalAddress(); 595 } catch (UnknownHostException e) { 596 return null; 597 } 598 } 599 600 @Override 601 public void connect(SocketAddress remoteAddr, int timeout) throws IOException { 602 if (!channel.isBlocking()) { 603 throw new IllegalBlockingModeException(); 604 } 605 if (isConnected()) { 606 throw new AlreadyConnectedException(); 607 } 608 super.connect(remoteAddr, timeout); 609 channel.initLocalAddressAndPort(); 610 if (super.isConnected()) { 611 channel.setConnected(); 612 channel.isBound = super.isBound(); 613 } 614 } 615 616 @Override 617 public void bind(SocketAddress localAddr) throws IOException { 618 if (channel.isConnected()) { 619 throw new AlreadyConnectedException(); 620 } 621 if (SocketChannelImpl.SOCKET_STATUS_PENDING == channel.status) { 622 throw new ConnectionPendingException(); 623 } 624 super.bind(localAddr); 625 // keep here to see if need next version 626 // channel.Address = getLocalSocketAddress(); 627 // channel.localport = getLocalPort(); 628 channel.isBound = true; 629 } 630 631 @Override 632 public void close() throws IOException { 633 synchronized (channel) { 634 if (channel.isOpen()) { 635 channel.close(); 636 } else { 637 super.close(); 638 } 639 channel.status = SocketChannelImpl.SOCKET_STATUS_CLOSED; 640 } 641 } 642 643 @Override 644 public OutputStream getOutputStream() throws IOException { 645 checkOpenAndConnected(); 646 if (isOutputShutdown()) { 647 throw new SocketException("Socket output is shutdown"); 648 } 649 return new SocketChannelOutputStream(channel); 650 } 651 652 @Override 653 public InputStream getInputStream() throws IOException { 654 checkOpenAndConnected(); 655 if (isInputShutdown()) { 656 throw new SocketException("Socket input is shutdown"); 657 } 658 return new SocketChannelInputStream(channel); 659 } 660 661 private void checkOpenAndConnected() throws SocketException { 662 if (!channel.isOpen()) { 663 throw new SocketException("Socket is closed"); 664 } 665 if (!channel.isConnected()) { 666 throw new SocketException("Socket is not connected"); 667 } 668 } 669 } 670 671 /* 672 * This output stream delegates all operations to the associated channel. 673 * Throws an IllegalBlockingModeException if the channel is in non-blocking 674 * mode when performing write operations. 675 */ 676 private static class SocketChannelOutputStream extends OutputStream { 677 private final SocketChannel channel; 678 679 public SocketChannelOutputStream(SocketChannel channel) { 680 this.channel = channel; 681 } 682 683 /* 684 * Closes this stream and channel. 685 * 686 * @exception IOException thrown if an error occurs during the close 687 */ 688 @Override 689 public void close() throws IOException { 690 channel.close(); 691 } 692 693 @Override 694 public void write(byte[] buffer, int offset, int count) throws IOException { 695 if (0 > offset || 0 > count || count + offset > buffer.length) { 696 throw new IndexOutOfBoundsException(); 697 } 698 ByteBuffer buf = ByteBuffer.wrap(buffer, offset, count); 699 if (!channel.isBlocking()) { 700 throw new IllegalBlockingModeException(); 701 } 702 channel.write(buf); 703 } 704 705 @Override 706 public void write(int oneByte) throws IOException { 707 if (!channel.isBlocking()) { 708 throw new IllegalBlockingModeException(); 709 } 710 ByteBuffer buffer = ByteBuffer.allocate(1); 711 buffer.put(0, (byte) (oneByte & 0xFF)); 712 channel.write(buffer); 713 } 714 } 715 716 /* 717 * This input stream delegates all operations to the associated channel. 718 * Throws an IllegalBlockingModeException if the channel is in non-blocking 719 * mode when performing read operations. 720 */ 721 private static class SocketChannelInputStream extends InputStream { 722 private final SocketChannel channel; 723 724 public SocketChannelInputStream(SocketChannel channel) { 725 this.channel = channel; 726 } 727 728 /* 729 * Closes this stream and channel. 730 */ 731 @Override 732 public void close() throws IOException { 733 channel.close(); 734 } 735 736 @Override 737 public int read() throws IOException { 738 if (!channel.isBlocking()) { 739 throw new IllegalBlockingModeException(); 740 } 741 ByteBuffer buf = ByteBuffer.allocate(1); 742 int result = channel.read(buf); 743 // BEGIN android-changed: input was already consumed 744 return (-1 == result) ? result : buf.get(0) & 0xFF; 745 // END android-changed 746 } 747 748 @Override 749 public int read(byte[] buffer, int offset, int count) throws IOException { 750 if (0 > offset || 0 > count || count + offset > buffer.length) { 751 throw new IndexOutOfBoundsException(); 752 } 753 if (!channel.isBlocking()) { 754 throw new IllegalBlockingModeException(); 755 } 756 ByteBuffer buf = ByteBuffer.wrap(buffer, offset, count); 757 return channel.read(buf); 758 } 759 } 760 } 761