From c623c946f51daf9c589d80381a440684d8a7a871 Mon Sep 17 00:00:00 2001 From: Suprabh Shukla Date: Tue, 15 Oct 2024 17:19:07 -0700 Subject: [PATCH] Make the idle timer for network data tracking configurable Networks can use widely varying timeouts for their connections. Because cellular modems use a lot of power, having this configurable is more useful for tuning this to an optimal value for power drain. Adding a similar configurable value for WiFi for parity. Test: atest ConnectivityCoverageTests:\ android.net.connectivity.com.android.server.CSNetworkActivityTest Bug: 373733012 Change-Id: I2418f09233b2e3fa4e9cdd8fbb6149a35106b735 --- .../android/server/ConnectivityService.java | 39 +++++- .../connectivity/ConnectivityFlags.java | 5 + .../server/ConnectivityServiceTest.java | 12 ++ .../CSNetworkActivityTest.kt | 129 ++++++++++++++++++ .../server/connectivityservice/base/CSTest.kt | 12 ++ 5 files changed, 193 insertions(+), 4 deletions(-) diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java index 665e6f9e8e..6f23e01e50 100755 --- a/service/src/com/android/server/ConnectivityService.java +++ b/service/src/com/android/server/ConnectivityService.java @@ -121,6 +121,7 @@ import static android.net.connectivity.ConnectivityCompatChanges.ENABLE_SELF_CER import static android.net.connectivity.ConnectivityCompatChanges.NETWORK_BLOCKED_WITHOUT_INTERNET_PERMISSION; import static android.os.Process.INVALID_UID; import static android.os.Process.VPN_UID; +import static android.provider.DeviceConfig.NAMESPACE_TETHERING; import static android.system.OsConstants.ETH_P_ALL; import static android.system.OsConstants.IPPROTO_TCP; import static android.system.OsConstants.IPPROTO_UDP; @@ -145,10 +146,12 @@ import static com.android.net.module.util.PermissionUtils.enforceNetworkStackPer import static com.android.net.module.util.PermissionUtils.enforceNetworkStackPermissionOr; import static com.android.net.module.util.PermissionUtils.hasAnyPermissionOf; import static com.android.server.ConnectivityStatsLog.CONNECTIVITY_STATE_SAMPLE; +import static com.android.server.connectivity.ConnectivityFlags.CELLULAR_DATA_INACTIVITY_TIMEOUT; import static com.android.server.connectivity.ConnectivityFlags.DELAY_DESTROY_SOCKETS; import static com.android.server.connectivity.ConnectivityFlags.INGRESS_TO_VPN_ADDRESS_FILTERING; import static com.android.server.connectivity.ConnectivityFlags.QUEUE_CALLBACKS_FOR_FROZEN_APPS; import static com.android.server.connectivity.ConnectivityFlags.REQUEST_RESTRICTED_WIFI; +import static com.android.server.connectivity.ConnectivityFlags.WIFI_DATA_INACTIVITY_TIMEOUT; import android.Manifest; import android.annotation.CheckResult; @@ -1610,6 +1613,18 @@ public class ConnectivityService extends IConnectivityManager.Stub connectivityServiceInternalHandler); } + /** Returns the data inactivity timeout to be used for cellular networks */ + public int getDefaultCellularDataInactivityTimeout() { + return DeviceConfigUtils.getDeviceConfigPropertyInt(NAMESPACE_TETHERING, + CELLULAR_DATA_INACTIVITY_TIMEOUT, 10); + } + + /** Returns the data inactivity timeout to be used for WiFi networks */ + public int getDefaultWifiDataInactivityTimeout() { + return DeviceConfigUtils.getDeviceConfigPropertyInt(NAMESPACE_TETHERING, + WIFI_DATA_INACTIVITY_TIMEOUT, 15); + } + /** * @see DeviceConfigUtils#isTetheringFeatureEnabled */ @@ -1958,8 +1973,13 @@ public class ConnectivityService extends IConnectivityManager.Stub // But reading the trunk stable flags from mainline modules is not supported yet. // So enabling this feature on V+ release. mTrackMultiNetworkActivities = mDeps.isAtLeastV(); + final int defaultCellularDataInactivityTimeout = + mDeps.getDefaultCellularDataInactivityTimeout(); + final int defaultWifiDataInactivityTimeout = + mDeps.getDefaultWifiDataInactivityTimeout(); mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mNetd, mHandler, - mTrackMultiNetworkActivities); + mTrackMultiNetworkActivities, defaultCellularDataInactivityTimeout, + defaultWifiDataInactivityTimeout); final NetdCallback netdCallback = new NetdCallback(); try { @@ -13023,6 +13043,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // Key is netId. Value is configured idle timer information. private final SparseArray mActiveIdleTimers = new SparseArray<>(); private final boolean mTrackMultiNetworkActivities; + private final int mDefaultCellularDataInactivityTimeout; + private final int mDefaultWifiDataInactivityTimeout; // Store netIds of Wi-Fi networks whose idletimers report that they are active private final Set mActiveWifiNetworks = new ArraySet<>(); // Store netIds of cellular networks whose idletimers report that they are active @@ -13039,11 +13061,14 @@ public class ConnectivityService extends IConnectivityManager.Stub } LegacyNetworkActivityTracker(@NonNull Context context, @NonNull INetd netd, - @NonNull Handler handler, boolean trackMultiNetworkActivities) { + @NonNull Handler handler, boolean trackMultiNetworkActivities, + int defaultCellularDataInactivityTimeout, int defaultWifiDataInactivityTimeout) { mContext = context; mNetd = netd; mHandler = handler; mTrackMultiNetworkActivities = trackMultiNetworkActivities; + mDefaultCellularDataInactivityTimeout = defaultCellularDataInactivityTimeout; + mDefaultWifiDataInactivityTimeout = defaultWifiDataInactivityTimeout; } private void ensureRunningOnConnectivityServiceThread() { @@ -13243,13 +13268,13 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkCapabilities.TRANSPORT_CELLULAR)) { timeout = Settings.Global.getInt(mContext.getContentResolver(), ConnectivitySettingsManager.DATA_ACTIVITY_TIMEOUT_MOBILE, - 10); + mDefaultCellularDataInactivityTimeout); type = NetworkCapabilities.TRANSPORT_CELLULAR; } else if (networkAgent.networkCapabilities.hasTransport( NetworkCapabilities.TRANSPORT_WIFI)) { timeout = Settings.Global.getInt(mContext.getContentResolver(), ConnectivitySettingsManager.DATA_ACTIVITY_TIMEOUT_WIFI, - 15); + mDefaultWifiDataInactivityTimeout); type = NetworkCapabilities.TRANSPORT_WIFI; } else { return false; // do not track any other networks @@ -13373,6 +13398,12 @@ public class ConnectivityService extends IConnectivityManager.Stub public void dump(IndentingPrintWriter pw) { pw.print("mTrackMultiNetworkActivities="); pw.println(mTrackMultiNetworkActivities); + + pw.print("mDefaultCellularDataInactivityTimeout="); + pw.println(mDefaultCellularDataInactivityTimeout); + pw.print("mDefaultWifiDataInactivityTimeout="); + pw.println(mDefaultWifiDataInactivityTimeout); + pw.print("mIsDefaultNetworkActive="); pw.println(mIsDefaultNetworkActive); pw.print("mDefaultNetwork="); pw.println(mDefaultNetwork); pw.println("Idle timers:"); diff --git a/service/src/com/android/server/connectivity/ConnectivityFlags.java b/service/src/com/android/server/connectivity/ConnectivityFlags.java index df8731619a..93335f15f5 100644 --- a/service/src/com/android/server/connectivity/ConnectivityFlags.java +++ b/service/src/com/android/server/connectivity/ConnectivityFlags.java @@ -44,6 +44,11 @@ public final class ConnectivityFlags { public static final String BACKGROUND_FIREWALL_CHAIN = "background_firewall_chain"; + public static final String CELLULAR_DATA_INACTIVITY_TIMEOUT = + "cellular_data_inactivity_timeout"; + + public static final String WIFI_DATA_INACTIVITY_TIMEOUT = "wifi_data_inactivity_timeout"; + public static final String DELAY_DESTROY_SOCKETS = "delay_destroy_sockets"; public static final String USE_DECLARED_METHODS_FOR_CALLBACKS = diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java index 999d17d7b0..f7d7c87882 100755 --- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java @@ -2369,6 +2369,18 @@ public class ConnectivityServiceTest { mScheduledEvaluationTimeouts.add(new Pair<>(network.netId, delayMs)); super.scheduleEvaluationTimeout(handler, network, delayMs); } + + @Override + public int getDefaultCellularDataInactivityTimeout() { + // Needed to mock out the dependency on DeviceConfig + return 10; + } + + @Override + public int getDefaultWifiDataInactivityTimeout() { + // Needed to mock out the dependency on DeviceConfig + return 15; + } } private class AutomaticOnOffKeepaliveTrackerDependencies diff --git a/tests/unit/java/com/android/server/connectivityservice/CSNetworkActivityTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSNetworkActivityTest.kt index df0a2cc97b..ccbd6b3370 100644 --- a/tests/unit/java/com/android/server/connectivityservice/CSNetworkActivityTest.kt +++ b/tests/unit/java/com/android/server/connectivityservice/CSNetworkActivityTest.kt @@ -21,6 +21,7 @@ import android.net.ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE import android.net.ConnectivityManager.EXTRA_DEVICE_TYPE import android.net.ConnectivityManager.EXTRA_IS_ACTIVE import android.net.ConnectivityManager.EXTRA_REALTIME_NS +import android.net.ConnectivitySettingsManager import android.net.LinkProperties import android.net.NetworkCapabilities import android.net.NetworkCapabilities.NET_CAPABILITY_IMS @@ -41,12 +42,14 @@ import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo import com.android.testutils.DevSdkIgnoreRunner import com.android.testutils.RecorderCallback.CallbackEntry.Lost import com.android.testutils.TestableNetworkCallback +import java.time.Duration import kotlin.test.assertNotNull import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.anyString import org.mockito.ArgumentMatchers.eq import org.mockito.Mockito.anyInt import org.mockito.Mockito.anyLong @@ -69,6 +72,18 @@ private const val TIMEOUT_MS = 250L @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) class CSNetworkActivityTest : CSTest() { + private fun setMobileDataActivityTimeout(timeoutSeconds: Int) { + ConnectivitySettingsManager.setMobileDataActivityTimeout( + context, Duration.ofSeconds(timeoutSeconds.toLong()) + ) + } + + private fun setWifiDataActivityTimeout(timeoutSeconds: Int) { + ConnectivitySettingsManager.setWifiDataActivityTimeout( + context, Duration.ofSeconds(timeoutSeconds.toLong()) + ) + } + private fun getRegisteredNetdUnsolicitedEventListener(): BaseNetdUnsolicitedEventListener { val captor = ArgumentCaptor.forClass(BaseNetdUnsolicitedEventListener::class.java) verify(netd).registerUnsolicitedEventListener(captor.capture()) @@ -252,8 +267,122 @@ class CSNetworkActivityTest : CSTest() { cm.unregisterNetworkCallback(dataNetworkCb) cm.unregisterNetworkCallback(imsNetworkCb) } + + @Test + fun testCellularIdleTimerSettingsTimeout() { + val cellNc = NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_SUSPENDED) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .build() + val cellLp = LinkProperties().apply { + interfaceName = DATA_CELL_IFNAME + } + + val settingsTimeout: Int = deps.defaultCellDataInactivityTimeoutForTest + 432 + // DATA_ACTIVITY_TIMEOUT_MOBILE is set, so the default should be ignored. + setMobileDataActivityTimeout(settingsTimeout) + val cellAgent = Agent(nc = cellNc, lp = cellLp) + cellAgent.connect() + + verify(netd).idletimerAddInterface(eq(DATA_CELL_IFNAME), eq(settingsTimeout), anyString()) + } + + @Test + fun testCellularIdleTimerDefaultTimeout() { + val cellNc = NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_SUSPENDED) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .build() + val cellLp = LinkProperties().apply { + interfaceName = DATA_CELL_IFNAME + } + + val testTimeout: Int = deps.defaultCellDataInactivityTimeoutForTest + // DATA_ACTIVITY_TIMEOUT_MOBILE is not set, so the default should be used. + val cellAgent = Agent(nc = cellNc, lp = cellLp) + cellAgent.connect() + + verify(netd).idletimerAddInterface(eq(DATA_CELL_IFNAME), eq(testTimeout), anyString()) + } + + @Test + fun testCellularIdleTimerDisabled() { + val cellNc = NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_SUSPENDED) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .build() + val cellLp = LinkProperties().apply { + interfaceName = DATA_CELL_IFNAME + } + setMobileDataActivityTimeout(0) + val cellAgent = Agent(nc = cellNc, lp = cellLp) + cellAgent.connect() + + verify(netd, never()).idletimerAddInterface(eq(DATA_CELL_IFNAME), anyInt(), anyString()) + } + + @Test + fun testWifiIdleTimerSettingsTimeout() { + val wifiNc = NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_WIFI) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .build() + val wifiLp = LinkProperties().apply { + interfaceName = WIFI_IFNAME + } + val settingsTimeout: Int = deps.defaultWifiDataInactivityTimeout + 435 + setWifiDataActivityTimeout(settingsTimeout) + // DATA_ACTIVITY_TIMEOUT_MOBILE is set, so the default should be ignored. + val wifiAgent = Agent(nc = wifiNc, lp = wifiLp) + wifiAgent.connect() + + verify(netd).idletimerAddInterface(eq(WIFI_IFNAME), eq(settingsTimeout), anyString()) + } + + @Test + fun testWifiIdleTimerDefaultTimeout() { + val wifiNc = NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_WIFI) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .build() + val wifiLp = LinkProperties().apply { + interfaceName = WIFI_IFNAME + } + val testTimeout: Int = deps.defaultWifiDataInactivityTimeoutForTest + // DATA_ACTIVITY_TIMEOUT_WIFI is not set, so the default should be used. + val wifiAgent = Agent(nc = wifiNc, lp = wifiLp) + wifiAgent.connect() + + verify(netd).idletimerAddInterface(eq(WIFI_IFNAME), eq(testTimeout), anyString()) + } + + @Test + fun testWifiIdleTimerDisabled() { + val wifiNc = NetworkCapabilities.Builder() + .addTransportType(TRANSPORT_WIFI) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED) + .build() + val wifiLp = LinkProperties().apply { + interfaceName = WIFI_IFNAME + } + setWifiDataActivityTimeout(0) + val wifiAgent = Agent(nc = wifiNc, lp = wifiLp) + wifiAgent.connect() + + verify(netd, never()).idletimerAddInterface(eq(WIFI_IFNAME), anyInt(), anyString()) + } } + internal fun CSContext.expectDataActivityBroadcast( deviceType: Int, isActive: Boolean, diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt index 46c25d2153..ae196a624b 100644 --- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt +++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt @@ -341,6 +341,18 @@ open class CSTest { } } + // Need a non-zero value to avoid disarming the timer. + val defaultCellDataInactivityTimeoutForTest: Int = 81 + override fun getDefaultCellularDataInactivityTimeout(): Int { + return defaultCellDataInactivityTimeoutForTest + } + + // Need a non-zero value to avoid disarming the timer. + val defaultWifiDataInactivityTimeoutForTest: Int = 121 + override fun getDefaultWifiDataInactivityTimeout(): Int { + return defaultWifiDataInactivityTimeoutForTest + } + override fun isChangeEnabled(changeId: Long, pkg: String, user: UserHandle) = changeId in enabledChangeIds override fun isChangeEnabled(changeId: Long, uid: Int) =