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                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