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