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