| /* | |
| * 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(); | |
| } | |
| } |