Merge "[Cronet] Move OWNERS file to parent tests directory"
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index 15b3938..3f35c6b 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -38,6 +38,13 @@
     name: "ConnectivityNextEnableDefaults",
     enabled: true,
 }
+java_defaults {
+    name: "NetworkStackApiShimSettingsForCurrentBranch",
+    // API shims to include in the networking modules built from the branch. Branches that disable
+    // the "next" targets must use stable shims (latest stable API level) instead of current shims
+    // (X_current API level).
+    static_libs: ["NetworkStackApiCurrentShims"],
+}
 apex_defaults {
     name: "ConnectivityApexDefaults",
     // Tethering app to include in the AOSP apex. Branches that disable the "next" targets may use
diff --git a/service/Android.bp b/service/Android.bp
index 1523af9..e1376a1 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -138,6 +138,14 @@
     name: "service-connectivity-pre-jarjar",
     sdk_version: "system_server_current",
     min_sdk_version: "30",
+    // NetworkStackApiShimSettingsForCurrentBranch provides the latest available shims depending on
+    // the branch to "service-connectivity".
+    // There are Tethering.apk and TetheringNext.apk variants for the tethering APEX,
+    // which use NetworkStackApiStableShims and NetworkStackApiCurrentShims respectively.
+    // Note that there can be no service-connectivity-next because it would need to be configured in
+    // default_art_config.mk which doesn't support conditionals, hence this scheme of using a
+    // variable here.
+    defaults: ["NetworkStackApiShimSettingsForCurrentBranch"],
     srcs: [
         "src/**/*.java",
         ":framework-connectivity-shared-srcs",
@@ -183,7 +191,6 @@
         "PlatformProperties",
         "service-connectivity-protos",
         "service-connectivity-stats-protos",
-        "NetworkStackApiStableShims",
     ],
     apex_available: [
         "com.android.tethering",
diff --git a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
index 6b7222a..7e288c6 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";
     /**
@@ -178,8 +179,7 @@
         private final Network mUnderpinnedNetwork;
 
         AutomaticOnOffKeepalive(@NonNull final KeepaliveTracker.KeepaliveInfo ki,
-                final boolean autoOnOff, @NonNull Context context,
-                @Nullable Network underpinnedNetwork)
+                final boolean autoOnOff, @Nullable Network underpinnedNetwork)
                 throws InvalidSocketException {
             this.mKi = Objects.requireNonNull(ki);
             mCallback = ki.mCallback;
@@ -280,12 +280,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 +324,7 @@
             handleMaybeResumeKeepalive(ki);
         }
         // TODO: listen to socket status instead of periodically check.
-        startTcpPollingAlarm(ki.mAlarmListener);
+        startTcpPollingAlarm(ki);
     }
 
     /**
@@ -402,7 +404,7 @@
         }
         mAutomaticOnOffKeepalives.add(autoKi);
         if (STATE_ALWAYS_ON != autoKi.mAutomaticOnOffState) {
-            startTcpPollingAlarm(autoKi.mAlarmListener);
+            startTcpPollingAlarm(autoKi);
         }
     }
 
@@ -463,7 +465,7 @@
         if (null == ki) return;
         try {
             final AutomaticOnOffKeepalive autoKi = new AutomaticOnOffKeepalive(ki,
-                    automaticOnOffKeepalives, mContext, underpinnedNetwork);
+                    automaticOnOffKeepalives, underpinnedNetwork);
             mConnectivityServiceHandler.obtainMessage(NetworkAgent.CMD_START_SOCKET_KEEPALIVE,
                     // TODO : move ConnectivityService#encodeBool to a static lib.
                     automaticOnOffKeepalives ? 1 : 0, 0, autoKi).sendToTarget();
@@ -493,7 +495,7 @@
         if (null == ki) return;
         try {
             final AutomaticOnOffKeepalive autoKi = new AutomaticOnOffKeepalive(ki,
-                    automaticOnOffKeepalives, mContext, underpinnedNetwork);
+                    automaticOnOffKeepalives, underpinnedNetwork);
             mConnectivityServiceHandler.obtainMessage(NetworkAgent.CMD_START_SOCKET_KEEPALIVE,
                     // TODO : move ConnectivityService#encodeBool to a static lib.
                     automaticOnOffKeepalives ? 1 : 0, 0, autoKi).sendToTarget();
@@ -523,7 +525,7 @@
         try {
             final AutomaticOnOffKeepalive autoKi = new AutomaticOnOffKeepalive(ki,
                     false /* autoOnOff, tcp keepalives are never auto on/off */,
-                    mContext, null /* underpinnedNetwork, tcp keepalives do not refer to this */);
+                    null /* underpinnedNetwork, tcp keepalives do not refer to this */);
             mConnectivityServiceHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, autoKi)
                     .sendToTarget();
         } catch (InvalidSocketException e) {
@@ -677,9 +679,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 +794,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/integration/Android.bp b/tests/integration/Android.bp
index e3d80a0..12919ae 100644
--- a/tests/integration/Android.bp
+++ b/tests/integration/Android.bp
@@ -21,7 +21,10 @@
 
 android_test {
     name: "FrameworksNetIntegrationTests",
-    defaults: ["framework-connectivity-internal-test-defaults"],
+    defaults: [
+        "framework-connectivity-internal-test-defaults",
+        "NetworkStackApiShimSettingsForCurrentBranch",
+    ],
     platform_apis: true,
     certificate: "platform",
     srcs: [
@@ -33,6 +36,13 @@
         "ServiceConnectivityResources",
     ],
     static_libs: [
+        // It does not matter if NetworkStackApiStableLib or NetworkStackApiCurrentLib is used here,
+        // since the shims for the branch are already included via
+        // NetworkStackApiShimSettingsForCurrentBranch, and will be used in priority as they are
+        // first in the classpath.
+        // If the wrong shims are used for some reason, tests that use newer APIs fail.
+        // TODO: have NetworkStackApiStableLib link dynamically against the shims to remove this
+        // order-dependent setup.
         "NetworkStackApiStableLib",
         "androidx.test.ext.junit",
         "frameworks-net-integration-testutils",
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