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.IOException;
23import java.net.InetSocketAddress;
24import java.net.ServerSocket;
25import java.net.Socket;
26import java.net.SocketAddress;
27import java.net.SocketTimeoutException;
28import java.nio.channels.ClosedChannelException;
29import java.nio.channels.IllegalBlockingModeException;
30import java.nio.channels.NotYetBoundException;
31import java.nio.channels.ServerSocketChannel;
32import java.nio.channels.SocketChannel;
33import java.nio.channels.spi.SelectorProvider;
34import java.nio.channels.UnresolvedAddressException;
35import java.nio.channels.UnsupportedAddressTypeException;
36import java.util.Set;
37import libcore.io.IoUtils;
38import static android.system.OsConstants.*;
39
40/**
41 * The default ServerSocketChannel.
42 */
43final class ServerSocketChannelImpl extends ServerSocketChannel implements FileDescriptorChannel {
44
45    private final ServerSocketAdapter socket;
46
47    private final Object acceptLock = new Object();
48
49    public ServerSocketChannelImpl(SelectorProvider sp) throws IOException {
50        super(sp);
51        this.socket = new ServerSocketAdapter(this);
52    }
53
54    @Override public ServerSocket socket() {
55        return socket;
56    }
57
58    @Override
59    public SocketChannel accept() throws IOException {
60        if (!isOpen()) {
61            throw new ClosedChannelException();
62        }
63        if (!socket.isBound()) {
64            throw new NotYetBoundException();
65        }
66
67        // Create an empty socket channel. This will be populated by ServerSocketAdapter.implAccept.
68        SocketChannelImpl result = new SocketChannelImpl(provider(), false);
69        try {
70            begin();
71            synchronized (acceptLock) {
72                try {
73                    socket.implAccept(result);
74                } catch (SocketTimeoutException e) {
75                    if (shouldThrowSocketTimeoutExceptionFromAccept(e)) {
76                        throw e;
77                    }
78                    // Otherwise, this is a non-blocking socket and there's nothing ready, so we'll
79                    // fall through and return null.
80                }
81            }
82        } finally {
83            end(result.isConnected());
84        }
85        return result.isConnected() ? result : null;
86    }
87
88    private boolean shouldThrowSocketTimeoutExceptionFromAccept(SocketTimeoutException e) {
89        if (isBlocking()) {
90            return true;
91        }
92        Throwable cause = e.getCause();
93        if (cause instanceof ErrnoException) {
94            if (((ErrnoException) cause).errno == EAGAIN) {
95                return false;
96            }
97        }
98        return true;
99    }
100
101    @Override protected void implConfigureBlocking(boolean blocking) throws IOException {
102        IoUtils.setBlocking(socket.getFD$(), blocking);
103    }
104
105    @Override
106    synchronized protected void implCloseSelectableChannel() throws IOException {
107        if (!socket.isClosed()) {
108            socket.close();
109        }
110    }
111
112    @Override
113    public FileDescriptor getFD() {
114        return socket.getFD$();
115    }
116
117    private static class ServerSocketAdapter extends ServerSocket {
118        private final ServerSocketChannelImpl channelImpl;
119
120        ServerSocketAdapter(ServerSocketChannelImpl aChannelImpl) throws IOException {
121            this.channelImpl = aChannelImpl;
122        }
123
124        @Override public Socket accept() throws IOException {
125            if (!isBound()) {
126                throw new IllegalBlockingModeException();
127            }
128            SocketChannel sc = channelImpl.accept();
129            if (sc == null) {
130                throw new IllegalBlockingModeException();
131            }
132            return sc.socket();
133        }
134
135        public Socket implAccept(SocketChannelImpl clientSocketChannel) throws IOException {
136            Socket clientSocket = clientSocketChannel.socket();
137            boolean connectOK = false;
138            try {
139                synchronized (this) {
140                    super.implAccept(clientSocket);
141
142                    // Sync the client socket's associated channel state with the Socket and OS.
143                    InetSocketAddress remoteAddress =
144                            new InetSocketAddress(
145                                    clientSocket.getInetAddress(), clientSocket.getPort());
146                    clientSocketChannel.onAccept(remoteAddress, false /* updateSocketState */);
147                }
148                connectOK = true;
149            } finally {
150                if (!connectOK) {
151                    clientSocket.close();
152                }
153            }
154            return clientSocket;
155        }
156
157        @Override public ServerSocketChannel getChannel() {
158            return channelImpl;
159        }
160
161        @Override public void close() throws IOException {
162            synchronized (channelImpl) {
163                super.close();
164                if (channelImpl.isOpen()) {
165                    channelImpl.close();
166                }
167            }
168        }
169
170        private FileDescriptor getFD$() {
171            return super.getImpl$().getFD$();
172        }
173    }
174}
175