Merge "Add test for using ESP_IP_VERSION and ESP_ENCAP_TYPE in Vpn"
diff --git a/Cronet/tools/import/copy.bara.sky b/Cronet/tools/import/copy.bara.sky
new file mode 100644
index 0000000..64256a0
--- /dev/null
+++ b/Cronet/tools/import/copy.bara.sky
@@ -0,0 +1,110 @@
+# Copyright 2023 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+common_excludes = [
+    # Exclude all Android build files
+    "**/Android.bp",
+    "**/Android.mk",
+
+    # Exclude existing OWNERS files
+    "**/OWNERS",
+]
+
+cronet_origin_files = glob(
+    include = [
+        "base/**",
+        "build/**",
+        "build/buildflag.h",
+        "chrome/VERSION",
+        "components/cronet/**",
+        "components/grpc_suport/**",
+        "components/metrics/**",
+        "components/nacl/**",
+        "components/prefs/**",
+        "crypto/**",
+        "ipc/**",
+        "net/**",
+        "url/**",
+        "LICENSE",
+    ],
+    exclude = common_excludes + [
+        # Per aosp/2367109
+        "build/android/CheckInstallApk-debug.apk",
+        "build/android/unused_resources/**",
+        "build/linux/**",
+
+        # Per aosp/2374766
+        "components/cronet/ios/**",
+        "components/cronet/native/**",
+
+
+        # Exclude all third-party directories. Those are specified explicitly
+        # below, so no dependency can accidentally creep in.
+        "**/third_party/**",
+    ],
+) + glob(
+    # Explicitly include third-party dependencies.
+    # Note: some third-party dependencies include a third_party folder within
+    # them. So far, this has not become a problem.
+    include = [
+        "base/third_party/cityhash/**",
+        "base/third_party/cityhash_v103/**",
+        "base/third_party/double_conversion/**",
+        "base/third_party/dynamic_annotations/**",
+        "base/third_party/icu/**",
+        "base/third_party/nspr/**",
+        "base/third_party/superfasthash/**",
+        # TODO: we should be able to remove this dependency.
+        "base/third_party/symbolize/**",
+        "base/third_party/valgrind/**",
+        "base/third_party/xdg_user_dirs/**",
+        # Not present in source repo; requires gclient sync.
+        "buildtools/third_party/libc++/**",
+        # Not present in source repo; requires gclient sync.
+        "buildtools/third_party/libc++abi/**",
+        "net/third_party/quiche/**",
+        "net/third_party/uri_template/**",
+        "third_party/abseil-cpp/**",
+        "third_party/android_ndk/sources/android/cpufeatures/**",
+        "third_party/ashmem/**",
+        "third_party/boringssl/**",
+        "third_party/brotli/**",
+        # Not present in source repo; requires gclient sync.
+        "third_party/icu/**",
+        "third_party/libevent/**",
+        "third_party/metrics_proto/**",
+        "third_party/modp_b64/**",
+        "third_party/protobuf/**",
+        "third_party/zlib/**",
+    ],
+    exclude = common_excludes,
+)
+
+core.workflow(
+    name = "import_cronet",
+    authoring = authoring.overwrite("Cronet Mainline Eng <cronet-mainline-eng+copybara@google.com>"),
+    origin = git.origin(
+        url = "rpc://chromium/chromium/src",
+        # Source ref is set by the invoking script.
+        ref = "overwritten-by-script",
+        partial_fetch = True,
+    ),
+    origin_files = cronet_origin_files,
+    destination = git.destination(
+        # The destination URL is set by the invoking script.
+        url = "overwritten/by/script",
+        push = "upstream-import",
+    ),
+    mode = "SQUASH",
+)
diff --git a/Cronet/tools/import/import_cronet.sh b/Cronet/tools/import/import_cronet.sh
new file mode 100755
index 0000000..7642914
--- /dev/null
+++ b/Cronet/tools/import/import_cronet.sh
@@ -0,0 +1,76 @@
+#!/bin/bash
+
+# Copyright 2023 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Script to invoke copybara locally to import Cronet into Android.
+# Inputs:
+#  Environment:
+#   ANDROID_BUILD_TOP: path the root of the current Android directory.
+#  Arguments:
+#   -l: The last revision that was imported.
+#   -n: The new revision to import.
+
+OPTSTRING=l:n:
+
+usage() {
+    cat <<EOF
+Usage: import_cronet.sh -l last-rev -n new-rev
+EOF
+    exit 1
+}
+
+#######################################
+# Runs the copybara import of Chromium
+# Globals:
+#   ANDROID_BUILD_TOP
+# Arguments:
+#   last_rev, string
+#   new_rev, string
+#######################################
+do_run_copybara() {
+    local _last_rev=$1
+    local _new_rev=$2
+
+    /google/bin/releases/copybara/public/copybara/copybara \
+        --git-destination-url="file://${ANDROID_BUILD_TOP}/external/cronet" \
+        --last-rev "${_last_rev}" \
+        --repo-timeout 3h \
+        "${ANDROID_BUILD_TOP}/packages/modules/Connectivity/Cronet/tools/import/copy.bara.sky" \
+        import_cronet "${_new_rev}"
+}
+
+while getopts $OPTSTRING opt; do
+    case "${opt}" in
+        l) last_rev="${OPTARG}" ;;
+        n) new_rev="${OPTARG}" ;;
+        ?) usage ;;
+        *) echo "'${opt}' '${OPTARG}'"
+    esac
+done
+
+# TODO: Get last-rev from METADATA file.
+# Setting last-rev may only be required for the first commit.
+if [ -z "${last_rev}" ]; then
+    echo "-l argument required"
+    usage
+fi
+
+if [ -z "${new_rev}" ]; then
+    echo "-n argument required"
+    usage
+fi
+
+do_run_copybara "${last_rev}" "${new_rev}"
+
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 34646e2..4d3ecdf 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -58,6 +58,17 @@
       ]
     },
     {
+      "name": "CtsNetTestCasesMaxTargetSdk33",
+      "options": [
+        {
+          "exclude-annotation": "com.android.testutils.SkipPresubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.RequiresDevice"
+        }
+      ]
+    },
+    {
       "name": "bpf_existence_test"
     },
     {
@@ -143,6 +154,17 @@
         }
       ]
     },
