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