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