Switch to using android.system.Os for more calls

The methods being switched here should involve no important
semantic changes. socket.getSoTimeout() is now implemented:
previously it would have returned 0 in all cases.

Some tidy up of unimplemented / commented code.

Switching other calls to use Os would carry more risk and
will be handled separately they can be switched safely.

Bug: 3106438
Change-Id: I5526249395565fee6e43f159a2b5975b0d41d058
This commit is contained in:
Neil Fuller
2015-07-03 10:59:17 +01:00
parent 78db19c25a
commit c80af6d84d
2 changed files with 90 additions and 239 deletions

View File

@@ -25,6 +25,8 @@ import java.net.SocketOptions;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructLinger;
import android.system.StructTimeval;
/**
* Socket implementation used for android.net.LocalSocket and
@@ -184,13 +186,6 @@ class LocalSocketImpl
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.
@@ -232,7 +227,7 @@ class LocalSocketImpl
* or {@link LocalSocket#SOCKET_SEQPACKET}
* @throws IOException
*/
public void create (int sockType) throws IOException {
public void create(int sockType) throws IOException {
// no error if socket already created
// need this for LocalServerSocket.accept()
if (fd == null) {
@@ -434,24 +429,49 @@ class LocalSocketImpl
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;
try {
Object toReturn;
switch (optID) {
case SocketOptions.SO_TIMEOUT:
StructTimeval timeval = Os.getsockoptTimeval(fd, OsConstants.SOL_SOCKET,
OsConstants.SO_SNDTIMEO);
toReturn = (int) timeval.toMillis();
break;
case SocketOptions.SO_RCVBUF:
case SocketOptions.SO_SNDBUF:
case SocketOptions.SO_REUSEADDR:
int osOpt = javaSoToOsOpt(optID);
toReturn = Os.getsockoptInt(fd, OsConstants.SOL_SOCKET, osOpt);
break;
case SocketOptions.SO_LINGER:
StructLinger linger=
Os.getsockoptLinger(fd, OsConstants.SOL_SOCKET, OsConstants.SO_LINGER);
if (!linger.isOn()) {
toReturn = -1;
} else {
toReturn = linger.l_linger;
}
break;
case SocketOptions.TCP_NODELAY:
toReturn = Os.getsockoptInt(fd, OsConstants.IPPROTO_TCP,
OsConstants.TCP_NODELAY);
break;
default:
throw new IOException("Unknown option: " + optID);
}
return toReturn;
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
}
}
public void setOption(int optID, Object value)
throws IOException {
if (fd == null) {
throw new IOException("socket not created");
}
/*
* Boolean.FALSE is used to disable some options, so it
* is important to distinguish between FALSE and unset.
@@ -460,11 +480,6 @@ class LocalSocketImpl
*/
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) {
@@ -473,7 +488,39 @@ class LocalSocketImpl
throw new IOException("bad value: " + value);
}
setOption_native(fd, optID, boolValue, intValue);
try {
switch (optID) {
case SocketOptions.SO_LINGER:
StructLinger linger = new StructLinger(boolValue, intValue);
Os.setsockoptLinger(fd, OsConstants.SOL_SOCKET, OsConstants.SO_LINGER, linger);
break;
case SocketOptions.SO_TIMEOUT:
/*
* SO_TIMEOUT from the core library gets converted to
* SO_SNDTIMEO, but the option is supposed to set both
* send and receive timeouts. Note: The incoming timeout
* value is in milliseconds.
*/
StructTimeval timeval = StructTimeval.fromMillis(intValue);
Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO,
timeval);
break;
case SocketOptions.SO_RCVBUF:
case SocketOptions.SO_SNDBUF:
case SocketOptions.SO_REUSEADDR:
int osOpt = javaSoToOsOpt(optID);
Os.setsockoptInt(fd, OsConstants.SOL_SOCKET, osOpt, intValue);
break;
case SocketOptions.TCP_NODELAY:
Os.setsockoptInt(fd, OsConstants.IPPROTO_TCP, OsConstants.TCP_NODELAY,
intValue);
break;
default:
throw new IOException("Unknown option: " + optID);
}
} catch (ErrnoException e) {
throw e.rethrowAsIOException();
}
}
/**
@@ -517,8 +564,7 @@ class LocalSocketImpl
* @return non-null; peer credentials
* @throws IOException
*/
public Credentials getPeerCredentials() throws IOException
{
public Credentials getPeerCredentials() throws IOException {
return getPeerCredentials_native(fd);
}
@@ -528,15 +574,26 @@ class LocalSocketImpl
* @return non-null; socket name
* @throws IOException on failure
*/
public LocalSocketAddress getSockAddress() throws IOException
{
public LocalSocketAddress getSockAddress() throws IOException {
// This method has never been implemented.
return null;
//TODO implement this
//return getSockName_native(fd);
}
@Override
protected void finalize() throws IOException {
close();
}
private static int javaSoToOsOpt(int optID) {
switch (optID) {
case SocketOptions.SO_SNDBUF:
return OsConstants.SO_SNDBUF;
case SocketOptions.SO_RCVBUF:
return OsConstants.SO_RCVBUF;
case SocketOptions.SO_REUSEADDR:
return OsConstants.SO_REUSEADDR;
default:
throw new UnsupportedOperationException("Unknown option: " + optID);
}
}
}

View File

@@ -199,156 +199,6 @@ socket_shutdown (JNIEnv *env, jobject object, jobject fileDescriptor,
}
}
static bool
java_opt_to_real(int optID, int* opt, int* level)
{
switch (optID)
{
case 4098:
*opt = SO_RCVBUF;
*level = SOL_SOCKET;
return true;
case 4097:
*opt = SO_SNDBUF;
*level = SOL_SOCKET;
return true;
case 4102:
*opt = SO_SNDTIMEO;
*level = SOL_SOCKET;
return true;
case 128:
*opt = SO_LINGER;
*level = SOL_SOCKET;
return true;
case 1:
*opt = TCP_NODELAY;
*level = IPPROTO_TCP;
return true;
case 4:
*opt = SO_REUSEADDR;
*level = SOL_SOCKET;
return true;
}
return false;
}
static jint
socket_getOption(JNIEnv *env, jobject object, jobject fileDescriptor, jint optID)
{
int ret, value;
int opt, level;
int fd;
socklen_t size = sizeof(int);
if (!java_opt_to_real(optID, &opt, &level)) {
jniThrowIOException(env, -1);
return 0;
}
fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
if (env->ExceptionCheck()) {
return 0;
}
switch (opt)
{
case SO_LINGER:
{
struct linger lingr;
size = sizeof(lingr);
ret = getsockopt(fd, level, opt, &lingr, &size);
if (!lingr.l_onoff) {
value = -1;
} else {
value = lingr.l_linger;
}
break;
}
default:
ret = getsockopt(fd, level, opt, &value, &size);
break;
}
if (ret != 0) {
jniThrowIOException(env, errno);
return 0;
}
return value;
}
static void socket_setOption(
JNIEnv *env, jobject object, jobject fileDescriptor, jint optID,
jint boolValue, jint intValue) {
int ret;
int optname;
int level;
int fd;
if (!java_opt_to_real(optID, &optname, &level)) {
jniThrowIOException(env, -1);
return;
}
fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
if (env->ExceptionCheck()) {
return;
}
switch (optname) {
case SO_LINGER: {
/*
* SO_LINGER is special because it needs to use a special
* "linger" struct as well as use the incoming boolean
* argument specially.
*/
struct linger lingr;
lingr.l_onoff = boolValue ? 1 : 0; // Force it to be 0 or 1.
lingr.l_linger = intValue;
ret = setsockopt(fd, level, optname, &lingr, sizeof(lingr));
break;
}
case SO_SNDTIMEO: {
/*
* SO_TIMEOUT from the core library gets converted to
* SO_SNDTIMEO, but the option is supposed to set both
* send and receive timeouts. Note: The incoming timeout
* value is in milliseconds.
*/
struct timeval timeout;
timeout.tv_sec = intValue / 1000;
timeout.tv_usec = (intValue % 1000) * 1000;
ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO,
(void *)&timeout, sizeof(timeout));
if (ret == 0) {
ret = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO,
(void *)&timeout, sizeof(timeout));
}
break;
}
default: {
/*
* In all other cases, the translated option level and
* optname may be used directly for a call to setsockopt().
*/
ret = setsockopt(fd, level, optname, &intValue, sizeof(intValue));
break;
}
}
if (ret != 0) {
jniThrowIOException(env, errno);
return;
}
}
static jint socket_pending (JNIEnv *env, jobject object,
jobject fileDescriptor)
{
@@ -803,64 +653,11 @@ static jobject socket_get_peer_credentials(JNIEnv *env,
creds.pid, creds.uid, creds.gid);
}
#if 0
//TODO change this to return an instance of LocalSocketAddress
static jobject socket_getSockName(JNIEnv *env,
jobject object, jobject fileDescriptor)
{
int err;
int fd;
if (fileDescriptor == NULL) {
jniThrowNullPointerException(env, NULL);
return NULL;
}
fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
if (env->ExceptionCheck()) {
return NULL;
}
union {
struct sockaddr address;
struct sockaddr_un un_address;
} sa;
memset(&sa, 0, sizeof(sa));
socklen_t namelen = sizeof(sa);
err = getsockname(fd, &(sa.address), &namelen);
if (err < 0) {
jniThrowIOException(env, errno);
return NULL;
}
if (sa.address.sa_family != AF_UNIX) {
// We think we're an impl only for AF_UNIX, so this should never happen.
jniThrowIOException(env, EINVAL);
return NULL;
}
if (sa.un_address.sun_path[0] == '\0') {
} else {
}
}
#endif
/*
* JNI registration.
*/
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{"getOption_native", "(Ljava/io/FileDescriptor;I)I", (void*)socket_getOption},
{"setOption_native", "(Ljava/io/FileDescriptor;III)V", (void*)socket_setOption},
{"connectLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V",
(void*)socket_connect_local},
{"bindLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V", (void*)socket_bind_local},
@@ -876,9 +673,6 @@ static JNINativeMethod gMethods[] = {
{"getPeerCredentials_native",
"(Ljava/io/FileDescriptor;)Landroid/net/Credentials;",
(void*) socket_get_peer_credentials}
//,{"getSockName_native", "(Ljava/io/FileDescriptor;)Ljava/lang/String;",
// (void *) socket_getSockName}
};
int register_android_net_LocalSocketImpl(JNIEnv *env)