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