summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErik Kline <ek@google.com>2015-06-05 17:37:45 +0900
committerErik Kline <ek@google.com>2015-06-09 16:19:24 +0900
commitd895999858db47e6f2d090a94b29749fa39278c2 (patch)
tree5bacc5298ee3dd3c5baa3457b88190ba8461f985
parente3d670dc1edc893e1baa98510f67bf929578d591 (diff)
downloadframeworks_base-d895999858db47e6f2d090a94b29749fa39278c2.zip
frameworks_base-d895999858db47e6f2d090a94b29749fa39278c2.tar.gz
frameworks_base-d895999858db47e6f2d090a94b29749fa39278c2.tar.bz2
Make public Network#bindSocket(FileDescriptor).
Code that uses android.system.Os to create sockets as FileDescriptors should be able to bind them to networks. Note that FileDescriptors could already be marked as "protected from VPNs" via NetworkUtils#protectFromVpn(), but heretofore were not easily bound to any particular network. Bug: 21449922 Change-Id: I4bb86db5d95d5a55bb2d7e245848d11eaa351e65
-rw-r--r--api/current.txt1
-rw-r--r--api/system-current.txt1
-rw-r--r--core/java/android/net/Network.java44
-rw-r--r--core/tests/coretests/src/android/net/NetworkTest.java96
4 files changed, 129 insertions, 13 deletions
diff --git a/api/current.txt b/api/current.txt
index 85c9ccd..0228d49 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -18329,6 +18329,7 @@ package android.net {
public class Network implements android.os.Parcelable {
method public void bindSocket(java.net.DatagramSocket) throws java.io.IOException;
method public void bindSocket(java.net.Socket) throws java.io.IOException;
+ method public void bindSocket(java.io.FileDescriptor) throws java.io.IOException;
method public int describeContents();
method public java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
method public java.net.InetAddress getByName(java.lang.String) throws java.net.UnknownHostException;
diff --git a/api/system-current.txt b/api/system-current.txt
index e416fe8..0d7a5b3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -19815,6 +19815,7 @@ package android.net {
public class Network implements android.os.Parcelable {
method public void bindSocket(java.net.DatagramSocket) throws java.io.IOException;
method public void bindSocket(java.net.Socket) throws java.io.IOException;
+ method public void bindSocket(java.io.FileDescriptor) throws java.io.IOException;
method public int describeContents();
method public java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
method public java.net.InetAddress getByName(java.lang.String) throws java.net.UnknownHostException;
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 754c6b3..9628bae 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -19,6 +19,8 @@ package android.net;
import android.os.Parcelable;
import android.os.Parcel;
import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -64,7 +66,7 @@ public class Network implements Parcelable {
// maybeInitHttpClient() must be called prior to reading either variable.
private volatile ConnectionPool mConnectionPool = null;
private volatile com.android.okhttp.internal.Network mNetwork = null;
- private Object mLock = new Object();
+ private final Object mLock = new Object();
// Default connection pool values. These are evaluated at startup, just
// like the OkHttp code. Also like the OkHttp code, we will throw parse
@@ -300,14 +302,10 @@ public class Network implements Parcelable {
* connected.
*/
public void bindSocket(DatagramSocket socket) throws IOException {
- // Apparently, the kernel doesn't update a connected UDP socket's routing upon mark changes.
- if (socket.isConnected()) {
- throw new SocketException("Socket is connected");
- }
// Query a property of the underlying socket to ensure that the socket's file descriptor
// exists, is available to bind to a network and is not closed.
socket.getReuseAddress();
- bindSocketFd(socket.getFileDescriptor$());
+ bindSocket(socket.getFileDescriptor$());
}
/**
@@ -316,18 +314,38 @@ public class Network implements Parcelable {
* {@link ConnectivityManager#bindProcessToNetwork}. The socket must not be connected.
*/
public void bindSocket(Socket socket) throws IOException {
- // Apparently, the kernel doesn't update a connected TCP socket's routing upon mark changes.
- if (socket.isConnected()) {
- throw new SocketException("Socket is connected");
- }
// Query a property of the underlying socket to ensure that the socket's file descriptor
// exists, is available to bind to a network and is not closed.
socket.getReuseAddress();
- bindSocketFd(socket.getFileDescriptor$());
+ bindSocket(socket.getFileDescriptor$());
}
- private void bindSocketFd(FileDescriptor fd) throws IOException {
- int err = NetworkUtils.bindSocketToNetwork(fd.getInt$(), netId);
+ /**
+ * Binds the specified {@link FileDescriptor} to this {@code Network}. All data traffic on the
+ * socket represented by this file descriptor will be sent on this {@code Network},
+ * irrespective of any process-wide network binding set by
+ * {@link ConnectivityManager#bindProcessToNetwork}. The socket must not be connected.
+ */
+ public void bindSocket(FileDescriptor fd) throws IOException {
+ try {
+ final SocketAddress peer = Os.getpeername(fd);
+ final InetAddress inetPeer = ((InetSocketAddress) peer).getAddress();
+ if (!inetPeer.isAnyLocalAddress()) {
+ // Apparently, the kernel doesn't update a connected UDP socket's
+ // routing upon mark changes.
+ throw new SocketException("Socket is connected");
+ }
+ } catch (ErrnoException e) {
+ // getpeername() failed.
+ if (e.errno != OsConstants.ENOTCONN) {
+ throw e.rethrowAsSocketException();
+ }
+ } catch (ClassCastException e) {
+ // Wasn't an InetSocketAddress.
+ throw new SocketException("Only AF_INET/AF_INET6 sockets supported");
+ }
+
+ final int err = NetworkUtils.bindSocketToNetwork(fd.getInt$(), netId);
if (err != 0) {
// bindSocketToNetwork returns negative errno.
throw new ErrnoException("Binding socket to network " + netId, -err)
diff --git a/core/tests/coretests/src/android/net/NetworkTest.java b/core/tests/coretests/src/android/net/NetworkTest.java
new file mode 100644
index 0000000..b0ecb04
--- /dev/null
+++ b/core/tests/coretests/src/android/net/NetworkTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2015 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 android.net.LocalServerSocket;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
+import android.net.Network;
+import android.test.suitebuilder.annotation.SmallTest;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.Inet6Address;
+import java.net.SocketException;
+import junit.framework.TestCase;
+
+public class NetworkTest extends TestCase {
+ final Network mNetwork = new Network(99);
+
+ @SmallTest
+ public void testBindSocketOfInvalidFdThrows() throws Exception {
+
+ final FileDescriptor fd = new FileDescriptor();
+ assertFalse(fd.valid());
+
+ try {
+ mNetwork.bindSocket(fd);
+ fail("SocketException not thrown");
+ } catch (SocketException expected) {}
+ }
+
+ @SmallTest
+ public void testBindSocketOfNonSocketFdThrows() throws Exception {
+ final File devNull = new File("/dev/null");
+ assertTrue(devNull.canRead());
+
+ final FileInputStream fis = new FileInputStream(devNull);
+ assertTrue(null != fis.getFD());
+ assertTrue(fis.getFD().valid());
+
+ try {
+ mNetwork.bindSocket(fis.getFD());
+ fail("SocketException not thrown");
+ } catch (SocketException expected) {}
+ }
+
+ @SmallTest
+ public void testBindSocketOfConnectedDatagramSocketThrows() throws Exception {
+ final DatagramSocket mDgramSocket = new DatagramSocket(0, (InetAddress) Inet6Address.ANY);
+ mDgramSocket.connect((InetAddress) Inet6Address.LOOPBACK, 53);
+ assertTrue(mDgramSocket.isConnected());
+
+ try {
+ mNetwork.bindSocket(mDgramSocket);
+ fail("SocketException not thrown");
+ } catch (SocketException expected) {}
+ }
+
+ @SmallTest
+ public void testBindSocketOfLocalSocketThrows() throws Exception {
+ final LocalSocket mLocalClient = new LocalSocket();
+ mLocalClient.bind(new LocalSocketAddress("testClient"));
+ assertTrue(mLocalClient.getFileDescriptor().valid());
+
+ try {
+ mNetwork.bindSocket(mLocalClient.getFileDescriptor());
+ fail("SocketException not thrown");
+ } catch (SocketException expected) {}
+
+ final LocalServerSocket mLocalServer = new LocalServerSocket("testServer");
+ mLocalClient.connect(mLocalServer.getLocalSocketAddress());
+ assertTrue(mLocalClient.isConnected());
+
+ try {
+ mNetwork.bindSocket(mLocalClient.getFileDescriptor());
+ fail("SocketException not thrown");
+ } catch (SocketException expected) {}
+ }
+}