+    {
+      "name": "CtsNetTestCasesMaxTargetSdk33[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
+      "options": [
+        {
+          "exclude-annotation": "com.android.testutils.SkipPresubmit"
+        },
+        {
+          "exclude-annotation": "androidx.test.filters.RequiresDevice"
+        }
+      ]
+    },
     // Test with APK modules only, in cases where APEX is not supported, or the other modules
     // were simply not updated
     {
diff --git a/Tethering/AndroidManifest.xml b/Tethering/AndroidManifest.xml
index 23467e7..6a363b0 100644
--- a/Tethering/AndroidManifest.xml
+++ b/Tethering/AndroidManifest.xml
@@ -45,7 +45,6 @@
 
     <!-- Sending non-protected broadcast from system uid is not allowed. -->
     <protected-broadcast android:name="com.android.server.connectivity.tethering.DISABLE_TETHERING" />
-    <protected-broadcast android:name="com.android.server.connectivity.KeepaliveTracker.TCP_POLLING_ALARM" />
 
     <application
         android:process="com.android.networkstack.process"
diff --git a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
index cd7a842..9f9b496 100644
--- a/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/AutomaticOnOffKeepaliveTracker.java
@@ -34,18 +34,13 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.net.INetd;
 import android.net.ISocketKeepaliveCallback;
 import android.net.MarkMaskParcel;
 import android.net.Network;
 import android.net.NetworkAgent;
 import android.net.SocketKeepalive.InvalidSocketException;
-import android.os.Bundle;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
@@ -61,7 +56,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.modules.utils.build.SdkLevel;
 import com.android.net.module.util.BinderUtils;
 import com.android.net.module.util.CollectionUtils;
 import com.android.net.module.util.DeviceConfigUtils;
@@ -94,8 +88,6 @@
 public class AutomaticOnOffKeepaliveTracker {
     private static final String TAG = "AutomaticOnOffKeepaliveTracker";
     private static final int[] ADDRESS_FAMILIES = new int[] {AF_INET6, AF_INET};
-    private static final String ACTION_TCP_POLLING_ALARM =
-            "com.android.server.connectivity.KeepaliveTracker.TCP_POLLING_ALARM";
     private static final String EXTRA_BINDER_TOKEN = "token";
     private static final long DEFAULT_TCP_POLLING_INTERVAL_MS = 120_000L;
     private static final long LOW_TCP_POLLING_INTERVAL_MS = 1_000L;
@@ -160,19 +152,6 @@
     // TODO: Remove this when TCP polling design is replaced with callback.
     private long mTestLowTcpPollingTimerUntilMs = 0;
 
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (ACTION_TCP_POLLING_ALARM.equals(intent.getAction())) {
-                Log.d(TAG, "Received TCP polling intent");
-                final IBinder token = intent.getBundleExtra(EXTRA_BINDER_TOKEN).getBinder(
-                        EXTRA_BINDER_TOKEN);
-                mConnectivityServiceHandler.obtainMessage(
-                        NetworkAgent.CMD_MONITOR_AUTOMATIC_KEEPALIVE, token).sendToTarget();
-            }
-        }
-    };
-
     /**
      * Information about a managed keepalive.
      *
@@ -193,7 +172,7 @@
         @Nullable
         private final FileDescriptor mFd;
         @Nullable
-        private final PendingIntent mTcpPollingAlarm;
+        private final AlarmManager.OnAlarmListener mAlarmListener;
         @AutomaticOnOffState
         private int mAutomaticOnOffState;
         @Nullable
@@ -219,17 +198,24 @@
                     Log.e(TAG, "Cannot dup fd: ", e);
                     throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
                 }
-                mTcpPollingAlarm = createTcpPollingAlarmIntent(context, mCallback.asBinder());
+                mAlarmListener = () -> mConnectivityServiceHandler.obtainMessage(
+                        NetworkAgent.CMD_MONITOR_AUTOMATIC_KEEPALIVE, mCallback.asBinder())
+                        .sendToTarget();
             } else {
                 mAutomaticOnOffState = STATE_ALWAYS_ON;
                 // A null fd is acceptable in KeepaliveInfo for backward compatibility of
                 // PacketKeepalive API, but it must never happen with automatic keepalives.
                 // TODO : remove mFd from KeepaliveInfo or from this class.
                 mFd = ki.mFd;
-                mTcpPollingAlarm = null;
+                mAlarmListener = null;
             }
         }
 
+        @VisibleForTesting
+        public ISocketKeepaliveCallback getCallback() {
+            return mCallback;
+        }
+
         public Network getNetwork() {
             return mKi.getNai().network;
         }
@@ -243,17 +229,6 @@
             return mKi.getNai().network().equals(network) && mKi.getSlot() == slot;
         }
 
-        private PendingIntent createTcpPollingAlarmIntent(@NonNull Context context,
-                @NonNull IBinder token) {
-            final Intent intent = new Intent(ACTION_TCP_POLLING_ALARM);
-            // Intent doesn't expose methods to put extra Binders, but Bundle does.
-            final Bundle b = new Bundle();
-            b.putBinder(EXTRA_BINDER_TOKEN, token);
-            intent.putExtra(EXTRA_BINDER_TOKEN, b);
-            return BinderUtils.withCleanCallingIdentity(() -> PendingIntent.getBroadcast(
-                    context, 0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE));
-        }
-
         @Override
         public void binderDied() {
             mConnectivityServiceHandler.post(() -> cleanupAutoOnOffKeepalive(this));
@@ -303,18 +278,15 @@
         mKeepaliveTracker = mDependencies.newKeepaliveTracker(
                 mContext, mConnectivityServiceHandler);
 
-        if (SdkLevel.isAtLeastU()) {
-            mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_TCP_POLLING_ALARM),
-                    null, handler);
-        }
-        mAlarmManager = mContext.getSystemService(AlarmManager.class);
+        mAlarmManager = mDependencies.getAlarmManager(context);
     }
 
-    private void startTcpPollingAlarm(@NonNull PendingIntent alarm) {
+    private void startTcpPollingAlarm(@NonNull final AlarmManager.OnAlarmListener listener) {
         final long triggerAtMillis =
                 SystemClock.elapsedRealtime() + getTcpPollingInterval();
         // Setup a non-wake up alarm.
-        mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, triggerAtMillis, alarm);
+        mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, triggerAtMillis, null /* tag */,
+                listener, mConnectivityServiceHandler);
     }
 
     /**
@@ -351,7 +323,7 @@
             handleMaybeResumeKeepalive(ki);
         }
         // TODO: listen to socket status instead of periodically check.
-        startTcpPollingAlarm(ki.mTcpPollingAlarm);
+        startTcpPollingAlarm(ki.mAlarmListener);
     }
 
     /**
@@ -431,7 +403,7 @@
         }
         mAutomaticOnOffKeepalives.add(autoKi);
         if (STATE_ALWAYS_ON != autoKi.mAutomaticOnOffState) {
-            startTcpPollingAlarm(autoKi.mTcpPollingAlarm);
+            startTcpPollingAlarm(autoKi.mAlarmListener);
         }
     }
 
@@ -463,7 +435,7 @@
     private void cleanupAutoOnOffKeepalive(@NonNull final AutomaticOnOffKeepalive autoKi) {
         ensureRunningOnHandlerThread();
         autoKi.close();
-        if (null != autoKi.mTcpPollingAlarm) mAlarmManager.cancel(autoKi.mTcpPollingAlarm);
+        if (null != autoKi.mAlarmListener) mAlarmManager.cancel(autoKi.mAlarmListener);
 
         // If the KI is not in the array, it's because it was already removed, or it was never
         // added ; the only ways this can happen is if the keepalive is stopped by the app and the
@@ -769,6 +741,13 @@
         }
 
         /**
+         * Get an instance of AlarmManager
+         */
+        public AlarmManager getAlarmManager(@NonNull final Context ctx) {
+            return ctx.getSystemService(AlarmManager.class);
+        }
+
+        /**
          * Receive the response message from kernel via given {@code FileDescriptor}.
          * The usage should follow the {@code #sendRequest} call with the same
          * FileDescriptor.
diff --git a/service/src/com/android/server/connectivity/KeepaliveTracker.java b/service/src/com/android/server/connectivity/KeepaliveTracker.java
index 5572361..06294db 100644
--- a/service/src/com/android/server/connectivity/KeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/KeepaliveTracker.java
@@ -58,6 +58,7 @@
 import android.util.Pair;
 
 import com.android.connectivity.resources.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.net.module.util.HexDump;
 import com.android.net.module.util.IpUtils;
@@ -125,7 +126,8 @@
      * All information about this keepalive is known at construction time except the slot number,
      * which is only returned when the hardware has successfully started the keepalive.
      */
