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 
17 package android.net;
18 
19 import java.io.Closeable;
20 import java.io.FileDescriptor;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24 import 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  */
31 public 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      */
LocalSocket()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      */
LocalSocket(int sockType)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      */
LocalSocket(FileDescriptor fd)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      */
LocalSocket(LocalSocketImpl impl, int sockType)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
toString()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      */
implCreateIfNeeded()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      */
connect(LocalSocketAddress endpoint)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      */
bind(LocalSocketAddress bindpoint)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      */
getLocalSocketAddress()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      */
getInputStream()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      */
getOutputStream()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
close()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      */
shutdownInput()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      */
shutdownOutput()215     public void shutdownOutput() throws IOException {
216         implCreateIfNeeded();
217         impl.shutdownOutput();
218     }
219 
setReceiveBufferSize(int size)220     public void setReceiveBufferSize(int size) throws IOException {
221         impl.setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size));
222     }
223 
getReceiveBufferSize()224     public int getReceiveBufferSize() throws IOException {
225         return ((Integer) impl.getOption(SocketOptions.SO_RCVBUF)).intValue();
226     }
227 
setSoTimeout(int n)228     public void setSoTimeout(int n) throws IOException {
229         impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(n));
230     }
231 
getSoTimeout()232     public int getSoTimeout() throws IOException {
233         return ((Integer) impl.getOption(SocketOptions.SO_TIMEOUT)).intValue();
234     }
235 
setSendBufferSize(int n)236     public void setSendBufferSize(int n) throws IOException {
237         impl.setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(n));
238     }
239 
getSendBufferSize()240     public int getSendBufferSize() throws IOException {
241         return ((Integer) impl.getOption(SocketOptions.SO_SNDBUF)).intValue();
242     }
243 
244     //???SEC
getRemoteSocketAddress()245     public LocalSocketAddress getRemoteSocketAddress() {
246         throw new UnsupportedOperationException();
247     }
248 
249     //???SEC
isConnected()250     public synchronized boolean isConnected() {
251         return isConnected;
252     }
253 
254     //???SEC
isClosed()255     public boolean isClosed() {
256         throw new UnsupportedOperationException();
257     }
258 
259     //???SEC
isBound()260     public synchronized boolean isBound() {
261         return isBound;
262     }
263 
264     //???SEC
isOutputShutdown()265     public boolean isOutputShutdown() {
266         throw new UnsupportedOperationException();
267     }
268 
269     //???SEC
isInputShutdown()270     public boolean isInputShutdown() {
271         throw new UnsupportedOperationException();
272     }
273 
274     //???SEC
connect(LocalSocketAddress endpoint, int timeout)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      */
setFileDescriptorsForSend(FileDescriptor[] fds)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      */
getAncillaryFileDescriptors()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      */
getPeerCredentials()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      */
getFileDescriptor()322     public FileDescriptor getFileDescriptor() {
323         return impl.getFileDescriptor();
324     }
325 }
326