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