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

Commit bf6aa91b authored by Mark Chien's avatar Mark Chien Committed by Automerger Merge Worker
Browse files

Merge "Add test for OffloadHardwareInterface" am: f77020b2 am: bf459e1c am: 346a146e

Change-Id: I98876aa34e283231dc37650fd3c5bf155328c6be
parents 8a3e7c6a 346a146e
Loading
Loading
Loading
Loading
+73 −51
Original line number Original line Diff line number Diff line
@@ -66,6 +66,7 @@ public class OffloadHardwareInterface {


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


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

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


        final NativeHandle h2 = createConntrackSocket(
        final NativeHandle h2 = mDeps.createConntrackSocket(
                NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY);
                NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY);
        if (h2 == null) {
        if (h2 == null) {
            closeFdInNativeHandle(h1);
            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. */
    /** Initialize the tethering offload HAL. */
    public boolean initOffloadControl(ControlCallback controlCb) {
    public boolean initOffloadControl(ControlCallback controlCb) {
        mControlCallback = controlCb;
        mControlCallback = controlCb;


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