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;
30import android.util.MutableInt;
31
32/**
33 * Socket implementation used for android.net.LocalSocket and
34 * android.net.LocalServerSocket. Supports only AF_LOCAL sockets.
35 */
36class 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            MutableInt avail = new MutableInt(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            MutableInt pending = new MutableInt(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) throws IOException
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        // no error if socket already created
239        // need this for LocalServerSocket.accept()
240        if (fd == null) {
241            int osType;
242            switch (sockType) {
243                case LocalSocket.SOCKET_DGRAM:
244                    osType = OsConstants.SOCK_DGRAM;
245                    break;
246                case LocalSocket.SOCKET_STREAM:
247                    osType = OsConstants.SOCK_STREAM;
248                    break;
249                case LocalSocket.SOCKET_SEQPACKET:
250                    osType = OsConstants.SOCK_SEQPACKET;
251                    break;
252                default:
253                    throw new IllegalStateException("unknown sockType");
254            }
255            try {
256                fd = Os.socket(OsConstants.AF_UNIX, osType, 0);
257                mFdCreatedInternally = true;
258            } catch (ErrnoException e) {
259                e.rethrowAsIOException();
260            }
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                    /*
520                     * SO_TIMEOUT from the core library gets converted to
521                     * SO_SNDTIMEO, but the option is supposed to set both
522                     * send and receive timeouts. Note: The incoming timeout
523                     * value is in milliseconds.
524                     */
525                    StructTimeval timeval = StructTimeval.fromMillis(intValue);
526                    Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO,
527                            timeval);
528                    break;
529                case SocketOptions.SO_RCVBUF:
530                case SocketOptions.SO_SNDBUF:
531                case SocketOptions.SO_REUSEADDR:
532                    int osOpt = javaSoToOsOpt(optID);
533                    Os.setsockoptInt(fd, OsConstants.SOL_SOCKET, osOpt, intValue);
534                    break;
535                case SocketOptions.TCP_NODELAY:
536                    Os.setsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_NODELAY,
537                            intValue);
538                    break;
539                default:
540                    throw new IOException("Unknown option: " + optID);
541            }
542        } catch (ErrnoException e) {
543            throw e.rethrowAsIOException();
544        }
545    }
546
547    /**
548     * Enqueues a set of file descriptors to send to the peer. The queue
549     * is one deep. The file descriptors will be sent with the next write
550     * of normal data, and will be delivered in a single ancillary message.
551     * See "man 7 unix" SCM_RIGHTS on a desktop Linux machine.
552     *
553     * @param fds non-null; file descriptors to send.
554     * @throws IOException
555     */
556    public void setFileDescriptorsForSend(FileDescriptor[] fds) {
557        synchronized(writeMonitor) {
558            outboundFileDescriptors = fds;
559        }
560    }
561
562    /**
563     * Retrieves a set of file descriptors that a peer has sent through
564     * an ancillary message. This method retrieves the most recent set sent,
565     * and then returns null until a new set arrives.
566     * File descriptors may only be passed along with regular data, so this
567     * method can only return a non-null after a read operation.
568     *
569     * @return null or file descriptor array
570     * @throws IOException
571     */
572    public FileDescriptor[] getAncillaryFileDescriptors() throws IOException {
573        synchronized(readMonitor) {
574            FileDescriptor[] result = inboundFileDescriptors;
575
576            inboundFileDescriptors = null;
577            return result;
578        }
579    }
580
581    /**
582     * Retrieves the credentials of this socket's peer. Only valid on
583     * connected sockets.
584     *
585     * @return non-null; peer credentials
586     * @throws IOException
587     */
588    public Credentials getPeerCredentials() throws IOException {
589        return getPeerCredentials_native(fd);
590    }
591
592    /**
593     * Retrieves the socket name from the OS.
594     *
595     * @return non-null; socket name
596     * @throws IOException on failure
597     */
598    public LocalSocketAddress getSockAddress() throws IOException {
599        // This method has never been implemented.
600        return null;
601    }
602
603    @Override
604    protected void finalize() throws IOException {
605        close();
606    }
607
608    private static int javaSoToOsOpt(int optID) {
609        switch (optID) {
610            case SocketOptions.SO_SNDBUF:
611                return OsConstants.SO_SNDBUF;
612            case SocketOptions.SO_RCVBUF:
613                return OsConstants.SO_RCVBUF;
614            case SocketOptions.SO_REUSEADDR:
615                return OsConstants.SO_REUSEADDR;
616            default:
617                throw new UnsupportedOperationException("Unknown option: " + optID);
618        }
619    }
620}
621