Move TapPacketReader into its own utility class.
This class is generally useful for tests that manipulate packets
using a TestNetworkInterface. It will be used in upcoming
tethering tests. Also make it take a FileDescriptor instead of a
ParcelFileDescriptor, since that is more generally more useful.
As part of this change, create a filegroup of files that the test
utilities can depend on.
Bug: 150644681
Test: atest NetworkStackIntegrationTests:IpClientIntegrationTest
Change-Id: I73e9c1919956aa665a8cccec11d3444e486426ec
diff --git a/common/moduleutils/Android.bp b/common/moduleutils/Android.bp
index d0dd584..81475d2 100644
--- a/common/moduleutils/Android.bp
+++ b/common/moduleutils/Android.bp
@@ -62,3 +62,15 @@
],
visibility: ["//frameworks/base/packages/Tethering"],
}
+
+// Utility sources used by test libraries.
+// This is its own group to limit indiscriminate dependency of test code on production code.
+// TODO: move these classes and NetworkStack/tests/lib to frameworks/libs/net, and remove this.
+filegroup {
+ name: "net-module-utils-srcs-for-tests",
+ visibility: ["//packages/modules/NetworkStack/tests/lib"],
+ srcs: [
+ "src/android/net/util/FdEventsReader.java",
+ "src/android/net/util/PacketReader.java",
+ ],
+}
diff --git a/tests/integration/src/android/net/ip/IpClientIntegrationTest.java b/tests/integration/src/android/net/ip/IpClientIntegrationTest.java
index 97da9fb..0fa6266 100644
--- a/tests/integration/src/android/net/ip/IpClientIntegrationTest.java
+++ b/tests/integration/src/android/net/ip/IpClientIntegrationTest.java
@@ -109,19 +109,16 @@
import android.net.util.InterfaceParams;
import android.net.util.IpUtils;
import android.net.util.NetworkStackUtils;
-import android.net.util.PacketReader;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.system.ErrnoException;
import android.system.Os;
-import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -134,6 +131,7 @@
import com.android.server.NetworkStackService.NetworkStackServiceManager;
import com.android.server.connectivity.ipmemorystore.IpMemoryStoreService;
import com.android.testutils.HandlerUtilsKt;
+import com.android.testutils.TapPacketReader;
import org.junit.After;
import org.junit.Before;
@@ -145,7 +143,6 @@
import org.mockito.Spy;
import java.io.FileDescriptor;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
@@ -159,8 +156,6 @@
import java.util.List;
import java.util.Objects;
import java.util.Random;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
/**
* Tests for IpClient.
@@ -192,6 +187,7 @@
private HandlerThread mPacketReaderThread;
private Handler mHandler;
private TapPacketReader mPacketReader;
+ private FileDescriptor mTapFd;
private IpClient mIpc;
private Dependencies mDependencies;
private byte[] mClientMac;
@@ -238,46 +234,6 @@
};
private static final byte TEST_VENDOR_SPECIFIC_TYPE = 0x06;
- private static class TapPacketReader extends PacketReader {
- private final ParcelFileDescriptor mTapFd;
- private final LinkedBlockingQueue<byte[]> mReceivedPackets =
- new LinkedBlockingQueue<byte[]>();
-
- TapPacketReader(Handler h, ParcelFileDescriptor tapFd) {
- super(h, DATA_BUFFER_LEN);
- mTapFd = tapFd;
- }
-
- @Override
- protected FileDescriptor createFd() {
- return mTapFd.getFileDescriptor();
- }
-
- @Override
- protected void handlePacket(byte[] recvbuf, int length) {
- final byte[] newPacket = Arrays.copyOf(recvbuf, length);
- try {
- mReceivedPackets.put(newPacket);
- } catch (InterruptedException e) {
- fail("fail to put the new packet in the queue");
- }
- }
-
- /**
- * Get the next packet that was received on the interface.
- *
- */
- @Nullable
- public byte[] popPacket(long timeoutMs) {
- try {
- return mReceivedPackets.poll(timeoutMs, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- // Fall through
- }
- return null;
- }
- }
-
private class Dependencies extends IpClient.Dependencies {
private boolean mIsDhcpLeaseCacheEnabled;
private boolean mIsDhcpRapidCommitEnabled;
@@ -411,6 +367,7 @@
public void tearDown() throws Exception {
if (mPacketReader != null) {
mHandler.post(() -> mPacketReader.stop()); // Also closes the socket
+ mTapFd = null;
}
if (mPacketReaderThread != null) {
mPacketReaderThread.quitSafely();
@@ -440,8 +397,8 @@
mPacketReaderThread.start();
mHandler = mPacketReaderThread.getThreadHandler();
- final ParcelFileDescriptor tapFd = iface.getFileDescriptor();
- mPacketReader = new TapPacketReader(mHandler, tapFd);
+ mTapFd = iface.getFileDescriptor().getFileDescriptor();
+ mPacketReader = new TapPacketReader(mHandler, mTapFd, DATA_BUFFER_LEN);
mHandler.post(() -> mPacketReader.start());
}
@@ -532,21 +489,12 @@
false /* broadcast */, "duplicated request IP address");
}
- private void sendResponse(final ByteBuffer packet) throws IOException {
- try (FileOutputStream out = new FileOutputStream(mPacketReader.createFd())) {
- byte[] packetBytes = new byte[packet.limit()];
- packet.get(packetBytes);
- packet.flip(); // So we can reuse it in the future.
- out.write(packetBytes);
- }
- }
-
private void sendArpReply(final byte[] clientMac) throws IOException {
final ByteBuffer packet = ArpPacket.buildArpPacket(clientMac /* dst */,
SERVER_MAC /* src */, INADDR_ANY.getAddress() /* target IP */,
clientMac /* target HW address */, CLIENT_ADDR.getAddress() /* sender IP */,
(short) ARP_REPLY);
- sendResponse(packet);
+ mPacketReader.sendResponse(packet);
}
private void sendArpProbe() throws IOException {
@@ -554,7 +502,7 @@
SERVER_MAC /* src */, CLIENT_ADDR.getAddress() /* target IP */,
new byte[ETHER_ADDR_LEN] /* target HW address */,
INADDR_ANY.getAddress() /* sender IP */, (short) ARP_REQUEST);
- sendResponse(packet);
+ mPacketReader.sendResponse(packet);
}
private void startIpClientProvisioning(final boolean isDhcpLeaseCacheEnabled,
@@ -667,18 +615,18 @@
packetList.add(packet);
if (packet instanceof DhcpDiscoverPacket) {
if (shouldReplyRapidCommitAck) {
- sendResponse(buildDhcpAckPacket(packet, leaseTimeSec, (short) mtu,
+ mPacketReader.sendResponse(buildDhcpAckPacket(packet, leaseTimeSec, (short) mtu,
true /* rapidCommit */, captivePortalApiUrl));
} else {
- sendResponse(buildDhcpOfferPacket(packet, leaseTimeSec, (short) mtu,
- captivePortalApiUrl));
+ mPacketReader.sendResponse(buildDhcpOfferPacket(packet, leaseTimeSec,
+ (short) mtu, captivePortalApiUrl));
}
} else if (packet instanceof DhcpRequestPacket) {
final ByteBuffer byteBuffer = isSuccessLease
? buildDhcpAckPacket(packet, leaseTimeSec, (short) mtu,
false /* rapidCommit */, captivePortalApiUrl)
: buildDhcpNakPacket(packet);
- sendResponse(byteBuffer);
+ mPacketReader.sendResponse(byteBuffer);
} else {
fail("invalid DHCP packet");
}
@@ -777,7 +725,7 @@
assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), mtu);
}
- if (shouldRemoveTapInterface) removeTapInterface(mPacketReader.createFd());
+ if (shouldRemoveTapInterface) removeTapInterface(mTapFd);
try {
mIpc.shutdown();
awaitIpClientShutdown();
@@ -845,12 +793,12 @@
final short mtu = (short) TEST_DEFAULT_MTU;
if (!shouldReplyRapidCommitAck) {
- sendResponse(buildDhcpOfferPacket(packet, TEST_LEASE_DURATION_S, mtu,
+ mPacketReader.sendResponse(buildDhcpOfferPacket(packet, TEST_LEASE_DURATION_S, mtu,
null /* captivePortalUrl */));
packet = getNextDhcpPacket();
assertTrue(packet instanceof DhcpRequestPacket);
}
- sendResponse(buildDhcpAckPacket(packet, TEST_LEASE_DURATION_S, mtu,
+ mPacketReader.sendResponse(buildDhcpAckPacket(packet, TEST_LEASE_DURATION_S, mtu,
shouldReplyRapidCommitAck, null /* captivePortalUrl */));
if (!shouldAbortPreconnection) {
@@ -1126,7 +1074,7 @@
@Test
public void testRestoreInitialInterfaceMtu_NotFoundInterfaceWhenStartingProvisioning()
throws Exception {
- removeTapInterface(mPacketReader.createFd());
+ removeTapInterface(mTapFd);
ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
.withoutIpReachabilityMonitor()
.withoutIPv6()
@@ -1284,7 +1232,7 @@
ByteBuffer ra = buildRaPacket(pio, rdnss1, rdnss2);
waitForRouterSolicitation();
- sendResponse(ra);
+ mPacketReader.sendResponse(ra);
ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class);
verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture());
@@ -1299,7 +1247,7 @@
// If the RDNSS lifetime is above the minimum, the DNS server is accepted.
rdnss1 = buildRdnssOption(68, lowlifeDnsServer);
ra = buildRaPacket(pio, rdnss1, rdnss2);
- sendResponse(ra);
+ mPacketReader.sendResponse(ra);
verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(captor.capture());
lp = captor.getValue();
assertNotNull(lp);
@@ -1312,7 +1260,7 @@
rdnss1 = buildRdnssOption(0, dnsServer);
rdnss2 = buildRdnssOption(0, lowlifeDnsServer);
ra = buildRaPacket(pio, rdnss1, rdnss2);
- sendResponse(ra);
+ mPacketReader.sendResponse(ra);
verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(captor.capture());
lp = captor.getValue();
@@ -1546,8 +1494,8 @@
// Send Offer and handle Request -> Ack
final String serverSentUrl = serverSendsOption ? TEST_CAPTIVE_PORTAL_URL : null;
- sendResponse(buildDhcpOfferPacket(discover, TEST_LEASE_DURATION_S, (short) TEST_DEFAULT_MTU,
- serverSentUrl));
+ mPacketReader.sendResponse(buildDhcpOfferPacket(discover, TEST_LEASE_DURATION_S,
+ (short) TEST_DEFAULT_MTU, serverSentUrl));
final int testMtu = 1345;
handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S,
false /* isDhcpRapidCommitEnabled */, testMtu,
diff --git a/tests/lib/Android.bp b/tests/lib/Android.bp
index 249088f..d43243f 100644
--- a/tests/lib/Android.bp
+++ b/tests/lib/Android.bp
@@ -32,8 +32,12 @@
srcs: [
"src/**/*.java",
"src/**/*.kt",
+ ":net-module-utils-srcs-for-tests",
],
defaults: ["lib_mockito_extended"],
+ libs: [
+ "androidx.annotation_annotation",
+ ],
static_libs: [
"net-tests-utils-multivariant",
],
diff --git a/tests/lib/src/com/android/testutils/TapPacketReader.java b/tests/lib/src/com/android/testutils/TapPacketReader.java
new file mode 100644
index 0000000..c68e5a5
--- /dev/null
+++ b/tests/lib/src/com/android/testutils/TapPacketReader.java
@@ -0,0 +1,76 @@
+/*
+ * 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 com.android.testutils;
+
+import android.net.util.PacketReader;
+import android.os.Handler;
+
+import androidx.annotation.Nullable;
+
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class TapPacketReader extends PacketReader {
+ private final FileDescriptor mTapFd;
+ private final LinkedBlockingQueue<byte[]> mReceivedPackets = new LinkedBlockingQueue<byte[]>();
+
+ public TapPacketReader(Handler h, FileDescriptor tapFd, int maxPacketSize) {
+ super(h, maxPacketSize);
+ mTapFd = tapFd;
+ }
+
+ @Override
+ protected FileDescriptor createFd() {
+ return mTapFd;
+ }
+
+ @Override
+ protected void handlePacket(byte[] recvbuf, int length) {
+ final byte[] newPacket = Arrays.copyOf(recvbuf, length);
+ if (!mReceivedPackets.offer(newPacket)) {
+ throw new AssertionError("More than " + Integer.MAX_VALUE + " packets outstanding!");
+ }
+ }
+
+ /**
+ * Get the next packet that was received on the interface.
+ *
+ */
+ @Nullable
+ public byte[] popPacket(long timeoutMs) {
+ try {
+ return mReceivedPackets.poll(timeoutMs, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ // Fall through
+ }
+ return null;
+ }
+
+ public void sendResponse(final ByteBuffer packet) throws IOException {
+ try (FileOutputStream out = new FileOutputStream(mTapFd)) {
+ byte[] packetBytes = new byte[packet.limit()];
+ packet.get(packetBytes);
+ packet.flip(); // So we can reuse it in the future.
+ out.write(packetBytes);
+ }
+ }
+}