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