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

Commit f77020b2 authored by Mark Chien's avatar Mark Chien Committed by Gerrit Code Review
Browse files

Merge "Add test for OffloadHardwareInterface"

parents bb11b35c 2c5c657a
Loading
Loading
Loading
Loading
+73 −51
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ public class OffloadHardwareInterface {

    private final Handler mHandler;
    private final SharedLog mLog;
    private final Dependencies mDeps;
    private IOffloadControl mOffloadControl;
    private TetheringOffloadCallback mTetheringOffloadCallback;
    private ControlCallback mControlCallback;
@@ -126,8 +127,76 @@ public class OffloadHardwareInterface {
    }

    public OffloadHardwareInterface(Handler h, SharedLog log) {
        this(h, log, new Dependencies(log));
    }

    OffloadHardwareInterface(Handler h, SharedLog log, Dependencies deps) {
        mHandler = h;
        mLog = log.forSubComponent(TAG);
        mDeps = deps;
    }

    /** Capture OffloadHardwareInterface dependencies, for injection. */
    static class Dependencies {
        private final SharedLog mLog;

        Dependencies(SharedLog log) {
            mLog = log;
        }

        public IOffloadConfig getOffloadConfig() {
            try {
                return IOffloadConfig.getService(true /*retry*/);
            } catch (RemoteException | NoSuchElementException e) {
                mLog.e("getIOffloadConfig error " + e);
                return null;
            }
        }

        public IOffloadControl getOffloadControl() {
            try {
                return IOffloadControl.getService(true /*retry*/);
            } catch (RemoteException | NoSuchElementException e) {
                mLog.e("tethering offload control not supported: " + e);
                return null;
            }
        }

        public NativeHandle createConntrackSocket(final int groups) {
            final FileDescriptor fd;
            try {
                fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER);
            } catch (ErrnoException e) {
                mLog.e("Unable to create conntrack socket " + e);
                return null;
            }

            final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups);
            try {
                Os.bind(fd, sockAddr);
            } catch (ErrnoException | SocketException e) {
                mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e);
                try {
                    SocketUtils.closeSocket(fd);
                } catch (IOException ie) {
                    // Nothing we can do here
                }
                return null;
            }
            try {
                Os.connect(fd, sockAddr);
            } catch (ErrnoException | SocketException e) {
                mLog.e("connect to kernel fail for groups " + groups + " error: " + e);
                try {
                    SocketUtils.closeSocket(fd);
                } catch (IOException ie) {
                    // Nothing we can do here
                }
                return null;
            }

            return new NativeHandle(fd, true);
        }
    }

    /** Get default value indicating whether offload is supported. */