-    class KeepaliveInfo implements IBinder.DeathRecipient {
+    @VisibleForTesting
+    public class KeepaliveInfo implements IBinder.DeathRecipient {
         // TODO : remove this member. Only AutoOnOffKeepalive should have a reference to this.
         public final ISocketKeepaliveCallback mCallback;
         // Bookkeeping data.
diff --git a/tests/cts/hostside/Android.bp b/tests/cts/hostside/Android.bp
index 47ea53e..ff9bd31 100644
--- a/tests/cts/hostside/Android.bp
+++ b/tests/cts/hostside/Android.bp
@@ -12,6 +12,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+next_app_data = [ ":CtsHostsideNetworkTestsAppNext" ]
+
+// The above line is put in place to prevent any future automerger merge conflict between aosp,
+// downstream branches. The CtsHostsideNetworkTestsAppNext target will not exist in
+// some downstream branches, but it should exist in aosp and some downstream branches.
+
+
+
+
+
 package {
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
@@ -37,7 +47,6 @@
     data: [
         ":CtsHostsideNetworkTestsApp",
         ":CtsHostsideNetworkTestsApp2",
-        ":CtsHostsideNetworkTestsAppNext",
-    ],
+    ] + next_app_data,
     per_testcase_directory: true,
 }
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index 23cb15c..f9fe5b0 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -114,34 +114,39 @@
     ],
 }
 
