blob: 7770eca7d02a055a33156c2a1a12a802745e53a9 [file]
/*
* 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
})
}
}