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