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 android.system.ErrnoException; 20 import android.system.Int32Ref; 21 import android.system.Os; 22 import android.system.OsConstants; 23 import android.system.StructLinger; 24 import android.system.StructTimeval; 25 26 import java.io.FileDescriptor; 27 import java.io.IOException; 28 import java.io.InputStream; 29 import java.io.OutputStream; 30 import java.net.SocketOptions; 31 32 /** 33 * Socket implementation used for android.net.LocalSocket and 34 * android.net.LocalServerSocket. Supports only AF_LOCAL sockets. 35 */ 36 class 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 available()61 public int available() throws IOException { 62 FileDescriptor myFd = fd; 63 if (myFd == null) throw new IOException("socket closed"); 64 65 Int32Ref avail = new Int32Ref(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 close()76 public void close() throws IOException { 77 LocalSocketImpl.this.close(); 78 } 79 80 /** {@inheritDoc} */ 81 @Override read()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 read(byte[] b)95 public int read(byte[] b) throws IOException { 96 return read(b, 0, b.length); 97 } 98 99 /** {@inheritDoc} */ 100 @Override read(byte[] b, int off, int len)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 close()124 public void close() throws IOException { 125 LocalSocketImpl.this.close(); 126 } 127 128 /** {@inheritDoc} */ 129 @Override write(byte[] b)130 public void write (byte[] b) throws IOException { 131 write(b, 0, b.length); 132 } 133 134 /** {@inheritDoc} */ 135 @Override write(byte[] b, int off, int len)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 write(int b)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 flush()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 Int32Ref pending = new Int32Ref(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 read_native(FileDescriptor fd)193 private native int read_native(FileDescriptor fd) throws IOException; readba_native(byte[] b, int off, int len, FileDescriptor fd)194 private native int readba_native(byte[] b, int off, int len, 195 FileDescriptor fd) throws IOException; writeba_native(byte[] b, int off, int len, FileDescriptor fd)196 private native void writeba_native(byte[] b, int off, int len, 197 FileDescriptor fd) throws IOException; write_native(int b, FileDescriptor fd)198 private native void write_native(int b, FileDescriptor fd) 199 throws IOException; connectLocal(FileDescriptor fd, String name, int namespace)200 private native void connectLocal(FileDescriptor fd, String name, 201 int namespace) throws IOException; bindLocal(FileDescriptor fd, String name, int namespace)202 private native void bindLocal(FileDescriptor fd, String name, int namespace) 203 throws IOException; getPeerCredentials_native( FileDescriptor fd)204 private native Credentials getPeerCredentials_native( 205 FileDescriptor fd) throws IOException; 206 207 /** 208 * Create a new instance. 209 */ LocalSocketImpl()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 */ LocalSocketImpl(FileDescriptor fd)221 /*package*/ LocalSocketImpl(FileDescriptor fd) 222 { 223 this.fd = fd; 224 } 225 toString()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 */ create(int sockType)237 public void create(int sockType) throws IOException { 238 if (fd != null) { 239 throw new IOException("LocalSocketImpl already has an fd"); 240 } 241 242 int osType; 243 switch (sockType) { 244 case LocalSocket.SOCKET_DGRAM: 245 osType = OsConstants.SOCK_DGRAM; 246 break; 247 case LocalSocket.SOCKET_STREAM: 248 osType = OsConstants.SOCK_STREAM; 249 break; 250 case LocalSocket.SOCKET_SEQPACKET: 251 osType = OsConstants.SOCK_SEQPACKET; 252 break; 253 default: 254 throw new IllegalStateException("unknown sockType"); 255 } 256 try { 257 fd = Os.socket(OsConstants.AF_UNIX, osType, 0); 258 mFdCreatedInternally = true; 259 } catch (ErrnoException e) { 260 e.rethrowAsIOException(); 261 } 262 } 263 264 /** 265 * Closes the socket. 266 * 267 * @throws IOException 268 */ close()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 */ connect(LocalSocketAddress address, int timeout)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 */ bind(LocalSocketAddress endpoint)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 listen(int backlog)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 */ accept(LocalSocketImpl s)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 */ getInputStream()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 */ getOutputStream()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 */ available()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 */ shutdownInput()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 */ shutdownOutput()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 getFileDescriptor()432 protected FileDescriptor getFileDescriptor() 433 { 434 return fd; 435 } 436 supportsUrgentData()437 protected boolean supportsUrgentData() 438 { 439 return false; 440 } 441 sendUrgentData(int data)442 protected void sendUrgentData(int data) throws IOException 443 { 444 throw new RuntimeException ("not impled"); 445 } 446 getOption(int optID)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 setOption(int optID, Object value)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 // The option must set both send and receive timeouts. 520 // Note: The incoming timeout value is in milliseconds. 521 StructTimeval timeval = StructTimeval.fromMillis(intValue); 522 Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO, 523 timeval); 524 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 } 543 } 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 */ setFileDescriptorsForSend(FileDescriptor[] fds)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 */ getAncillaryFileDescriptors()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 */ getPeerCredentials()586 public Credentials getPeerCredentials() throws IOException { 587 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 */ getSockAddress()596 public LocalSocketAddress getSockAddress() throws IOException { 597 // This method has never been implemented. 598 return null; 599 } 600 601 @Override finalize()602 protected void finalize() throws IOException { 603 close(); 604 } 605 javaSoToOsOpt(int optID)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 } 618 } 619