-android_test {
-    name: "CtsNetTestCasesMaxTargetSdk31",  // Must match CtsNetTestCasesMaxTargetSdk31 annotation.
+java_defaults {
+    name: "CtsNetTestCasesMaxTargetSdkDefaults",
     defaults: [
         "CtsNetTestCasesDefaults",
         "CtsNetTestCasesApiStableDefaults",
     ],
-    target_sdk_version: "31",
-    package_name: "android.net.cts.maxtargetsdk31",  // CTS package names must be unique.
-    instrumentation_target_package: "android.net.cts.maxtargetsdk31",
     test_suites: [
         "cts",
         "general-tests",
-        "mts-networking",
+        "mts-tethering",
     ],
 }
 
 android_test {
+    name: "CtsNetTestCasesMaxTargetSdk33",  // Must match CtsNetTestCasesMaxTargetSdk33 annotation.
+    defaults: ["CtsNetTestCasesMaxTargetSdkDefaults"],
+    target_sdk_version: "33",
+    package_name: "android.net.cts.maxtargetsdk33",
+    instrumentation_target_package: "android.net.cts.maxtargetsdk33",
+}
+
+android_test {
+    name: "CtsNetTestCasesMaxTargetSdk31",  // Must match CtsNetTestCasesMaxTargetSdk31 annotation.
+    defaults: ["CtsNetTestCasesMaxTargetSdkDefaults"],
+    target_sdk_version: "31",
+    package_name: "android.net.cts.maxtargetsdk31",  // CTS package names must be unique.
+    instrumentation_target_package: "android.net.cts.maxtargetsdk31",
+}
+
+android_test {
     name: "CtsNetTestCasesMaxTargetSdk30",  // Must match CtsNetTestCasesMaxTargetSdk30 annotation.
-    defaults: [
-        "CtsNetTestCasesDefaults",
-        "CtsNetTestCasesApiStableDefaults",
-    ],
+    defaults: ["CtsNetTestCasesMaxTargetSdkDefaults"],
     target_sdk_version: "30",
     package_name: "android.net.cts.maxtargetsdk30",  // CTS package names must be unique.
     instrumentation_target_package: "android.net.cts.maxtargetsdk30",
-    test_suites: [
-        "cts",
-        "general-tests",
-        "mts-networking",
-    ],
 }
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index d2a3f91..9416c66 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -3372,7 +3372,7 @@
     }
 
     private void checkFirewallBlocking(final DatagramSocket srcSock, final DatagramSocket dstSock,
-            final boolean expectBlock) throws Exception {
+            final boolean expectBlock, final int chain) throws Exception {
         final Random random = new Random();
         final byte[] sendData = new byte[100];
         random.nextBytes(sendData);
@@ -3385,11 +3385,17 @@
             if (expectBlock) {
                 return;
             }
-            fail("Expect not to be blocked by firewall but sending packet was blocked");
+            fail("Expect not to be blocked by firewall but sending packet was blocked:"
+                    + " chain=" + chain
+                    + " chainEnabled=" + mCm.getFirewallChainEnabled(chain)
+                    + " uidFirewallRule=" + mCm.getUidFirewallRule(chain, Process.myUid()));
         }
 
         if (expectBlock) {
-            fail("Expect to be blocked by firewall but sending packet was not blocked");
+            fail("Expect to be blocked by firewall but sending packet was not blocked:"
+                    + " chain=" + chain
+                    + " chainEnabled=" + mCm.getFirewallChainEnabled(chain)
+                    + " uidFirewallRule=" + mCm.getUidFirewallRule(chain, Process.myUid()));
         }
 
         dstSock.receive(pkt);
