blob: 89ee53e84869f209b69f668d0207873dcfff5278 [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
* Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.net;
import android.system.ErrnoException;
import java.io.IOException;
import java.io.FileDescriptor;
import java.util.Set;
import java.util.HashSet;
import java.util.Collections;
import libcore.io.AsynchronousCloseMonitor;
import libcore.io.IoBridge;
import libcore.io.IoUtils;
import libcore.io.Libcore;
import jdk.net.*;
import static android.system.OsConstants.AF_INET6;
import static android.system.OsConstants.AF_UNIX;
import static android.system.OsConstants.EAGAIN;
import static android.system.OsConstants.EBADF;
import static android.system.OsConstants.EINVAL;
import static android.system.OsConstants.MSG_OOB;
import static android.system.OsConstants.POLLERR;
import static android.system.OsConstants.POLLIN;
import static android.system.OsConstants.SOCK_DGRAM;
import static android.system.OsConstants.SOCK_STREAM;
import static android.system.OsConstants.SHUT_RDWR;
import static sun.net.ExtendedOptionsImpl.*;
// Android-changed: Rewritten to use android.system POSIX calls and assume AF_INET6.
/*
* On Unix systems we simply delegate to native methods.
*
* @author Chris Hegarty
*/
class PlainSocketImpl extends AbstractPlainSocketImpl
{
// Android-removed: Android doesn't need to call native initProto.
/*
static {
initProto();
}
*/
/**
* Constructs an empty instance.
*/
PlainSocketImpl() {
// Android-changed: Let PlainSocketImpl construct its own FileDescriptor.
this.fd = new FileDescriptor();
}
/**
* Constructs an instance with the given file descriptor.
*/
// Android-removed: Let PlainSocketImpl construct its own FileDescriptor.
/*
PlainSocketImpl(FileDescriptor fd) {
this.fd = fd;
}
*/
protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
if (!name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) {
super.setOption(name, value);
} else {
if (isClosedOrPending()) {
throw new SocketException("Socket closed");
}
checkSetOptionPermission(name);
checkValueType(value, SocketFlow.class);
setFlowOption(getFileDescriptor(), (SocketFlow)value);
}
}
protected <T> T getOption(SocketOption<T> name) throws IOException {
if (!name.equals(ExtendedSocketOptions.SO_FLOW_SLA)) {
return super.getOption(name);
}
if (isClosedOrPending()) {
throw new SocketException("Socket closed");
}
checkGetOptionPermission(name);
SocketFlow flow = SocketFlow.create();
getFlowOption(getFileDescriptor(), flow);
return (T)flow;
}
// BEGIN Android-changed: Rewrote on top of Libcore.io.
protected void socketSetOption(int opt, Object val) throws SocketException {
try {
socketSetOption0(opt, val);
} catch (SocketException se) {
if (socket == null || !socket.isConnected())
throw se;
}
}
void socketCreate(boolean isStream) throws IOException {
// The fd object must not change after calling bind, because we rely on this undocumented
// behaviour. See libcore.java.net.SocketTest#testFileDescriptorStaysSame.
fd.setInt$(IoBridge.socket(AF_INET6, isStream ? SOCK_STREAM : SOCK_DGRAM, 0).getInt$());
IoUtils.setFdOwner(fd, this);
if (serverSocket != null) {
IoUtils.setBlocking(fd, false);
IoBridge.setSocketOption(fd, SO_REUSEADDR, true);
}
}
void socketConnect(InetAddress address, int port, int timeout) throws IOException {
if (fd == null || !fd.valid()) {
throw new SocketException("Socket closed");
}
IoBridge.connect(fd, address, port, timeout);
this.address = address;
this.port = port;
if (localport == 0) {
// If socket is pending close, fd becomes an AF_UNIX socket and calling
// getLocalInetSocketAddress will fail.
// http://b/34645743
if (!isClosedOrPending()) {
localport = IoBridge.getLocalInetSocketAddress(fd).getPort();
}
}
}
void socketBind(InetAddress address, int port) throws IOException {
if (fd == null || !fd.valid()) {
throw new SocketException("Socket closed");
}
IoBridge.bind(fd, address, port);
this.address = address;
if (port == 0) {
// Now that we're a connected socket, let's extract the port number that the system
// chose for us and store it in the Socket object.
localport = IoBridge.getLocalInetSocketAddress(fd).getPort();
} else {
localport = port;
}
}
void socketListen(int count) throws IOException {
if (fd == null || !fd.valid()) {
throw new SocketException("Socket closed");
}
try {
Libcore.os.listen(fd, count);
} catch (ErrnoException errnoException) {
throw errnoException.rethrowAsSocketException();
}
}
void socketAccept(SocketImpl s) throws IOException {
if (fd == null || !fd.valid()) {
throw new SocketException("Socket closed");
}
// poll() with a timeout of 0 means "poll for zero millis", but a Socket timeout == 0 means
// "wait forever". When timeout == 0 we pass -1 to poll.
if (timeout <= 0) {
IoBridge.poll(fd, POLLIN | POLLERR, -1);
} else {
IoBridge.poll(fd, POLLIN | POLLERR, timeout);
}
InetSocketAddress peerAddress = new InetSocketAddress();
try {
FileDescriptor newfd = Libcore.os.accept(fd, peerAddress);
s.fd.setInt$(newfd.getInt$());
IoUtils.setFdOwner(s.fd, s);
s.address = peerAddress.getAddress();
s.port = peerAddress.getPort();
} catch (ErrnoException errnoException) {
if (errnoException.errno == EAGAIN) {
SocketTimeoutException e = new SocketTimeoutException();
e.initCause(errnoException);
throw e;
} else if (errnoException.errno == EINVAL || errnoException.errno == EBADF) {
throw new SocketException("Socket closed");
}
errnoException.rethrowAsSocketException();
}
s.localport = IoBridge.getLocalInetSocketAddress(s.fd).getPort();
}
int socketAvailable() throws IOException {
return IoBridge.available(fd);
}
void socketClose0(boolean useDeferredClose) throws IOException {
if (fd == null || !fd.valid()) {
throw new SocketException("socket already closed");
}
FileDescriptor markerFD = null;
if (useDeferredClose) {
markerFD = getMarkerFD();
}
if (useDeferredClose && markerFD != null) {
try {
Libcore.os.dup2(markerFD, fd.getInt$());
Libcore.os.close(markerFD);
// This effectively closes the socket, needs to signal threads that blocks on this
// file descriptor.
AsynchronousCloseMonitor.signalBlockedThreads(fd);
} catch (ErrnoException errnoException) {
// close should not throw
}
} else {
// If requested or a markerFD cannot be created, a non-deferred close is performed
// instead.
IoBridge.closeAndSignalBlockedThreads(fd);
}
}
/*
* Create the marker file descriptor by establishing a loopback connection which we shutdown but
* do not close the fd. The result is an fd that can be used for read/write.
*
* The purpose is to keep hold of the raw fd handle until we are sure it is not used in any
* thread. Otherwise if we close the file descriptor directly, the system might reuse the raw fd
* number and threads holding old fd value might behave incorrectly.
*/
private FileDescriptor getMarkerFD() throws SocketException {
FileDescriptor fd1 = new FileDescriptor();
FileDescriptor fd2 = new FileDescriptor();
try {
Libcore.os.socketpair(AF_UNIX, SOCK_STREAM, 0, fd1, fd2);
// Shutdown fd1, any reads to this fd will get EOF; any writes will get an error.
Libcore.os.shutdown(fd1, SHUT_RDWR);
Libcore.os.close(fd2);
} catch (ErrnoException errnoException) {
// We might have reached the maximum file descriptor number and socketpair(2) would
// fail. In this case, return null and let caller to fall back to an alternative method
// that does not allocate more file descriptors.
return null;
}
return fd1;
}
void socketShutdown(int howto) throws IOException {
try {
Libcore.os.shutdown(fd, howto);
} catch (ErrnoException errnoException) {
throw errnoException.rethrowAsIOException();
}
}
void socketSetOption0(int cmd, Object value) throws SocketException {
// OpenJDK does not set SO_TIMEOUT on Linux.
if (cmd == SO_TIMEOUT) {
return;
}
IoBridge.setSocketOption(fd, cmd, value);
}
Object socketGetOption(int opt) throws SocketException {
return IoBridge.getSocketOption(fd, opt);
}
void socketSendUrgentData(int data) throws IOException {
if (fd == null || !fd.valid()) {
throw new SocketException("Socket closed");
}
try {
byte[] buffer = new byte[] { (byte) data };
Libcore.os.sendto(fd, buffer, 0, 1, MSG_OOB, null, 0);
} catch (ErrnoException errnoException) {
throw errnoException.rethrowAsSocketException();
}
}
// END Android-changed: Rewrote on top of Libcore.io.
}