Adjust polling interval for auto on/off keepalives
The system polls TCP connections every 2 minutes for the auto
on/off keepalives. For some carriers, the NAT-T keepalive delay
is lower than this, which means it's possible a TCP connection
is opened seconds after the poll. This may cause connections
being disconnected because keepalive timed-out.
The minimal acceptable value should be the same as the keepalive
delay. But the system may take time to restart keepalive. Adjust
the delay of the polling alarm to be lower than the keepalive
delay to preserve some time for the system to restart the
keepalive.
Bug: 273411114
Test: atest FrameworksNetTests
Change-Id: I28f71ceb7b72d83e6e0f8f7d22d3d1e9dbdaa467
diff --git a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
index 6b7222a..9b6ec64 100644
--- a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
@@ -18,6 +18,7 @@
import static android.net.NetworkAgent.CMD_START_SOCKET_KEEPALIVE;
import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
+import static android.net.SocketKeepalive.MIN_INTERVAL_SEC;
import static android.net.SocketKeepalive.SUCCESS_PAUSED;
import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
import static android.system.OsConstants.AF_INET;
@@ -88,8 +89,8 @@
public class AutomaticOnOffKeepaliveTracker {
private static final String TAG = "AutomaticOnOffKeepaliveTracker";
private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET6, AF_INET};
- private static final long DEFAULT_TCP_POLLING_INTERVAL_MS = 120_000L;
private static final long LOW_TCP_POLLING_INTERVAL_MS = 1_000L;
+ private static final int ADJUST_TCP_POLLING_DELAY_MS = 2000;
private static final String AUTOMATIC_ON_OFF_KEEPALIVE_VERSION =
"automatic_on_off_keepalive_version";
/**
@@ -280,12 +281,14 @@
mAlarmManager = mDependencies.getAlarmManager(context);
}
- private void startTcpPollingAlarm(@NonNull final AlarmManager.OnAlarmListener listener) {
+ private void startTcpPollingAlarm(@NonNull AutomaticOnOffKeepalive ki) {
+ if (ki.mAlarmListener == null) return;
+
final long triggerAtMillis =
- SystemClock.elapsedRealtime() + getTcpPollingInterval();
+ mDependencies.getElapsedRealtime() + getTcpPollingIntervalMs(ki);
// Setup a non-wake up alarm.
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, triggerAtMillis, null /* tag */,
- listener, mConnectivityServiceHandler);
+ ki.mAlarmListener, mConnectivityServiceHandler);
}
/**
@@ -322,7 +325,7 @@
handleMaybeResumeKeepalive(ki);
}
// TODO: listen to socket status instead of periodically check.
- startTcpPollingAlarm(ki.mAlarmListener);
+ startTcpPollingAlarm(ki);
}
/**
@@ -402,7 +405,7 @@
}
mAutomaticOnOffKeepalives.add(autoKi);
if (STATE_ALWAYS_ON != autoKi.mAutomaticOnOffState) {
- startTcpPollingAlarm(autoKi.mAlarmListener);
+ startTcpPollingAlarm(autoKi);
}
}
@@ -677,9 +680,15 @@
}
}
- private long getTcpPollingInterval() {
+ private long getTcpPollingIntervalMs(@NonNull AutomaticOnOffKeepalive ki) {
final boolean useLowTimer = mTestLowTcpPollingTimerUntilMs > System.currentTimeMillis();
- return useLowTimer ? LOW_TCP_POLLING_INTERVAL_MS : DEFAULT_TCP_POLLING_INTERVAL_MS;
+ // Adjust the polling interval to be smaller than the keepalive delay to preserve
+ // some time for the system to restart the keepalive.
+ final int timer = ki.mKi.getKeepaliveIntervalSec() * 1000 - ADJUST_TCP_POLLING_DELAY_MS;
+ if (timer < MIN_INTERVAL_SEC) {
+ Log.wtf(TAG, "Unreasonably low keepalive delay: " + ki.mKi.getKeepaliveIntervalSec());
+ }
+ return useLowTimer ? LOW_TCP_POLLING_INTERVAL_MS : Math.max(timer, MIN_INTERVAL_SEC);
}
/**
@@ -786,5 +795,14 @@
return DeviceConfigUtils.isFeatureEnabled(mContext, NAMESPACE_TETHERING, name,
defaultEnabled);
}
+
+ /**
+ * Returns milliseconds since boot, including time spent in sleep.
+ *
+ * @return elapsed milliseconds since boot.
+ */
+ public long getElapsedRealtime() {
+ return SystemClock.elapsedRealtime();
+ }
}
}
diff --git a/service/src/com/android/server/connectivity/KeepaliveTracker.java b/service/src/com/android/server/connectivity/KeepaliveTracker.java
index 06294db..60485b3 100644
--- a/service/src/com/android/server/connectivity/KeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/KeepaliveTracker.java
@@ -264,6 +264,10 @@
return mSlot;
}
+ int getKeepaliveIntervalSec() {
+ return mInterval;
+ }
+
private int checkNetworkConnected() {
if (!mNai.networkInfo.isConnectedOrConnecting()) {
return ERROR_INVALID_NETWORK;
diff --git a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
index 4f0b9c4..696eff4 100644
--- a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
@@ -28,8 +28,8 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.longThat;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -57,6 +57,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.SystemClock;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
@@ -94,6 +95,7 @@
private static final int NETID_MASK = 0xffff;
private static final int TIMEOUT_MS = 30_000;
private static final int MOCK_RESOURCE_ID = 5;
+ private static final int TEST_KEEPALIVE_INTERVAL_SEC = 10;
private AutomaticOnOffKeepaliveTracker mAOOKeepaliveTracker;
private HandlerThread mHandlerThread;
@@ -334,9 +336,13 @@
final KeepalivePacketData kpd = new NattKeepalivePacketData(srcAddress, srcPort,
dstAddress, dstPort, new byte[] {1});
final KeepaliveInfo ki = mKeepaliveTracker.new KeepaliveInfo(cb, nai, kpd,
- 10 /* interval */, KeepaliveInfo.TYPE_NATT, fd);
+ TEST_KEEPALIVE_INTERVAL_SEC, KeepaliveInfo.TYPE_NATT, fd);
mKeepaliveTracker.setReturnedKeepaliveInfo(ki);
+ // Mock elapsed real time to verify the alarm timer.
+ final long time = SystemClock.elapsedRealtime();
+ doReturn(time).when(mDependencies).getElapsedRealtime();
+
mAOOKeepaliveTracker.startNattKeepalive(nai, fd, 10 /* intervalSeconds */, cb,
srcAddress.toString(), srcPort, dstAddress.toString(), dstPort,
true /* automaticOnOffKeepalives */, underpinnedNetwork);
@@ -344,8 +350,11 @@
final ArgumentCaptor<AlarmManager.OnAlarmListener> listenerCaptor =
ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
- verify(mAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME), anyLong(),
- any(), listenerCaptor.capture(), eq(mTestHandler));
+ // The alarm timer should be smaller than the keepalive delay. Verify the alarm trigger time
+ // is higher than base time but smaller than the keepalive delay.
+ verify(mAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME),
+ longThat(t -> t > time + 1000L && t < time + TEST_KEEPALIVE_INTERVAL_SEC * 1000L),
+ any() /* tag */, listenerCaptor.capture(), eq(mTestHandler));
final AlarmManager.OnAlarmListener listener = listenerCaptor.getValue();
// For realism, the listener should be posted on the handler