@@ -3422,25 +3428,27 @@
                 dstSock.setSoTimeout(SOCKET_TIMEOUT_MS);
 
                 // Chain disabled, UID not on chain.
-                checkFirewallBlocking(srcSock, dstSock, EXPECT_PASS);
+                checkFirewallBlocking(srcSock, dstSock, EXPECT_PASS, chain);
 
                 // Chain enabled, UID not on chain.
                 mCm.setFirewallChainEnabled(chain, true /* enable */);
                 assertTrue(mCm.getFirewallChainEnabled(chain));
-                checkFirewallBlocking(srcSock, dstSock, isAllowList ? EXPECT_BLOCK : EXPECT_PASS);
+                checkFirewallBlocking(
+                        srcSock, dstSock, isAllowList ? EXPECT_BLOCK : EXPECT_PASS, chain);
 
                 // Chain enabled, UID on chain.
                 mCm.setUidFirewallRule(chain, myUid, ruleToAddMatch);
-                checkFirewallBlocking(srcSock, dstSock, isAllowList ?  EXPECT_PASS : EXPECT_BLOCK);
+                checkFirewallBlocking(
+                        srcSock, dstSock, isAllowList ?  EXPECT_PASS : EXPECT_BLOCK, chain);
 
                 // Chain disabled, UID on chain.
                 mCm.setFirewallChainEnabled(chain, false /* enable */);
                 assertFalse(mCm.getFirewallChainEnabled(chain));
-                checkFirewallBlocking(srcSock, dstSock, EXPECT_PASS);
+                checkFirewallBlocking(srcSock, dstSock, EXPECT_PASS, chain);
 
                 // Chain disabled, UID not on chain.
                 mCm.setUidFirewallRule(chain, myUid, ruleToRemoveMatch);
-                checkFirewallBlocking(srcSock, dstSock, EXPECT_PASS);
+                checkFirewallBlocking(srcSock, dstSock, EXPECT_PASS, chain);
             }, /* cleanup */ () -> {
                     srcSock.close();
                     dstSock.close();
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index 093c7f8..77afe4f 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -20,6 +20,8 @@
 import android.net.ConnectivityManager
 import android.net.ConnectivityManager.NetworkCallback
 import android.net.LinkProperties
+import android.net.LocalSocket
+import android.net.LocalSocketAddress
 import android.net.Network
 import android.net.NetworkAgentConfig
 import android.net.NetworkCapabilities
@@ -63,6 +65,8 @@
 import android.util.Log
 import androidx.test.platform.app.InstrumentationRegistry
 import androidx.test.runner.AndroidJUnit4
+import com.android.compatibility.common.util.PollingCheck
+import com.android.compatibility.common.util.PropertyUtil
 import com.android.net.module.util.ArrayTrackRecord
 import com.android.net.module.util.TrackRecord
 import com.android.networkstack.apishim.NsdShimImpl
@@ -72,14 +76,17 @@
 import com.android.testutils.TestableNetworkAgent
 import com.android.testutils.TestableNetworkCallback
 import com.android.testutils.filters.CtsNetTestCasesMaxTargetSdk30
+import com.android.testutils.filters.CtsNetTestCasesMaxTargetSdk33
 import com.android.testutils.runAsShell
 import com.android.testutils.tryTest
 import com.android.testutils.waitForIdle
 import java.io.File
+import java.io.IOException
 import java.net.ServerSocket
 import java.nio.charset.StandardCharsets
 import java.util.Random
 import java.util.concurrent.Executor
+import kotlin.math.min
 import kotlin.test.assertEquals
 import kotlin.test.assertFailsWith
 import kotlin.test.assertNotNull
@@ -763,6 +770,69 @@
         }
     }
 
