1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.net.cts 17 18 import android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS 19 import android.Manifest.permission.MANAGE_TEST_NETWORKS 20 import android.Manifest.permission.NETWORK_SETTINGS 21 import android.content.Context 22 import android.net.ConnectivityManager 23 import android.net.EthernetManager 24 import android.net.EthernetManager.InterfaceStateListener 25 import android.net.EthernetManager.ROLE_CLIENT 26 import android.net.EthernetManager.ROLE_NONE 27 import android.net.EthernetManager.ROLE_SERVER 28 import android.net.EthernetManager.STATE_ABSENT 29 import android.net.EthernetManager.STATE_LINK_DOWN 30 import android.net.EthernetManager.STATE_LINK_UP 31 import android.net.EthernetManager.TetheredInterfaceCallback 32 import android.net.EthernetManager.TetheredInterfaceRequest 33 import android.net.EthernetNetworkSpecifier 34 import android.net.InetAddresses 35 import android.net.IpConfiguration 36 import android.net.MacAddress 37 import android.net.Network 38 import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED 39 import android.net.NetworkCapabilities.TRANSPORT_ETHERNET 40 import android.net.NetworkCapabilities.TRANSPORT_TEST 41 import android.net.NetworkRequest 42 import android.net.TestNetworkInterface 43 import android.net.TestNetworkManager 44 import android.net.cts.EthernetManagerTest.EthernetStateListener.CallbackEntry.InterfaceStateChanged 45 import android.os.Build 46 import android.os.Handler 47 import android.os.HandlerExecutor 48 import android.os.Looper 49 import android.os.SystemProperties 50 import android.platform.test.annotations.AppModeFull 51 import android.util.ArraySet 52 import androidx.test.platform.app.InstrumentationRegistry 53 import com.android.net.module.util.ArrayTrackRecord 54 import com.android.net.module.util.TrackRecord 55 import com.android.testutils.anyNetwork 56 import com.android.testutils.DevSdkIgnoreRule 57 import com.android.testutils.DevSdkIgnoreRunner 58 import com.android.testutils.RecorderCallback.CallbackEntry.Available 59 import com.android.testutils.RecorderCallback.CallbackEntry.Lost 60 import com.android.testutils.RouterAdvertisementResponder 61 import com.android.testutils.TapPacketReader 62 import com.android.testutils.TestableNetworkCallback 63 import com.android.testutils.runAsShell 64 import com.android.testutils.waitForIdle 65 import org.junit.After 66 import org.junit.Assume.assumeFalse 67 import org.junit.Before 68 import org.junit.Test 69 import org.junit.runner.RunWith 70 import java.net.Inet6Address 71 import java.util.concurrent.CompletableFuture 72 import java.util.concurrent.ExecutionException 73 import java.util.concurrent.TimeUnit 74 import kotlin.test.assertEquals 75 import kotlin.test.assertFailsWith 76 import kotlin.test.assertFalse 77 import kotlin.test.assertNotNull 78 import kotlin.test.assertNull 79 import kotlin.test.assertTrue 80 import kotlin.test.fail 81 82 // TODO: try to lower this timeout in the future. Currently, ethernet tests are still flaky because 83 // the interface is not ready fast enough (mostly due to the up / up / down / up issue). 84 private const val TIMEOUT_MS = 2000L 85 private const val NO_CALLBACK_TIMEOUT_MS = 200L 86 private val DEFAULT_IP_CONFIGURATION = IpConfiguration(IpConfiguration.IpAssignment.DHCP, 87 IpConfiguration.ProxySettings.NONE, null, null) 88 private val ETH_REQUEST: NetworkRequest = NetworkRequest.Builder() 89 .addTransportType(TRANSPORT_TEST) 90 .addTransportType(TRANSPORT_ETHERNET) 91 .removeCapability(NET_CAPABILITY_TRUSTED) 92 .build() 93 94 @AppModeFull(reason = "Instant apps can't access EthernetManager") 95 // EthernetManager is not updatable before T, so tests do not need to be backwards compatible. 96 @RunWith(DevSdkIgnoreRunner::class) 97 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2) 98 class EthernetManagerTest { 99 <lambda>null100 private val context by lazy { InstrumentationRegistry.getInstrumentation().context } <lambda>null101 private val em by lazy { context.getSystemService(EthernetManager::class.java) } <lambda>null102 private val cm by lazy { context.getSystemService(ConnectivityManager::class.java) } 103 104 private val ifaceListener = EthernetStateListener() 105 private val createdIfaces = ArrayList<EthernetTestInterface>() 106 private val addedListeners = ArrayList<EthernetStateListener>() 107 private val networkRequests = ArrayList<TestableNetworkCallback>() 108 109 private var tetheredInterfaceRequest: TetheredInterfaceRequest? = null 110 111 private class EthernetTestInterface( 112 context: Context, 113 private val handler: Handler 114 ) { 115 private val tapInterface: TestNetworkInterface 116 private val packetReader: TapPacketReader 117 private val raResponder: RouterAdvertisementResponder 118 val interfaceName get() = tapInterface.interfaceName 119 120 init { <lambda>null121 tapInterface = runAsShell(MANAGE_TEST_NETWORKS) { 122 val tnm = context.getSystemService(TestNetworkManager::class.java) 123 tnm.createTapInterface(false /* bringUp */) 124 } 125 val mtu = 1500 126 packetReader = TapPacketReader(handler, tapInterface.fileDescriptor.fileDescriptor, mtu) 127 raResponder = RouterAdvertisementResponder(packetReader) 128 raResponder.addRouterEntry(MacAddress.fromString("01:23:45:67:89:ab"), 129 InetAddresses.parseNumericAddress("fe80::abcd") as Inet6Address) 130 131 packetReader.startAsyncForTest() 132 raResponder.start() 133 } 134 destroynull135 fun destroy() { 136 raResponder.stop() 137 handler.post({ packetReader.stop() }) 138 handler.waitForIdle(TIMEOUT_MS) 139 } 140 } 141 142 private open class EthernetStateListener private constructor( 143 private val history: ArrayTrackRecord<CallbackEntry> 144 ) : InterfaceStateListener, 145 TrackRecord<EthernetStateListener.CallbackEntry> by history { 146 constructor() : this(ArrayTrackRecord()) 147 148 val events = history.newReadHead() 149 150 sealed class CallbackEntry { 151 data class InterfaceStateChanged( 152 val iface: String, 153 val state: Int, 154 val role: Int, 155 val configuration: IpConfiguration? 156 ) : CallbackEntry() 157 } 158 onInterfaceStateChangednull159 override fun onInterfaceStateChanged( 160 iface: String, 161 state: Int, 162 role: Int, 163 cfg: IpConfiguration? 164 ) { 165 add(InterfaceStateChanged(iface, state, role, cfg)) 166 } 167 expectCallbacknull168 fun <T : CallbackEntry> expectCallback(expected: T): T { 169 val event = pollForNextCallback() 170 assertEquals(expected, event) 171 return event as T 172 } 173 expectCallbacknull174 fun expectCallback(iface: EthernetTestInterface, state: Int, role: Int) { 175 expectCallback(createChangeEvent(iface.interfaceName, state, role)) 176 } 177 createChangeEventnull178 fun createChangeEvent(iface: String, state: Int, role: Int) = 179 InterfaceStateChanged(iface, state, role, 180 if (state != STATE_ABSENT) DEFAULT_IP_CONFIGURATION else null) 181 182 fun pollForNextCallback(): CallbackEntry { 183 return events.poll(TIMEOUT_MS) ?: fail("Did not receive callback after ${TIMEOUT_MS}ms") 184 } 185 <lambda>null186 fun eventuallyExpect(expected: CallbackEntry) = events.poll(TIMEOUT_MS) { it == expected } 187 eventuallyExpectnull188 fun eventuallyExpect(interfaceName: String, state: Int, role: Int) { 189 assertNotNull(eventuallyExpect(createChangeEvent(interfaceName, state, role))) 190 } 191 eventuallyExpectnull192 fun eventuallyExpect(iface: EthernetTestInterface, state: Int, role: Int) { 193 eventuallyExpect(iface.interfaceName, state, role) 194 } 195 assertNoCallbacknull196 fun assertNoCallback() { 197 val cb = events.poll(NO_CALLBACK_TIMEOUT_MS) 198 assertNull(cb, "Expected no callback but got $cb") 199 } 200 } 201 202 private class TetheredInterfaceListener : TetheredInterfaceCallback { 203 private val available = CompletableFuture<String>() 204 onAvailablenull205 override fun onAvailable(iface: String) { 206 available.complete(iface) 207 } 208 onUnavailablenull209 override fun onUnavailable() { 210 available.completeExceptionally(IllegalStateException("onUnavailable was called")) 211 } 212 expectOnAvailablenull213 fun expectOnAvailable(): String { 214 return available.get(TIMEOUT_MS, TimeUnit.MILLISECONDS) 215 } 216 expectOnUnavailablenull217 fun expectOnUnavailable() { 218 // Assert that the future fails with the IllegalStateException from the 219 // completeExceptionally() call inside onUnavailable. 220 assertFailsWith(IllegalStateException::class) { 221 try { 222 available.get(TIMEOUT_MS, TimeUnit.MILLISECONDS) 223 } catch (e: ExecutionException) { 224 throw e.cause!! 225 } 226 } 227 } 228 } 229 230 @Before setUpnull231 fun setUp() { 232 setIncludeTestInterfaces(true) 233 addInterfaceStateListener(ifaceListener) 234 } 235 236 @After tearDownnull237 fun tearDown() { 238 setIncludeTestInterfaces(false) 239 for (iface in createdIfaces) { 240 iface.destroy() 241 ifaceListener.eventuallyExpect(iface, STATE_ABSENT, ROLE_NONE) 242 } 243 for (listener in addedListeners) { 244 em.removeInterfaceStateListener(listener) 245 } 246 networkRequests.forEach { cm.unregisterNetworkCallback(it) } 247 releaseTetheredInterface() 248 } 249 addInterfaceStateListenernull250 private fun addInterfaceStateListener(listener: EthernetStateListener) { 251 runAsShell(CONNECTIVITY_USE_RESTRICTED_NETWORKS) { 252 em.addInterfaceStateListener(HandlerExecutor(Handler(Looper.getMainLooper())), listener) 253 } 254 addedListeners.add(listener) 255 } 256 createInterfacenull257 private fun createInterface(): EthernetTestInterface { 258 val iface = EthernetTestInterface( 259 context, 260 Handler(Looper.getMainLooper()) 261 ).also { createdIfaces.add(it) } 262 with(ifaceListener) { 263 // when an interface comes up, we should always see a down cb before an up cb. 264 eventuallyExpect(iface, STATE_LINK_DOWN, ROLE_CLIENT) 265 expectCallback(iface, STATE_LINK_UP, ROLE_CLIENT) 266 } 267 return iface 268 } 269 setIncludeTestInterfacesnull270 private fun setIncludeTestInterfaces(value: Boolean) { 271 runAsShell(NETWORK_SETTINGS) { 272 em.setIncludeTestInterfaces(value) 273 } 274 } 275 removeInterfacenull276 private fun removeInterface(iface: EthernetTestInterface) { 277 iface.destroy() 278 createdIfaces.remove(iface) 279 ifaceListener.eventuallyExpect(iface, STATE_ABSENT, ROLE_NONE) 280 } 281 requestNetworknull282 private fun requestNetwork(request: NetworkRequest): TestableNetworkCallback { 283 return TestableNetworkCallback().also { 284 cm.requestNetwork(request, it) 285 networkRequests.add(it) 286 } 287 } 288 releaseNetworknull289 private fun releaseNetwork(cb: TestableNetworkCallback) { 290 cm.unregisterNetworkCallback(cb) 291 networkRequests.remove(cb) 292 } 293 <lambda>null294 private fun requestTetheredInterface() = TetheredInterfaceListener().also { 295 tetheredInterfaceRequest = runAsShell(NETWORK_SETTINGS) { 296 em.requestTetheredInterface(HandlerExecutor(Handler(Looper.getMainLooper())), it) 297 } 298 } 299 releaseTetheredInterfacenull300 private fun releaseTetheredInterface() { 301 runAsShell(NETWORK_SETTINGS) { 302 tetheredInterfaceRequest?.release() 303 tetheredInterfaceRequest = null 304 } 305 } 306 NetworkRequestnull307 private fun NetworkRequest.createCopyWithEthernetSpecifier(ifaceName: String) = 308 NetworkRequest.Builder(NetworkRequest(ETH_REQUEST)) 309 .setNetworkSpecifier(EthernetNetworkSpecifier(ifaceName)).build() 310 311 // It can take multiple seconds for the network to become available. 312 private fun TestableNetworkCallback.expectAvailable() = 313 expectCallback<Available>(anyNetwork(), 5000/*ms timeout*/).network 314 315 // b/233534110: eventuallyExpect<Lost>() does not advance ReadHead, use 316 // eventuallyExpect(Lost::class) instead. 317 private fun TestableNetworkCallback.eventuallyExpectLost(n: Network? = null) = 318 eventuallyExpect(Lost::class, TIMEOUT_MS) { n?.equals(it.network) ?: true } 319 assertNotLostnull320 private fun TestableNetworkCallback.assertNotLost(n: Network? = null) = 321 assertNoCallbackThat() { it is Lost && (n?.equals(it.network) ?: true) } 322 323 @Test testCallbacksnull324 fun testCallbacks() { 325 // If an interface exists when the callback is registered, it is reported on registration. 326 val iface = createInterface() 327 val listener1 = EthernetStateListener() 328 addInterfaceStateListener(listener1) 329 validateListenerOnRegistration(listener1) 330 331 // If an interface appears, existing callbacks see it. 332 // TODO: fix the up/up/down/up callbacks and only send down/up. 333 val iface2 = createInterface() 334 listener1.expectCallback(iface2, STATE_LINK_UP, ROLE_CLIENT) 335 listener1.expectCallback(iface2, STATE_LINK_UP, ROLE_CLIENT) 336 listener1.expectCallback(iface2, STATE_LINK_DOWN, ROLE_CLIENT) 337 listener1.expectCallback(iface2, STATE_LINK_UP, ROLE_CLIENT) 338 339 // Register a new listener, it should see state of all existing interfaces immediately. 340 val listener2 = EthernetStateListener() 341 addInterfaceStateListener(listener2) 342 validateListenerOnRegistration(listener2) 343 344 // Removing interfaces first sends link down, then STATE_ABSENT/ROLE_NONE. 345 removeInterface(iface) 346 for (listener in listOf(listener1, listener2)) { 347 listener.expectCallback(iface, STATE_LINK_DOWN, ROLE_CLIENT) 348 listener.expectCallback(iface, STATE_ABSENT, ROLE_NONE) 349 } 350 351 removeInterface(iface2) 352 for (listener in listOf(listener1, listener2)) { 353 listener.expectCallback(iface2, STATE_LINK_DOWN, ROLE_CLIENT) 354 listener.expectCallback(iface2, STATE_ABSENT, ROLE_NONE) 355 listener.assertNoCallback() 356 } 357 } 358 359 // TODO: this function is now used in two places (EthernetManagerTest and 360 // EthernetTetheringTest), so it should be moved to testutils. isAdbOverNetworknull361 private fun isAdbOverNetwork(): Boolean { 362 // If adb TCP port opened, this test may running by adb over network. 363 return (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1 || 364 SystemProperties.getInt("service.adb.tcp.port", -1) > -1) 365 } 366 367 @Test testCallbacks_forServerModeInterfacesnull368 fun testCallbacks_forServerModeInterfaces() { 369 // do not run this test when adb might be connected over ethernet. 370 assumeFalse(isAdbOverNetwork()) 371 372 val listener = EthernetStateListener() 373 addInterfaceStateListener(listener) 374 375 // it is possible that a physical interface is present, so it is not guaranteed that iface 376 // will be put into server mode. This should not matter for the test though. Calling 377 // createInterface() makes sure we have at least one interface available. 378 val iface = createInterface() 379 val cb = requestTetheredInterface() 380 val ifaceName = cb.expectOnAvailable() 381 listener.eventuallyExpect(ifaceName, STATE_LINK_UP, ROLE_SERVER) 382 383 releaseTetheredInterface() 384 listener.eventuallyExpect(ifaceName, STATE_LINK_UP, ROLE_CLIENT) 385 } 386 387 /** 388 * Validate all interfaces are returned for an EthernetStateListener upon registration. 389 */ validateListenerOnRegistrationnull390 private fun validateListenerOnRegistration(listener: EthernetStateListener) { 391 // Get all tracked interfaces to validate on listener registration. Ordering and interface 392 // state (up/down) can't be validated for interfaces not created as part of testing. 393 val ifaces = em.getInterfaceList() 394 val polledIfaces = ArraySet<String>() 395 for (i in ifaces) { 396 val event = (listener.pollForNextCallback() as InterfaceStateChanged) 397 val iface = event.iface 398 assertTrue(polledIfaces.add(iface), "Duplicate interface $iface returned") 399 assertTrue(ifaces.contains(iface), "Untracked interface $iface returned") 400 // If the event's iface was created in the test, additional criteria can be validated. 401 createdIfaces.find { it.interfaceName.equals(iface) }?.let { 402 assertEquals(event, 403 listener.createChangeEvent(it.interfaceName, 404 STATE_LINK_UP, 405 ROLE_CLIENT)) 406 } 407 } 408 // Assert all callbacks are accounted for. 409 listener.assertNoCallback() 410 } 411 412 @Test testGetInterfaceListnull413 fun testGetInterfaceList() { 414 setIncludeTestInterfaces(true) 415 416 // Create two test interfaces and check the return list contains the interface names. 417 val iface1 = createInterface() 418 val iface2 = createInterface() 419 var ifaces = em.getInterfaceList() 420 assertTrue(ifaces.size > 0) 421 assertTrue(ifaces.contains(iface1.interfaceName)) 422 assertTrue(ifaces.contains(iface2.interfaceName)) 423 424 // Remove one existing test interface and check the return list doesn't contain the 425 // removed interface name. 426 removeInterface(iface1) 427 ifaces = em.getInterfaceList() 428 assertFalse(ifaces.contains(iface1.interfaceName)) 429 assertTrue(ifaces.contains(iface2.interfaceName)) 430 431 removeInterface(iface2) 432 } 433 434 @Test testNetworkRequest_withSingleExistingInterfacenull435 fun testNetworkRequest_withSingleExistingInterface() { 436 setIncludeTestInterfaces(true) 437 createInterface() 438 439 // install a listener which will later be used to verify the Lost callback 440 val listenerCb = TestableNetworkCallback() 441 cm.registerNetworkCallback(ETH_REQUEST, listenerCb) 442 networkRequests.add(listenerCb) 443 444 val cb = requestNetwork(ETH_REQUEST) 445 val network = cb.expectAvailable() 446 447 cb.assertNotLost() 448 releaseNetwork(cb) 449 listenerCb.eventuallyExpectLost(network) 450 } 451 452 @Test testNetworkRequest_beforeSingleInterfaceIsUpnull453 fun testNetworkRequest_beforeSingleInterfaceIsUp() { 454 setIncludeTestInterfaces(true) 455 456 val cb = requestNetwork(ETH_REQUEST) 457 458 // bring up interface after network has been requested 459 val iface = createInterface() 460 val network = cb.expectAvailable() 461 462 // remove interface before network request has been removed 463 cb.assertNotLost() 464 removeInterface(iface) 465 cb.eventuallyExpectLost() 466 467 releaseNetwork(cb) 468 } 469 470 @Test testNetworkRequest_withMultipleInterfacesnull471 fun testNetworkRequest_withMultipleInterfaces() { 472 setIncludeTestInterfaces(true) 473 474 val iface1 = createInterface() 475 val iface2 = createInterface() 476 477 val cb = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface2.interfaceName)) 478 479 val network = cb.expectAvailable() 480 cb.expectCapabilitiesThat(network) { 481 it.networkSpecifier == EthernetNetworkSpecifier(iface2.interfaceName) 482 } 483 484 removeInterface(iface1) 485 cb.assertNotLost() 486 removeInterface(iface2) 487 cb.eventuallyExpectLost() 488 489 releaseNetwork(cb) 490 } 491 492 @Test testNetworkRequest_withInterfaceBeingReplacednull493 fun testNetworkRequest_withInterfaceBeingReplaced() { 494 setIncludeTestInterfaces(true) 495 val iface1 = createInterface() 496 497 val cb = requestNetwork(ETH_REQUEST) 498 val network = cb.expectAvailable() 499 500 // create another network and verify the request sticks to the current network 501 val iface2 = createInterface() 502 cb.assertNotLost() 503 504 // remove iface1 and verify the request brings up iface2 505 removeInterface(iface1) 506 cb.eventuallyExpectLost(network) 507 val network2 = cb.expectAvailable() 508 509 releaseNetwork(cb) 510 } 511 512 @Test testNetworkRequest_withMultipleInterfacesAndRequestsnull513 fun testNetworkRequest_withMultipleInterfacesAndRequests() { 514 setIncludeTestInterfaces(true) 515 val iface1 = createInterface() 516 val iface2 = createInterface() 517 518 val cb1 = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface1.interfaceName)) 519 val cb2 = requestNetwork(ETH_REQUEST.createCopyWithEthernetSpecifier(iface2.interfaceName)) 520 val cb3 = requestNetwork(ETH_REQUEST) 521 522 cb1.expectAvailable() 523 cb2.expectAvailable() 524 cb3.expectAvailable() 525 526 cb1.assertNotLost() 527 cb2.assertNotLost() 528 cb3.assertNotLost() 529 530 releaseNetwork(cb1) 531 releaseNetwork(cb2) 532 releaseNetwork(cb3) 533 } 534 } 535