blob: 0f0e9c49a0b04657b1678fd64530307cc12955c4 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
Elliott Hughes34385d32014-04-28 11:11:32 -070025import android.system.ErrnoException;
26import android.system.Os;
27import android.system.OsConstants;
Neil Fullerc80af6d2015-07-03 10:59:17 +010028import android.system.StructLinger;
29import android.system.StructTimeval;
Neil Fullerd2df87e2015-07-03 15:06:23 +010030import android.util.MutableInt;
Mike Lockwoode7d309a2013-07-16 11:36:22 -070031
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032/**
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;
Zhihai Xu118c8512013-07-25 13:44:50 -070045 /** whether fd is created internally */
46 private boolean mFdCreatedInternally;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047
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 {
Ian McKellar54d50082014-04-11 15:41:00 -070062 FileDescriptor myFd = fd;
63 if (myFd == null) throw new IOException("socket closed");
64
Neil Fullerd2df87e2015-07-03 15:06:23 +010065 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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 }
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 }
zzy71bfafc2013-04-16 17:17:37 -0700157
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");
Neil Fullerd2df87e2015-07-03 15:06:23 +0100168
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
zzy71bfafc2013-04-16 17:17:37 -0700184 try {
185 Thread.sleep(10);
186 } catch (InterruptedException ie) {
Neil Fullerd2df87e2015-07-03 15:06:23 +0100187 break;
zzy71bfafc2013-04-16 17:17:37 -0700188 }
189 }
190 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 }
192
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 private native Credentials getPeerCredentials_native(
205 FileDescriptor fd) throws IOException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206
207 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208 * 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 *
Mike Lockwoode7d309a2013-07-16 11:36:22 -0700233 * @param sockType either {@link LocalSocket#SOCKET_DGRAM}, {@link LocalSocket#SOCKET_STREAM}
234 * or {@link LocalSocket#SOCKET_SEQPACKET}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 * @throws IOException
236 */
Neil Fullerc80af6d2015-07-03 10:59:17 +0100237 public void create(int sockType) throws IOException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 // no error if socket already created
239 // need this for LocalServerSocket.accept()
240 if (fd == null) {
Mike Lockwoode7d309a2013-07-16 11:36:22 -0700241 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 {
Elliott Hughes34385d32014-04-28 11:11:32 -0700256 fd = Os.socket(OsConstants.AF_UNIX, osType, 0);
Zhihai Xu118c8512013-07-25 13:44:50 -0700257 mFdCreatedInternally = true;
Mike Lockwoode7d309a2013-07-16 11:36:22 -0700258 } catch (ErrnoException e) {
259 e.rethrowAsIOException();
260 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800261 }
262 }
263
264 /**
265 * Closes the socket.
266 *
267 * @throws IOException
268 */
269 public void close() throws IOException {
270 synchronized (LocalSocketImpl.this) {
Zhihai Xu118c8512013-07-25 13:44:50 -0700271 if ((fd == null) || (mFdCreatedInternally == false)) {
272 fd = null;
273 return;
274 }
Mike Lockwoode7d309a2013-07-16 11:36:22 -0700275 try {
Elliott Hughes34385d32014-04-28 11:11:32 -0700276 Os.close(fd);
Mike Lockwoode7d309a2013-07-16 11:36:22 -0700277 } catch (ErrnoException e) {
278 e.rethrowAsIOException();
279 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 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 }
Neil Fuller4fa438e2015-07-03 17:58:00 +0100316 try {
317 Os.listen(fd, backlog);
318 } catch (ErrnoException e) {
319 throw e.rethrowAsIOException();
320 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800321 }
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 */
Neil Fullerc1eaeb92015-07-08 11:33:54 +0100330 protected void accept(LocalSocketImpl s) throws IOException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 if (fd == null) {
332 throw new IOException("socket not created");
333 }
334
Neil Fullerc1eaeb92015-07-08 11:33:54 +0100335 try {
336 s.fd = Os.accept(fd, null /* address */);
337 s.mFdCreatedInternally = true;
338 } catch (ErrnoException e) {
339 throw e.rethrowAsIOException();
340 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 }
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
Neil Fullera7f7c242015-07-08 12:57:15 +0100407 try {
408 Os.shutdown(fd, OsConstants.SHUT_RD);
409 } catch (ErrnoException e) {
410 throw e.rethrowAsIOException();
411 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 }
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
Neil Fullera7f7c242015-07-08 12:57:15 +0100425 try {
426 Os.shutdown(fd, OsConstants.SHUT_WR);
427 } catch (ErrnoException e) {
428 throw e.rethrowAsIOException();
429 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 }
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
Neil Fullerc80af6d2015-07-03 10:59:17 +0100453 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();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 }
487 }
488
489 public void setOption(int optID, Object value)
490 throws IOException {
Neil Fullerc80af6d2015-07-03 10:59:17 +0100491
492 if (fd == null) {
493 throw new IOException("socket not created");
494 }
495
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 /*
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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 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
Neil Fullerc80af6d2015-07-03 10:59:17 +0100512 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:
Neil Fuller8d5ec8d2016-08-31 11:32:17 +0100519 // The option must set both send and receive timeouts.
520 // Note: The incoming timeout value is in milliseconds.
Neil Fullerc80af6d2015-07-03 10:59:17 +0100521 StructTimeval timeval = StructTimeval.fromMillis(intValue);
Neil Fuller8d5ec8d2016-08-31 11:32:17 +0100522 Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO,
523 timeval);
Neil Fullerc80af6d2015-07-03 10:59:17 +0100524 Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO,
525 timeval);
526 break;
527 case SocketOptions.SO_RCVBUF:
528 case SocketOptions.SO_SNDBUF:
529 case SocketOptions.SO_REUSEADDR:
530 int osOpt = javaSoToOsOpt(optID);
531 Os.setsockoptInt(fd, OsConstants.SOL_SOCKET, osOpt, intValue);
532 break;
533 case SocketOptions.TCP_NODELAY:
534 Os.setsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_NODELAY,
535 intValue);
536 break;
537 default:
538 throw new IOException("Unknown option: " + optID);
539 }
540 } catch (ErrnoException e) {
541 throw e.rethrowAsIOException();
542 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543 }
544
545 /**
546 * Enqueues a set of file descriptors to send to the peer. The queue
547 * is one deep. The file descriptors will be sent with the next write
548 * of normal data, and will be delivered in a single ancillary message.
549 * See "man 7 unix" SCM_RIGHTS on a desktop Linux machine.
550 *
551 * @param fds non-null; file descriptors to send.
552 * @throws IOException
553 */
554 public void setFileDescriptorsForSend(FileDescriptor[] fds) {
555 synchronized(writeMonitor) {
556 outboundFileDescriptors = fds;
557 }
558 }
559
560 /**
561 * Retrieves a set of file descriptors that a peer has sent through
562 * an ancillary message. This method retrieves the most recent set sent,
563 * and then returns null until a new set arrives.
564 * File descriptors may only be passed along with regular data, so this
565 * method can only return a non-null after a read operation.
566 *
567 * @return null or file descriptor array
568 * @throws IOException
569 */
570 public FileDescriptor[] getAncillaryFileDescriptors() throws IOException {
571 synchronized(readMonitor) {
572 FileDescriptor[] result = inboundFileDescriptors;
573
574 inboundFileDescriptors = null;
575 return result;
576 }
577 }
578
579 /**
580 * Retrieves the credentials of this socket's peer. Only valid on
581 * connected sockets.
582 *
583 * @return non-null; peer credentials
584 * @throws IOException
585 */
Neil Fullerc80af6d2015-07-03 10:59:17 +0100586 public Credentials getPeerCredentials() throws IOException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 return getPeerCredentials_native(fd);
588 }
589
590 /**
591 * Retrieves the socket name from the OS.
592 *
593 * @return non-null; socket name
594 * @throws IOException on failure
595 */
Neil Fullerc80af6d2015-07-03 10:59:17 +0100596 public LocalSocketAddress getSockAddress() throws IOException {
597 // This method has never been implemented.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800598 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 }
600
601 @Override
602 protected void finalize() throws IOException {
603 close();
604 }
Neil Fullerc80af6d2015-07-03 10:59:17 +0100605
606 private static int javaSoToOsOpt(int optID) {
607 switch (optID) {
608 case SocketOptions.SO_SNDBUF:
609 return OsConstants.SO_SNDBUF;
610 case SocketOptions.SO_RCVBUF:
611 return OsConstants.SO_RCVBUF;
612 case SocketOptions.SO_REUSEADDR:
613 return OsConstants.SO_REUSEADDR;
614 default:
615 throw new UnsupportedOperationException("Unknown option: " + optID);
616 }
617 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618}