+    private fun checkConnectSocketToMdnsd(shouldFail: Boolean) {
+        val discoveryRecord = NsdDiscoveryRecord()
+        val localSocket = LocalSocket()
+        tryTest {
+            // Discover any service from NsdManager to enforce NsdService to start the mdnsd.
+            nsdManager.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, discoveryRecord)
+            discoveryRecord.expectCallback<DiscoveryStarted>()
+
+            // Checks the /dev/socket/mdnsd is created.
+            val socket = File("/dev/socket/mdnsd")
+            val doesSocketExist = PollingCheck.waitFor(
+                TIMEOUT_MS,
+                {
+                    socket.exists()
+                },
+                { isSocketExist ->
+                    isSocketExist
+                },
+            )
+
+            // If the socket is not created, then no need to check the access.
+            if (doesSocketExist) {
+                // Create a LocalSocket and try to connect to mdnsd.
+                assertFalse("LocalSocket is connected.", localSocket.isConnected)
+                val address = LocalSocketAddress("mdnsd", LocalSocketAddress.Namespace.RESERVED)
+                if (shouldFail) {
+                    assertFailsWith<IOException>("Expect fail but socket connected") {
+                        localSocket.connect(address)
+                    }
+                } else {
+                    localSocket.connect(address)
+                    assertTrue("LocalSocket is not connected.", localSocket.isConnected)
+                }
+            }
+        } cleanup {
+            localSocket.close()
+            nsdManager.stopServiceDiscovery(discoveryRecord)
+            discoveryRecord.expectCallback<DiscoveryStopped>()
+        }
+    }
+
+    /**
+     * Starting from Android U, the access to the /dev/socket/mdnsd is blocked by the
+     * sepolicy(b/265364111).
+     */
+    @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+    @Test
+    fun testCannotConnectSocketToMdnsd() {
+        val targetSdkVersion = context.packageManager
+                .getTargetSdkVersion(context.applicationInfo.packageName)
+        assumeTrue(targetSdkVersion >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+        val firstApiLevel = min(PropertyUtil.getFirstApiLevel(), PropertyUtil.getVendorApiLevel())
+        // The sepolicy is implemented in the vendor image, so the access may not be blocked if
+        // the vendor image is not update to date.
+        assumeTrue(firstApiLevel >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+        checkConnectSocketToMdnsd(shouldFail = true)
+    }
+
+    @Test @CtsNetTestCasesMaxTargetSdk33("mdnsd socket is accessible up to target SDK 33")
+    fun testCanConnectSocketToMdnsd() {
+        checkConnectSocketToMdnsd(shouldFail = false)
+    }
+
     @Test @CtsNetTestCasesMaxTargetSdk30("Socket is started with the service up to target SDK 30")
     fun testManagerCreatesLegacySocket() {
         nsdManager // Ensure the lazy-init member is initialized, so NsdManager is created
diff --git a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
index ddf1d4d..4f0b9c4 100644
--- a/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/AutomaticOnOffKeepaliveTrackerTest.java
@@ -16,32 +16,70 @@
 
 package com.android.server.connectivity;
 
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 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.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
+import android.app.AlarmManager;
 import android.content.Context;
+import android.content.res.Resources;
+import android.net.ConnectivityResources;
 import android.net.INetd;
+import android.net.ISocketKeepaliveCallback;
+import android.net.KeepalivePacketData;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
 import android.net.MarkMaskParcel;
+import android.net.NattKeepalivePacketData;
+import android.net.Network;
+import android.net.NetworkAgent;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.os.Binder;
 import android.os.Build;
+import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.server.connectivity.KeepaliveTracker.KeepaliveInfo;
 import com.android.testutils.DevSdkIgnoreRule;
 import com.android.testutils.DevSdkIgnoreRunner;
+import com.android.testutils.HandlerUtils;
 
 import libcore.util.HexEncoding;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.io.FileDescriptor;
+import java.net.InetAddress;
+import java.net.Socket;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 
@@ -49,17 +87,22 @@
 @SmallTest
 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
 public class AutomaticOnOffKeepaliveTrackerTest {
+    private static final String TAG = AutomaticOnOffKeepaliveTrackerTest.class.getSimpleName();
     private static final int TEST_NETID = 0xA85;
     private static final int TEST_NETID_FWMARK = 0x0A85;
     private static final int OTHER_NETID = 0x1A85;
     private static final int NETID_MASK = 0xffff;
+    private static final int TIMEOUT_MS = 30_000;
+    private static final int MOCK_RESOURCE_ID = 5;
     private AutomaticOnOffKeepaliveTracker mAOOKeepaliveTracker;
     private HandlerThread mHandlerThread;
 
     @Mock INetd mNetd;
     @Mock AutomaticOnOffKeepaliveTracker.Dependencies mDependencies;
     @Mock Context mCtx;
-    @Mock KeepaliveTracker mKeepaliveTracker;
+    @Mock AlarmManager mAlarmManager;
+    TestKeepaliveTracker mKeepaliveTracker;
+    AOOTestHandler mTestHandler;
 
     // Hexadecimal representation of a SOCK_DIAG response with tcp info.
     private static final String SOCK_DIAG_TCP_INET_HEX =
@@ -158,11 +201,46 @@
     private static final byte[] TEST_RESPONSE_BYTES =
             HexEncoding.decode(TEST_RESPONSE_HEX.toCharArray(), false);
 
+    private class TestKeepaliveTracker extends KeepaliveTracker {
+        private KeepaliveInfo mKi;
+
+        TestKeepaliveTracker(@NonNull final Context context, @NonNull final Handler handler) {
+            super(context, handler);
+        }
+
+        public void setReturnedKeepaliveInfo(@NonNull final KeepaliveInfo ki) {
+            mKi = ki;
+        }
+
+        @NonNull
+        @Override
+        public KeepaliveInfo makeNattKeepaliveInfo(@Nullable final NetworkAgentInfo nai,
+                @Nullable final FileDescriptor fd, final int intervalSeconds,
+                @NonNull final ISocketKeepaliveCallback cb, @NonNull final String srcAddrString,
+                final int srcPort,
+                @NonNull final String dstAddrString, final int dstPort) {
+            if (null == mKi) {
+                throw new IllegalStateException("Must call setReturnedKeepaliveInfo");
+            }
+            return mKi;
+        }
+    }
+
     @Before
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
 
+        doReturn(PERMISSION_GRANTED).when(mCtx).checkPermission(any() /* permission */,
+                anyInt() /* pid */, anyInt() /* uid */);
+        ConnectivityResources.setResourcesContextForTest(mCtx);
+        final Resources mockResources = mock(Resources.class);
+        doReturn(MOCK_RESOURCE_ID).when(mockResources).getIdentifier(any() /* name */,
+                any() /* defType */, any() /* defPackage */);
+        doReturn(new String[] { "0,3", "3,3" }).when(mockResources)
+                .getStringArray(MOCK_RESOURCE_ID);
+        doReturn(mockResources).when(mCtx).getResources();
         doReturn(mNetd).when(mDependencies).getNetd();
+        doReturn(mAlarmManager).when(mDependencies).getAlarmManager(any());
         doReturn(makeMarkMaskParcel(NETID_MASK, TEST_NETID_FWMARK)).when(mNetd)
                 .getFwmarkForNetwork(TEST_NETID);
 
@@ -170,11 +248,34 @@
 
         mHandlerThread = new HandlerThread("KeepaliveTrackerTest");
         mHandlerThread.start();
-        doReturn(mKeepaliveTracker).when(mDependencies).newKeepaliveTracker(
-                mCtx, mHandlerThread.getThreadHandler());
+        mTestHandler = new AOOTestHandler(mHandlerThread.getLooper());
+        mKeepaliveTracker = new TestKeepaliveTracker(mCtx, mTestHandler);
+        doReturn(mKeepaliveTracker).when(mDependencies).newKeepaliveTracker(mCtx, mTestHandler);
         doReturn(true).when(mDependencies).isFeatureEnabled(any(), anyBoolean());
-        mAOOKeepaliveTracker = new AutomaticOnOffKeepaliveTracker(
-                mCtx, mHandlerThread.getThreadHandler(), mDependencies);
+        mAOOKeepaliveTracker =
+                new AutomaticOnOffKeepaliveTracker(mCtx, mTestHandler, mDependencies);
+    }
+
+    private final class AOOTestHandler extends Handler {
+        public AutomaticOnOffKeepaliveTracker.AutomaticOnOffKeepalive mLastAutoKi = null;
+
+        AOOTestHandler(@NonNull final Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(@NonNull final Message msg) {
+            switch (msg.what) {
+                case NetworkAgent.CMD_START_SOCKET_KEEPALIVE:
+                    Log.d(TAG, "Test handler received CMD_START_SOCKET_KEEPALIVE : " + msg);
+                    mAOOKeepaliveTracker.handleStartKeepalive(msg);
+                    break;
+                case NetworkAgent.CMD_MONITOR_AUTOMATIC_KEEPALIVE:
+                    Log.d(TAG, "Test handler received CMD_MONITOR_AUTOMATIC_KEEPALIVE : " + msg);
+                    mLastAutoKi = mAOOKeepaliveTracker.getKeepaliveForBinder((IBinder) msg.obj);
+                    break;
+            }
+        }
     }
 
     @Test
@@ -187,24 +288,79 @@
     @Test
     public void testIsAnyTcpSocketConnected_withTargetNetId() throws Exception {
         setupResponseWithSocketExisting();
-        mHandlerThread.getThreadHandler().post(
+        mTestHandler.post(
                 () -> assertTrue(mAOOKeepaliveTracker.isAnyTcpSocketConnected(TEST_NETID)));
     }
 
     @Test
     public void testIsAnyTcpSocketConnected_withIncorrectNetId() throws Exception {
         setupResponseWithSocketExisting();
-        mHandlerThread.getThreadHandler().post(
+        mTestHandler.post(
                 () -> assertFalse(mAOOKeepaliveTracker.isAnyTcpSocketConnected(OTHER_NETID)));
     }
 
     @Test
     public void testIsAnyTcpSocketConnected_noSocketExists() throws Exception {
         setupResponseWithoutSocketExisting();
-        mHandlerThread.getThreadHandler().post(
+        mTestHandler.post(
                 () -> assertFalse(mAOOKeepaliveTracker.isAnyTcpSocketConnected(TEST_NETID)));
     }
 
+    @Test
+    public void testAlarm() throws Exception {
+        final InetAddress srcAddress = InetAddress.getByAddress(
+                new byte[] { (byte) 192, 0, 0, (byte) 129 });
+        final int srcPort = 12345;
+        final InetAddress dstAddress = InetAddress.getByAddress(new byte[] { 8, 8, 8, 8});
+        final int dstPort = 12345;
+
+        final NetworkAgentInfo nai = mock(NetworkAgentInfo.class);
+        nai.networkCapabilities = new NetworkCapabilities.Builder()
+                .addTransportType(TRANSPORT_CELLULAR).build();
+        nai.networkInfo = new NetworkInfo(TYPE_MOBILE, 0 /* subtype */, "LTE", "LTE");
+        nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "test reason",
+                "test extra info");
+        nai.linkProperties = new LinkProperties();
+        nai.linkProperties.addLinkAddress(new LinkAddress(srcAddress, 24));
+
+        final Socket socket = new Socket();
+        socket.bind(null);
+        final FileDescriptor fd = socket.getFileDescriptor$();
+        final IBinder binder = new Binder();
+        final ISocketKeepaliveCallback cb = mock(ISocketKeepaliveCallback.class);
+        doReturn(binder).when(cb).asBinder();
+        final Network underpinnedNetwork = mock(Network.class);
+
+        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);
+        mKeepaliveTracker.setReturnedKeepaliveInfo(ki);
+
+        mAOOKeepaliveTracker.startNattKeepalive(nai, fd, 10 /* intervalSeconds */, cb,
+                srcAddress.toString(), srcPort, dstAddress.toString(), dstPort,
+                true /* automaticOnOffKeepalives */, underpinnedNetwork);
+        HandlerUtils.waitForIdle(mTestHandler, TIMEOUT_MS);
+
+        final ArgumentCaptor<AlarmManager.OnAlarmListener> listenerCaptor =
+                ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
+        verify(mAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME), anyLong(),
+                any(), listenerCaptor.capture(), eq(mTestHandler));
+        final AlarmManager.OnAlarmListener listener = listenerCaptor.getValue();
+
+        // For realism, the listener should be posted on the handler
+        mTestHandler.post(() -> listener.onAlarm());
+        // Wait for the listener to be called. The listener enqueues a message to the handler.
+        HandlerUtils.waitForIdle(mTestHandler, TIMEOUT_MS);
+        // Wait for the message posted by the listener to be processed.
+        HandlerUtils.waitForIdle(mTestHandler, TIMEOUT_MS);
+
+        assertNotNull(mTestHandler.mLastAutoKi);
+        assertEquals(cb, mTestHandler.mLastAutoKi.getCallback());
+        assertEquals(underpinnedNetwork, mTestHandler.mLastAutoKi.getUnderpinnedNetwork());
+        socket.close();
+    }
+
     private void setupResponseWithSocketExisting() throws Exception {
         final ByteBuffer tcpBufferV6 = getByteBuffer(TEST_RESPONSE_BYTES);
         final ByteBuffer tcpBufferV4 = getByteBuffer(TEST_RESPONSE_BYTES);