<lambda>null1 package com.android.metrics
2
3 import android.net.ConnectivityThread
4 import android.net.NetworkCapabilities
5 import android.net.NetworkCapabilities.CONNECTIVITY_MANAGED_CAPABILITIES
6 import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL
7 import android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE
8 import android.net.NetworkCapabilities.NET_CAPABILITY_IMS
9 import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED
10 import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING
11 import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED
12 import android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY
13 import android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED
14 import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
15 import android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1
16 import android.net.NetworkCapabilities.NET_ENTERPRISE_ID_3
17 import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
18 import android.net.NetworkCapabilities.TRANSPORT_WIFI
19 import android.net.NetworkRequest
20 import android.net.NetworkScore
21 import android.net.NetworkScore.KEEP_CONNECTED_FOR_TEST
22 import android.net.NetworkScore.POLICY_EXITING
23 import android.net.NetworkScore.POLICY_TRANSPORT_PRIMARY
24 import android.os.Build
25 import android.os.Handler
26 import android.os.Process
27 import android.os.Process.SYSTEM_UID
28 import android.stats.connectivity.MeteredState
29 import android.stats.connectivity.RequestType
30 import android.stats.connectivity.RequestType.RT_APP
31 import android.stats.connectivity.RequestType.RT_SYSTEM
32 import android.stats.connectivity.RequestType.RT_SYSTEM_ON_BEHALF_OF_APP
33 import android.stats.connectivity.ValidatedState
34 import androidx.test.filters.SmallTest
35 import com.android.net.module.util.BitUtils
36 import com.android.server.CSTest
37 import com.android.server.FromS
38 import com.android.server.connectivity.FullScore
39 import com.android.server.connectivity.FullScore.POLICY_IS_UNMETERED
40 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
41 import com.android.testutils.DevSdkIgnoreRunner
42 import com.android.testutils.TestableNetworkCallback
43 import java.util.concurrent.CompletableFuture
44 import kotlin.test.assertEquals
45 import kotlin.test.fail
46 import org.junit.Assert.assertTrue
47 import org.junit.Test
48 import org.junit.runner.RunWith
49
50 private fun <T> Handler.onHandler(f: () -> T): T {
51 val future = CompletableFuture<T>()
52 post { future.complete(f()) }
53 return future.get()
54 }
55
accnull56 private fun flags(vararg flags: Int) = flags.fold(0L) { acc, it -> acc or (1L shl it) }
57
toTransportsStringnull58 private fun Number.toTransportsString() = StringBuilder().also { sb ->
59 BitUtils.appendStringRepresentationOfBitMaskToStringBuilder(sb, this.toLong(),
60 { NetworkCapabilities.transportNameOf(it) }, "|") }.toString()
61
Numbernull62 private fun Number.toCapsString() = StringBuilder().also { sb ->
63 BitUtils.appendStringRepresentationOfBitMaskToStringBuilder(sb, this.toLong(),
64 { NetworkCapabilities.capabilityNameOf(it) }, "&") }.toString()
65
Numbernull66 private fun Number.toPolicyString() = StringBuilder().also {sb ->
67 BitUtils.appendStringRepresentationOfBitMaskToStringBuilder(sb, this.toLong(),
68 { FullScore.policyNameOf(it) }, "|") }.toString()
69
Numbernull70 private fun Number.exceptCSManaged() = this.toLong() and CONNECTIVITY_MANAGED_CAPABILITIES.inv()
71
72 private val NetworkCapabilities.meteredState get() = when {
73 hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED) ->
74 MeteredState.METERED_TEMPORARILY_UNMETERED
75 hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) ->
76 MeteredState.METERED_NO
77 else ->
78 MeteredState.METERED_YES
79 }
80
81 private val NetworkCapabilities.validatedState get() = when {
82 hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL) -> ValidatedState.VS_PORTAL
83 hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY) -> ValidatedState.VS_PARTIAL
84 hasCapability(NET_CAPABILITY_VALIDATED) -> ValidatedState.VS_VALID
85 else -> ValidatedState.VS_INVALID
86 }
87
88 @RunWith(DevSdkIgnoreRunner::class)
89 @SmallTest
90 @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
91 class ConnectivitySampleMetricsTest : CSTest() {
92 @Test
testSampleConnectivityState_Networknull93 fun testSampleConnectivityState_Network() {
94 val wifi1Caps = NetworkCapabilities.Builder()
95 .addTransportType(TRANSPORT_WIFI)
96 .addCapability(NET_CAPABILITY_NOT_METERED)
97 .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
98 .addCapability(NET_CAPABILITY_NOT_ROAMING)
99 .build()
100 val wifi1Score = NetworkScore.Builder()
101 .setKeepConnectedReason(KEEP_CONNECTED_FOR_TEST)
102 .setExiting(true)
103 .build()
104 val agentWifi1 = Agent(nc = wifi1Caps, score = FromS(wifi1Score)).also { it.connect() }
105
106 val wifi2Caps = NetworkCapabilities.Builder()
107 .addTransportType(TRANSPORT_WIFI)
108 .addCapability(NET_CAPABILITY_ENTERPRISE)
109 .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
110 .addCapability(NET_CAPABILITY_NOT_ROAMING)
111 .addEnterpriseId(NET_ENTERPRISE_ID_3)
112 .build()
113 val wifi2Score = NetworkScore.Builder()
114 .setKeepConnectedReason(KEEP_CONNECTED_FOR_TEST)
115 .setTransportPrimary(true)
116 .build()
117 val agentWifi2 = Agent(nc = wifi2Caps, score = FromS(wifi2Score)).also { it.connect() }
118
119 val cellCaps = NetworkCapabilities.Builder()
120 .addTransportType(TRANSPORT_CELLULAR)
121 .addCapability(NET_CAPABILITY_IMS)
122 .addCapability(NET_CAPABILITY_ENTERPRISE)
123 .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
124 .addCapability(NET_CAPABILITY_NOT_ROAMING)
125 .addEnterpriseId(NET_ENTERPRISE_ID_1)
126 .build()
127 val cellScore = NetworkScore.Builder()
128 .setKeepConnectedReason(KEEP_CONNECTED_FOR_TEST)
129 .build()
130 val agentCell = Agent(nc = cellCaps, score = FromS(cellScore)).also { it.connect() }
131
132 val stats = csHandler.onHandler { service.sampleConnectivityState() }
133 assertEquals(3, stats.networks.networkDescriptionList.size)
134 val foundCell = stats.networks.networkDescriptionList.find {
135 it.transportTypes == (1 shl TRANSPORT_CELLULAR)
136 } ?: fail("Can't find cell network (searching by transport)")
137 val foundWifi1 = stats.networks.networkDescriptionList.find {
138 it.transportTypes == (1 shl TRANSPORT_WIFI) &&
139 0L != (it.capabilities and (1L shl NET_CAPABILITY_NOT_METERED))
140 } ?: fail("Can't find wifi1 (searching by WIFI transport and the NOT_METERED capability)")
141 val foundWifi2 = stats.networks.networkDescriptionList.find {
142 it.transportTypes == (1 shl TRANSPORT_WIFI) &&
143 0L != (it.capabilities and (1L shl NET_CAPABILITY_ENTERPRISE))
144 } ?: fail("Can't find wifi2 (searching by WIFI transport and the ENTERPRISE capability)")
145
146 fun checkNetworkDescription(
147 network: String,
148 found: NetworkDescription,
149 expected: NetworkCapabilities
150 ) {
151 assertEquals(expected.transportTypesInternal, found.transportTypes.toLong(),
152 "Transports differ for network $network, " +
153 "expected ${expected.transportTypesInternal.toTransportsString()}, " +
154 "found ${found.transportTypes.toTransportsString()}")
155 val expectedCaps = expected.capabilitiesInternal.exceptCSManaged()
156 val foundCaps = found.capabilities.exceptCSManaged()
157 assertEquals(expectedCaps, foundCaps,
158 "Capabilities differ for network $network, " +
159 "expected ${expectedCaps.toCapsString()}, " +
160 "found ${foundCaps.toCapsString()}")
161 assertEquals(expected.enterpriseIdsInternal, found.enterpriseId,
162 "Enterprise IDs differ for network $network, " +
163 "expected ${expected.enterpriseIdsInternal}," +
164 " found ${found.enterpriseId}")
165 assertEquals(expected.meteredState, found.meteredState,
166 "Metered states differ for network $network, " +
167 "expected ${expected.meteredState}, " +
168 "found ${found.meteredState}")
169 assertEquals(expected.validatedState, found.validatedState,
170 "Validated states differ for network $network, " +
171 "expected ${expected.validatedState}, " +
172 "found ${found.validatedState}")
173 }
174
175 checkNetworkDescription("Cell network", foundCell, cellCaps)
176 checkNetworkDescription("Wifi1", foundWifi1, wifi1Caps)
177 checkNetworkDescription("Wifi2", foundWifi2, wifi2Caps)
178
179 assertEquals(0, foundCell.scorePolicies, "Cell score policies incorrect, expected 0, " +
180 "found ${foundCell.scorePolicies.toPolicyString()}")
181 val expectedWifi1Policies = flags(POLICY_EXITING, POLICY_IS_UNMETERED)
182 assertEquals(expectedWifi1Policies, foundWifi1.scorePolicies,
183 "Wifi1 score policies incorrect, " +
184 "expected ${expectedWifi1Policies.toPolicyString()}, " +
185 "found ${foundWifi1.scorePolicies.toPolicyString()}")
186 val expectedWifi2Policies = flags(POLICY_TRANSPORT_PRIMARY)
187 assertEquals(expectedWifi2Policies, foundWifi2.scorePolicies,
188 "Wifi2 score policies incorrect, " +
189 "expected ${expectedWifi2Policies.toPolicyString()}, " +
190 "found ${foundWifi2.scorePolicies.toPolicyString()}")
191 }
192
fileNetworkRequestnull193 private fun fileNetworkRequest(requestType: RequestType, requestCount: Int, uid: Int? = null) {
194 if (uid != null) {
195 deps.setCallingUid(uid)
196 }
197 try {
198 repeat(requestCount) {
199 when (requestType) {
200 RT_APP, RT_SYSTEM -> cm.requestNetwork(
201 NetworkRequest.Builder().build(),
202 TestableNetworkCallback()
203 )
204
205 RT_SYSTEM_ON_BEHALF_OF_APP -> cm.registerDefaultNetworkCallbackForUid(
206 Process.myUid(),
207 TestableNetworkCallback(),
208 Handler(ConnectivityThread.getInstanceLooper()))
209
210 else -> fail("invalid requestType: " + requestType)
211 }
212 }
213 } finally {
214 deps.unmockCallingUid()
215 }
216 }
217
218
219 @Test
testSampleConnectivityState_NetworkRequestnull220 fun testSampleConnectivityState_NetworkRequest() {
221 val requestCount = 5
222 fileNetworkRequest(RT_APP, requestCount);
223 fileNetworkRequest(RT_SYSTEM, requestCount, SYSTEM_UID);
224 fileNetworkRequest(RT_SYSTEM_ON_BEHALF_OF_APP, requestCount, SYSTEM_UID);
225
226 val stats = csHandler.onHandler { service.sampleConnectivityState() }
227
228 assertEquals(3, stats.networkRequestCount.requestCountForTypeList.size)
229 val appRequest = stats.networkRequestCount.requestCountForTypeList.find {
230 it.requestType == RT_APP
231 } ?: fail("Can't find RT_APP request")
232 val systemRequest = stats.networkRequestCount.requestCountForTypeList.find {
233 it.requestType == RT_SYSTEM
234 } ?: fail("Can't find RT_SYSTEM request")
235 val systemOnBehalfOfAppRequest = stats.networkRequestCount.requestCountForTypeList.find {
236 it.requestType == RT_SYSTEM_ON_BEHALF_OF_APP
237 } ?: fail("Can't find RT_SYSTEM_ON_BEHALF_OF_APP request")
238
239 // Verify request count is equal or larger than the number of request this test filed
240 // since ConnectivityService internally files network requests
241 assertTrue("Unexpected RT_APP count, expected >= $requestCount, " +
242 "found ${appRequest.requestCount}", appRequest.requestCount >= requestCount)
243 assertTrue("Unexpected RT_SYSTEM count, expected >= $requestCount, " +
244 "found ${systemRequest.requestCount}", systemRequest.requestCount >= requestCount)
245 assertTrue("Unexpected RT_SYSTEM_ON_BEHALF_OF_APP count, expected >= $requestCount, " +
246 "found ${systemOnBehalfOfAppRequest.requestCount}",
247 systemOnBehalfOfAppRequest.requestCount >= requestCount)
248 }
249 }
250