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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 package org.apache.harmony.nio.internal;
21 // In this class the address length was changed from long to int.
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;
55 * The default implementation class of java.nio.channels.SocketChannel.
57 class SocketChannelImpl extends SocketChannel implements FileDescriptorHandler {
59 private static final int EOF = -1;
61 // The singleton to do the native network operation.
62 static final INetworkSystem networkSystem = Platform.getNetworkSystem();
64 // Status un-init, not initialized.
65 static final int SOCKET_STATUS_UNINIT = EOF;
67 // Status before connect.
68 static final int SOCKET_STATUS_UNCONNECTED = 0;
70 // Status connection pending.
71 static final int SOCKET_STATUS_PENDING = 1;
73 // Status after connection success.
74 static final int SOCKET_STATUS_CONNECTED = 2;
77 static final int SOCKET_STATUS_CLOSED = 3;
79 // The descriptor to interact with native code.
82 // Our internal Socket.
83 private SocketAdapter socket = null;
85 // The address to be connected.
86 InetSocketAddress connectAddress = null;
88 // Local address of the this socket (package private for adapter).
89 InetAddress localAddress = null;
94 // At first, uninitialized.
95 int status = SOCKET_STATUS_UNINIT;
97 // Whether the socket is bound.
98 volatile boolean isBound = false;
100 private static class ReadLock {}
101 private final Object readLock = new ReadLock();
103 private static class WriteLock {}
104 private final Object writeLock = new WriteLock();
107 * Constructor for creating a connected socket channel.
109 public SocketChannelImpl(SelectorProvider selectorProvider) throws IOException {
110 this(selectorProvider, true);
114 * Constructor for creating an optionally connected socket channel.
116 public SocketChannelImpl(SelectorProvider selectorProvider, boolean connect) throws IOException {
117 super(selectorProvider);
118 fd = new FileDescriptor();
119 status = SOCKET_STATUS_UNCONNECTED;
121 networkSystem.socket(fd, true);
126 * Getting the internal Socket If we have not the socket, we create a new
130 synchronized public Socket socket() {
131 if (socket == null) {
133 InetAddress addr = null;
135 if (connectAddress != null) {
136 addr = connectAddress.getAddress();
137 port = connectAddress.getPort();
139 socket = new SocketAdapter(new PlainSocketImpl(fd, localPort, addr, port), this);
140 } catch (SocketException e) {
148 synchronized public boolean isConnected() {
149 return status == SOCKET_STATUS_CONNECTED;
153 * Status setting used by other class.
155 synchronized void setConnected() {
156 status = SOCKET_STATUS_CONNECTED;
159 void setBound(boolean flag) {
164 synchronized public boolean isConnectionPending() {
165 return status == SOCKET_STATUS_PENDING;
169 public boolean connect(SocketAddress socketAddress) throws IOException {
170 // status must be open and unconnected
174 InetSocketAddress inetSocketAddress = validateAddress(socketAddress);
175 InetAddress normalAddr = inetSocketAddress.getAddress();
177 // When connecting, map ANY address to Localhost
178 if (normalAddr.isAnyLocalAddress()) {
179 normalAddr = InetAddress.getLocalHost();
182 int port = inetSocketAddress.getPort();
183 String hostName = normalAddr.getHostName();
185 SecurityManager sm = System.getSecurityManager();
187 sm.checkConnect(hostName, port);
192 boolean finished = false;
197 networkSystem.connect(fd, normalAddr, port, 0);
198 finished = true; // Or we'd have thrown an exception.
200 finished = networkSystem.connectNonBlocking(fd, normalAddr, port);
201 // set back to nonblocking to work around with a bug in portlib
203 IoUtils.setBlocking(fd, false);
207 } catch (IOException e) {
208 if (e instanceof ConnectException && !isBlocking()) {
209 status = SOCKET_STATUS_PENDING;
223 initLocalAddressAndPort();
224 connectAddress = inetSocketAddress;
225 if (socket != null) {
226 socket.socketImpl().initRemoteAddressAndPort(connectAddress.getAddress(),
227 connectAddress.getPort());
230 synchronized (this) {
232 status = (finished ? SOCKET_STATUS_CONNECTED : SOCKET_STATUS_UNCONNECTED);
234 status = SOCKET_STATUS_PENDING;
240 private void initLocalAddressAndPort() {
241 localAddress = networkSystem.getSocketLocalAddress(fd);
242 localPort = networkSystem.getSocketLocalPort(fd);
243 if (socket != null) {
244 socket.socketImpl().initLocalPort(localPort);
249 public boolean finishConnect() throws IOException {
251 synchronized (this) {
253 throw new ClosedChannelException();
255 if (status == SOCKET_STATUS_CONNECTED) {
258 if (status != SOCKET_STATUS_PENDING) {
259 throw new NoConnectionPendingException();
263 boolean finished = false;
266 final int WAIT_FOREVER = -1;
268 finished = networkSystem.isConnected(fd, isBlocking() ? WAIT_FOREVER : POLL);
270 initLocalAddressAndPort();
271 } catch (ConnectException e) {
281 synchronized (this) {
282 status = (finished ? SOCKET_STATUS_CONNECTED : status);
284 // TPE: Workaround for bug that turns socket back to blocking
285 if (!isBlocking()) implConfigureBlocking(false);
290 void finishAccept() {
291 initLocalAddressAndPort();
295 public int read(ByteBuffer target) throws IOException {
296 FileChannelImpl.checkWritable(target);
297 checkOpenConnected();
298 if (!target.hasRemaining()) {
303 if (target.isDirect() || target.hasArray()) {
304 readCount = readImpl(target);
306 target.position(target.position() + readCount);
309 ByteBuffer readBuffer = null;
310 byte[] readArray = null;
311 readArray = new byte[target.remaining()];
312 readBuffer = ByteBuffer.wrap(readArray);
313 readCount = readImpl(readBuffer);
315 target.put(readArray, 0, readCount);
322 public long read(ByteBuffer[] targets, int offset, int length) throws IOException {
323 if (!isIndexValid(targets, offset, length)) {
324 throw new IndexOutOfBoundsException();
327 checkOpenConnected();
328 int totalCount = FileChannelImpl.calculateTotalRemaining(targets, offset, length, true);
329 if (totalCount == 0) {
332 byte[] readArray = new byte[totalCount];
333 ByteBuffer readBuffer = ByteBuffer.wrap(readArray);
335 // read data to readBuffer, and then transfer data from readBuffer to
337 readCount = readImpl(readBuffer);
339 int left = readCount;
341 // transfer data from readArray to targets
343 int putLength = Math.min(targets[index].remaining(), left);
344 targets[index].put(readArray, readCount - left, putLength);
352 private boolean isIndexValid(ByteBuffer[] targets, int offset, int length) {
353 return (length >= 0) && (offset >= 0)
354 && ((long) length + (long) offset <= targets.length);
358 * Read from channel, and store the result in the target.
363 private int readImpl(ByteBuffer target) throws IOException {
364 synchronized (readLock) {
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
379 // target is assured to have array.
380 byte[] array = target.array();
381 offset += target.arrayOffset();
382 readCount = networkSystem.read(fd, array, offset, length);
394 public int write(ByteBuffer source) throws IOException {
395 if (null == source) {
396 throw new NullPointerException();
398 checkOpenConnected();
399 if (!source.hasRemaining()) {
402 return writeImpl(source);
406 public long write(ByteBuffer[] sources, int offset, int length) throws IOException {
407 if (!isIndexValid(sources, offset, length)) {
408 throw new IndexOutOfBoundsException();
411 checkOpenConnected();
412 int count = FileChannelImpl.calculateTotalRemaining(sources, offset, length, false);
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);
424 int result = writeImpl(writeBuf);
426 int written = result;
428 ByteBuffer source = sources[val];
429 int gap = Math.min(result, source.remaining());
430 source.position(source.position() + gap);
438 * Write the source. return the count of bytes written.
440 private int writeImpl(ByteBuffer source) throws IOException {
441 synchronized (writeLock) {
442 if (!source.hasRemaining()) {
447 int pos = source.position();
448 int length = source.remaining();
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);
459 byte[] array = new byte[length];
461 writeCount = networkSystem.write(fd, array, 0, length);
463 source.position(pos + writeCount);
466 end(writeCount >= 0);
474 * Status check, open and "connected", when read and write.
476 synchronized private void checkOpenConnected() throws ClosedChannelException {
478 throw new ClosedChannelException();
480 if (!isConnected()) {
481 throw new NotYetConnectedException();
486 * Status check, open and "unconnected", before connection.
488 synchronized private void checkUnconnected() throws IOException {
490 throw new ClosedChannelException();
492 if (status == SOCKET_STATUS_CONNECTED) {
493 throw new AlreadyConnectedException();
495 if (status == SOCKET_STATUS_PENDING) {
496 throw new ConnectionPendingException();
501 * Shared by this class and DatagramChannelImpl, to do the address transfer
504 static InetSocketAddress validateAddress(SocketAddress socketAddress) {
505 if (null == socketAddress) {
506 throw new IllegalArgumentException();
508 if (!(socketAddress instanceof InetSocketAddress)) {
509 throw new UnsupportedAddressTypeException();
511 InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
512 if (inetSocketAddress.isUnresolved()) {
513 throw new UnresolvedAddressException();
515 return inetSocketAddress;
521 public InetAddress getLocalAddress() throws UnknownHostException {
523 byte[] any_bytes = { 0, 0, 0, 0 };
524 return InetAddress.getByAddress(any_bytes);
530 * Do really closing action here.
533 synchronized protected void implCloseSelectableChannel() throws IOException {
534 if (SOCKET_STATUS_CLOSED != status) {
535 status = SOCKET_STATUS_CLOSED;
536 if (null != socket && !socket.isClosed()) {
539 networkSystem.close(fd);
545 protected void implConfigureBlocking(boolean blockMode) throws IOException {
546 synchronized (blockingLock()) {
547 IoUtils.setBlocking(fd, blockMode);
554 public FileDescriptor getFD() {
559 * Adapter classes for internal socket.
561 private static class SocketAdapter extends Socket {
562 private final SocketChannelImpl channel;
563 private final PlainSocketImpl socketImpl;
565 SocketAdapter(PlainSocketImpl socketImpl, SocketChannelImpl channel) throws SocketException {
567 this.socketImpl = socketImpl;
568 this.channel = channel;
569 SocketUtils.setCreated(this);
572 PlainSocketImpl socketImpl() {
577 public SocketChannel getChannel() {
582 public boolean isBound() {
583 return channel.isBound;
587 public boolean isConnected() {
588 return channel.isConnected();
592 public InetAddress getLocalAddress() {
594 return channel.getLocalAddress();
595 } catch (UnknownHostException e) {
601 public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
602 if (!channel.isBlocking()) {
603 throw new IllegalBlockingModeException();
606 throw new AlreadyConnectedException();
608 super.connect(remoteAddr, timeout);
609 channel.initLocalAddressAndPort();
610 if (super.isConnected()) {
611 channel.setConnected();
612 channel.isBound = super.isBound();
617 public void bind(SocketAddress localAddr) throws IOException {
618 if (channel.isConnected()) {
619 throw new AlreadyConnectedException();
621 if (SocketChannelImpl.SOCKET_STATUS_PENDING == channel.status) {
622 throw new ConnectionPendingException();
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;
632 public void close() throws IOException {
633 synchronized (channel) {
634 if (channel.isOpen()) {
639 channel.status = SocketChannelImpl.SOCKET_STATUS_CLOSED;
644 public OutputStream getOutputStream() throws IOException {
645 checkOpenAndConnected();
646 if (isOutputShutdown()) {
647 throw new SocketException("Socket output is shutdown");
649 return new SocketChannelOutputStream(channel);
653 public InputStream getInputStream() throws IOException {
654 checkOpenAndConnected();
655 if (isInputShutdown()) {
656 throw new SocketException("Socket input is shutdown");
658 return new SocketChannelInputStream(channel);
661 private void checkOpenAndConnected() throws SocketException {
662 if (!channel.isOpen()) {
663 throw new SocketException("Socket is closed");
665 if (!channel.isConnected()) {
666 throw new SocketException("Socket is not connected");
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.
676 private static class SocketChannelOutputStream extends OutputStream {
677 private final SocketChannel channel;
679 public SocketChannelOutputStream(SocketChannel channel) {
680 this.channel = channel;
684 * Closes this stream and channel.
686 * @exception IOException thrown if an error occurs during the close
689 public void close() throws IOException {
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();
698 ByteBuffer buf = ByteBuffer.wrap(buffer, offset, count);
699 if (!channel.isBlocking()) {
700 throw new IllegalBlockingModeException();
706 public void write(int oneByte) throws IOException {
707 if (!channel.isBlocking()) {
708 throw new IllegalBlockingModeException();
710 ByteBuffer buffer = ByteBuffer.allocate(1);
711 buffer.put(0, (byte) (oneByte & 0xFF));
712 channel.write(buffer);
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.
721 private static class SocketChannelInputStream extends InputStream {
722 private final SocketChannel channel;
724 public SocketChannelInputStream(SocketChannel channel) {
725 this.channel = channel;
729 * Closes this stream and channel.
732 public void close() throws IOException {
737 public int read() throws IOException {
738 if (!channel.isBlocking()) {
739 throw new IllegalBlockingModeException();
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
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();
753 if (!channel.isBlocking()) {
754 throw new IllegalBlockingModeException();
756 ByteBuffer buf = ByteBuffer.wrap(buffer, offset, count);
757 return channel.read(buf);