1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.nio;
19
20import android.system.ErrnoException;
21import java.io.FileDescriptor;
22import java.io.FilterInputStream;
23import java.io.FilterOutputStream;
24import java.io.InputStream;
25import java.io.IOException;
26import java.io.OutputStream;
27import java.net.ConnectException;
28import java.net.Inet4Address;
29import java.net.InetAddress;
30import java.net.InetSocketAddress;
31import java.net.PlainSocketImpl;
32import java.net.Socket;
33import java.net.SocketAddress;
34import java.net.SocketException;
35import java.net.SocketUtils;
36import java.nio.channels.AlreadyConnectedException;
37import java.nio.channels.ClosedChannelException;
38import java.nio.channels.ConnectionPendingException;
39import java.nio.channels.IllegalBlockingModeException;
40import java.nio.channels.NoConnectionPendingException;
41import java.nio.channels.NotYetConnectedException;
42import java.nio.channels.SocketChannel;
43import java.nio.channels.spi.SelectorProvider;
44import java.nio.channels.UnresolvedAddressException;
45import java.nio.channels.UnsupportedAddressTypeException;
46import java.util.Arrays;
47import java.util.Set;
48import libcore.io.IoBridge;
49import libcore.io.IoUtils;
50import libcore.io.Libcore;
51import static android.system.OsConstants.*;
52
53/*
54 * The default implementation class of java.nio.channels.SocketChannel.
55 */
56class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
57    private static final int SOCKET_STATUS_UNINITIALIZED = -1;
58
59    // Status before connect.
60    private static final int SOCKET_STATUS_UNCONNECTED = 0;
61
62    // Status connection pending.
63    private static final int SOCKET_STATUS_PENDING = 1;
64
65    // Status after connection success.
66    private static final int SOCKET_STATUS_CONNECTED = 2;
67
68    // Status closed.
69    private static final int SOCKET_STATUS_CLOSED = 3;
70
71    private final FileDescriptor fd;
72
73    // Our internal Socket.
74    private SocketAdapter socket = null;
75
76    // The address to be connected.
77    private InetSocketAddress connectAddress = null;
78
79    // The local address the socket is bound to.
80    private InetAddress localAddress = null;
81    private int localPort;
82
83    private int status = SOCKET_STATUS_UNINITIALIZED;
84
85    // Whether the socket is bound.
86    private volatile boolean isBound = false;
87
88    private final Object readLock = new Object();
89
90    private final Object writeLock = new Object();
91
92    /*
93     * Constructor for creating a connected socket channel.
94     */
95    public SocketChannelImpl(SelectorProvider selectorProvider) throws IOException {
96        this(selectorProvider, true);
97    }
98
99    /*
100     * Constructor for creating an optionally connected socket channel.
101     */
102    public SocketChannelImpl(SelectorProvider selectorProvider, boolean connect) throws IOException {
103        super(selectorProvider);
104        status = SOCKET_STATUS_UNCONNECTED;
105        fd = (connect ? IoBridge.socket(true) : new FileDescriptor());
106    }
107
108    /*
109     * Constructor for use by Pipe.SinkChannel and Pipe.SourceChannel.
110     */
111    public SocketChannelImpl(SelectorProvider selectorProvider, FileDescriptor existingFd) throws IOException {
112        super(selectorProvider);
113        status = SOCKET_STATUS_CONNECTED;
114        fd = existingFd;
115    }
116
117    /*
118     * Getting the internal Socket If we have not the socket, we create a new
119     * one.
120     */
121    @Override
122    synchronized public Socket socket() {
123        if (socket == null) {
124            try {
125                InetAddress addr = null;
126                int port = 0;
127                if (connectAddress != null) {
128                    addr = connectAddress.getAddress();
129                    port = connectAddress.getPort();
130                }
131                socket = new SocketAdapter(new PlainSocketImpl(fd, localPort, addr, port), this);
132            } catch (SocketException e) {
133                return null;
134            }
135        }
136        return socket;
137    }
138
139    /**
140     * Initialise the isBound, localAddress and localPort state from the file descriptor. Used when
141     * some or all of the bound state has been left to the OS to decide, or when the Socket handled
142     * bind() or connect().
143     *
144     * @param updateSocketState
145     *      if the associated socket (if present) needs to be updated
146     * @hide package visible for other nio classes
147     */
148    void onBind(boolean updateSocketState) {
149        SocketAddress sa;
150        try {
151            sa = Libcore.os.getsockname(fd);
152        } catch (ErrnoException errnoException) {
153            throw new AssertionError(errnoException);
154        }
155        isBound = true;
156        InetSocketAddress localSocketAddress = (InetSocketAddress) sa;
157        localAddress = localSocketAddress.getAddress();
158        localPort = localSocketAddress.getPort();
159        if (updateSocketState && socket != null) {
160            socket.onBind(localAddress, localPort);
161        }
162    }
163
164    @Override
165    synchronized public boolean isConnected() {
166        return status == SOCKET_STATUS_CONNECTED;
167    }
168
169    @Override
170    synchronized public boolean isConnectionPending() {
171        return status == SOCKET_STATUS_PENDING;
172    }
173
174    @Override
175    public boolean connect(SocketAddress socketAddress) throws IOException {
176        // status must be open and unconnected
177        checkUnconnected();
178
179        // check the address
180        InetSocketAddress inetSocketAddress = validateAddress(socketAddress);
181        InetAddress normalAddr = inetSocketAddress.getAddress();
182        int port = inetSocketAddress.getPort();
183
184        // When connecting, map ANY address to localhost
185        if (normalAddr.isAnyLocalAddress()) {
186            normalAddr = InetAddress.getLocalHost();
187        }
188
189        boolean isBlocking = isBlocking();
190        boolean finished = false;
191        int newStatus;
192        try {
193            if (isBlocking) {
194                begin();
195            }
196            // When in blocking mode, IoBridge.connect() will return without an exception when the
197            // socket is connected. When in non-blocking mode it will return without an exception
198            // without knowing the result of the connection attempt, which could still be going on.
199            IoBridge.connect(fd, normalAddr, port);
200            newStatus = isBlocking ? SOCKET_STATUS_CONNECTED : SOCKET_STATUS_PENDING;
201            finished = true;
202        } catch (IOException e) {
203            if (isEINPROGRESS(e)) {
204                newStatus = SOCKET_STATUS_PENDING;
205            } else {
206                if (isOpen()) {
207                    close();
208                    finished = true;
209                }
210                throw e;
211            }
212        } finally {
213            if (isBlocking) {
214                end(finished);
215            }
216        }
217
218        // If the channel was not bound, a connection attempt will have caused an implicit bind() to
219        // take place. Keep the local address state held by the channel and the socket up to date.
220        if (!isBound) {
221            onBind(true /* updateSocketState */);
222        }
223
224        // Keep the connected state held by the channel and the socket up to date.
225        onConnectStatusChanged(inetSocketAddress, newStatus, true /* updateSocketState */);
226
227        return status == SOCKET_STATUS_CONNECTED;
228    }
229
230    /**
231     * Initialise the connect() state with the supplied information.
232     *
233     * @param updateSocketState
234     *     if the associated socket (if present) needs to be updated
235     * @hide package visible for other nio classes
236     */
237    void onConnectStatusChanged(InetSocketAddress address, int status, boolean updateSocketState) {
238        this.status = status;
239        connectAddress = address;
240        if (status == SOCKET_STATUS_CONNECTED && updateSocketState && socket != null) {
241            socket.onConnect(connectAddress.getAddress(), connectAddress.getPort());
242        }
243    }
244
245    private boolean isEINPROGRESS(IOException e) {
246        if (isBlocking()) {
247            return false;
248        }
249        if (e instanceof ConnectException) {
250            Throwable cause = e.getCause();
251            if (cause instanceof ErrnoException) {
252                return ((ErrnoException) cause).errno == EINPROGRESS;
253            }
254        }
255        return false;
256    }
257
258    @Override
259    public boolean finishConnect() throws IOException {
260        synchronized (this) {
261            if (!isOpen()) {
262                throw new ClosedChannelException();
263            }
264            if (status == SOCKET_STATUS_CONNECTED) {
265                return true;
266            }
267            if (status != SOCKET_STATUS_PENDING) {
268                throw new NoConnectionPendingException();
269            }
270        }
271
272        boolean finished = false;
273        try {
274            begin();
275            InetAddress inetAddress = connectAddress.getAddress();
276            int port = connectAddress.getPort();
277            finished = IoBridge.isConnected(fd, inetAddress, port, 0, 0); // Return immediately.
278        } catch (ConnectException e) {
279            if (isOpen()) {
280                close();
281                finished = true;
282            }
283            throw e;
284        } finally {
285            end(finished);
286        }
287
288        synchronized (this) {
289            status = (finished ? SOCKET_STATUS_CONNECTED : status);
290            if (finished && socket != null) {
291                socket.onConnect(connectAddress.getAddress(), connectAddress.getPort());
292            }
293        }
294        return finished;
295    }
296
297    @Override
298    public int read(ByteBuffer dst) throws IOException {
299        dst.checkWritable();
300        checkOpenConnected();
301        if (!dst.hasRemaining()) {
302            return 0;
303        }
304        return readImpl(dst);
305    }
306
307    @Override
308    public long read(ByteBuffer[] targets, int offset, int length) throws IOException {
309        Arrays.checkOffsetAndCount(targets.length, offset, length);
310        checkOpenConnected();
311        int totalCount = FileChannelImpl.calculateTotalRemaining(targets, offset, length, true);
312        if (totalCount == 0) {
313            return 0;
314        }
315        byte[] readArray = new byte[totalCount];
316        ByteBuffer readBuffer = ByteBuffer.wrap(readArray);
317        int readCount;
318        // read data to readBuffer, and then transfer data from readBuffer to targets.
319        readCount = readImpl(readBuffer);
320        readBuffer.flip();
321        if (readCount > 0) {
322            int left = readCount;
323            int index = offset;
324            // transfer data from readArray to targets
325            while (left > 0) {
326                int putLength = Math.min(targets[index].remaining(), left);
327                targets[index].put(readArray, readCount - left, putLength);
328                index++;
329                left -= putLength;
330            }
331        }
332        return readCount;
333    }
334
335    private int readImpl(ByteBuffer dst) throws IOException {
336        synchronized (readLock) {
337            int readCount = 0;
338            try {
339                if (isBlocking()) {
340                    begin();
341                }
342                readCount = IoBridge.recvfrom(true, fd, dst, 0, null, false);
343            } finally {
344                if (isBlocking()) {
345                    end(readCount > 0);
346                }
347            }
348            return readCount;
349        }
350    }
351
352    @Override
353    public int write(ByteBuffer src) throws IOException {
354        if (src == null) {
355            throw new NullPointerException("src == null");
356        }
357        checkOpenConnected();
358        if (!src.hasRemaining()) {
359            return 0;
360        }
361        return writeImpl(src);
362    }
363
364    @Override
365    public long write(ByteBuffer[] sources, int offset, int length) throws IOException {
366        Arrays.checkOffsetAndCount(sources.length, offset, length);
367        checkOpenConnected();
368        int count = FileChannelImpl.calculateTotalRemaining(sources, offset, length, false);
369        if (count == 0) {
370            return 0;
371        }
372        ByteBuffer writeBuf = ByteBuffer.allocate(count);
373        for (int val = offset; val < length + offset; val++) {
374            ByteBuffer source = sources[val];
375            int oldPosition = source.position();
376            writeBuf.put(source);
377            source.position(oldPosition);
378        }
379        writeBuf.flip();
380        int result = writeImpl(writeBuf);
381        int val = offset;
382        int written = result;
383        while (result > 0) {
384            ByteBuffer source = sources[val];
385            int gap = Math.min(result, source.remaining());
386            source.position(source.position() + gap);
387            val++;
388            result -= gap;
389        }
390        return written;
391    }
392
393    private int writeImpl(ByteBuffer src) throws IOException {
394        synchronized (writeLock) {
395            if (!src.hasRemaining()) {
396                return 0;
397            }
398            int writeCount = 0;
399            try {
400                if (isBlocking()) {
401                    begin();
402                }
403                writeCount = IoBridge.sendto(fd, src, 0, null, 0);
404            } finally {
405                if (isBlocking()) {
406                    end(writeCount >= 0);
407                }
408            }
409            return writeCount;
410        }
411    }
412
413    /*
414     * Status check, open and "connected", when read and write.
415     */
416    synchronized private void checkOpenConnected() throws ClosedChannelException {
417        if (!isOpen()) {
418            throw new ClosedChannelException();
419        }
420        if (!isConnected()) {
421            throw new NotYetConnectedException();
422        }
423    }
424
425    /*
426     * Status check, open and "unconnected", before connection.
427     */
428    synchronized private void checkUnconnected() throws IOException {
429        if (!isOpen()) {
430            throw new ClosedChannelException();
431        }
432        if (status == SOCKET_STATUS_CONNECTED) {
433            throw new AlreadyConnectedException();
434        }
435        if (status == SOCKET_STATUS_PENDING) {
436            throw new ConnectionPendingException();
437        }
438    }
439
440    /*
441     * Shared by this class and DatagramChannelImpl, to do the address transfer
442     * and check.
443     */
444    static InetSocketAddress validateAddress(SocketAddress socketAddress) {
445        if (socketAddress == null) {
446            throw new IllegalArgumentException("socketAddress == null");
447        }
448        if (!(socketAddress instanceof InetSocketAddress)) {
449            throw new UnsupportedAddressTypeException();
450        }
451        InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
452        if (inetSocketAddress.isUnresolved()) {
453            throw new UnresolvedAddressException();
454        }
455        return inetSocketAddress;
456    }
457
458    /*
459     * Do really closing action here.
460     */
461    @Override
462    protected synchronized void implCloseSelectableChannel() throws IOException {
463        if (status != SOCKET_STATUS_CLOSED) {
464            status = SOCKET_STATUS_CLOSED;
465            // IoBridge.closeAndSignalBlockedThreads(fd) is idempotent: It is safe to call on an
466            // already-closed file descriptor.
467            IoBridge.closeAndSignalBlockedThreads(fd);
468            if (socket != null && !socket.isClosed()) {
469                socket.onClose();
470            }
471        }
472    }
473
474    @Override protected void implConfigureBlocking(boolean blocking) throws IOException {
475        IoUtils.setBlocking(fd, blocking);
476    }
477
478    /*
479     * Get the fd.
480     */
481    public FileDescriptor getFD() {
482        return fd;
483    }
484
485    /* @hide used by ServerSocketChannelImpl to sync channel state during accept() */
486    public void onAccept(InetSocketAddress remoteAddress, boolean updateSocketState) {
487        onBind(updateSocketState);
488        onConnectStatusChanged(remoteAddress, SOCKET_STATUS_CONNECTED, updateSocketState);
489    }
490
491    /*
492     * Adapter classes for internal socket.
493     */
494    private static class SocketAdapter extends Socket {
495        private final SocketChannelImpl channel;
496        private final PlainSocketImpl socketImpl;
497
498        SocketAdapter(PlainSocketImpl socketImpl, SocketChannelImpl channel)
499                throws SocketException {
500            super(socketImpl);
501            this.socketImpl = socketImpl;
502            this.channel = channel;
503            SocketUtils.setCreated(this);
504
505            // Sync state socket state with the channel it is being created from
506            if (channel.isBound) {
507                onBind(channel.localAddress, channel.localPort);
508            }
509            if (channel.isConnected()) {
510                onConnect(channel.connectAddress.getAddress(), channel.connectAddress.getPort());
511            }
512            if (!channel.isOpen()) {
513                onClose();
514            }
515
516        }
517
518        @Override
519        public SocketChannel getChannel() {
520            return channel;
521        }
522
523        @Override
524        public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
525            if (!channel.isBlocking()) {
526                throw new IllegalBlockingModeException();
527            }
528            if (isConnected()) {
529                throw new AlreadyConnectedException();
530            }
531            super.connect(remoteAddr, timeout);
532            channel.onBind(false);
533            if (super.isConnected()) {
534                InetSocketAddress remoteInetAddress = (InetSocketAddress) remoteAddr;
535                channel.onConnectStatusChanged(
536                        remoteInetAddress, SOCKET_STATUS_CONNECTED, false /* updateSocketState */);
537            }
538        }
539
540        @Override
541        public void bind(SocketAddress localAddr) throws IOException {
542            if (channel.isConnected()) {
543                throw new AlreadyConnectedException();
544            }
545            if (SocketChannelImpl.SOCKET_STATUS_PENDING == channel.status) {
546                throw new ConnectionPendingException();
547            }
548            super.bind(localAddr);
549            channel.onBind(false);
550        }
551
552        @Override
553        public void close() throws IOException {
554            synchronized (channel) {
555                super.close();
556                if (channel.isOpen()) {
557                    // channel.close() recognizes the socket is closed and avoids recursion. There
558                    // is no channel.onClose() because the "closed" field is private.
559                    channel.close();
560                }
561            }
562        }
563
564        @Override
565        public OutputStream getOutputStream() throws IOException {
566            return new BlockingCheckOutputStream(super.getOutputStream(), channel);
567        }
568
569        @Override
570        public InputStream getInputStream() throws IOException {
571            return new BlockingCheckInputStream(super.getInputStream(), channel);
572        }
573
574        @Override
575        public FileDescriptor getFileDescriptor$() {
576            return socketImpl.getFD$();
577        }
578    }
579
580    /*
581     * Throws an IllegalBlockingModeException if the channel is in non-blocking
582     * mode when performing write operations.
583     */
584    private static class BlockingCheckOutputStream extends FilterOutputStream {
585        private final SocketChannel channel;
586
587        public BlockingCheckOutputStream(OutputStream out, SocketChannel channel) {
588            super(out);
589            this.channel = channel;
590        }
591
592        @Override
593        public void write(byte[] buffer, int offset, int byteCount) throws IOException {
594            checkBlocking();
595            out.write(buffer, offset, byteCount);
596        }
597
598        @Override
599        public void write(int oneByte) throws IOException {
600            checkBlocking();
601            out.write(oneByte);
602        }
603
604        @Override
605        public void write(byte[] buffer) throws IOException {
606            checkBlocking();
607            out.write(buffer);
608        }
609
610        @Override
611        public void close() throws IOException {
612            super.close();
613            // channel.close() recognizes the socket is closed and avoids recursion. There is no
614            // channel.onClose() because the "closed" field is private.
615            channel.close();
616        }
617
618        private void checkBlocking() {
619            if (!channel.isBlocking()) {
620                throw new IllegalBlockingModeException();
621            }
622        }
623    }
624
625    /*
626     * Throws an IllegalBlockingModeException if the channel is in non-blocking
627     * mode when performing read operations.
628     */
629    private static class BlockingCheckInputStream extends FilterInputStream {
630        private final SocketChannel channel;
631
632        public BlockingCheckInputStream(InputStream in, SocketChannel channel) {
633            super(in);
634            this.channel = channel;
635        }
636
637        @Override
638        public int read() throws IOException {
639            checkBlocking();
640            return in.read();
641        }
642
643        @Override
644        public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
645            checkBlocking();
646            return in.read(buffer, byteOffset, byteCount);
647        }
648
649        @Override
650        public int read(byte[] buffer) throws IOException {
651            checkBlocking();
652            return in.read(buffer);
653        }
654
655        @Override
656        public void close() throws IOException {
657            super.close();
658            // channel.close() recognizes the socket is closed and avoids recursion. There is no
659            // channel.onClose() because the "closed" field is private.
660            channel.close();
661        }
662
663        private void checkBlocking() {
664            if (!channel.isBlocking()) {
665                throw new IllegalBlockingModeException();
666            }
667        }
668    }
669}
670