Fix wrong request_count in connectivityStateSample and add tests

This CL updates CSTest to use mocked appOpsManager to bypass
checkPackage enforcement so that test can file network request as
SYSTEM_UID

Bug: 314627716
Test: atest ConnectivitySampleMetricsTest
Change-Id: I0a5f3b1bf2c343644e6f5e2a4ec1b0460e2645c4
This commit is contained in:
Motomu Utsumi 2024-02-28 15:58:42 +09:00
commit e08de4d322
3 changed files with 90 additions and 5 deletions

View file

@ -2607,7 +2607,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// Not the system, so it's an app requesting on its own behalf.
type = RequestType.RT_APP.getNumber();
}
countPerType.put(type, countPerType.get(type, 0));
countPerType.put(type, countPerType.get(type, 0) + 1);
}
for (int i = countPerType.size() - 1; i >= 0; --i) {
final RequestCountForType.Builder r = RequestCountForType.newBuilder();

View file

@ -1,5 +1,6 @@
package com.android.metrics
import android.net.ConnectivityThread
import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.CONNECTIVITY_MANAGED_CAPABILITIES
import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL
@ -15,13 +16,20 @@ import android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1
import android.net.NetworkCapabilities.NET_ENTERPRISE_ID_3
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
import android.net.NetworkCapabilities.TRANSPORT_WIFI
import android.net.NetworkRequest
import android.net.NetworkScore
import android.net.NetworkScore.KEEP_CONNECTED_FOR_TEST
import android.net.NetworkScore.POLICY_EXITING
import android.net.NetworkScore.POLICY_TRANSPORT_PRIMARY
import android.os.Build
import android.os.Handler
import android.os.Process
import android.os.Process.SYSTEM_UID
import android.stats.connectivity.MeteredState
import android.stats.connectivity.RequestType
import android.stats.connectivity.RequestType.RT_APP
import android.stats.connectivity.RequestType.RT_SYSTEM
import android.stats.connectivity.RequestType.RT_SYSTEM_ON_BEHALF_OF_APP
import android.stats.connectivity.ValidatedState
import androidx.test.filters.SmallTest
import com.android.net.module.util.BitUtils
@ -31,11 +39,13 @@ import com.android.server.connectivity.FullScore
import com.android.server.connectivity.FullScore.POLICY_IS_UNMETERED
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
import org.junit.Test
import org.junit.runner.RunWith
import com.android.testutils.TestableNetworkCallback
import java.util.concurrent.CompletableFuture
import kotlin.test.assertEquals
import kotlin.test.fail
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
private fun <T> Handler.onHandler(f: () -> T): T {
val future = CompletableFuture<T>()
@ -80,7 +90,7 @@ private val NetworkCapabilities.validatedState get() = when {
@IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
class ConnectivitySampleMetricsTest : CSTest() {
@Test
fun testSampleConnectivityState() {
fun testSampleConnectivityState_Network() {
val wifi1Caps = NetworkCapabilities.Builder()
.addTransportType(TRANSPORT_WIFI)
.addCapability(NET_CAPABILITY_NOT_METERED)
@ -179,4 +189,61 @@ class ConnectivitySampleMetricsTest : CSTest() {
"expected ${expectedWifi2Policies.toPolicyString()}, " +
"found ${foundWifi2.scorePolicies.toPolicyString()}")
}
private fun fileNetworkRequest(requestType: RequestType, requestCount: Int, uid: Int? = null) {
if (uid != null) {
deps.setCallingUid(uid)
}
try {
repeat(requestCount) {
when (requestType) {
RT_APP, RT_SYSTEM -> cm.requestNetwork(
NetworkRequest.Builder().build(),
TestableNetworkCallback()
)
RT_SYSTEM_ON_BEHALF_OF_APP -> cm.registerDefaultNetworkCallbackForUid(
Process.myUid(),
TestableNetworkCallback(),
Handler(ConnectivityThread.getInstanceLooper()))
else -> fail("invalid requestType: " + requestType)
}
}
} finally {
deps.unmockCallingUid()
}
}
@Test
fun testSampleConnectivityState_NetworkRequest() {
val requestCount = 5
fileNetworkRequest(RT_APP, requestCount);
fileNetworkRequest(RT_SYSTEM, requestCount, SYSTEM_UID);
fileNetworkRequest(RT_SYSTEM_ON_BEHALF_OF_APP, requestCount, SYSTEM_UID);
val stats = csHandler.onHandler { service.sampleConnectivityState() }
assertEquals(3, stats.networkRequestCount.requestCountForTypeList.size)
val appRequest = stats.networkRequestCount.requestCountForTypeList.find {
it.requestType == RT_APP
} ?: fail("Can't find RT_APP request")
val systemRequest = stats.networkRequestCount.requestCountForTypeList.find {
it.requestType == RT_SYSTEM
} ?: fail("Can't find RT_SYSTEM request")
val systemOnBehalfOfAppRequest = stats.networkRequestCount.requestCountForTypeList.find {
it.requestType == RT_SYSTEM_ON_BEHALF_OF_APP
} ?: fail("Can't find RT_SYSTEM_ON_BEHALF_OF_APP request")
// Verify request count is equal or larger than the number of request this test filed
// since ConnectivityService internally files network requests
assertTrue("Unexpected RT_APP count, expected >= $requestCount, " +
"found ${appRequest.requestCount}", appRequest.requestCount >= requestCount)
assertTrue("Unexpected RT_SYSTEM count, expected >= $requestCount, " +
"found ${systemRequest.requestCount}", systemRequest.requestCount >= requestCount)
assertTrue("Unexpected RT_SYSTEM_ON_BEHALF_OF_APP count, expected >= $requestCount, " +
"found ${systemOnBehalfOfAppRequest.requestCount}",
systemOnBehalfOfAppRequest.requestCount >= requestCount)
}
}

View file

@ -17,6 +17,7 @@
package com.android.server
import android.app.AlarmManager
import android.app.AppOpsManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
@ -75,8 +76,8 @@ import com.android.testutils.waitForIdle
import java.util.concurrent.Executors
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.TimeUnit
import java.util.function.Consumer
import java.util.function.BiConsumer
import java.util.function.Consumer
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.fail
@ -103,6 +104,8 @@ internal const val VERSION_U = 4
internal const val VERSION_V = 5
internal const val VERSION_MAX = VERSION_V
internal const val CALLING_UID_UNMOCKED = Process.INVALID_UID
private fun NetworkCapabilities.getLegacyType() =
when (transportTypes.getOrElse(0) { TRANSPORT_WIFI }) {
TRANSPORT_BLUETOOTH -> ConnectivityManager.TYPE_BLUETOOTH
@ -176,6 +179,7 @@ open class CSTest {
val systemConfigManager = makeMockSystemConfigManager()
val batteryStats = mock<IBatteryStats>()
val batteryManager = BatteryStatsManager(batteryStats)
val appOpsManager = mock<AppOpsManager>()
val telephonyManager = mock<TelephonyManager>().also {
doReturn(true).`when`(it).isDataCapable()
}
@ -298,6 +302,19 @@ open class CSTest {
override fun isAtLeastT() = if (isSdkUnmocked) super.isAtLeastT() else sdkLevel >= VERSION_T
override fun isAtLeastU() = if (isSdkUnmocked) super.isAtLeastU() else sdkLevel >= VERSION_U
override fun isAtLeastV() = if (isSdkUnmocked) super.isAtLeastV() else sdkLevel >= VERSION_V
private var callingUid = CALLING_UID_UNMOCKED
fun unmockCallingUid() {
setCallingUid(CALLING_UID_UNMOCKED)
}
fun setCallingUid(callingUid: Int) {
visibleOnHandlerThread(csHandler) { this.callingUid = callingUid }
}
override fun getCallingUid() =
if (callingUid == CALLING_UID_UNMOCKED) super.getCallingUid() else callingUid
}
inner class CSContext(base: Context) : BroadcastInterceptingContext(base) {
@ -398,6 +415,7 @@ open class CSTest {
Context.TELEPHONY_SERVICE -> telephonyManager
Context.BATTERY_STATS_SERVICE -> batteryManager
Context.STATS_MANAGER -> null // Stats manager is final and can't be mocked
Context.APP_OPS_SERVICE -> appOpsManager
else -> super.getSystemService(serviceName)
}