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