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