• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 
17 package com.android.testutils
18 
19 import android.content.Context
20 import android.net.ConnectivityManager
21 import android.net.ConnectivityManager.NetworkCallback
22 import android.net.LinkAddress
23 import android.net.Network
24 import android.net.NetworkCapabilities
25 import android.net.NetworkRequest
26 import android.net.LinkProperties
27 import android.net.TestNetworkInterface
28 import android.net.TestNetworkManager
29 import android.os.Binder
30 import android.os.Build
31 import androidx.annotation.RequiresApi
32 import com.android.modules.utils.build.SdkLevel.isAtLeastR
33 import com.android.modules.utils.build.SdkLevel.isAtLeastS
34 import java.util.concurrent.CompletableFuture
35 import java.util.concurrent.TimeUnit
36 import kotlin.test.assertTrue
37 
38 /**
39  * Create a test network based on a TUN interface with a LinkAddress.
40  *
41  * TODO: remove this function after fixing all the callers to use a list of LinkAddresses.
42  * This method will block until the test network is available. Requires
43  * [android.Manifest.permission.CHANGE_NETWORK_STATE] and
44  * [android.Manifest.permission.MANAGE_TEST_NETWORKS].
45  */
initTestNetworknull46 fun initTestNetwork(
47     context: Context,
48     interfaceAddr: LinkAddress,
49     setupTimeoutMs: Long = 10_000L
50 ): TestNetworkTracker {
51     return initTestNetwork(context, listOf(interfaceAddr), setupTimeoutMs)
52 }
53 
54 /**
55  * Create a test network based on a TUN interface with a LinkAddress list.
56  *
57  * This method will block until the test network is available. Requires
58  * [android.Manifest.permission.CHANGE_NETWORK_STATE] and
59  * [android.Manifest.permission.MANAGE_TEST_NETWORKS].
60  */
initTestNetworknull61 fun initTestNetwork(
62     context: Context,
63     linkAddrs: List<LinkAddress>,
64     setupTimeoutMs: Long = 10_000L
65 ): TestNetworkTracker {
66     return initTestNetwork(context, linkAddrs, lp = null, setupTimeoutMs = setupTimeoutMs)
67 }
68 
69 /**
70  * Create a test network based on a TUN interface
71  *
72  * This method will block until the test network is available. Requires
73  * [android.Manifest.permission.CHANGE_NETWORK_STATE] and
74  * [android.Manifest.permission.MANAGE_TEST_NETWORKS].
75  *
76  * This is only usable starting from R as [TestNetworkManager] has no support for specifying
77  * LinkProperties on Q.
78  */
79 @RequiresApi(Build.VERSION_CODES.R)
initTestNetworknull80 fun initTestNetwork(
81     context: Context,
82     lp: LinkProperties,
83     setupTimeoutMs: Long = 10_000L
84 ): TestNetworkTracker {
85     return initTestNetwork(context, lp.linkAddresses, lp, setupTimeoutMs)
86 }
87 
initTestNetworknull88 private fun initTestNetwork(
89     context: Context,
90     linkAddrs: List<LinkAddress>,
91     lp: LinkProperties?,
92     setupTimeoutMs: Long = 10_000L
93 ): TestNetworkTracker {
94     val tnm = context.getSystemService(TestNetworkManager::class.java)
95     val iface = if (isAtLeastS()) tnm.createTunInterface(linkAddrs)
96     else tnm.createTunInterface(linkAddrs.toTypedArray())
97     val lpWithIface = if (lp == null) null else LinkProperties(lp).apply {
98         interfaceName = iface.interfaceName
99     }
100     return TestNetworkTracker(context, iface, tnm, lpWithIface, setupTimeoutMs)
101 }
102 
103 /**
104  * Utility class to create and track test networks.
105  *
106  * This class is not thread-safe.
107  */
108 class TestNetworkTracker internal constructor(
109     val context: Context,
110     val iface: TestNetworkInterface,
111     val tnm: TestNetworkManager,
112     val lp: LinkProperties?,
113     setupTimeoutMs: Long
114 ) : TestableNetworkCallback.HasNetwork {
115     private val cm = context.getSystemService(ConnectivityManager::class.java)
116     private val binder = Binder()
117 
118     private val networkCallback: NetworkCallback
119     override val network: Network
120     val testIface: TestNetworkInterface
121 
122     init {
123         val networkFuture = CompletableFuture<Network>()
124         val networkRequest = NetworkRequest.Builder()
125                 .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
126                 // Test networks do not have NOT_VPN or TRUSTED capabilities by default
127                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
128                 .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
129                 .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(iface.interfaceName))
130                 .build()
131         networkCallback = object : NetworkCallback() {
onAvailablenull132             override fun onAvailable(network: Network) {
133                 networkFuture.complete(network)
134             }
135         }
136         cm.requestNetwork(networkRequest, networkCallback)
137 
138         network = try {
139             if (lp != null) {
140                 assertTrue(isAtLeastR(), "Cannot specify TestNetwork LinkProperties before R")
141                 tnm.setupTestNetwork(lp, true /* isMetered */, binder)
142             } else {
143                 tnm.setupTestNetwork(iface.interfaceName, binder)
144             }
145             networkFuture.get(setupTimeoutMs, TimeUnit.MILLISECONDS)
146         } catch (e: Throwable) {
147             cm.unregisterNetworkCallback(networkCallback)
148             throw e
149         }
150 
151         testIface = iface
152     }
153 
teardownnull154     fun teardown() {
155         cm.unregisterNetworkCallback(networkCallback)
156         tnm.teardownTestNetwork(network)
157     }
158 }
159