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