491 lines
14 KiB
Java
491 lines
14 KiB
Java
|
/*
|
||
|
* 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 java.io.IOException;
|
||
|
import java.io.OutputStream;
|
||
|
import java.io.InputStream;
|
||
|
import java.io.FileDescriptor;
|
||
|
import java.net.SocketOptions;
|
||
|
|
||
|
/**
|
||
|
* Socket implementation used for android.net.LocalSocket and
|
||
|
* android.net.LocalServerSocket. Supports only AF_LOCAL sockets.
|
||
|
*/
|
||
|
class LocalSocketImpl
|
||
|
{
|
||
|
private SocketInputStream fis;
|
||
|
private SocketOutputStream fos;
|
||
|
private Object readMonitor = new Object();
|
||
|
private Object writeMonitor = new Object();
|
||
|
|
||
|
/** null if closed or not yet created */
|
||
|
private FileDescriptor fd;
|
||
|
|
||
|
// These fields are accessed by native code;
|
||
|
/** file descriptor array received during a previous read */
|
||
|
FileDescriptor[] inboundFileDescriptors;
|
||
|
/** file descriptor array that should be written during next write */
|
||
|
FileDescriptor[] outboundFileDescriptors;
|
||
|
|
||
|
/**
|
||
|
* An input stream for local sockets. Needed because we may
|
||
|
* need to read ancillary data.
|
||
|
*/
|
||
|
class SocketInputStream extends InputStream {
|
||
|
/** {@inheritDoc} */
|
||
|
@Override
|
||
|
public int available() throws IOException {
|
||
|
return available_native(fd);
|
||
|
}
|
||
|
|
||
|
/** {@inheritDoc} */
|
||
|
@Override
|
||
|
public void close() throws IOException {
|
||
|
LocalSocketImpl.this.close();
|
||
|
}
|
||
|
|
||
|
/** {@inheritDoc} */
|
||
|
@Override
|
||
|
public int read() throws IOException {
|
||
|
int ret;
|
||
|
synchronized (readMonitor) {
|
||
|
FileDescriptor myFd = fd;
|
||
|
if (myFd == null) throw new IOException("socket closed");
|
||
|
|
||
|
ret = read_native(myFd);
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** {@inheritDoc} */
|
||
|
@Override
|
||
|
public int read(byte[] b) throws IOException {
|
||
|
return read(b, 0, b.length);
|
||
|
}
|
||
|
|
||
|
/** {@inheritDoc} */
|
||
|
@Override
|
||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||
|
synchronized (readMonitor) {
|
||
|
FileDescriptor myFd = fd;
|
||
|
if (myFd == null) throw new IOException("socket closed");
|
||
|
|
||
|
if (off < 0 || len < 0 || (off + len) > b.length ) {
|
||
|
throw new ArrayIndexOutOfBoundsException();
|
||
|
}
|
||
|
|
||
|
int ret = readba_native(b, off, len, myFd);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* An output stream for local sockets. Needed because we may
|
||
|
* need to read ancillary data.
|
||
|
*/
|
||
|
class SocketOutputStream extends OutputStream {
|
||
|
/** {@inheritDoc} */
|
||
|
@Override
|
||
|
public void close() throws IOException {
|
||
|
LocalSocketImpl.this.close();
|
||
|
}
|
||
|
|
||
|
/** {@inheritDoc} */
|
||
|
@Override
|
||
|
public void write (byte[] b) throws IOException {
|
||
|
write(b, 0, b.length);
|
||
|
}
|
||
|
|
||
|
/** {@inheritDoc} */
|
||
|
@Override
|
||
|
public void write (byte[] b, int off, int len) throws IOException {
|
||
|
synchronized (writeMonitor) {
|
||
|
FileDescriptor myFd = fd;
|
||
|
if (myFd == null) throw new IOException("socket closed");
|
||
|
|
||
|
if (off < 0 || len < 0 || (off + len) > b.length ) {
|
||
|
throw new ArrayIndexOutOfBoundsException();
|
||
|
}
|
||
|
writeba_native(b, off, len, myFd);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** {@inheritDoc} */
|
||
|
@Override
|
||
|
public void write (int b) throws IOException {
|
||
|
synchronized (writeMonitor) {
|
||
|
FileDescriptor myFd = fd;
|
||
|
if (myFd == null) throw new IOException("socket closed");
|
||
|
write_native(b, myFd);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private native int available_native(FileDescriptor fd) throws IOException;
|
||
|
private native void close_native(FileDescriptor fd) throws IOException;
|
||
|
private native int read_native(FileDescriptor fd) throws IOException;
|
||
|
private native int readba_native(byte[] b, int off, int len,
|
||
|
FileDescriptor fd) throws IOException;
|
||
|
private native void writeba_native(byte[] b, int off, int len,
|
||
|
FileDescriptor fd) throws IOException;
|
||
|
private native void write_native(int b, FileDescriptor fd)
|
||
|
throws IOException;
|
||
|
private native void connectLocal(FileDescriptor fd, String name,
|
||
|
int namespace) throws IOException;
|
||
|
private native void bindLocal(FileDescriptor fd, String name, int namespace)
|
||
|
throws IOException;
|
||
|
private native FileDescriptor create_native(boolean stream)
|
||
|
throws IOException;
|
||
|
private native void listen_native(FileDescriptor fd, int backlog)
|
||
|
throws IOException;
|
||
|
private native void shutdown(FileDescriptor fd, boolean shutdownInput);
|
||
|
private native Credentials getPeerCredentials_native(
|
||
|
FileDescriptor fd) throws IOException;
|
||
|
private native int getOption_native(FileDescriptor fd, int optID)
|
||
|
throws IOException;
|
||
|
private native void setOption_native(FileDescriptor fd, int optID,
|
||
|
int b, int value) throws IOException;
|
||
|
|
||
|
// private native LocalSocketAddress getSockName_native
|
||
|
// (FileDescriptor fd) throws IOException;
|
||
|
|
||
|
/**
|
||
|
* Accepts a connection on a server socket.
|
||
|
*
|
||
|
* @param fd file descriptor of server socket
|
||
|
* @param s socket implementation that will become the new socket
|
||
|
* @return file descriptor of new socket
|
||
|
*/
|
||
|
private native FileDescriptor accept
|
||
|
(FileDescriptor fd, LocalSocketImpl s) throws IOException;
|
||
|
|
||
|
/**
|
||
|
* Create a new instance.
|
||
|
*/
|
||
|
/*package*/ LocalSocketImpl()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a new instance from a file descriptor representing
|
||
|
* a bound socket. The state of the file descriptor is not checked here
|
||
|
* but the caller can verify socket state by calling listen().
|
||
|
*
|
||
|
* @param fd non-null; bound file descriptor
|
||
|
*/
|
||
|
/*package*/ LocalSocketImpl(FileDescriptor fd) throws IOException
|
||
|
{
|
||
|
this.fd = fd;
|
||
|
}
|
||
|
|
||
|
public String toString() {
|
||
|
return super.toString() + " fd:" + fd;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates a socket in the underlying OS.
|
||
|
*
|
||
|
* @param stream true if this should be a stream socket, false for
|
||
|
* datagram.
|
||
|
* @throws IOException
|
||
|
*/
|
||
|
public void create (boolean stream) throws IOException {
|
||
|
// no error if socket already created
|
||
|
// need this for LocalServerSocket.accept()
|
||
|
if (fd == null) {
|
||
|
fd = create_native(stream);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Closes the socket.
|
||
|
*
|
||
|
* @throws IOException
|
||
|
*/
|
||
|
public void close() throws IOException {
|
||
|
synchronized (LocalSocketImpl.this) {
|
||
|
if (fd == null) return;
|
||
|
close_native(fd);
|
||
|
fd = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** note timeout presently ignored */
|
||
|
protected void connect(LocalSocketAddress address, int timeout)
|
||
|
throws IOException
|
||
|
{
|
||
|
if (fd == null) {
|
||
|
throw new IOException("socket not created");
|
||
|
}
|
||
|
|
||
|
connectLocal(fd, address.getName(), address.getNamespace().getId());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Binds this socket to an endpoint name. May only be called on an instance
|
||
|
* that has not yet been bound.
|
||
|
*
|
||
|
* @param endpoint endpoint address
|
||
|
* @throws IOException
|
||
|
*/
|
||
|
public void bind(LocalSocketAddress endpoint) throws IOException
|
||
|
{
|
||
|
if (fd == null) {
|
||
|
throw new IOException("socket not created");
|
||
|
}
|
||
|
|
||
|
bindLocal(fd, endpoint.getName(), endpoint.getNamespace().getId());
|
||
|
}
|
||
|
|
||
|
protected void listen(int backlog) throws IOException
|
||
|
{
|
||
|
if (fd == null) {
|
||
|
throw new IOException("socket not created");
|
||
|
}
|
||
|
|
||
|
listen_native(fd, backlog);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Accepts a new connection to the socket. Blocks until a new
|
||
|
* connection arrives.
|
||
|
*
|
||
|
* @param s a socket that will be used to represent the new connection.
|
||
|
* @throws IOException
|
||
|
*/
|
||
|
protected void accept(LocalSocketImpl s) throws IOException
|
||
|
{
|
||
|
if (fd == null) {
|
||
|
throw new IOException("socket not created");
|
||
|
}
|
||
|
|
||
|
s.fd = accept(fd, s);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieves the input stream for this instance.
|
||
|
*
|
||
|
* @return input stream
|
||
|
* @throws IOException if socket has been closed or cannot be created.
|
||
|
*/
|
||
|
protected InputStream getInputStream() throws IOException
|
||
|
{
|
||
|
if (fd == null) {
|
||
|
throw new IOException("socket not created");
|
||
|
}
|
||
|
|
||
|
synchronized (this) {
|
||
|
if (fis == null) {
|
||
|
fis = new SocketInputStream();
|
||
|
}
|
||
|
|
||
|
return fis;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieves the output stream for this instance.
|
||
|
*
|
||
|
* @return output stream
|
||
|
* @throws IOException if socket has been closed or cannot be created.
|
||
|
*/
|
||
|
protected OutputStream getOutputStream() throws IOException
|
||
|
{
|
||
|
if (fd == null) {
|
||
|
throw new IOException("socket not created");
|
||
|
}
|
||
|
|
||
|
synchronized (this) {
|
||
|
if (fos == null) {
|
||
|
fos = new SocketOutputStream();
|
||
|
}
|
||
|
|
||
|
return fos;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Returns the number of bytes available for reading without blocking.
|
||
|
*
|
||
|
* @return >= 0 count bytes available
|
||
|
* @throws IOException
|
||
|
*/
|
||
|
protected int available() throws IOException
|
||
|
{
|
||
|
return getInputStream().available();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Shuts down the input side of the socket.
|
||
|
*
|
||
|
* @throws IOException
|
||
|
*/
|
||
|
protected void shutdownInput() throws IOException
|
||
|
{
|
||
|
if (fd == null) {
|
||
|
throw new IOException("socket not created");
|
||
|
}
|
||
|
|
||
|
shutdown(fd, true);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Shuts down the output side of the socket.
|
||
|
*
|
||
|
* @throws IOException
|
||
|
*/
|
||
|
protected void shutdownOutput() throws IOException
|
||
|
{
|
||
|
if (fd == null) {
|
||
|
throw new IOException("socket not created");
|
||
|
}
|
||
|
|
||
|
shutdown(fd, false);
|
||
|
}
|
||
|
|
||
|
protected FileDescriptor getFileDescriptor()
|
||
|
{
|
||
|
return fd;
|
||
|
}
|
||
|
|
||
|
protected boolean supportsUrgentData()
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
protected void sendUrgentData(int data) throws IOException
|
||
|
{
|
||
|
throw new RuntimeException ("not impled");
|
||
|
}
|
||
|
|
||
|
public Object getOption(int optID) throws IOException
|
||
|
{
|
||
|
if (fd == null) {
|
||
|
throw new IOException("socket not created");
|
||
|
}
|
||
|
|
||
|
if (optID == SocketOptions.SO_TIMEOUT) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int value = getOption_native(fd, optID);
|
||
|
switch (optID)
|
||
|
{
|
||
|
case SocketOptions.SO_RCVBUF:
|
||
|
case SocketOptions.SO_SNDBUF:
|
||
|
return value;
|
||
|
case SocketOptions.SO_REUSEADDR:
|
||
|
default:
|
||
|
return value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void setOption(int optID, Object value)
|
||
|
throws IOException {
|
||
|
/*
|
||
|
* Boolean.FALSE is used to disable some options, so it
|
||
|
* is important to distinguish between FALSE and unset.
|
||
|
* We define it here that -1 is unset, 0 is FALSE, and 1
|
||
|
* is TRUE.
|
||
|
*/
|
||
|
int boolValue = -1;
|
||
|
int intValue = 0;
|
||
|
|
||
|
if (fd == null) {
|
||
|
throw new IOException("socket not created");
|
||
|
}
|
||
|
|
||
|
if (value instanceof Integer) {
|
||
|
intValue = (Integer)value;
|
||
|
} else if (value instanceof Boolean) {
|
||
|
boolValue = ((Boolean) value)? 1 : 0;
|
||
|
} else {
|
||
|
throw new IOException("bad value: " + value);
|
||
|
}
|
||
|
|
||
|
setOption_native(fd, optID, boolValue, intValue);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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.
|
||
|
* @throws IOException
|
||
|
*/
|
||
|
public void setFileDescriptorsForSend(FileDescriptor[] fds) {
|
||
|
synchronized(writeMonitor) {
|
||
|
outboundFileDescriptors = 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 {
|
||
|
synchronized(readMonitor) {
|
||
|
FileDescriptor[] result = inboundFileDescriptors;
|
||
|
|
||
|
inboundFileDescriptors = null;
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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 getPeerCredentials_native(fd);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieves the socket name from the OS.
|
||
|
*
|
||
|
* @return non-null; socket name
|
||
|
* @throws IOException on failure
|
||
|
*/
|
||
|
public LocalSocketAddress getSockAddress() throws IOException
|
||
|
{
|
||
|
return null;
|
||
|
//TODO implement this
|
||
|
//return getSockName_native(fd);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void finalize() throws IOException {
|
||
|
close();
|
||
|
}
|
||
|
}
|
||
|
|