• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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