/* * 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 import android.content.Context import android.net.IIpMemoryStoreCallbacks import android.net.INetd import android.net.INetworkMonitorCallbacks import android.net.INetworkStackConnector import android.net.InetAddresses.parseNumericAddress import android.net.Network import android.net.dhcp.DhcpServer import android.net.dhcp.DhcpServingParamsParcel import android.net.dhcp.IDhcpServer import android.net.dhcp.IDhcpServerCallbacks import android.net.ip.IIpClientCallbacks import android.net.ip.IpClient import android.os.Binder import android.os.Build import android.os.IBinder import android.os.Process import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH import com.android.networkstack.ipmemorystore.IpMemoryStoreService import com.android.server.NetworkStackService.Dependencies import com.android.server.NetworkStackService.NetworkStackConnector import com.android.server.NetworkStackService.PermissionChecker import com.android.server.connectivity.NetworkMonitor import com.android.testutils.DevSdkIgnoreRule import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo import com.android.testutils.assertThrows import java.io.FileDescriptor import java.io.PrintWriter import java.io.StringWriter import java.net.Inet4Address import kotlin.reflect.KVisibility import kotlin.reflect.full.declaredMemberFunctions import kotlin.test.assertEquals import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mockito.doReturn import org.mockito.Mockito.eq import org.mockito.Mockito.mock import org.mockito.Mockito.spy import org.mockito.Mockito.times import org.mockito.Mockito.verify private val TEST_NETD_VERSION = 9991001 private val TEST_NETD_HASH = "test_netd_hash" private val TEST_IFACE = "test_iface" @RunWith(AndroidJUnit4::class) @SmallTest class NetworkStackServiceTest { @Rule @JvmField val ignoreRule = DevSdkIgnoreRule() private val permChecker = mock(PermissionChecker::class.java) private val mockIpMemoryStoreService = mock(IpMemoryStoreService::class.java) private val mockDhcpServer = mock(DhcpServer::class.java) private val mockNetworkMonitor = mock(NetworkMonitor::class.java) private val mockIpClient = mock(IpClient::class.java) private val deps = mock(Dependencies::class.java).apply { doReturn(mockIpMemoryStoreService).`when`(this).makeIpMemoryStoreService(any()) doReturn(mockDhcpServer).`when`(this).makeDhcpServer(any(), any(), any(), any()) doReturn(mockNetworkMonitor).`when`(this).makeNetworkMonitor(any(), any(), any(), any(), any()) doReturn(mockIpClient).`when`(this).makeIpClient(any(), any(), any(), any()) } private val netd = mock(INetd::class.java).apply { doReturn(TEST_NETD_VERSION).`when`(this).interfaceVersion doReturn(TEST_NETD_HASH).`when`(this).interfaceHash } private val netdBinder = mock(IBinder::class.java).apply { doReturn(netd).`when`(this).queryLocalInterface(any()) } private val context = mock(Context::class.java).apply { doReturn(netdBinder).`when`(this).getSystemService(Context.NETD_SERVICE) } private val connector = NetworkStackConnector(context, permChecker, deps) @Test @IgnoreAfter(Build.VERSION_CODES.Q) fun testDumpVersion_Q() { prepareDumpVersionTest() val dumpsysOut = StringWriter() connector.dump(FileDescriptor(), PrintWriter(dumpsysOut, true /* autoFlush */), arrayOf("version") /* args */) assertEquals("NetworkStack version:\n" + "NetworkStackConnector: ${INetworkStackConnector.VERSION}\n" + "SystemServer: {9990001, 9990002, 9990003, 9990004, 9990005}\n" + "Netd: $TEST_NETD_VERSION\n\n", dumpsysOut.toString()) } @Test @IgnoreUpTo(Build.VERSION_CODES.Q) fun testDumpVersion() { prepareDumpVersionTest() val connectorVersion = INetworkStackConnector.VERSION val connectorHash = INetworkStackConnector.HASH val dumpsysOut = StringWriter() connector.dump(FileDescriptor(), PrintWriter(dumpsysOut, true /* autoFlush */), arrayOf("version") /* args */) assertEquals("NetworkStack version:\n" + "LocalInterface:$connectorVersion:$connectorHash\n" + "ipmemorystore:9990001:ipmemorystore_hash\n" + "netd:$TEST_NETD_VERSION:$TEST_NETD_HASH\n" + "networkstack:9990002:dhcp_server_hash\n" + "networkstack:9990003:networkmonitor_hash\n" + "networkstack:9990004:ipclient_hash\n" + "networkstack:9990005:multiple_use_hash\n\n", dumpsysOut.toString()) } fun prepareDumpVersionTest() { // Call each method on INetworkStackConnector and verify that it notes down the version of // the remote. This is usually a component in the system server that implements one of the // NetworkStack AIDL callback interfaces (e.g., IIpClientCallbacks). On a device there may // be different versions of the generated AIDL classes for different components, even within // the same process (e.g., system_server). // Call fetchIpMemoryStore val mockIpMemoryStoreCb = mock(IIpMemoryStoreCallbacks::class.java) doReturn(9990001).`when`(mockIpMemoryStoreCb).interfaceVersion doReturn("ipmemorystore_hash").`when`(mockIpMemoryStoreCb).interfaceHash connector.fetchIpMemoryStore(mockIpMemoryStoreCb) // IpMemoryStore was created at initialization time verify(mockIpMemoryStoreCb).onIpMemoryStoreFetched(any()) // Call makeDhcpServer val testParams = DhcpServingParamsParcel() testParams.linkMtu = 1500 testParams.dhcpLeaseTimeSecs = 3600L testParams.serverAddr = inet4AddressToIntHTH( parseNumericAddress("192.168.1.1") as Inet4Address) testParams.serverAddrPrefixLength = 24 val mockDhcpCb = mock(IDhcpServerCallbacks::class.java) doReturn(9990002).`when`(mockDhcpCb).interfaceVersion doReturn("dhcp_server_hash").`when`(mockDhcpCb).interfaceHash connector.makeDhcpServer(TEST_IFACE, testParams, mockDhcpCb) verify(deps).makeDhcpServer(any(), eq(TEST_IFACE), any(), any()) verify(mockDhcpCb).onDhcpServerCreated(eq(IDhcpServer.STATUS_SUCCESS), any()) // Call makeNetworkMonitor // Use a spy of INetworkMonitorCallbacks and not a mock, as mockito can't create a mock on Q // because of the missing CaptivePortalData class that is an argument of one of the methods val mockBinder = mock(IBinder::class.java) val mockNetworkMonitorCb = spy(INetworkMonitorCallbacks.Stub.asInterface(mockBinder)) doReturn(9990003).`when`(mockNetworkMonitorCb).interfaceVersion doReturn("networkmonitor_hash").`when`(mockNetworkMonitorCb).interfaceHash // Oneway transactions are always successful (return true). INetworkMonitorCallbacks is a // oneway interface. This avoids the stub throwing because the method is not implemented by // the (mock) remote. doReturn(true).`when`(mockBinder).transact(anyInt(), any(), any(), eq(Binder.FLAG_ONEWAY)) connector.makeNetworkMonitor(Network(123), "test_nm", mockNetworkMonitorCb) verify(deps).makeNetworkMonitor(any(), any(), eq(Network(123)), any(), any()) verify(mockNetworkMonitorCb).onNetworkMonitorCreated(any()) // Call makeIpClient // Use a spy of IIpClientCallbacks instead of a mock, as mockito cannot create a mock on Q // because of the missing CaptivePortalData class that is an argument on one of the methods val mockIpClientCb = mock(IIpClientCallbacks::class.java) doReturn(9990004).`when`(mockIpClientCb).interfaceVersion doReturn("ipclient_hash").`when`(mockIpClientCb).interfaceHash connector.makeIpClient(TEST_IFACE, mockIpClientCb) verify(deps).makeIpClient(any(), eq(TEST_IFACE), any(), any()) verify(mockIpClientCb).onIpClientCreated(any()) // Call some methods one more time with a shared version number and hash to verify no // duplicates are reported doReturn(9990005).`when`(mockIpClientCb).interfaceVersion doReturn("multiple_use_hash").`when`(mockIpClientCb).interfaceHash connector.makeIpClient(TEST_IFACE, mockIpClientCb) verify(mockIpClientCb, times(2)).onIpClientCreated(any()) doReturn(9990005).`when`(mockDhcpCb).interfaceVersion doReturn("multiple_use_hash").`when`(mockDhcpCb).interfaceHash connector.makeDhcpServer(TEST_IFACE, testParams, mockDhcpCb) verify(mockDhcpCb, times(2)).onDhcpServerCreated(eq(IDhcpServer.STATUS_SUCCESS), any()) // allowTestUid does not need to record the caller's version assertThrows(SecurityException::class.java) { // Should throw because the test does not run as root connector.allowTestUid(Process.myUid(), null) } // Verify all methods were covered by the test (5 methods + getVersion + getHash) assertEquals(7, INetworkStackConnector::class.declaredMemberFunctions.count { it.visibility == KVisibility.PUBLIC }) } }