| /* |
| * 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 static android.annotation.SystemApi.Client.MODULE_LIBRARIES; |
| |
| import android.annotation.NonNull; |
| import android.annotation.SuppressLint; |
| import android.annotation.SystemApi; |
| import android.compat.annotation.UnsupportedAppUsage; |
| import android.system.ErrnoException; |
| import android.system.Os; |
| |
| 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; |
| } |
| |
| private void checkConnected() { |
| try { |
| Os.getpeername(impl.getFileDescriptor()); |
| } catch (ErrnoException e) { |
| throw new IllegalArgumentException("Not a connected socket", e); |
| } |
| isConnected = true; |
| isBound = true; |
| implCreated = true; |
| } |
| |
| /** |
| * Creates a LocalSocket instance using the {@link FileDescriptor} for an already-connected |
| * AF_LOCAL/UNIX domain stream socket. The passed-in FileDescriptor is not managed by this class |
| * and must be closed by the caller. Calling {@link #close()} on a socket created by this |
| * method has no effect. |
| * |
| * @param fd the filedescriptor to adopt |
| * |
| * @hide |
| */ |
| @SystemApi(client = MODULE_LIBRARIES) |
| public LocalSocket(@NonNull @SuppressLint("UseParcelFileDescriptor") FileDescriptor fd) { |
| this(new LocalSocketImpl(fd), SOCKET_UNKNOWN); |
| checkConnected(); |
| } |
| |
| /** |
| * for use with LocalServerSocket.accept() |
| */ |
| static LocalSocket createLocalSocketForAccept(LocalSocketImpl impl) { |
| LocalSocket socket = new LocalSocket(impl, SOCKET_UNKNOWN); |
| socket.checkConnected(); |
| 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(); |
| } |
| } |