content: /*
   * Copyright (C) 2007 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.net;
  
  import android.annotation.UnsupportedAppUsage;
  import java.io.Closeable;
  import java.io.FileDescriptor;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.net.SocketOptions;
  
  /**
   * Creates a (non-server) socket in the UNIX-domain namespace. The interface
   * here is not entirely unlike that of java.net.Socket. This class and the streams
   * returned from it may be used from multiple threads.
   */
  public class LocalSocket implements Closeable {
  
      @UnsupportedAppUsage
      private final LocalSocketImpl impl;
      /** false if impl.create() needs to be called */
      private volatile boolean implCreated;
      private LocalSocketAddress localAddress;
      private boolean isBound;
      private boolean isConnected;
      private final int sockType;
  
      /** unknown socket type (used for constructor with existing file descriptor) */
      /* package */ static final int SOCKET_UNKNOWN = 0;
      /** Datagram socket type */
      public static final int SOCKET_DGRAM = 1;
      /** Stream socket type */
      public static final int SOCKET_STREAM = 2;
      /** Sequential packet socket type */
      public static final int SOCKET_SEQPACKET = 3;
  
      /**
       * Creates a AF_LOCAL/UNIX domain stream socket.
       */
      public LocalSocket() {
          this(SOCKET_STREAM);
      }
  
      /**
       * Creates a AF_LOCAL/UNIX domain stream socket with given socket type
       *
       * @param sockType either {@link #SOCKET_DGRAM}, {@link #SOCKET_STREAM}
       * or {@link #SOCKET_SEQPACKET}
       */
      public LocalSocket(int sockType) {
          this(new LocalSocketImpl(), sockType);
      }
  
      private LocalSocket(LocalSocketImpl impl, int sockType) {
          this.impl = impl;
          this.sockType = sockType;
          this.isConnected = false;
          this.isBound = false;
      }
  
      /**
       * Creates a LocalSocket instances using the FileDescriptor for an already-connected
       * AF_LOCAL/UNIX domain stream socket. Note: the FileDescriptor must be closed by the caller:
       * closing the LocalSocket will not close it.
       *
       * @hide - used by BluetoothSocket.
       */
      public static LocalSocket createConnectedLocalSocket(FileDescriptor fd) {
          return createConnectedLocalSocket(new LocalSocketImpl(fd), SOCKET_UNKNOWN);
      }
  
      /**
       * for use with LocalServerSocket.accept()
       */
      static LocalSocket createLocalSocketForAccept(LocalSocketImpl impl) {
          return createConnectedLocalSocket(impl, SOCKET_UNKNOWN);
      }
  
      /**
       * Creates a LocalSocket from an existing LocalSocketImpl that is already connected.
       */
      private static LocalSocket createConnectedLocalSocket(LocalSocketImpl impl, int sockType) {
          LocalSocket socket = new LocalSocket(impl, sockType);
          socket.isConnected = true;
          socket.isBound = true;
          socket.implCreated = true;
          return socket;
      }
  
      /** {@inheritDoc} */
      @Override
      public String toString() {
          return super.toString() + " impl:" + impl;
      }
  
      /**
       * It's difficult to discern from the spec when impl.create() should be
       * called, but it seems like a reasonable rule is "as soon as possible,
       * but not in a context where IOException cannot be thrown"
       *
       * @throws IOException from SocketImpl.create()
       */
      private void implCreateIfNeeded() throws IOException {
          if (!implCreated) {
              synchronized (this) {
                  if (!implCreated) {
                      try {
                          impl.create(sockType);
                      } finally {
                          implCreated = true;
                      }
                  }
              }
          }
      }
  
      /**
       * Connects this socket to an endpoint. May only be called on an instance
       * that has not yet been connected.
       *
       * @param endpoint endpoint address
       * @throws IOException if socket is in invalid state or the address does
       * not exist.
       */
      public void connect(LocalSocketAddress endpoint) throws IOException {
          synchronized (this) {
              if (isConnected) {
                  throw new IOException("already connected");
              }
  
              implCreateIfNeeded();
              impl.connect(endpoint, 0);
              isConnected = true;
              isBound = true;
          }
      }
  
      /**
       * Binds this socket to an endpoint name. May only be called on an instance
       * that has not yet been bound.
       *
       * @param bindpoint endpoint address
       * @throws IOException
       */
      public void bind(LocalSocketAddress bindpoint) throws IOException {
          implCreateIfNeeded();
  
          synchronized (this) {
              if (isBound) {
                  throw new IOException("already bound");
              }
  
              localAddress = bindpoint;
              impl.bind(localAddress);
              isBound = true;
          }
      }
  
      /**
       * Retrieves the name that this socket is bound to, if any.
       *
       * @return Local address or null if anonymous
       */
      public LocalSocketAddress getLocalSocketAddress() {
          return localAddress;
      }
  
      /**
       * Retrieves the input stream for this instance.
       *
       * @return input stream
       * @throws IOException if socket has been closed or cannot be created.
       */
      public InputStream getInputStream() throws IOException {
          implCreateIfNeeded();
          return impl.getInputStream();
      }
  
      /**
       * Retrieves the output stream for this instance.
       *
       * @return output stream
       * @throws IOException if socket has been closed or cannot be created.
       */
      public OutputStream getOutputStream() throws IOException {
          implCreateIfNeeded();
          return impl.getOutputStream();
      }
  
      /**
       * Closes the socket.
       *
       * @throws IOException
       */
      @Override
      public void close() throws IOException {
          implCreateIfNeeded();
          impl.close();
      }
  
      /**
       * Shuts down the input side of the socket.
       *
       * @throws IOException
       */
      public void shutdownInput() throws IOException {
          implCreateIfNeeded();
          impl.shutdownInput();
      }
  
      /**
       * Shuts down the output side of the socket.
       *
       * @throws IOException
       */
      public void shutdownOutput() throws IOException {
          implCreateIfNeeded();
          impl.shutdownOutput();
      }
  
      public void setReceiveBufferSize(int size) throws IOException {
          impl.setOption(SocketOptions.SO_RCVBUF, Integer.valueOf(size));
      }
  
      public int getReceiveBufferSize() throws IOException {
          return ((Integer) impl.getOption(SocketOptions.SO_RCVBUF)).intValue();
      }
  
      public void setSoTimeout(int n) throws IOException {
          impl.setOption(SocketOptions.SO_TIMEOUT, Integer.valueOf(n));
      }
  
      public int getSoTimeout() throws IOException {
          return ((Integer) impl.getOption(SocketOptions.SO_TIMEOUT)).intValue();
      }
  
      public void setSendBufferSize(int n) throws IOException {
          impl.setOption(SocketOptions.SO_SNDBUF, Integer.valueOf(n));
      }
  
      public int getSendBufferSize() throws IOException {
          return ((Integer) impl.getOption(SocketOptions.SO_SNDBUF)).intValue();
      }
  
      //???SEC
      public LocalSocketAddress getRemoteSocketAddress() {
          throw new UnsupportedOperationException();
      }
  
      //???SEC
      public synchronized boolean isConnected() {
          return isConnected;
      }
  
      //???SEC
      public boolean isClosed() {
          throw new UnsupportedOperationException();
      }
  
      //???SEC
      public synchronized boolean isBound() {
          return isBound;
      }
  
      //???SEC
      public boolean isOutputShutdown() {
          throw new UnsupportedOperationException();
      }
  
      //???SEC
      public boolean isInputShutdown() {
          throw new UnsupportedOperationException();
      }
  
      //???SEC
      public void connect(LocalSocketAddress endpoint, int timeout)
              throws IOException {
          throw new UnsupportedOperationException();
      }
  
      /**
       * Enqueues a set of file descriptors to send to the peer. The queue
       * is one deep. The file descriptors will be sent with the next write
       * of normal data, and will be delivered in a single ancillary message.
       * See "man 7 unix" SCM_RIGHTS on a desktop Linux machine.
       *
       * @param fds non-null; file descriptors to send.
       */
      public void setFileDescriptorsForSend(FileDescriptor[] fds) {
          impl.setFileDescriptorsForSend(fds);
      }
  
      /**
       * Retrieves a set of file descriptors that a peer has sent through
       * an ancillary message. This method retrieves the most recent set sent,
       * and then returns null until a new set arrives.
       * File descriptors may only be passed along with regular data, so this
       * method can only return a non-null after a read operation.
       *
       * @return null or file descriptor array
       * @throws IOException
       */
      public FileDescriptor[] getAncillaryFileDescriptors() throws IOException {
          return impl.getAncillaryFileDescriptors();
      }
  
      /**
       * Retrieves the credentials of this socket's peer. Only valid on
       * connected sockets.
       *
       * @return non-null; peer credentials
       * @throws IOException
       */
      public Credentials getPeerCredentials() throws IOException {
          return impl.getPeerCredentials();
      }
  
      /**
       * Returns file descriptor or null if not yet open/already closed
       *
       * @return fd or null
       */
      public FileDescriptor getFileDescriptor() {
          return impl.getFileDescriptor();
      }
  }
  