Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3e814f79 authored by Automerger Merge Worker's avatar Automerger Merge Worker Committed by Remi NGUYEN VAN
Browse files

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
Merged-In: I73e9c1919956aa665a8cccec11d3444e486426ec
(cherry picked from commit f5e20cdc)
parent 858f64b4
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -62,3 +62,15 @@ filegroup {
    ],
    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",
    ],
}
+20 −72
Original line number Diff line number Diff line
@@ -109,19 +109,16 @@ import android.net.shared.ProvisioningConfiguration.ScanResultInfo;
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.NetworkObserverRegistry;
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.MockitoAnnotations;
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.HashMap;
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 @@ public class IpClientIntegrationTest {
    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 @@ public class IpClientIntegrationTest {
    };
    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 class IpClientIntegrationTest {
    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 @@ public class IpClientIntegrationTest {
        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 @@ public class IpClientIntegrationTest {
            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 @@ public class IpClientIntegrationTest {
                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 @@ public class IpClientIntegrationTest {
            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 @@ public class IpClientIntegrationTest {
            assertEquals(NetworkInterface.getByName(mIfaceName).getMTU(), mtu);
        }

        if (shouldRemoveTapInterface) removeTapInterface(mPacketReader.createFd());
        if (shouldRemoveTapInterface) removeTapInterface(mTapFd);
        try {
            mIpc.shutdown();
            awaitIpClientShutdown();
@@ -845,12 +793,12 @@ public class IpClientIntegrationTest {

        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 @@ public class IpClientIntegrationTest {
    @Test
    public void testRestoreInitialInterfaceMtu_NotFoundInterfaceWhenStartingProvisioning()
            throws Exception {
        removeTapInterface(mPacketReader.createFd());
        removeTapInterface(mTapFd);
        ProvisioningConfiguration config = new ProvisioningConfiguration.Builder()
                .withoutIpReachabilityMonitor()
                .withoutIPv6()
@@ -1284,7 +1232,7 @@ public class IpClientIntegrationTest {
        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 @@ public class IpClientIntegrationTest {
        // 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 @@ public class IpClientIntegrationTest {
        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 @@ public class IpClientIntegrationTest {

        // 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,
+4 −0
Original line number Diff line number Diff line
@@ -32,8 +32,12 @@ java_library {
    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",
    ],
+76 −0
Original line number Diff line number Diff line
/*
 * 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);
        }
    }
}