@@ -141,13 +210,7 @@ public class OffloadHardwareInterface {
     * share them with offload management process.
     */
    public boolean initOffloadConfig() {
        IOffloadConfig offloadConfig;
        try {
            offloadConfig = IOffloadConfig.getService(true /*retry*/);
        } catch (RemoteException | NoSuchElementException e) {
            mLog.e("getIOffloadConfig error " + e);
            return false;
        }
        final IOffloadConfig offloadConfig = mDeps.getOffloadConfig();
        if (offloadConfig == null) {
            mLog.e("Could not find IOffloadConfig service");
            return false;
@@ -159,11 +222,11 @@ public class OffloadHardwareInterface {
        //
        // h2    provides a file descriptor bound to the following netlink groups
        //       (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
        final NativeHandle h1 = createConntrackSocket(
        final NativeHandle h1 = mDeps.createConntrackSocket(
                NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY);
        if (h1 == null) return false;

        final NativeHandle h2 = createConntrackSocket(
        final NativeHandle h2 = mDeps.createConntrackSocket(
                NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY);
        if (h2 == null) {
            closeFdInNativeHandle(h1);
@@ -198,53 +261,12 @@ public class OffloadHardwareInterface {
        }
    }

    private NativeHandle createConntrackSocket(final int groups) {
        FileDescriptor fd;
        try {
            fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER);
        } catch (ErrnoException e) {
            mLog.e("Unable to create conntrack socket " + e);
            return null;
        }

        final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups);
        try {
            Os.bind(fd, sockAddr);
        } catch (ErrnoException | SocketException e) {
            mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e);
            try {
                SocketUtils.closeSocket(fd);
            } catch (IOException ie) {
                // Nothing we can do here
            }
            return null;
        }
        try {
            Os.connect(fd, sockAddr);
        } catch (ErrnoException | SocketException e) {
            mLog.e("connect to kernel fail for groups " + groups + " error: " + e);
            try {
                SocketUtils.closeSocket(fd);
            } catch (IOException ie) {
                // Nothing we can do here
            }
            return null;
        }

        return new NativeHandle(fd, true);
    }

    /** Initialize the tethering offload HAL. */
    public boolean initOffloadControl(ControlCallback controlCb) {
        mControlCallback = controlCb;

        if (mOffloadControl == null) {
            try {
                mOffloadControl = IOffloadControl.getService(true /*retry*/);
            } catch (RemoteException | NoSuchElementException e) {
                mLog.e("tethering offload control not supported: " + e);
                return false;
            }
            mOffloadControl = mDeps.getOffloadControl();
            if (mOffloadControl == null) {
                mLog.e("tethering IOffloadControl.getService() returned null");
                return false;
+215 −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.networkstack.tethering;

import static android.net.util.TetheringUtils.uint16;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.NativeHandle;
import android.os.test.TestLooper;
import android.system.OsConstants;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.ArrayList;

@RunWith(AndroidJUnit4.class)
@SmallTest
public final class OffloadHardwareInterfaceTest {
    private static final String RMNET0 = "test_rmnet_data0";

    private final TestLooper mTestLooper = new TestLooper();

    private OffloadHardwareInterface mOffloadHw;
    private ITetheringOffloadCallback mTetheringOffloadCallback;
    private OffloadHardwareInterface.ControlCallback mControlCallback;

    @Mock private IOffloadConfig mIOffloadConfig;
    @Mock private IOffloadControl mIOffloadControl;
    @Mock private NativeHandle mNativeHandle;

    class MyDependencies extends OffloadHardwareInterface.Dependencies {
        MyDependencies(SharedLog log) {
            super(log);
        }

        @Override
        public IOffloadConfig getOffloadConfig() {
            return mIOffloadConfig;
        }

        @Override
        public IOffloadControl getOffloadControl() {
            return mIOffloadControl;
        }

        @Override
        public NativeHandle createConntrackSocket(final int groups) {
            return mNativeHandle;
        }
    }

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        final SharedLog log = new SharedLog("test");
        mOffloadHw = new OffloadHardwareInterface(new Handler(mTestLooper.getLooper()), log,
                new MyDependencies(log));
        mControlCallback = spy(new OffloadHardwareInterface.ControlCallback());
    }

    private void startOffloadHardwareInterface() throws Exception {
        mOffloadHw.initOffloadConfig();
        mOffloadHw.initOffloadControl(mControlCallback);
        final ArgumentCaptor<ITetheringOffloadCallback> mOffloadCallbackCaptor =
                ArgumentCaptor.forClass(ITetheringOffloadCallback.class);
        verify(mIOffloadControl).initOffload(mOffloadCallbackCaptor.capture(), any());
        mTetheringOffloadCallback = mOffloadCallbackCaptor.getValue();
    }

    @Test
    public void testGetForwardedStats() throws Exception {
        startOffloadHardwareInterface();
        final OffloadHardwareInterface.ForwardedStats stats = mOffloadHw.getForwardedStats(RMNET0);
        verify(mIOffloadControl).getForwardedStats(eq(RMNET0), any());
        assertNotNull(stats);
    }

    @Test
    public void testSetLocalPrefixes() throws Exception {
        startOffloadHardwareInterface();
        final ArrayList<String> localPrefixes = new ArrayList<>();
        localPrefixes.add("127.0.0.0/8");
        localPrefixes.add("fe80::/64");
        mOffloadHw.setLocalPrefixes(localPrefixes);
        verify(mIOffloadControl).setLocalPrefixes(eq(localPrefixes), any());
    }

    @Test
    public void testSetDataLimit() throws Exception {
        startOffloadHardwareInterface();
        final long limit = 12345;
        mOffloadHw.setDataLimit(RMNET0, limit);
        verify(mIOffloadControl).setDataLimit(eq(RMNET0), eq(limit), any());
    }

    @Test
    public void testSetUpstreamParameters() throws Exception {
        startOffloadHardwareInterface();
        final String v4addr = "192.168.10.1";
        final String v4gateway = "192.168.10.255";
        final ArrayList<String> v6gws = new ArrayList<>(0);
        v6gws.add("2001:db8::1");
        mOffloadHw.setUpstreamParameters(RMNET0, v4addr, v4gateway, v6gws);
        verify(mIOffloadControl).setUpstreamParameters(eq(RMNET0), eq(v4addr), eq(v4gateway),
                eq(v6gws), any());

        final ArgumentCaptor<ArrayList<String>> mArrayListCaptor =
                ArgumentCaptor.forClass(ArrayList.class);
        mOffloadHw.setUpstreamParameters(null, null, null, null);
        verify(mIOffloadControl).setUpstreamParameters(eq(""), eq(""), eq(""),
                mArrayListCaptor.capture(), any());
        assertEquals(mArrayListCaptor.getValue().size(), 0);
    }

    @Test
    public void testUpdateDownstreamPrefix() throws Exception {
        startOffloadHardwareInterface();
        final String ifName = "wlan1";
        final String prefix = "192.168.43.0/24";
        mOffloadHw.addDownstreamPrefix(ifName, prefix);
        verify(mIOffloadControl).addDownstream(eq(ifName), eq(prefix), any());

        mOffloadHw.removeDownstreamPrefix(ifName, prefix);
        verify(mIOffloadControl).removeDownstream(eq(ifName), eq(prefix), any());
    }

    @Test
    public void testTetheringOffloadCallback() throws Exception {
        startOffloadHardwareInterface();

        mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STARTED);
        mTestLooper.dispatchAll();
        verify(mControlCallback).onStarted();

        mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR);
        mTestLooper.dispatchAll();
        verify(mControlCallback).onStoppedError();

        mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED);
        mTestLooper.dispatchAll();
        verify(mControlCallback).onStoppedUnsupported();

        mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE);
        mTestLooper.dispatchAll();
        verify(mControlCallback).onSupportAvailable();

        mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED);
        mTestLooper.dispatchAll();
        verify(mControlCallback).onStoppedLimitReached();

        final NatTimeoutUpdate tcpParams = buildNatTimeoutUpdate(NetworkProtocol.TCP);
        mTetheringOffloadCallback.updateTimeout(tcpParams);
        mTestLooper.dispatchAll();
        verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_TCP),
                eq(tcpParams.src.addr),
                eq(uint16(tcpParams.src.port)),
                eq(tcpParams.dst.addr),
                eq(uint16(tcpParams.dst.port)));

        final NatTimeoutUpdate udpParams = buildNatTimeoutUpdate(NetworkProtocol.UDP);
        mTetheringOffloadCallback.updateTimeout(udpParams);
        mTestLooper.dispatchAll();
        verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_UDP),
                eq(udpParams.src.addr),
                eq(uint16(udpParams.src.port)),
                eq(udpParams.dst.addr),
                eq(uint16(udpParams.dst.port)));
    }

    private NatTimeoutUpdate buildNatTimeoutUpdate(final int proto) {
        final NatTimeoutUpdate params = new NatTimeoutUpdate();
        params.proto = proto;
        params.src.addr = "192.168.43.200";
        params.src.port = 100;
        params.dst.addr = "172.50.46.169";
        params.dst.port = 150;
        return params;
    }
}