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