Home | History | Annotate | Download | only in internal
      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