summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/android/net/dhcp/DhcpLeaseRepository.java22
-rw-r--r--src/android/net/dhcp/DhcpServer.java241
-rw-r--r--src/android/net/ip/IpClient.java10
-rw-r--r--tests/integration/src/android/net/netlink/InetDiagSocketIntegrationTest.java223
-rw-r--r--tests/unit/Android.bp2
-rw-r--r--tests/unit/AndroidManifest.xml11
-rw-r--r--tests/unit/src/android/net/dhcp/DhcpServerTest.java144
-rw-r--r--tests/unit/src/android/net/ip/IpClientTest.java3
-rw-r--r--tests/unit/src/android/net/netlink/InetDiagSocketTest.java188
-rw-r--r--tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java24
10 files changed, 552 insertions, 316 deletions
diff --git a/src/android/net/dhcp/DhcpLeaseRepository.java b/src/android/net/dhcp/DhcpLeaseRepository.java
index 3639a2d..b7a2572 100644
--- a/src/android/net/dhcp/DhcpLeaseRepository.java
+++ b/src/android/net/dhcp/DhcpLeaseRepository.java
@@ -37,6 +37,7 @@ import android.util.ArrayMap;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import java.net.Inet4Address;
import java.util.ArrayList;
@@ -158,7 +159,7 @@ class DhcpLeaseRepository {
/**
* From a map keyed by {@link Inet4Address}, remove entries where the key is invalid (as
* specified by {@link #isValidAddress(Inet4Address)}), or is a reserved address.
- * @return true iff at least one entry was removed.
+ * @return true if and only if at least one entry was removed.
*/
private <T> boolean cleanMap(Map<Inet4Address, T> map) {
final Iterator<Entry<Inet4Address, T>> it = map.entrySet().iterator();
@@ -397,7 +398,8 @@ class DhcpLeaseRepository {
mEventCallbacks.finishBroadcast();
}
- public void markLeaseDeclined(@NonNull Inet4Address addr) {
+ @VisibleForTesting
+ void markLeaseDeclined(@NonNull Inet4Address addr) {
if (mDeclinedAddrs.containsKey(addr) || !isValidAddress(addr)) {
mLog.logf("Not marking %s as declined: already declined or not assignable",
inet4AddrToString(addr));
@@ -410,6 +412,22 @@ class DhcpLeaseRepository {
}
/**
+ * Mark a committed lease matching the passed in clientId and hardware address parameters to be
+ * declined, and delete it from the repository.
+ *
+ * @param clientId Client identifier option if specified, or {@link #CLIENTID_UNSPEC}
+ * @param hwAddr client's mac address
+ * @param Addr IPv4 address to be declined
+ * @return true if a lease matching parameters was removed from committed repository.
+ */
+ public boolean markAndReleaseDeclinedLease(@Nullable byte[] clientId,
+ @NonNull MacAddress hwAddr, @NonNull Inet4Address addr) {
+ if (!releaseLease(clientId, hwAddr, addr)) return false;
+ markLeaseDeclined(addr);
+ return true;
+ }
+
+ /**
* Get the list of currently valid committed leases in the repository.
*/
@NonNull
diff --git a/src/android/net/dhcp/DhcpServer.java b/src/android/net/dhcp/DhcpServer.java
index 212e609..55b1f28 100644
--- a/src/android/net/dhcp/DhcpServer.java
+++ b/src/android/net/dhcp/DhcpServer.java
@@ -45,6 +45,7 @@ import static java.lang.Integer.toUnsignedLong;
import android.content.Context;
import android.net.INetworkStackStatusCallback;
+import android.net.IpPrefix;
import android.net.MacAddress;
import android.net.TrafficStats;
import android.net.util.NetworkStackUtils;
@@ -96,7 +97,8 @@ public class DhcpServer extends StateMachine {
private static final int CMD_START_DHCP_SERVER = 1;
private static final int CMD_STOP_DHCP_SERVER = 2;
private static final int CMD_UPDATE_PARAMS = 3;
- private static final int CMD_SUSPEND_DHCP_SERVER = 4;
+ @VisibleForTesting
+ protected static final int CMD_RECEIVE_PACKET = 4;
@NonNull
private final Context mContext;
@@ -126,6 +128,8 @@ public class DhcpServer extends StateMachine {
private final StoppedState mStoppedState = new StoppedState();
private final StartedState mStartedState = new StartedState();
private final RunningState mRunningState = new RunningState();
+ private final WaitBeforeRetrievalState mWaitBeforeRetrievalState =
+ new WaitBeforeRetrievalState();
/**
* Clock to be used by DhcpServer to track time for lease expiration.
@@ -262,6 +266,7 @@ public class DhcpServer extends StateMachine {
addState(mStoppedState);
addState(mStartedState);
addState(mRunningState, mStartedState);
+ addState(mWaitBeforeRetrievalState, mStartedState);
// CHECKSTYLE:ON IndentationCheck
setInitialState(mStoppedState);
@@ -372,6 +377,17 @@ public class DhcpServer extends StateMachine {
}
}
+ private void handleUpdateServingParams(@NonNull DhcpServingParams params,
+ @Nullable INetworkStackStatusCallback cb) {
+ mServingParams = params;
+ mLeaseRepo.updateParams(
+ DhcpServingParams.makeIpPrefix(params.serverAddr),
+ params.excludedAddrs,
+ params.dhcpLeaseTimeSecs * 1000,
+ params.singleClientAddr);
+ maybeNotifyStatus(cb, STATUS_SUCCESS);
+ }
+
class StoppedState extends State {
private INetworkStackStatusCallback mOnStopCallback;
@@ -422,6 +438,8 @@ public class DhcpServer extends StateMachine {
mLeaseRepo.addLeaseCallbacks(mEventCallbacks);
}
maybeNotifyStatus(mOnStartCallback, STATUS_SUCCESS);
+ // Clear INetworkStackStatusCallback binder token, so that it's freed
+ // on the other side.
mOnStartCallback = null;
}
@@ -431,14 +449,7 @@ public class DhcpServer extends StateMachine {
case CMD_UPDATE_PARAMS:
final Pair<DhcpServingParams, INetworkStackStatusCallback> pair =
(Pair<DhcpServingParams, INetworkStackStatusCallback>) msg.obj;
- final DhcpServingParams params = pair.first;
- mServingParams = params;
- mLeaseRepo.updateParams(
- DhcpServingParams.makeIpPrefix(params.serverAddr),
- params.excludedAddrs,
- params.dhcpLeaseTimeSecs * 1000,
- params.singleClientAddr);
- maybeNotifyStatus(pair.second, STATUS_SUCCESS);
+ handleUpdateServingParams(pair.first, pair.second);
return HANDLED;
case CMD_START_DHCP_SERVER:
@@ -466,100 +477,158 @@ public class DhcpServer extends StateMachine {
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
- case CMD_SUSPEND_DHCP_SERVER:
- // TODO: transition to the state which waits for IpServer to reconfigure the
- // new selected prefix.
+ case CMD_RECEIVE_PACKET:
+ processPacket((DhcpPacket) msg.obj);
return HANDLED;
+
default:
// Fall through to StartedState.
return NOT_HANDLED;
}
}
- }
- @VisibleForTesting
- void processPacket(@NonNull DhcpPacket packet, int srcPort) {
- final String packetType = packet.getClass().getSimpleName();
- if (srcPort != DHCP_CLIENT) {
- mLog.logf("Ignored packet of type %s sent from client port %d", packetType, srcPort);
- return;
- }
+ private void processPacket(@NonNull DhcpPacket packet) {
+ mLog.log("Received packet of type " + packet.getClass().getSimpleName());
- mLog.log("Received packet of type " + packetType);
- final Inet4Address sid = packet.mServerIdentifier;
- if (sid != null && !sid.equals(mServingParams.serverAddr.getAddress())) {
- mLog.log("Packet ignored due to wrong server identifier: " + sid);
- return;
- }
+ final Inet4Address sid = packet.mServerIdentifier;
+ if (sid != null && !sid.equals(mServingParams.serverAddr.getAddress())) {
+ mLog.log("Packet ignored due to wrong server identifier: " + sid);
+ return;
+ }
- try {
- if (packet instanceof DhcpDiscoverPacket) {
- processDiscover((DhcpDiscoverPacket) packet);
- } else if (packet instanceof DhcpRequestPacket) {
- processRequest((DhcpRequestPacket) packet);
- } else if (packet instanceof DhcpReleasePacket) {
- processRelease((DhcpReleasePacket) packet);
- } else {
- mLog.e("Unknown packet type: " + packet.getClass().getSimpleName());
+ try {
+ if (packet instanceof DhcpDiscoverPacket) {
+ processDiscover((DhcpDiscoverPacket) packet);
+ } else if (packet instanceof DhcpRequestPacket) {
+ processRequest((DhcpRequestPacket) packet);
+ } else if (packet instanceof DhcpReleasePacket) {
+ processRelease((DhcpReleasePacket) packet);
+ } else if (packet instanceof DhcpDeclinePacket) {
+ processDecline((DhcpDeclinePacket) packet);
+ } else {
+ mLog.e("Unknown packet type: " + packet.getClass().getSimpleName());
+ }
+ } catch (MalformedPacketException e) {
+ // Not an internal error: only logging exception message, not stacktrace
+ mLog.e("Ignored malformed packet: " + e.getMessage());
}
- } catch (MalformedPacketException e) {
+ }
+
+ private void logIgnoredPacketInvalidSubnet(DhcpLeaseRepository.InvalidSubnetException e) {
// Not an internal error: only logging exception message, not stacktrace
- mLog.e("Ignored malformed packet: " + e.getMessage());
+ mLog.e("Ignored packet from invalid subnet: " + e.getMessage());
}
- }
- private void logIgnoredPacketInvalidSubnet(DhcpLeaseRepository.InvalidSubnetException e) {
- // Not an internal error: only logging exception message, not stacktrace
- mLog.e("Ignored packet from invalid subnet: " + e.getMessage());
- }
+ private void processDiscover(@NonNull DhcpDiscoverPacket packet)
+ throws MalformedPacketException {
+ final DhcpLease lease;
+ final MacAddress clientMac = getMacAddr(packet);
+ try {
+ if (mDhcpRapidCommitEnabled && packet.mRapidCommit) {
+ lease = mLeaseRepo.getCommittedLease(packet.getExplicitClientIdOrNull(),
+ clientMac, packet.mRelayIp, packet.mHostName);
+ transmitAck(packet, lease, clientMac);
+ } else {
+ lease = mLeaseRepo.getOffer(packet.getExplicitClientIdOrNull(), clientMac,
+ packet.mRelayIp, packet.mRequestedIp, packet.mHostName);
+ transmitOffer(packet, lease, clientMac);
+ }
+ } catch (DhcpLeaseRepository.OutOfAddressesException e) {
+ transmitNak(packet, "Out of addresses to offer");
+ } catch (DhcpLeaseRepository.InvalidSubnetException e) {
+ logIgnoredPacketInvalidSubnet(e);
+ }
+ }
- private void processDiscover(@NonNull DhcpDiscoverPacket packet)
- throws MalformedPacketException {
- final DhcpLease lease;
- final MacAddress clientMac = getMacAddr(packet);
- try {
- if (mDhcpRapidCommitEnabled && packet.mRapidCommit) {
- lease = mLeaseRepo.getCommittedLease(packet.getExplicitClientIdOrNull(), clientMac,
- packet.mRelayIp, packet.mHostName);
- transmitAck(packet, lease, clientMac);
- } else {
- lease = mLeaseRepo.getOffer(packet.getExplicitClientIdOrNull(), clientMac,
- packet.mRelayIp, packet.mRequestedIp, packet.mHostName);
- transmitOffer(packet, lease, clientMac);
+ private void processRequest(@NonNull DhcpRequestPacket packet)
+ throws MalformedPacketException {
+ // If set, packet SID matches with this server's ID as checked in processPacket().
+ final boolean sidSet = packet.mServerIdentifier != null;
+ final DhcpLease lease;
+ final MacAddress clientMac = getMacAddr(packet);
+ try {
+ lease = mLeaseRepo.requestLease(packet.getExplicitClientIdOrNull(), clientMac,
+ packet.mClientIp, packet.mRelayIp, packet.mRequestedIp, sidSet,
+ packet.mHostName);
+ } catch (DhcpLeaseRepository.InvalidAddressException e) {
+ transmitNak(packet, "Invalid requested address");
+ return;
+ } catch (DhcpLeaseRepository.InvalidSubnetException e) {
+ logIgnoredPacketInvalidSubnet(e);
+ return;
}
- } catch (DhcpLeaseRepository.OutOfAddressesException e) {
- transmitNak(packet, "Out of addresses to offer");
- } catch (DhcpLeaseRepository.InvalidSubnetException e) {
- logIgnoredPacketInvalidSubnet(e);
+
+ transmitAck(packet, lease, clientMac);
}
- }
- private void processRequest(@NonNull DhcpRequestPacket packet) throws MalformedPacketException {
- // If set, packet SID matches with this server's ID as checked in processPacket().
- final boolean sidSet = packet.mServerIdentifier != null;
- final DhcpLease lease;
- final MacAddress clientMac = getMacAddr(packet);
- try {
- lease = mLeaseRepo.requestLease(packet.getExplicitClientIdOrNull(), clientMac,
- packet.mClientIp, packet.mRelayIp, packet.mRequestedIp, sidSet,
- packet.mHostName);
- } catch (DhcpLeaseRepository.InvalidAddressException e) {
- transmitNak(packet, "Invalid requested address");
- return;
- } catch (DhcpLeaseRepository.InvalidSubnetException e) {
- logIgnoredPacketInvalidSubnet(e);
- return;
+ private void processRelease(@NonNull DhcpReleasePacket packet)
+ throws MalformedPacketException {
+ final byte[] clientId = packet.getExplicitClientIdOrNull();
+ final MacAddress macAddr = getMacAddr(packet);
+ // Don't care about success (there is no ACK/NAK); logging is already done
+ // in the repository.
+ mLeaseRepo.releaseLease(clientId, macAddr, packet.mClientIp);
}
- transmitAck(packet, lease, clientMac);
+ private void processDecline(@NonNull DhcpDeclinePacket packet)
+ throws MalformedPacketException {
+ final byte[] clientId = packet.getExplicitClientIdOrNull();
+ final MacAddress macAddr = getMacAddr(packet);
+ int committedLeasesCount = mLeaseRepo.getCommittedLeases().size();
+
+ // If peer's clientID and macAddr doesn't match with any issued lease, nothing to do.
+ if (!mLeaseRepo.markAndReleaseDeclinedLease(clientId, macAddr, packet.mRequestedIp)) {
+ return;
+ }
+
+ // Check whether the boolean flag which requests a new prefix is enabled, and if
+ // it's enabled, make sure the issued lease count should be only one, otherwise,
+ // changing a different prefix will cause other exist host(s) configured with the
+ // current prefix lose appropriate route.
+ if (!mServingParams.changePrefixOnDecline || committedLeasesCount > 1) return;
+
+ if (mEventCallbacks == null) {
+ mLog.e("changePrefixOnDecline enabled but caller didn't pass a valid"
+ + "IDhcpEventCallbacks callback.");
+ return;
+ }
+
+ try {
+ mEventCallbacks.onNewPrefixRequest(
+ DhcpServingParams.makeIpPrefix(mServingParams.serverAddr));
+ transitionTo(mWaitBeforeRetrievalState);
+ } catch (RemoteException e) {
+ mLog.e("could not request a new prefix to caller", e);
+ }
+ }
}
- private void processRelease(@NonNull DhcpReleasePacket packet)
- throws MalformedPacketException {
- final byte[] clientId = packet.getExplicitClientIdOrNull();
- final MacAddress macAddr = getMacAddr(packet);
- // Don't care about success (there is no ACK/NAK); logging is already done in the repository
- mLeaseRepo.releaseLease(clientId, macAddr, packet.mClientIp);
+ class WaitBeforeRetrievalState extends State {
+ @Override
+ public boolean processMessage(Message msg) {
+ switch (msg.what) {
+ case CMD_UPDATE_PARAMS:
+ final Pair<DhcpServingParams, INetworkStackStatusCallback> pair =
+ (Pair<DhcpServingParams, INetworkStackStatusCallback>) msg.obj;
+ final IpPrefix currentPrefix =
+ DhcpServingParams.makeIpPrefix(mServingParams.serverAddr);
+ final IpPrefix newPrefix =
+ DhcpServingParams.makeIpPrefix(pair.first.serverAddr);
+ handleUpdateServingParams(pair.first, pair.second);
+ if (currentPrefix != null && !currentPrefix.equals(newPrefix)) {
+ transitionTo(mRunningState);
+ }
+ return HANDLED;
+
+ case CMD_RECEIVE_PACKET:
+ deferMessage(msg);
+ return HANDLED;
+
+ default:
+ // Fall through to StartedState.
+ return NOT_HANDLED;
+ }
+ }
}
private Inet4Address getAckOrOfferDst(@NonNull DhcpPacket request, @NonNull DhcpLease lease,
@@ -748,7 +817,13 @@ public class DhcpServer extends StateMachine {
@Override
protected void onReceive(@NonNull DhcpPacket packet, @NonNull Inet4Address srcAddr,
int srcPort) {
- processPacket(packet, srcPort);
+ if (srcPort != DHCP_CLIENT) {
+ final String packetType = packet.getClass().getSimpleName();
+ mLog.logf("Ignored packet of type %s sent from client port %d",
+ packetType, srcPort);
+ return;
+ }
+ sendMessage(CMD_RECEIVE_PACKET, packet);
}
@Override
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java
index 2079b2b..f3dcdc8 100644
--- a/src/android/net/ip/IpClient.java
+++ b/src/android/net/ip/IpClient.java
@@ -457,7 +457,7 @@ public class IpClient extends StateMachine {
private final SharedLog mLog;
private final LocalLog mConnectivityPacketLog;
private final MessageHandlingLogger mMsgStateLogger;
- private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
+ private final IpConnectivityLog mMetricsLog;
private final InterfaceController mInterfaceCtrl;
// Ignore nonzero RDNSS option lifetimes below this value. 0 = disabled.
@@ -536,6 +536,13 @@ public class IpClient extends StateMachine {
return NetworkStackUtils.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, name,
defaultValue);
}
+
+ /**
+ * Get a IpConnectivityLog instance.
+ */
+ public IpConnectivityLog getIpConnectivityLog() {
+ return new IpConnectivityLog();
+ }
}
public IpClient(Context context, String ifName, IIpClientCallbacks callback,
@@ -557,6 +564,7 @@ public class IpClient extends StateMachine {
mInterfaceName = ifName;
mClatInterfaceName = CLAT_PREFIX + ifName;
mDependencies = deps;
+ mMetricsLog = deps.getIpConnectivityLog();
mShutdownLatch = new CountDownLatch(1);
mCm = mContext.getSystemService(ConnectivityManager.class);
mObserverRegistry = observerRegistry;
diff --git a/tests/integration/src/android/net/netlink/InetDiagSocketIntegrationTest.java b/tests/integration/src/android/net/netlink/InetDiagSocketIntegrationTest.java
new file mode 100644
index 0000000..e474d8a
--- /dev/null
+++ b/tests/integration/src/android/net/netlink/InetDiagSocketIntegrationTest.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2020 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.netlink;
+
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.IPPROTO_TCP;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOCK_STREAM;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.os.Process;
+import android.system.Os;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.networkstack.apishim.common.ShimUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileDescriptor;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class InetDiagSocketIntegrationTest {
+ private ConnectivityManager mCm;
+ private Context mContext;
+
+ @Before
+ public void setUp() throws Exception {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ mContext = instrumentation.getTargetContext();
+ mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+
+ private class Connection {
+ public int socketDomain;
+ public int socketType;
+ public InetAddress localAddress;
+ public InetAddress remoteAddress;
+ public InetAddress localhostAddress;
+ public InetSocketAddress local;
+ public InetSocketAddress remote;
+ public int protocol;
+ public FileDescriptor localFd;
+ public FileDescriptor remoteFd;
+
+ public FileDescriptor createSocket() throws Exception {
+ return Os.socket(socketDomain, socketType, protocol);
+ }
+
+ Connection(String to, String from) throws Exception {
+ remoteAddress = InetAddress.getByName(to);
+ if (from != null) {
+ localAddress = InetAddress.getByName(from);
+ } else {
+ localAddress = (remoteAddress instanceof Inet4Address)
+ ? Inet4Address.getByName("localhost") : Inet6Address.getByName("::");
+ }
+ if ((localAddress instanceof Inet4Address) && (remoteAddress instanceof Inet4Address)) {
+ socketDomain = AF_INET;
+ localhostAddress = Inet4Address.getByName("localhost");
+ } else {
+ socketDomain = AF_INET6;
+ localhostAddress = Inet6Address.getByName("::");
+ }
+ }
+
+ public void close() throws Exception {
+ Os.close(localFd);
+ }
+ }
+
+ private class TcpConnection extends Connection {
+ TcpConnection(String to, String from) throws Exception {
+ super(to, from);
+ protocol = IPPROTO_TCP;
+ socketType = SOCK_STREAM;
+
+ remoteFd = createSocket();
+ Os.bind(remoteFd, remoteAddress, 0);
+ Os.listen(remoteFd, 10);
+ int remotePort = ((InetSocketAddress) Os.getsockname(remoteFd)).getPort();
+
+ localFd = createSocket();
+ Os.bind(localFd, localAddress, 0);
+ Os.connect(localFd, remoteAddress, remotePort);
+
+ local = (InetSocketAddress) Os.getsockname(localFd);
+ remote = (InetSocketAddress) Os.getpeername(localFd);
+ }
+
+ public void close() throws Exception {
+ super.close();
+ Os.close(remoteFd);
+ }
+ }
+ private class UdpConnection extends Connection {
+ UdpConnection(String to, String from) throws Exception {
+ super(to, from);
+ protocol = IPPROTO_UDP;
+ socketType = SOCK_DGRAM;
+
+ remoteFd = null;
+ localFd = createSocket();
+ Os.bind(localFd, localAddress, 0);
+
+ Os.connect(localFd, remoteAddress, 7);
+ local = (InetSocketAddress) Os.getsockname(localFd);
+ remote = new InetSocketAddress(remoteAddress, 7);
+ }
+ }
+
+ private void checkConnectionOwnerUid(int protocol, InetSocketAddress local,
+ InetSocketAddress remote, boolean expectSuccess) {
+ final int uid = mCm.getConnectionOwnerUid(protocol, local, remote);
+
+ if (expectSuccess) {
+ assertEquals(Process.myUid(), uid);
+ } else {
+ assertNotEquals(Process.myUid(), uid);
+ }
+ }
+
+ private int findLikelyFreeUdpPort(UdpConnection conn) throws Exception {
+ UdpConnection udp = new UdpConnection(conn.remoteAddress.getHostAddress(),
+ conn.localAddress.getHostAddress());
+ final int localPort = udp.local.getPort();
+ udp.close();
+ return localPort;
+ }
+
+ /**
+ * Create a test connection for UDP and TCP sockets and verify that this
+ * {protocol, local, remote} socket result in receiving a valid UID.
+ */
+ public void checkGetConnectionOwnerUid(String to, String from) throws Exception {
+ TcpConnection tcp = new TcpConnection(to, from);
+ checkConnectionOwnerUid(tcp.protocol, tcp.local, tcp.remote, true);
+ checkConnectionOwnerUid(IPPROTO_UDP, tcp.local, tcp.remote, false);
+ checkConnectionOwnerUid(tcp.protocol, new InetSocketAddress(0), tcp.remote, false);
+ checkConnectionOwnerUid(tcp.protocol, tcp.local, new InetSocketAddress(0), false);
+ tcp.close();
+
+ UdpConnection udp = new UdpConnection(to, from);
+ checkConnectionOwnerUid(udp.protocol, udp.local, udp.remote, true);
+ checkConnectionOwnerUid(IPPROTO_TCP, udp.local, udp.remote, false);
+ checkConnectionOwnerUid(udp.protocol, new InetSocketAddress(findLikelyFreeUdpPort(udp)),
+ udp.remote, false);
+ udp.close();
+ }
+
+ @Test
+ public void testGetConnectionOwnerUid() throws Exception {
+ // Skip the test for API <= Q, as b/141603906 this was only fixed in Q-QPR2
+ assumeTrue(ShimUtils.isAtLeastR());
+ checkGetConnectionOwnerUid("::", null);
+ checkGetConnectionOwnerUid("::", "::");
+ checkGetConnectionOwnerUid("0.0.0.0", null);
+ checkGetConnectionOwnerUid("0.0.0.0", "0.0.0.0");
+ checkGetConnectionOwnerUid("127.0.0.1", null);
+ checkGetConnectionOwnerUid("127.0.0.1", "127.0.0.2");
+ checkGetConnectionOwnerUid("::1", null);
+ checkGetConnectionOwnerUid("::1", "::1");
+ }
+
+ /* Verify fix for b/141603906 */
+ @Test
+ public void testB141603906() throws Exception {
+ // Skip the test for API <= Q, as b/141603906 this was only fixed in Q-QPR2
+ assumeTrue(ShimUtils.isAtLeastR());
+ final InetSocketAddress src = new InetSocketAddress(0);
+ final InetSocketAddress dst = new InetSocketAddress(0);
+ final int numThreads = 8;
+ final int numSockets = 5000;
+ final Thread[] threads = new Thread[numThreads];
+
+ for (int i = 0; i < numThreads; i++) {
+ threads[i] = new Thread(() -> {
+ for (int j = 0; j < numSockets; j++) {
+ mCm.getConnectionOwnerUid(IPPROTO_TCP, src, dst);
+ }
+ });
+ }
+
+ for (Thread thread : threads) {
+ thread.start();
+ }
+
+ for (Thread thread : threads) {
+ thread.join();
+ }
+ }
+}
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index 670b8e6..f6f4b86 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -16,7 +16,7 @@
java_defaults {
name: "NetworkStackTestsDefaults",
- certificate: "platform",
+ platform_apis: true,
srcs: ["src/**/*.java", "src/**/*.kt"],
resource_dirs: ["res"],
static_libs: [
diff --git a/tests/unit/AndroidManifest.xml b/tests/unit/AndroidManifest.xml
index f45cfc7..e3b232b 100644
--- a/tests/unit/AndroidManifest.xml
+++ b/tests/unit/AndroidManifest.xml
@@ -21,23 +21,12 @@
NetworkStackCoverageTests, which is not signed by the platform key,
and on Q rebooting the device would cause a bootloop because of
the missing priv-app whitelisting. -->
- <!-- TODO: many of the below permissions seem to be unused, remove them. -->
- <uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
- <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
- <uses-permission android:name="android.permission.GET_DETAILED_TASKS" />
- <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
- <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
- <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
- <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
- <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
- <uses-permission android:name="android.permission.NETWORK_STACK" />
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
diff --git a/tests/unit/src/android/net/dhcp/DhcpServerTest.java b/tests/unit/src/android/net/dhcp/DhcpServerTest.java
index c925789..2e8a3c2 100644
--- a/tests/unit/src/android/net/dhcp/DhcpServerTest.java
+++ b/tests/unit/src/android/net/dhcp/DhcpServerTest.java
@@ -17,12 +17,13 @@
package android.net.dhcp;
import static android.net.InetAddresses.parseNumericAddress;
-import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
import static android.net.dhcp.DhcpPacket.INADDR_ANY;
import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
+import static android.net.dhcp.DhcpServer.CMD_RECEIVE_PACKET;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
+import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH;
import static android.net.util.NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION;
import static junit.framework.Assert.assertEquals;
@@ -31,7 +32,6 @@ import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doNothing;
@@ -44,12 +44,14 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.net.INetworkStackStatusCallback;
+import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.MacAddress;
import android.net.dhcp.DhcpLeaseRepository.InvalidAddressException;
import android.net.dhcp.DhcpLeaseRepository.OutOfAddressesException;
import android.net.dhcp.DhcpServer.Clock;
import android.net.dhcp.DhcpServer.Dependencies;
+import android.net.shared.Inet4AddressUtils;
import android.net.util.SharedLog;
import android.testing.AndroidTestingRunner;
@@ -69,6 +71,8 @@ import org.mockito.MockitoAnnotations;
import java.net.Inet4Address;
import java.nio.ByteBuffer;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -156,6 +160,7 @@ public class DhcpServerTest {
.setServerAddr(TEST_SERVER_LINKADDR)
.setLinkMtu(TEST_MTU)
.setExcludedAddrs(TEST_EXCLUDED_ADDRS)
+ .setChangePrefixOnDecline(false)
.build();
}
@@ -215,7 +220,8 @@ public class DhcpServerTest {
final DhcpDiscoverPacket discover = new DhcpDiscoverPacket(TEST_TRANSACTION_ID,
(short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
false /* broadcast */, INADDR_ANY /* srcIp */, false /* rapidCommit */);
- mServer.processPacket(discover, DHCP_CLIENT);
+ mServer.sendMessage(CMD_RECEIVE_PACKET, discover);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
assertResponseSentTo(TEST_CLIENT_ADDR);
final DhcpOfferPacket packet = assertOffer(getPacket());
@@ -233,7 +239,8 @@ public class DhcpServerTest {
final DhcpDiscoverPacket discover = new DhcpDiscoverPacket(TEST_TRANSACTION_ID,
(short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
false /* broadcast */, INADDR_ANY /* srcIp */, true /* rapidCommit */);
- mServer.processPacket(discover, DHCP_CLIENT);
+ mServer.sendMessage(CMD_RECEIVE_PACKET, discover);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
assertResponseSentTo(TEST_CLIENT_ADDR);
final DhcpAckPacket packet = assertAck(getPacket());
@@ -251,7 +258,8 @@ public class DhcpServerTest {
final DhcpDiscoverPacket discover = new DhcpDiscoverPacket(TEST_TRANSACTION_ID,
(short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
false /* broadcast */, INADDR_ANY /* srcIp */, false /* rapidCommit */);
- mServer.processPacket(discover, DHCP_CLIENT);
+ mServer.sendMessage(CMD_RECEIVE_PACKET, discover);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
assertResponseSentTo(INADDR_BROADCAST);
final DhcpNakPacket packet = assertNak(getPacket());
@@ -279,7 +287,8 @@ public class DhcpServerTest {
final DhcpRequestPacket request = makeRequestSelectingPacket();
request.mHostName = TEST_HOSTNAME;
request.mRequestedParams = new byte[] { DHCP_HOST_NAME };
- mServer.processPacket(request, DHCP_CLIENT);
+ mServer.sendMessage(CMD_RECEIVE_PACKET, request);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
assertResponseSentTo(TEST_CLIENT_ADDR);
final DhcpAckPacket packet = assertAck(getPacket());
@@ -296,7 +305,8 @@ public class DhcpServerTest {
.thenThrow(new InvalidAddressException("Test error"));
final DhcpRequestPacket request = makeRequestSelectingPacket();
- mServer.processPacket(request, DHCP_CLIENT);
+ mServer.sendMessage(CMD_RECEIVE_PACKET, request);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
assertResponseSentTo(INADDR_BROADCAST);
final DhcpNakPacket packet = assertNak(getPacket());
@@ -304,46 +314,134 @@ public class DhcpServerTest {
}
@Test
- public void testRequest_Selecting_WrongClientPort() throws Exception {
- startServer();
-
- final DhcpRequestPacket request = makeRequestSelectingPacket();
- mServer.processPacket(request, 50000);
-
- verify(mRepository, never())
- .requestLease(any(), any(), any(), any(), any(), anyBoolean(), any());
- verify(mDeps, never()).sendPacket(any(), any(), any());
- }
-
- @Test
public void testRelease() throws Exception {
startServer();
final DhcpReleasePacket release = new DhcpReleasePacket(TEST_TRANSACTION_ID,
TEST_SERVER_ADDR, TEST_CLIENT_ADDR,
INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES);
- mServer.processPacket(release, DHCP_CLIENT);
+ mServer.sendMessage(CMD_RECEIVE_PACKET, release);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
verify(mRepository, times(1))
.releaseLease(isNull(), eq(TEST_CLIENT_MAC), eq(TEST_CLIENT_ADDR));
}
+ @Test
+ public void testDecline_LeaseDoesNotExist() throws Exception {
+ when(mRepository.markAndReleaseDeclinedLease(isNull(), eq(TEST_CLIENT_MAC),
+ eq(TEST_CLIENT_ADDR))).thenReturn(false);
+
+ startServer();
+ runOnReceivedDeclinePacket();
+ verify(mEventCallbacks, never()).onNewPrefixRequest(any());
+ }
+
+ private void runOnReceivedDeclinePacket() throws Exception {
+ when(mRepository.getCommittedLeases()).thenReturn(
+ Arrays.asList(new DhcpLease(null, TEST_CLIENT_MAC, TEST_CLIENT_ADDR,
+ TEST_PREFIX_LENGTH, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME,
+ TEST_HOSTNAME)));
+ final DhcpDeclinePacket decline = new DhcpDeclinePacket(TEST_TRANSACTION_ID,
+ (short) 0 /* secs */, INADDR_ANY /* clientIp */, INADDR_ANY /* yourIp */,
+ INADDR_ANY /* nextIp */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
+ TEST_CLIENT_ADDR /* requestedIp */, TEST_SERVER_ADDR /* serverIdentifier */);
+ mServer.sendMessage(CMD_RECEIVE_PACKET, decline);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+
+ verify(mRepository).markAndReleaseDeclinedLease(isNull(), eq(TEST_CLIENT_MAC),
+ eq(TEST_CLIENT_ADDR));
+ }
+
+ private int[] toIntArray(@NonNull Collection<Inet4Address> addrs) {
+ return addrs.stream().mapToInt(Inet4AddressUtils::inet4AddressToIntHTH).toArray();
+ }
+
+ private void updateServingParams(Set<Inet4Address> defaultRouters,
+ Set<Inet4Address> dnsServers, Set<Inet4Address> excludedAddrs, LinkAddress serverAddr,
+ boolean changePrefixOnDecline) throws Exception {
+ final DhcpServingParamsParcel params = new DhcpServingParamsParcel();
+ params.serverAddr = inet4AddressToIntHTH((Inet4Address) serverAddr.getAddress());
+ params.serverAddrPrefixLength = serverAddr.getPrefixLength();
+ params.defaultRouters = toIntArray(defaultRouters);
+ params.dnsServers = toIntArray(dnsServers);
+ params.excludedAddrs = toIntArray(excludedAddrs);
+ params.dhcpLeaseTimeSecs = TEST_LEASE_EXPTIME_SECS;
+ params.linkMtu = TEST_MTU;
+ params.metered = true;
+ params.changePrefixOnDecline = changePrefixOnDecline;
+
+ mServer.updateParams(params, mAssertSuccessCallback);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+ }
+
+ @Test
+ public void testChangePrefixOnDecline() throws Exception {
+ when(mRepository.markAndReleaseDeclinedLease(isNull(), eq(TEST_CLIENT_MAC),
+ eq(TEST_CLIENT_ADDR))).thenReturn(true);
+
+ mServer.start(mAssertSuccessCallback, mEventCallbacks);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+ verify(mRepository).addLeaseCallbacks(eq(mEventCallbacks));
+
+ // Enable changePrefixOnDecline
+ updateServingParams(TEST_DEFAULT_ROUTERS, TEST_DNS_SERVERS, TEST_EXCLUDED_ADDRS,
+ TEST_SERVER_LINKADDR, true /* changePrefixOnDecline */);
+
+ runOnReceivedDeclinePacket();
+ final IpPrefix servingPrefix = DhcpServingParams.makeIpPrefix(TEST_SERVER_LINKADDR);
+ verify(mEventCallbacks).onNewPrefixRequest(eq(servingPrefix));
+
+ final Inet4Address serverAddr = parseAddr("192.168.51.129");
+ final LinkAddress srvLinkAddr = new LinkAddress(serverAddr, 24);
+ final Set<Inet4Address> srvAddr = new HashSet<>(Collections.singletonList(serverAddr));
+ final Set<Inet4Address> excludedAddrs = new HashSet<>(
+ Arrays.asList(parseAddr("192.168.51.200"), parseAddr("192.168.51.201")));
+
+ // Simulate IpServer updates the serving params with a new prefix.
+ updateServingParams(srvAddr, srvAddr, excludedAddrs, srvLinkAddr,
+ true /* changePrefixOnDecline */);
+
+ final Inet4Address clientAddr = parseAddr("192.168.51.42");
+ final DhcpLease lease = new DhcpLease(null, TEST_CLIENT_MAC,
+ clientAddr, 24 /*prefixLen*/, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME,
+ null /* hostname */);
+ when(mRepository.getOffer(isNull() /* clientId */, eq(TEST_CLIENT_MAC),
+ eq(INADDR_ANY) /* relayAddr */, isNull() /* reqAddr */, isNull() /* hostname */))
+ .thenReturn(lease);
+
+ // Test discover packet
+ final DhcpDiscoverPacket discover = new DhcpDiscoverPacket(TEST_TRANSACTION_ID,
+ (short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
+ false /* broadcast */, INADDR_ANY /* srcIp */, false /* rapidCommit */);
+ mServer.sendMessage(CMD_RECEIVE_PACKET, discover);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+ assertResponseSentTo(clientAddr);
+ final DhcpOfferPacket packet = assertOffer(getPacket());
+ assertMatchesLease(packet, serverAddr, clientAddr, null);
+ }
+
/* TODO: add more tests once packet construction is refactored, including:
* - usage of giaddr
* - usage of broadcast bit
* - other request states (init-reboot/renewing/rebinding)
*/
- private void assertMatchesTestLease(@NonNull DhcpPacket packet, @Nullable String hostname) {
+ private void assertMatchesLease(@NonNull DhcpPacket packet, @NonNull Inet4Address srvAddr,
+ @NonNull Inet4Address clientAddr, @Nullable String hostname) {
assertMatchesClient(packet);
assertFalse(packet.hasExplicitClientId());
- assertEquals(TEST_SERVER_ADDR, packet.mServerIdentifier);
- assertEquals(TEST_CLIENT_ADDR, packet.mYourIp);
+ assertEquals(srvAddr, packet.mServerIdentifier);
+ assertEquals(clientAddr, packet.mYourIp);
assertNotNull(packet.mLeaseTime);
assertEquals(TEST_LEASE_EXPTIME_SECS, (int) packet.mLeaseTime);
assertEquals(hostname, packet.mHostName);
}
+ private void assertMatchesTestLease(@NonNull DhcpPacket packet, @Nullable String hostname) {
+ assertMatchesLease(packet, TEST_SERVER_ADDR, TEST_CLIENT_ADDR, hostname);
+ }
+
private void assertMatchesTestLease(@NonNull DhcpPacket packet) {
assertMatchesTestLease(packet, null);
}
diff --git a/tests/unit/src/android/net/ip/IpClientTest.java b/tests/unit/src/android/net/ip/IpClientTest.java
index 5fa5a6c..fe6dddb 100644
--- a/tests/unit/src/android/net/ip/IpClientTest.java
+++ b/tests/unit/src/android/net/ip/IpClientTest.java
@@ -49,6 +49,7 @@ import android.net.MacAddress;
import android.net.NetworkStackIpMemoryStore;
import android.net.RouteInfo;
import android.net.ipmemorystore.NetworkAttributes;
+import android.net.metrics.IpConnectivityLog;
import android.net.shared.InitialConfiguration;
import android.net.shared.ProvisioningConfiguration;
import android.net.util.InterfaceParams;
@@ -120,6 +121,7 @@ public class IpClientTest {
@Mock private NetworkStackIpMemoryStore mIpMemoryStore;
@Mock private IpMemoryStoreService mIpMemoryStoreService;
@Mock private InterfaceParams mInterfaceParams;
+ @Mock private IpConnectivityLog mMetricsLog;
private NetworkObserver mObserver;
private InterfaceParams mIfParams;
@@ -139,6 +141,7 @@ public class IpClientTest {
when(mDependencies.getInterfaceParams(any())).thenReturn(mInterfaceParams);
when(mDependencies.getIpMemoryStore(mContext, mNetworkStackServiceManager))
.thenReturn(mIpMemoryStore);
+ when(mDependencies.getIpConnectivityLog()).thenReturn(mMetricsLog);
mIfParams = null;
}
diff --git a/tests/unit/src/android/net/netlink/InetDiagSocketTest.java b/tests/unit/src/android/net/netlink/InetDiagSocketTest.java
index 4879144..3478276 100644
--- a/tests/unit/src/android/net/netlink/InetDiagSocketTest.java
+++ b/tests/unit/src/android/net/netlink/InetDiagSocketTest.java
@@ -22,38 +22,21 @@ import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_INET6;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
-import static android.system.OsConstants.SOCK_DGRAM;
-import static android.system.OsConstants.SOCK_STREAM;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
-import android.app.Instrumentation;
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.os.Process;
-import android.system.Os;
-
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.networkstack.apishim.common.ShimUtils;
-
import libcore.util.HexEncoding;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.FileDescriptor;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
@@ -62,177 +45,6 @@ import java.nio.ByteOrder;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class InetDiagSocketTest {
- private final String TAG = "InetDiagSocketTest";
- private ConnectivityManager mCm;
- private Context mContext;
- private final static int SOCKET_TIMEOUT_MS = 100;
-
- @Before
- public void setUp() throws Exception {
- Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- mContext = instrumentation.getTargetContext();
- mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- }
-
- private class Connection {
- public int socketDomain;
- public int socketType;
- public InetAddress localAddress;
- public InetAddress remoteAddress;
- public InetAddress localhostAddress;
- public InetSocketAddress local;
- public InetSocketAddress remote;
- public int protocol;
- public FileDescriptor localFd;
- public FileDescriptor remoteFd;
-
- public FileDescriptor createSocket() throws Exception {
- return Os.socket(socketDomain, socketType, protocol);
- }
-
- public Connection(String to, String from) throws Exception {
- remoteAddress = InetAddress.getByName(to);
- if (from != null) {
- localAddress = InetAddress.getByName(from);
- } else {
- localAddress = (remoteAddress instanceof Inet4Address) ?
- Inet4Address.getByName("localhost") : Inet6Address.getByName("::");
- }
- if ((localAddress instanceof Inet4Address) && (remoteAddress instanceof Inet4Address)) {
- socketDomain = AF_INET;
- localhostAddress = Inet4Address.getByName("localhost");
- } else {
- socketDomain = AF_INET6;
- localhostAddress = Inet6Address.getByName("::");
- }
- }
-
- public void close() throws Exception {
- Os.close(localFd);
- }
- }
-
- private class TcpConnection extends Connection {
- public TcpConnection(String to, String from) throws Exception {
- super(to, from);
- protocol = IPPROTO_TCP;
- socketType = SOCK_STREAM;
-
- remoteFd = createSocket();
- Os.bind(remoteFd, remoteAddress, 0);
- Os.listen(remoteFd, 10);
- int remotePort = ((InetSocketAddress) Os.getsockname(remoteFd)).getPort();
-
- localFd = createSocket();
- Os.bind(localFd, localAddress, 0);
- Os.connect(localFd, remoteAddress, remotePort);
-
- local = (InetSocketAddress) Os.getsockname(localFd);
- remote = (InetSocketAddress) Os.getpeername(localFd);
- }
-
- public void close() throws Exception {
- super.close();
- Os.close(remoteFd);
- }
- }
- private class UdpConnection extends Connection {
- public UdpConnection(String to, String from) throws Exception {
- super(to, from);
- protocol = IPPROTO_UDP;
- socketType = SOCK_DGRAM;
-
- remoteFd = null;
- localFd = createSocket();
- Os.bind(localFd, localAddress, 0);
-
- Os.connect(localFd, remoteAddress, 7);
- local = (InetSocketAddress) Os.getsockname(localFd);
- remote = new InetSocketAddress(remoteAddress, 7);
- }
- }
-
- private void checkConnectionOwnerUid(int protocol, InetSocketAddress local,
- InetSocketAddress remote, boolean expectSuccess) {
- final int uid = mCm.getConnectionOwnerUid(protocol, local, remote);
-
- if (expectSuccess) {
- assertEquals(Process.myUid(), uid);
- } else {
- assertNotEquals(Process.myUid(), uid);
- }
- }
-
- private int findLikelyFreeUdpPort(UdpConnection conn) throws Exception {
- UdpConnection udp = new UdpConnection(conn.remoteAddress.getHostAddress(),
- conn.localAddress.getHostAddress());
- final int localPort = udp.local.getPort();
- udp.close();
- return localPort;
- }
-
- /**
- * Create a test connection for UDP and TCP sockets and verify that this
- * {protocol, local, remote} socket result in receiving a valid UID.
- */
- public void checkGetConnectionOwnerUid(String to, String from) throws Exception {
- TcpConnection tcp = new TcpConnection(to, from);
- checkConnectionOwnerUid(tcp.protocol, tcp.local, tcp.remote, true);
- checkConnectionOwnerUid(IPPROTO_UDP, tcp.local, tcp.remote, false);
- checkConnectionOwnerUid(tcp.protocol, new InetSocketAddress(0), tcp.remote, false);
- checkConnectionOwnerUid(tcp.protocol, tcp.local, new InetSocketAddress(0), false);
- tcp.close();
-
- UdpConnection udp = new UdpConnection(to,from);
- checkConnectionOwnerUid(udp.protocol, udp.local, udp.remote, true);
- checkConnectionOwnerUid(IPPROTO_TCP, udp.local, udp.remote, false);
- checkConnectionOwnerUid(udp.protocol, new InetSocketAddress(findLikelyFreeUdpPort(udp)),
- udp.remote, false);
- udp.close();
- }
-
- @Test
- public void testGetConnectionOwnerUid() throws Exception {
- // Skip the test for API <= Q, as b/141603906 this was only fixed in Q-QPR2
- assumeTrue(ShimUtils.isAtLeastR());
- checkGetConnectionOwnerUid("::", null);
- checkGetConnectionOwnerUid("::", "::");
- checkGetConnectionOwnerUid("0.0.0.0", null);
- checkGetConnectionOwnerUid("0.0.0.0", "0.0.0.0");
- checkGetConnectionOwnerUid("127.0.0.1", null);
- checkGetConnectionOwnerUid("127.0.0.1", "127.0.0.2");
- checkGetConnectionOwnerUid("::1", null);
- checkGetConnectionOwnerUid("::1", "::1");
- }
-
- /* Verify fix for b/141603906 */
- @Test
- public void testB141603906() throws Exception {
- // Skip the test for API <= Q, as b/141603906 this was only fixed in Q-QPR2
- assumeTrue(ShimUtils.isAtLeastR());
- final InetSocketAddress src = new InetSocketAddress(0);
- final InetSocketAddress dst = new InetSocketAddress(0);
- final int numThreads = 8;
- final int numSockets = 5000;
- final Thread[] threads = new Thread[numThreads];
-
- for (int i = 0; i < numThreads; i++) {
- threads[i] = new Thread(() -> {
- for (int j = 0; j < numSockets; j++) {
- mCm.getConnectionOwnerUid(IPPROTO_TCP, src, dst);
- }
- });
- }
-
- for (Thread thread : threads) {
- thread.start();
- }
-
- for (Thread thread : threads) {
- thread.join();
- }
- }
-
// Hexadecimal representation of InetDiagReqV2 request.
private static final String INET_DIAG_REQ_V2_UDP_INET4_HEX =
// struct nlmsghdr
diff --git a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
index dace986..dda7f08 100644
--- a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -69,6 +69,8 @@ import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
@@ -1895,7 +1897,6 @@ public class NetworkMonitorTest {
setStatus(mHttpConnection, 500);
setStatus(mFallbackConnection, 204);
nm.forceReevaluation(Process.myUid());
- final ArgumentCaptor<Integer> intCaptor = ArgumentCaptor.forClass(Integer.class);
// Expect to send HTTP, HTTPs, FALLBACK and evaluation results.
verifyNetworkTested(NETWORK_VALIDATION_RESULT_PARTIAL,
NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_FALLBACK);
@@ -2085,8 +2086,11 @@ public class NetworkMonitorTest {
setPortal302(mOtherHttpConnection1);
runPortalNetworkTest();
// Get conclusive result from one of the HTTP probe. Expect to create 2 HTTP and 2 HTTPS
- // probes as resource configuration.
- verify(mCleartextDnsNetwork, times(4)).openConnection(any());
+ // probes as resource configuration, but the portal can be detected before other probes
+ // start.
+ verify(mCleartextDnsNetwork, atMost(4)).openConnection(any());
+ verify(mCleartextDnsNetwork, atLeastOnce()).openConnection(any());
+ verify(mOtherHttpConnection1).getResponseCode();
}
@Test
@@ -2096,12 +2100,15 @@ public class NetworkMonitorTest {
setStatus(mOtherHttpsConnection2, 204);
runValidatedNetworkTest();
// Get conclusive result from one of the HTTPS probe. Expect to create 2 HTTP and 2 HTTPS
- // probes as resource configuration.
- verify(mCleartextDnsNetwork, times(4)).openConnection(any());
+ // probes as resource configuration, but the network may validate from the HTTPS probe
+ // before other probes start.
+ verify(mCleartextDnsNetwork, atMost(4)).openConnection(any());
+ verify(mCleartextDnsNetwork, atLeastOnce()).openConnection(any());
+ verify(mOtherHttpsConnection2).getResponseCode();
}
@Test
- public void testMultipleProbesOnInValiadNetworkForPrioritizedResource() throws Exception {
+ public void testMultipleProbesOnInValidNetworkForPrioritizedResource() throws Exception {
setupResourceForMultipleProbes();
// The configuration resource is prioritized. Only use configurations from resource.(i.e
// Only configuration for mOtherHttpsConnection2, mOtherHttpsConnection2,
@@ -2110,8 +2117,11 @@ public class NetworkMonitorTest {
setStatus(mHttpsConnection, 204);
runFailedNetworkTest();
// No conclusive result from both HTTP and HTTPS probes. Expect to create 2 HTTP and 2 HTTPS
- // probes as resource configuration.
+ // probes as resource configuration. All probes are expected to have been run because this
+ // network is set to never validate (no probe has a success or portal result), so NM tests
+ // all probes to completion.
verify(mCleartextDnsNetwork, times(4)).openConnection(any());
+ verify(mHttpsConnection, never()).getResponseCode();
}
@Test