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