1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net;
18
19import java.io.FileDescriptor;
20import java.io.IOException;
21import java.io.InputStream;
22import java.io.OutputStream;
23import java.net.SocketOptions;
24
25/**
26 * Creates a (non-server) socket in the UNIX-domain namespace. The interface
27 * here is not entirely unlike that of java.net.Socket
28 */
29public class LocalSocket {
30
31    private LocalSocketImpl impl;
32    private volatile boolean implCreated;
33    private LocalSocketAddress localAddress;
34    private boolean isBound;
35    private boolean isConnected;
36
37    /**
38     * Creates a AF_LOCAL/UNIX domain stream socket.
39     */
40    public LocalSocket() {
41        this(new LocalSocketImpl());
42        isBound = false;
43        isConnected = false;
44    }
45
46    /**
47     * for use with AndroidServerSocket
48     * @param impl a SocketImpl
49     */
50    /*package*/ LocalSocket(LocalSocketImpl impl) {
51        this.impl = impl;
52        this.isConnected = false;
53        this.isBound = false;
54    }
55
56    /** {@inheritDoc} */
57    @Override
58    public String toString() {
59        return super.toString() + " impl:" + impl;
60    }
61
62    /**
63     * It's difficult to discern from the spec when impl.create() should be
64     * called, but it seems like a reasonable rule is "as soon as possible,
65     * but not in a context where IOException cannot be thrown"
66     *
67     * @throws IOException from SocketImpl.create()
68     */
69    private void implCreateIfNeeded() throws IOException {
70        if (!implCreated) {
71            synchronized (this) {
72                if (!implCreated) {
73                    try {
74                        impl.create(true);
75                    } finally {
76                        implCreated = true;
77                    }
78                }
79            }
80        }
81    }
82
83    /**
84     * Connects this socket to an endpoint. May only be called on an instance
85     * that has not yet been connected.
86     *
87     * @param endpoint endpoint address
88     * @throws IOException if socket is in invalid state or the address does
89     * not exist.
90     */
91    public void connect(LocalSocketAddress endpoint) throws IOException {
92        synchronized (this) {
93            if (isConnected) {
94                throw new IOException("already connected");
95            }
96
97            implCreateIfNeeded();
98            impl.connect(endpoint, 0);
99            isConnected = true;
100            isBound = true;
101        }
102    }
103
104    /**
105     * Binds this socket to an endpoint name. May only be called on an instance
106     * that has not yet been bound.
107     *
108     * @param bindpoint endpoint address
109     * @throws IOException
110     */
111    public void bind(LocalSocketAddress bindpoint) throws IOException {
112        implCreateIfNeeded();
113
114        synchronized (this) {
115            if (isBound) {
116                throw new IOException("already bound");
117            }
118
119            localAddress = bindpoint;
120            impl.bind(localAddress);
121            isBound = true;
122        }
123    }
124
125    /**
126     * Retrieves the name that this socket is bound to, if any.
127     *
128     * @return Local address or null if anonymous
129     */
130    public LocalSocketAddress getLocalSocketAddress() {
131        return localAddress;
132    }
133
134    /**
135     * Retrieves the input stream for this instance.
136     *
137     * @return input stream
138     * @throws IOException if socket has been closed or cannot be created.
139     */
140    public InputStream getInputStream() throws IOException {
141        implCreateIfNeeded();
142        return impl.getInputStream();
143    }
144
145    /**
146     * Retrieves the output stream for this instance.
147     *
148     * @return output stream
149     * @throws IOException if socket has been closed or cannot be created.
150     */
151    public OutputStream getOutputStream() throws IOException {
152        implCreateIfNeeded();
153        return impl.getOutputStream();
154    }
155
156    /**
157     * Closes the socket.
158     *
159     * @throws IOException
160     */
161    public void close() throws IOException {
162        implCreateIfNeeded();
163        impl.close();
164    }
165
166    /**
167     * Shuts down the input side of the socket.
168     *
169     * @throws IOException
170     */
171    public void shutdownInput() throws IOException {
172        implCreateIfNeeded();
173        impl.shutdownInput();
174    }
175
176    /**
177     * Shuts down the output side of the socket.
178     *
179     * @throws IOException
180     */
181    public void shutdownOutput() throws IOException {
182        implCreateIfNeeded();
183        impl.shutdownOutput();
184    }
185
186    public void setReceiveBufferSize(int size) throws IOException {
187        impl.setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size));
188    }
189
190    public int getReceiveBufferSize() throws IOException {
191        return ((Integer) impl.getOption(SocketOptions.SO_RCVBUF)).intValue();
192    }
193
194    public void setSoTimeout(int n) throws IOException {
195        impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(n));
196    }
197
198    public int getSoTimeout() throws IOException {
199        return ((Integer) impl.getOption(SocketOptions.SO_TIMEOUT)).intValue();
200    }
201
202    public void setSendBufferSize(int n) throws IOException {
203        impl.setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(n));
204    }
205
206    public int getSendBufferSize() throws IOException {
207        return ((Integer) impl.getOption(SocketOptions.SO_SNDBUF)).intValue();
208    }
209
210    //???SEC
211    public LocalSocketAddress getRemoteSocketAddress() {
212        throw new UnsupportedOperationException();
213    }
214
215    //???SEC
216    public synchronized boolean isConnected() {
217        return isConnected;
218    }
219
220    //???SEC
221    public boolean isClosed() {
222        throw new UnsupportedOperationException();
223    }
224
225    //???SEC
226    public synchronized boolean isBound() {
227        return isBound;
228    }
229
230    //???SEC
231    public boolean isOutputShutdown() {
232        throw new UnsupportedOperationException();
233    }
234
235    //???SEC
236    public boolean isInputShutdown() {
237        throw new UnsupportedOperationException();
238    }
239
240    //???SEC
241    public void connect(LocalSocketAddress endpoint, int timeout)
242            throws IOException {
243        throw new UnsupportedOperationException();
244    }
245
246    /**
247     * Enqueues a set of file descriptors to send to the peer. The queue
248     * is one deep. The file descriptors will be sent with the next write
249     * of normal data, and will be delivered in a single ancillary message.
250     * See "man 7 unix" SCM_RIGHTS on a desktop Linux machine.
251     *
252     * @param fds non-null; file descriptors to send.
253     */
254    public void setFileDescriptorsForSend(FileDescriptor[] fds) {
255        impl.setFileDescriptorsForSend(fds);
256    }
257
258    /**
259     * Retrieves a set of file descriptors that a peer has sent through
260     * an ancillary message. This method retrieves the most recent set sent,
261     * and then returns null until a new set arrives.
262     * File descriptors may only be passed along with regular data, so this
263     * method can only return a non-null after a read operation.
264     *
265     * @return null or file descriptor array
266     * @throws IOException
267     */
268    public FileDescriptor[] getAncillaryFileDescriptors() throws IOException {
269        return impl.getAncillaryFileDescriptors();
270    }
271
272    /**
273     * Retrieves the credentials of this socket's peer. Only valid on
274     * connected sockets.
275     *
276     * @return non-null; peer credentials
277     * @throws IOException
278     */
279    public Credentials getPeerCredentials() throws IOException {
280        return impl.getPeerCredentials();
281    }
282
283    /**
284     * Returns file descriptor or null if not yet open/already closed
285     *
286     * @return fd or null
287     */
288    public FileDescriptor getFileDescriptor() {
289        return impl.getFileDescriptor();
290    }
291}
292