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
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 665e6f9..6f23e01 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.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.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 @@
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 @@
// 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 @@
// Key is netId. Value is configured idle timer information.
private final SparseArray<IdleTimerParams> 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<Integer> mActiveWifiNetworks = new ArraySet<>();
// Store netIds of cellular networks whose idletimers report that they are active
@@ -13039,11 +13061,14 @@
}
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 @@
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 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 df87316..93335f1 100644
--- a/service/src/com/android/server/connectivity/ConnectivityFlags.java
+++ b/service/src/com/android/server/connectivity/ConnectivityFlags.java
@@ -44,6 +44,11 @@
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 999d17d..f7d7c87 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -2369,6 +2369,18 @@
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 df0a2cc..ccbd6b3 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.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.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 @@
@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 @@
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 46c25d2..ae196a6 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 @@
}
}
+ // 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) =