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