Home | History | Annotate | Download | only in nio
      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 java.nio;
     19 
     20 import android.system.ErrnoException;
     21 import java.io.FileDescriptor;
     22 import java.io.FilterInputStream;
     23 import java.io.FilterOutputStream;
     24 import java.io.InputStream;
     25 import java.io.IOException;
     26 import java.io.OutputStream;
     27 import java.net.ConnectException;
     28 import java.net.Inet4Address;
     29 import java.net.InetAddress;
     30 import java.net.InetSocketAddress;
     31 import java.net.PlainSocketImpl;
     32 import java.net.Socket;
     33 import java.net.SocketAddress;
     34 import java.net.SocketException;
     35 import java.net.SocketUtils;
     36 import java.nio.channels.AlreadyConnectedException;
     37 import java.nio.channels.ClosedChannelException;
     38 import java.nio.channels.ConnectionPendingException;
     39 import java.nio.channels.IllegalBlockingModeException;
     40 import java.nio.channels.NoConnectionPendingException;
     41 import java.nio.channels.NotYetConnectedException;
     42 import java.nio.channels.SocketChannel;
     43 import java.nio.channels.spi.SelectorProvider;
     44 import java.nio.channels.UnresolvedAddressException;
     45 import java.nio.channels.UnsupportedAddressTypeException;
     46 import java.util.Arrays;
     47 import java.util.Set;
     48 import libcore.io.IoBridge;
     49 import libcore.io.IoUtils;
     50 import libcore.io.Libcore;
     51 import static android.system.OsConstants.*;
     52 
     53 /*
     54  * The default implementation class of java.nio.channels.SocketChannel.
     55  */
     56 class 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