<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