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 @uppressLintR) 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.
200      *
201      * @return input stream
202      * @throws IOException if socket has been closed or cannot be created.
203      */
getInputStream()204     public InputStream getInputStream() throws IOException {
205         implCreateIfNeeded();
206         return impl.getInputStream();
207     }
208 
209     /**
210      * Retrieves the output stream for this instance.
211      *
212      * @return output stream
213      * @throws IOException if socket has been closed or cannot be created.
214      */
getOutputStream()215     public OutputStream getOutputStream() throws IOException {
216         implCreateIfNeeded();
217         return impl.getOutputStream();
218     }
219 
220     /**
221      * Closes the socket.
222      *
223      * @throws IOException
224      */
225     @Override
close()226     public void close() throws IOException {
227         implCreateIfNeeded();
228         impl.close();
229     }
230 
231     /**
232      * Shuts down the input side of the socket.
233      *
234      * @throws IOException
235      */
shutdownInput()236     public void shutdownInput() throws IOException {
237         implCreateIfNeeded();
238         impl.shutdownInput();
239     }
240 
241     /**
242      * Shuts down the output side of the socket.
243      *
244      * @throws IOException
245      */
shutdownOutput()246     public void shutdownOutput() throws IOException {
247         implCreateIfNeeded();
248         impl.shutdownOutput();
249     }
250 
setReceiveBufferSize(int size)251     public void setReceiveBufferSize(int size) throws IOException {
252         impl.setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size));
253     }
254 
getReceiveBufferSize()255     public int getReceiveBufferSize() throws IOException {
256         return ((Integer) impl.getOption(SocketOptions.SO_RCVBUF)).intValue();
257     }
258 
setSoTimeout(int n)259     public void setSoTimeout(int n) throws IOException {
260         impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(n));
261     }
262 
getSoTimeout()263     public int getSoTimeout() throws IOException {
264         return ((Integer) impl.getOption(SocketOptions.SO_TIMEOUT)).intValue();
265     }
266 
setSendBufferSize(int n)267     public void setSendBufferSize(int n) throws IOException {
268         impl.setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(n));
269     }
270 
getSendBufferSize()271     public int getSendBufferSize() throws IOException {
272         return ((Integer) impl.getOption(SocketOptions.SO_SNDBUF)).intValue();
273     }
274 
275     //???SEC
getRemoteSocketAddress()276     public LocalSocketAddress getRemoteSocketAddress() {
277         throw new UnsupportedOperationException();
278     }
279 
280     //???SEC
isConnected()281     public synchronized boolean isConnected() {
282         return isConnected;
283     }
284 
285     //???SEC
isClosed()286     public boolean isClosed() {
287         throw new UnsupportedOperationException();
288     }
289 
290     //???SEC
isBound()291     public synchronized boolean isBound() {
292         return isBound;
293     }
294 
295     //???SEC
isOutputShutdown()296     public boolean isOutputShutdown() {
297         throw new UnsupportedOperationException();
298     }
299 
300     //???SEC
isInputShutdown()301     public boolean isInputShutdown() {
302         throw new UnsupportedOperationException();
303     }
304 
305     //???SEC
connect(LocalSocketAddress endpoint, int timeout)306     public void connect(LocalSocketAddress endpoint, int timeout)
307             throws IOException {
308         throw new UnsupportedOperationException();
309     }
310 
311     /**
312      * Enqueues a set of file descriptors to send to the peer. The queue
313      * is one deep. The file descriptors will be sent with the next write
314      * of normal data, and will be delivered in a single ancillary message.
315      * See "man 7 unix" SCM_RIGHTS on a desktop Linux machine.
316      *
317      * @param fds non-null; file descriptors to send.
318      */
setFileDescriptorsForSend(FileDescriptor[] fds)319     public void setFileDescriptorsForSend(FileDescriptor[] fds) {
320         impl.setFileDescriptorsForSend(fds);
321     }
322 
323     /**
324      * Retrieves a set of file descriptors that a peer has sent through
325      * an ancillary message. This method retrieves the most recent set sent,
326      * and then returns null until a new set arrives.
327      * File descriptors may only be passed along with regular data, so this
328      * method can only return a non-null after a read operation.
329      *
330      * @return null or file descriptor array
331      * @throws IOException
332      */
getAncillaryFileDescriptors()333     public FileDescriptor[] getAncillaryFileDescriptors() throws IOException {
334         return impl.getAncillaryFileDescriptors();
335     }
336 
337     /**
338      * Retrieves the credentials of this socket's peer. Only valid on
339      * connected sockets.
340      *
341      * @return non-null; peer credentials
342      * @throws IOException
343      */
getPeerCredentials()344     public Credentials getPeerCredentials() throws IOException {
345         return impl.getPeerCredentials();
346     }
347 
348     /**
349      * Returns file descriptor or null if not yet open/already closed
350      *
351      * @return fd or null
352      */
getFileDescriptor()353     public FileDescriptor getFileDescriptor() {
354         return impl.getFileDescriptor();
355     }
356 }
357