• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.compat.annotation.UnsupportedAppUsage;
20 import android.os.Build;
21 import android.system.ErrnoException;
22 import android.system.Os;
23 import android.system.OsConstants;
24 import android.system.StructLinger;
25 import android.system.StructTimeval;
26 
27 import java.io.FileDescriptor;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.OutputStream;
31 import java.net.SocketOptions;
32 
33 /**
34  * Socket implementation used for android.net.LocalSocket and
35  * android.net.LocalServerSocket. Supports only AF_LOCAL sockets.
36  */
37 class LocalSocketImpl
38 {
39     private SocketInputStream fis;
40     private SocketOutputStream fos;
41     private Object readMonitor = new Object();
42     private Object writeMonitor = new Object();
43 
44     /** null if closed or not yet created */
45     private FileDescriptor fd;
46     /** whether fd is created internally */
47     private boolean mFdCreatedInternally;
48 
49     // These fields are accessed by native code;
50     /** file descriptor array received during a previous read */
51     @UnsupportedAppUsage
52     FileDescriptor[] inboundFileDescriptors;
53     /** file descriptor array that should be written during next write */
54     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
55     FileDescriptor[] outboundFileDescriptors;
56 
57     /**
58      * An input stream for local sockets. Needed because we may
59      * need to read ancillary data.
60      */
61     class SocketInputStream extends InputStream {
62         /** {@inheritDoc} */
63         @Override
available()64         public int available() throws IOException {
65             FileDescriptor myFd = fd;
66             if (myFd == null) throw new IOException("socket closed");
67             try {
68                 return Os.ioctlInt(myFd, OsConstants.FIONREAD);
69             } catch (ErrnoException e) {
70                 throw e.rethrowAsIOException();
71             }
72         }
73 
74         /** {@inheritDoc} */
75         @Override
close()76         public void close() throws IOException {
77             LocalSocketImpl.this.close();
78         }
79 
80         /** {@inheritDoc} */
81         @Override
read()82         public int read() throws IOException {
83             int ret;
84             synchronized (readMonitor) {
85                 FileDescriptor myFd = fd;
86                 if (myFd == null) throw new IOException("socket closed");
87 
88                 ret = read_native(myFd);
89                 return ret;
90             }
91         }
92 
93         /** {@inheritDoc} */
94         @Override
read(byte[] b)95         public int read(byte[] b) throws IOException {
96             return read(b, 0, b.length);
97         }
98 
99         /** {@inheritDoc} */
100         @Override
read(byte[] b, int off, int len)101         public int read(byte[] b, int off, int len) throws IOException {
102             synchronized (readMonitor) {
103                 FileDescriptor myFd = fd;
104                 if (myFd == null) throw new IOException("socket closed");
105 
106                 if (off < 0 || len < 0 || (off + len) > b.length ) {
107                     throw new ArrayIndexOutOfBoundsException();
108                 }
109 
110                 int ret = readba_native(b, off, len, myFd);
111 
112                 return ret;
113             }
114         }
115     }
116 
117     /**
118      * An output stream for local sockets. Needed because we may
119      * need to read ancillary data.
120      */
121     class SocketOutputStream extends OutputStream {
122         /** {@inheritDoc} */
123         @Override
close()124         public void close() throws IOException {
125             LocalSocketImpl.this.close();
126         }
127 
128         /** {@inheritDoc} */
129         @Override
write(byte[] b)130         public void write (byte[] b) throws IOException {
131             write(b, 0, b.length);
132         }
133 
134         /** {@inheritDoc} */
135         @Override
write(byte[] b, int off, int len)136         public void write (byte[] b, int off, int len) throws IOException {
137             synchronized (writeMonitor) {
138                 FileDescriptor myFd = fd;
139                 if (myFd == null) throw new IOException("socket closed");
140 
141                 if (off < 0 || len < 0 || (off + len) > b.length ) {
142                     throw new ArrayIndexOutOfBoundsException();
143                 }
144                 writeba_native(b, off, len, myFd);
145             }
146         }
147 
148         /** {@inheritDoc} */
149         @Override
write(int b)150         public void write (int b) throws IOException {
151             synchronized (writeMonitor) {
152                 FileDescriptor myFd = fd;
153                 if (myFd == null) throw new IOException("socket closed");
154                 write_native(b, myFd);
155             }
156         }
157     }
158 
read_native(FileDescriptor fd)159     private native int read_native(FileDescriptor fd) throws IOException;
readba_native(byte[] b, int off, int len, FileDescriptor fd)160     private native int readba_native(byte[] b, int off, int len,
161             FileDescriptor fd) throws IOException;
writeba_native(byte[] b, int off, int len, FileDescriptor fd)162     private native void writeba_native(byte[] b, int off, int len,
163             FileDescriptor fd) throws IOException;
write_native(int b, FileDescriptor fd)164     private native void write_native(int b, FileDescriptor fd)
165             throws IOException;
connectLocal(FileDescriptor fd, String name, int namespace)166     private native void connectLocal(FileDescriptor fd, String name,
167             int namespace) throws IOException;
bindLocal(FileDescriptor fd, String name, int namespace)168     private native void bindLocal(FileDescriptor fd, String name, int namespace)
169             throws IOException;
getPeerCredentials_native( FileDescriptor fd)170     private native Credentials getPeerCredentials_native(
171             FileDescriptor fd) throws IOException;
172 
173     /**
174      * Create a new instance.
175      */
176     @UnsupportedAppUsage
LocalSocketImpl()177     /*package*/ LocalSocketImpl()
178     {
179     }
180 
181     /**
182      * Create a new instance from a file descriptor representing
183      * a bound socket. The state of the file descriptor is not checked here
184      *  but the caller can verify socket state by calling listen().
185      *
186      * @param fd non-null; bound file descriptor
187      */
LocalSocketImpl(FileDescriptor fd)188     /*package*/ LocalSocketImpl(FileDescriptor fd)
189     {
190         this.fd = fd;
191     }
192 
toString()193     public String toString() {
194         return super.toString() + " fd:" + fd;
195     }
196 
197     /**
198      * Creates a socket in the underlying OS.
199      *
200      * @param sockType either {@link LocalSocket#SOCKET_DGRAM}, {@link LocalSocket#SOCKET_STREAM}
201      * or {@link LocalSocket#SOCKET_SEQPACKET}
202      * @throws IOException
203      */
create(int sockType)204     public void create(int sockType) throws IOException {
205         if (fd != null) {
206             throw new IOException("LocalSocketImpl already has an fd");
207         }
208 
209         int osType;
210         switch (sockType) {
211             case LocalSocket.SOCKET_DGRAM:
212                 osType = OsConstants.SOCK_DGRAM;
213                 break;
214             case LocalSocket.SOCKET_STREAM:
215                 osType = OsConstants.SOCK_STREAM;
216                 break;
217             case LocalSocket.SOCKET_SEQPACKET:
218                 osType = OsConstants.SOCK_SEQPACKET;
219                 break;
220             default:
221                 throw new IllegalStateException("unknown sockType");
222         }
223         try {
224             fd = Os.socket(OsConstants.AF_UNIX, osType, 0);
225             mFdCreatedInternally = true;
226         } catch (ErrnoException e) {
227             e.rethrowAsIOException();
228         }
229     }
230 
231     /**
232      * Closes the socket.
233      *
234      * @throws IOException
235      */
close()236     public void close() throws IOException {
237         synchronized (LocalSocketImpl.this) {
238             if ((fd == null) || (mFdCreatedInternally == false)) {
239                 fd = null;
240                 return;
241             }
242             try {
243                 Os.close(fd);
244             } catch (ErrnoException e) {
245                 e.rethrowAsIOException();
246             }
247             fd = null;
248         }
249     }
250 
251     /** note timeout presently ignored */
connect(LocalSocketAddress address, int timeout)252     protected void connect(LocalSocketAddress address, int timeout)
253                         throws IOException
254     {
255         if (fd == null) {
256             throw new IOException("socket not created");
257         }
258 
259         connectLocal(fd, address.getName(), address.getNamespace().getId());
260     }
261 
262     /**
263      * Binds this socket to an endpoint name. May only be called on an instance
264      * that has not yet been bound.
265      *
266      * @param endpoint endpoint address
267      * @throws IOException
268      */
bind(LocalSocketAddress endpoint)269     public void bind(LocalSocketAddress endpoint) throws IOException
270     {
271         if (fd == null) {
272             throw new IOException("socket not created");
273         }
274 
275         bindLocal(fd, endpoint.getName(), endpoint.getNamespace().getId());
276     }
277 
listen(int backlog)278     protected void listen(int backlog) throws IOException
279     {
280         if (fd == null) {
281             throw new IOException("socket not created");
282         }
283         try {
284             Os.listen(fd, backlog);
285         } catch (ErrnoException e) {
286             throw e.rethrowAsIOException();
287         }
288     }
289 
290     /**
291      * Accepts a new connection to the socket. Blocks until a new
292      * connection arrives.
293      *
294      * @param s a socket that will be used to represent the new connection.
295      * @throws IOException
296      */
accept(LocalSocketImpl s)297     protected void accept(LocalSocketImpl s) throws IOException {
298         if (fd == null) {
299             throw new IOException("socket not created");
300         }
301 
302         try {
303             s.fd = Os.accept(fd, null /* address */);
304             s.mFdCreatedInternally = true;
305         } catch (ErrnoException e) {
306             throw e.rethrowAsIOException();
307         }
308     }
309 
310     /**
311      * Retrieves the input stream for this instance.
312      *
313      * @return input stream
314      * @throws IOException if socket has been closed or cannot be created.
315      */
getInputStream()316     protected InputStream getInputStream() throws IOException
317     {
318         if (fd == null) {
319             throw new IOException("socket not created");
320         }
321 
322         synchronized (this) {
323             if (fis == null) {
324                 fis = new SocketInputStream();
325             }
326 
327             return fis;
328         }
329     }
330 
331     /**
332      * Retrieves the output stream for this instance.
333      *
334      * @return output stream
335      * @throws IOException if socket has been closed or cannot be created.
336      */
getOutputStream()337     protected OutputStream getOutputStream() throws IOException
338     {
339         if (fd == null) {
340             throw new IOException("socket not created");
341         }
342 
343         synchronized (this) {
344             if (fos == null) {
345                 fos = new SocketOutputStream();
346             }
347 
348             return fos;
349         }
350     }
351 
352     /**
353      * Returns the number of bytes available for reading without blocking.
354      *
355      * @return >= 0 count bytes available
356      * @throws IOException
357      */
available()358     protected int available() throws IOException
359     {
360         return getInputStream().available();
361     }
362 
363     /**
364      * Shuts down the input side of the socket.
365      *
366      * @throws IOException
367      */
shutdownInput()368     protected void shutdownInput() throws IOException
369     {
370         if (fd == null) {
371             throw new IOException("socket not created");
372         }
373 
374         try {
375             Os.shutdown(fd, OsConstants.SHUT_RD);
376         } catch (ErrnoException e) {
377             throw e.rethrowAsIOException();
378         }
379     }
380 
381     /**
382      * Shuts down the output side of the socket.
383      *
384      * @throws IOException
385      */
shutdownOutput()386     protected void shutdownOutput() throws IOException
387     {
388         if (fd == null) {
389             throw new IOException("socket not created");
390         }
391 
392         try {
393             Os.shutdown(fd, OsConstants.SHUT_WR);
394         } catch (ErrnoException e) {
395             throw e.rethrowAsIOException();
396         }
397     }
398 
getFileDescriptor()399     protected FileDescriptor getFileDescriptor()
400     {
401         return fd;
402     }
403 
supportsUrgentData()404     protected boolean supportsUrgentData()
405     {
406         return false;
407     }
408 
sendUrgentData(int data)409     protected void sendUrgentData(int data) throws IOException
410     {
411         throw new RuntimeException ("not impled");
412     }
413 
getOption(int optID)414     public Object getOption(int optID) throws IOException
415     {
416         if (fd == null) {
417             throw new IOException("socket not created");
418         }
419 
420         try {
421             Object toReturn;
422             switch (optID) {
423                 case SocketOptions.SO_TIMEOUT:
424                     StructTimeval timeval = Os.getsockoptTimeval(fd, OsConstants.SOL_SOCKET,
425                             OsConstants.SO_SNDTIMEO);
426                     toReturn = (int) timeval.toMillis();
427                     break;
428                 case SocketOptions.SO_RCVBUF:
429                 case SocketOptions.SO_SNDBUF:
430                 case SocketOptions.SO_REUSEADDR:
431                     int osOpt = javaSoToOsOpt(optID);
432                     toReturn = Os.getsockoptInt(fd, OsConstants.SOL_SOCKET, osOpt);
433                     break;
434                 case SocketOptions.SO_LINGER:
435                     StructLinger linger=
436                             Os.getsockoptLinger(fd, OsConstants.SOL_SOCKET, OsConstants.SO_LINGER);
437                     if (!linger.isOn()) {
438                         toReturn = -1;
439                     } else {
440                         toReturn = linger.l_linger;
441                     }
442                     break;
443                 case SocketOptions.TCP_NODELAY:
444                     toReturn = Os.getsockoptInt(fd, OsConstants.IPPROTO_TCP,
445                             OsConstants.TCP_NODELAY);
446                     break;
447                 default:
448                     throw new IOException("Unknown option: " + optID);
449             }
450             return toReturn;
451         } catch (ErrnoException e) {
452             throw e.rethrowAsIOException();
453         }
454     }
455 
setOption(int optID, Object value)456     public void setOption(int optID, Object value)
457             throws IOException {
458 
459         if (fd == null) {
460             throw new IOException("socket not created");
461         }
462 
463         /*
464          * Boolean.FALSE is used to disable some options, so it
465          * is important to distinguish between FALSE and unset.
466          * We define it here that -1 is unset, 0 is FALSE, and 1
467          * is TRUE.
468          */
469         int boolValue = -1;
470         int intValue = 0;
471         if (value instanceof Integer) {
472             intValue = (Integer)value;
473         } else if (value instanceof Boolean) {
474             boolValue = ((Boolean) value)? 1 : 0;
475         } else {
476             throw new IOException("bad value: " + value);
477         }
478 
479         try {
480             switch (optID) {
481                 case SocketOptions.SO_LINGER:
482                     StructLinger linger = new StructLinger(boolValue, intValue);
483                     Os.setsockoptLinger(fd, OsConstants.SOL_SOCKET, OsConstants.SO_LINGER, linger);
484                     break;
485                 case SocketOptions.SO_TIMEOUT:
486                     // The option must set both send and receive timeouts.
487                     // Note: The incoming timeout value is in milliseconds.
488                     StructTimeval timeval = StructTimeval.fromMillis(intValue);
489                     Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO,
490                             timeval);
491                     Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO,
492                             timeval);
493                     break;
494                 case SocketOptions.SO_RCVBUF:
495                 case SocketOptions.SO_SNDBUF:
496                 case SocketOptions.SO_REUSEADDR:
497                     int osOpt = javaSoToOsOpt(optID);
498                     Os.setsockoptInt(fd, OsConstants.SOL_SOCKET, osOpt, intValue);
499                     break;
500                 case SocketOptions.TCP_NODELAY:
501                     Os.setsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_NODELAY,
502                             intValue);
503                     break;
504                 default:
505                     throw new IOException("Unknown option: " + optID);
506             }
507         } catch (ErrnoException e) {
508             throw e.rethrowAsIOException();
509         }
510     }
511 
512     /**
513      * Enqueues a set of file descriptors to send to the peer. The queue
514      * is one deep. The file descriptors will be sent with the next write
515      * of normal data, and will be delivered in a single ancillary message.
516      * See "man 7 unix" SCM_RIGHTS on a desktop Linux machine.
517      *
518      * @param fds non-null; file descriptors to send.
519      * @throws IOException
520      */
setFileDescriptorsForSend(FileDescriptor[] fds)521     public void setFileDescriptorsForSend(FileDescriptor[] fds) {
522         synchronized(writeMonitor) {
523             outboundFileDescriptors = fds;
524         }
525     }
526 
527     /**
528      * Retrieves a set of file descriptors that a peer has sent through
529      * an ancillary message. This method retrieves the most recent set sent,
530      * and then returns null until a new set arrives.
531      * File descriptors may only be passed along with regular data, so this
532      * method can only return a non-null after a read operation.
533      *
534      * @return null or file descriptor array
535      * @throws IOException
536      */
getAncillaryFileDescriptors()537     public FileDescriptor[] getAncillaryFileDescriptors() throws IOException {
538         synchronized(readMonitor) {
539             FileDescriptor[] result = inboundFileDescriptors;
540 
541             inboundFileDescriptors = null;
542             return result;
543         }
544     }
545 
546     /**
547      * Retrieves the credentials of this socket's peer. Only valid on
548      * connected sockets.
549      *
550      * @return non-null; peer credentials
551      * @throws IOException
552      */
getPeerCredentials()553     public Credentials getPeerCredentials() throws IOException {
554         return getPeerCredentials_native(fd);
555     }
556 
557     /**
558      * Retrieves the socket name from the OS.
559      *
560      * @return non-null; socket name
561      * @throws IOException on failure
562      */
getSockAddress()563     public LocalSocketAddress getSockAddress() throws IOException {
564         // This method has never been implemented.
565         return null;
566     }
567 
568     @Override
finalize()569     protected void finalize() throws IOException {
570         close();
571     }
572 
javaSoToOsOpt(int optID)573     private static int javaSoToOsOpt(int optID) {
574         switch (optID) {
575             case SocketOptions.SO_SNDBUF:
576                 return OsConstants.SO_SNDBUF;
577             case SocketOptions.SO_RCVBUF:
578                 return OsConstants.SO_RCVBUF;
579             case SocketOptions.SO_REUSEADDR:
580                 return OsConstants.SO_REUSEADDR;
581             default:
582                 throw new UnsupportedOperationException("Unknown option: " + optID);
583         }
584     }
585 }
586