diff options
-rw-r--r-- | src/android/net/dhcp/DhcpLeaseRepository.java | 22 | ||||
-rw-r--r-- | src/android/net/dhcp/DhcpServer.java | 241 | ||||
-rw-r--r-- | src/android/net/ip/IpClient.java | 10 | ||||
-rw-r--r-- | tests/integration/src/android/net/netlink/InetDiagSocketIntegrationTest.java | 223 | ||||
-rw-r--r-- | tests/unit/Android.bp | 2 | ||||
-rw-r--r-- | tests/unit/AndroidManifest.xml | 11 | ||||
-rw-r--r-- | tests/unit/src/android/net/dhcp/DhcpServerTest.java | 144 | ||||
-rw-r--r-- | tests/unit/src/android/net/ip/IpClientTest.java | 3 | ||||
-rw-r--r-- | tests/unit/src/android/net/netlink/InetDiagSocketTest.java | 188 | ||||
-rw-r--r-- | tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java | 24 |
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 |