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     }
    330 
    331     /**
    332      * Retrieves the input stream for this instance.
    333      *
    334      * @return input stream
    335      * @throws IOException if socket has been closed or cannot be created.
    336      */
    337     protected InputStream getInputStream() throws IOException
    338     {
    339         if (fd == null) {
    340             throw new IOException("socket not created");
    341         }
    342 
    343         synchronized (this) {
    344             if (fis == null) {
    345                 fis = new SocketInputStream();
    346             }
    347 
    348             return fis;
    349         }
    350     }
    351 
    352     /**
    353      * Retrieves the output stream for this instance.
    354      *
    355      * @return output stream
    356      * @throws IOException if socket has been closed or cannot be created.
    357      */
    358     protected OutputStream getOutputStream() throws IOException
    359     {
    360         if (fd == null) {
    361             throw new IOException("socket not created");
    362         }
    363 
    364         synchronized (this) {
    365             if (fos == null) {
    366                 fos = new SocketOutputStream();
    367             }
    368 
    369             return fos;
    370         }
    371     }
    372 
    373     /**
    374      * Returns the number of bytes available for reading without blocking.
    375      *
    376      * @return >= 0 count bytes available
    377      * @throws IOException
    378      */
    379     protected int available() throws IOException
    380     {
    381         return getInputStream().available();
    382     }
    383 
    384     /**
    385      * Shuts down the input side of the socket.
    386      *
    387      * @throws IOException
    388      */
    389     protected void shutdownInput() throws IOException
    390     {
    391         if (fd == null) {
    392             throw new IOException("socket not created");
    393         }
    394 
    395         shutdown(fd, true);
    396     }
    397 
    398     /**
    399      * Shuts down the output side of the socket.
    400      *
    401      * @throws IOException
    402      */
    403     protected void shutdownOutput() throws IOException
    404     {
    405         if (fd == null) {
    406             throw new IOException("socket not created");
    407         }
    408 
    409         shutdown(fd, false);
    410     }
    411 
    412     protected FileDescriptor getFileDescriptor()
    413     {
    414         return fd;
    415     }
    416 
    417     protected boolean supportsUrgentData()
    418     {
    419         return false;
    420     }
    421 
    422     protected void sendUrgentData(int data) throws IOException
    423     {
    424         throw new RuntimeException ("not impled");
    425     }
    426 
    427     public Object getOption(int optID) throws IOException
    428     {
    429         if (fd == null) {
    430             throw new IOException("socket not created");
    431         }
    432 
    433         if (optID == SocketOptions.SO_TIMEOUT) {
    434             return 0;
    435         }
    436 
    437         int value = getOption_native(fd, optID);
    438         switch (optID)
    439         {
    440             case SocketOptions.SO_RCVBUF:
    441             case SocketOptions.SO_SNDBUF:
    442                 return value;
    443             case SocketOptions.SO_REUSEADDR:
    444             default:
    445                 return value;
    446         }
    447     }
    448 
    449     public void setOption(int optID, Object value)
    450             throws IOException {
    451         /*
    452          * Boolean.FALSE is used to disable some options, so it
    453          * is important to distinguish between FALSE and unset.
    454          * We define it here that -1 is unset, 0 is FALSE, and 1
    455          * is TRUE.
    456          */
    457         int boolValue = -1;
    458         int intValue = 0;
    459 
    460         if (fd == null) {
    461             throw new IOException("socket not created");
    462         }
    463 
    464         if (value instanceof Integer) {
    465             intValue = (Integer)value;
    466         } else if (value instanceof Boolean) {
    467             boolValue = ((Boolean) value)? 1 : 0;
    468         } else {
    469             throw new IOException("bad value: " + value);
    470         }
    471 
    472         setOption_native(fd, optID, boolValue, intValue);
    473     }
    474 
    475     /**
    476      * Enqueues a set of file descriptors to send to the peer. The queue
    477      * is one deep. The file descriptors will be sent with the next write
    478      * of normal data, and will be delivered in a single ancillary message.
    479      * See "man 7 unix" SCM_RIGHTS on a desktop Linux machine.
    480      *
    481      * @param fds non-null; file descriptors to send.
    482      * @throws IOException
    483      */
    484     public void setFileDescriptorsForSend(FileDescriptor[] fds) {
    485         synchronized(writeMonitor) {
    486             outboundFileDescriptors = fds;
    487         }
    488     }
    489 
    490     /**
    491      * Retrieves a set of file descriptors that a peer has sent through
    492      * an ancillary message. This method retrieves the most recent set sent,
    493      * and then returns null until a new set arrives.
    494      * File descriptors may only be passed along with regular data, so this
    495      * method can only return a non-null after a read operation.
    496      *
    497      * @return null or file descriptor array
    498      * @throws IOException
    499      */
    500     public FileDescriptor[] getAncillaryFileDescriptors() throws IOException {
    501         synchronized(readMonitor) {
    502             FileDescriptor[] result = inboundFileDescriptors;
    503 
    504             inboundFileDescriptors = null;
    505             return result;
    506         }
    507     }
    508 
    509     /**
    510      * Retrieves the credentials of this socket's peer. Only valid on
    511      * connected sockets.
    512      *
    513      * @return non-null; peer credentials
    514      * @throws IOException
    515      */
    516     public Credentials getPeerCredentials() throws IOException
    517     {
    518         return getPeerCredentials_native(fd);
    519     }
    520 
    521     /**
    522      * Retrieves the socket name from the OS.
    523      *
    524      * @return non-null; socket name
    525      * @throws IOException on failure
    526      */
    527     public LocalSocketAddress getSockAddress() throws IOException
    528     {
    529         return null;
    530         //TODO implement this
    531         //return getSockName_native(fd);
    532     }
    533 
    534     @Override
    535     protected void finalize() throws IOException {
    536         close();
    537     }
    538 }
    539 
    540