/* * 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 android.net.LinkAddress import android.net.MacAddress import android.net.TetheredClient import android.net.TetheredClient.AddressInfo import android.net.TetheringManager.TETHERING_USB import android.net.TetheringManager.TETHERING_WIFI import android.net.ip.IpServer import android.net.wifi.WifiClient import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.doReturn import org.mockito.Mockito.mock import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue @RunWith(AndroidJUnit4::class) @SmallTest class ConnectedClientsTrackerTest { private val server1 = mock(IpServer::class.java) private val server2 = mock(IpServer::class.java) private val servers = listOf(server1, server2) private val clock = TestClock(1324L) private val client1Addr = MacAddress.fromString("01:23:45:67:89:0A") private val client1 = TetheredClient(client1Addr, listOf( makeAddrInfo("192.168.43.44/32", null /* hostname */, clock.time + 20)), TETHERING_WIFI) private val wifiClient1 = makeWifiClient(client1Addr) private val client2Addr = MacAddress.fromString("02:34:56:78:90:AB") private val client2Exp30AddrInfo = makeAddrInfo( "192.168.43.45/32", "my_hostname", clock.time + 30) private val client2 = TetheredClient(client2Addr, listOf( client2Exp30AddrInfo, makeAddrInfo("2001:db8:12::34/72", "other_hostname", clock.time + 10)), TETHERING_WIFI) private val wifiClient2 = makeWifiClient(client2Addr) private val client3Addr = MacAddress.fromString("03:45:67:89:0A:BC") private val client3 = TetheredClient(client3Addr, listOf(makeAddrInfo("2001:db8:34::34/72", "other_other_hostname", clock.time + 10)), TETHERING_USB) private fun makeAddrInfo(addr: String, hostname: String?, expTime: Long) = LinkAddress(addr).let { AddressInfo(LinkAddress(it.address, it.prefixLength, it.flags, it.scope, expTime /* deprecationTime */, expTime /* expirationTime */), hostname) } @Test fun testUpdateConnectedClients() { doReturn(emptyList()).`when`(server1).allLeases doReturn(emptyList()).`when`(server2).allLeases val tracker = ConnectedClientsTracker(clock) assertFalse(tracker.updateConnectedClients(servers, null)) // Obtain a lease for client 1 doReturn(listOf(client1)).`when`(server1).allLeases assertSameClients(listOf(client1), assertNewClients(tracker, servers, listOf(wifiClient1))) // Client 2 L2-connected, no lease yet val client2WithoutAddr = TetheredClient(client2Addr, emptyList(), TETHERING_WIFI) assertSameClients(listOf(client1, client2WithoutAddr), assertNewClients(tracker, servers, listOf(wifiClient1, wifiClient2))) // Client 2 lease obtained doReturn(listOf(client1, client2)).`when`(server1).allLeases assertSameClients(listOf(client1, client2), assertNewClients(tracker, servers, null)) // Client 3 lease obtained doReturn(listOf(client3)).`when`(server2).allLeases assertSameClients(listOf(client1, client2, client3), assertNewClients(tracker, servers, null)) // Client 2 L2-disconnected assertSameClients(listOf(client1, client3), assertNewClients(tracker, servers, listOf(wifiClient1))) // Client 1 L2-disconnected assertSameClients(listOf(client3), assertNewClients(tracker, servers, emptyList())) // Client 1 comes back assertSameClients(listOf(client1, client3), assertNewClients(tracker, servers, listOf(wifiClient1))) // Leases lost, client 1 still L2-connected doReturn(emptyList()).`when`(server1).allLeases doReturn(emptyList()).`when`(server2).allLeases assertSameClients(listOf(TetheredClient(client1Addr, emptyList(), TETHERING_WIFI)), assertNewClients(tracker, servers, null)) } @Test fun testUpdateConnectedClients_LeaseExpiration() { val tracker = ConnectedClientsTracker(clock) doReturn(listOf(client1, client2)).`when`(server1).allLeases doReturn(listOf(client3)).`when`(server2).allLeases assertSameClients(listOf(client1, client2, client3), assertNewClients( tracker, servers, listOf(wifiClient1, wifiClient2))) clock.time += 20 // Client 3 has no remaining lease: removed val expectedClients = listOf( // Client 1 has no remaining lease but is L2-connected TetheredClient(client1Addr, emptyList(), TETHERING_WIFI), // Client 2 has some expired leases TetheredClient( client2Addr, // Only the "t + 30" address is left, the "t + 10" address expired listOf(client2Exp30AddrInfo), TETHERING_WIFI)) assertSameClients(expectedClients, assertNewClients(tracker, servers, null)) } private fun assertNewClients( tracker: ConnectedClientsTracker, ipServers: Iterable, wifiClients: List? ): List { assertTrue(tracker.updateConnectedClients(ipServers, wifiClients)) return tracker.lastTetheredClients } private fun assertSameClients(expected: List, actual: List) { val expectedSet = HashSet(expected) assertEquals(expected.size, expectedSet.size) assertEquals(expectedSet, HashSet(actual)) } private fun makeWifiClient(macAddr: MacAddress): WifiClient { // Use a mock WifiClient as the constructor is not part of the WiFi module exported API. return mock(WifiClient::class.java).apply { doReturn(macAddr).`when`(this).macAddress } } private class TestClock(var time: Long) : ConnectedClientsTracker.Clock() { override fun elapsedRealtime(): Long { return time } } }