Merge "Support getting transform state in IpSecService" into main
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index c065cd6..a8c8408 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -33,7 +33,6 @@
 
 import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
 import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH;
-import static com.android.networkstack.tethering.TetheringConfiguration.USE_SYNC_SM;
 import static com.android.networkstack.tethering.UpstreamNetworkState.isVcnInterface;
 import static com.android.networkstack.tethering.util.PrefixUtils.asIpPrefix;
 import static com.android.networkstack.tethering.util.TetheringMessageBase.BASE_IPSERVER;
@@ -316,6 +315,7 @@
 
     private final TetheringMetrics mTetheringMetrics;
     private final Handler mHandler;
+    private final boolean mIsSyncSM;
 
     // TODO: Add a dependency object to pass the data members or variables from the tethering
     // object. It helps to reduce the arguments of the constructor.
@@ -325,7 +325,7 @@
             @Nullable LateSdk<RoutingCoordinatorManager> routingCoordinator, Callback callback,
             TetheringConfiguration config, PrivateAddressCoordinator addressCoordinator,
             TetheringMetrics tetheringMetrics, Dependencies deps) {
-        super(ifaceName, USE_SYNC_SM ? null : handler.getLooper());
+        super(ifaceName, config.isSyncSM() ? null : handler.getLooper());
         mHandler = handler;
         mLog = log.forSubComponent(ifaceName);
         mNetd = netd;
@@ -338,6 +338,7 @@
         mLinkProperties = new LinkProperties();
         mUsingLegacyDhcp = config.useLegacyDhcpServer();
         mP2pLeasesSubnetPrefixLength = config.getP2pLeasesSubnetPrefixLength();
+        mIsSyncSM = config.isSyncSM();
         mPrivateAddressCoordinator = addressCoordinator;
         mDeps = deps;
         mTetheringMetrics = tetheringMetrics;
@@ -515,7 +516,7 @@
 
         private void handleError() {
             mLastError = TETHER_ERROR_DHCPSERVER_ERROR;
-            if (USE_SYNC_SM) {
+            if (mIsSyncSM) {
                 sendMessage(CMD_SERVICE_FAILED_TO_START, TETHER_ERROR_DHCPSERVER_ERROR);
             } else {
                 sendMessageAtFrontOfQueueToAsyncSM(CMD_SERVICE_FAILED_TO_START,
@@ -1170,7 +1171,7 @@
                 // in previous versions of the mainline module.
                 // TODO : remove sendMessageAtFrontOfQueueToAsyncSM after migrating to the Sync
                 // StateMachine.
-                if (USE_SYNC_SM) {
+                if (mIsSyncSM) {
                     sendSelfMessageToSyncSM(CMD_SERVICE_FAILED_TO_START, mLastError);
                 } else {
                     sendMessageAtFrontOfQueueToAsyncSM(CMD_SERVICE_FAILED_TO_START, mLastError);
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index 0678525..d09183a 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -132,15 +132,15 @@
 
     public static final String TETHER_FORCE_RANDOM_PREFIX_BASE_SELECTION =
             "tether_force_random_prefix_base_selection";
+
+    public static final String TETHER_ENABLE_SYNC_SM = "tether_enable_sync_sm";
+
     /**
      * Default value that used to periodic polls tether offload stats from tethering offload HAL
      * to make the data warnings work.
      */
     public static final int DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS = 5000;
 
-    /** A flag for using synchronous or asynchronous state machine. */
-    public static final boolean USE_SYNC_SM = false;
-
     public final String[] tetherableUsbRegexs;
     public final String[] tetherableWifiRegexs;
     public final String[] tetherableWigigRegexs;
@@ -174,6 +174,7 @@
 
     private final boolean mEnableWearTethering;
     private final boolean mRandomPrefixBase;
+    private final boolean mEnableSyncSm;
 
     private final int mUsbTetheringFunction;
     protected final ContentResolver mContentResolver;
@@ -292,6 +293,7 @@
         mEnableWearTethering = shouldEnableWearTethering(ctx);
 
         mRandomPrefixBase = mDeps.isFeatureEnabled(ctx, TETHER_FORCE_RANDOM_PREFIX_BASE_SELECTION);
+        mEnableSyncSm = mDeps.isFeatureEnabled(ctx, TETHER_ENABLE_SYNC_SM);
 
         configLog.log(toString());
     }
@@ -385,6 +387,10 @@
         return mRandomPrefixBase;
     }
 
+    public boolean isSyncSM() {
+        return mEnableSyncSm;
+    }
+
     /** Does the dumping.*/
     public void dump(PrintWriter pw) {
         pw.print("activeDataSubId: ");
@@ -438,6 +444,9 @@
 
         pw.print("mRandomPrefixBase: ");
         pw.println(mRandomPrefixBase);
+
+        pw.print("mEnableSyncSm: ");
+        pw.println(mEnableSyncSm);
     }
 
     /** Returns the string representation of this object.*/
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
index aa322dc..19c6e5a 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -754,4 +754,26 @@
                 new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps);
         assertEquals(p2pLeasesSubnetPrefixLength, p2pCfg.getP2pLeasesSubnetPrefixLength());
     }
+
+    private void setTetherEnableSyncSMFlagEnabled(Boolean enabled) {
+        mDeps.setFeatureEnabled(TetheringConfiguration.TETHER_ENABLE_SYNC_SM, enabled);
+    }
+
+    private void assertEnableSyncSMIs(boolean value) {
+        assertEquals(value, new TetheringConfiguration(
+                mMockContext, mLog, INVALID_SUBSCRIPTION_ID, mDeps).isSyncSM());
+    }
+
+    @Test
+    public void testEnableSyncSMFlag() throws Exception {
+        // Test default disabled
+        setTetherEnableSyncSMFlagEnabled(null);
+        assertEnableSyncSMIs(false);
+
+        setTetherEnableSyncSMFlagEnabled(true);
+        assertEnableSyncSMIs(true);
+
+        setTetherEnableSyncSMFlagEnabled(false);
+        assertEnableSyncSMIs(false);
+    }
 }
diff --git a/staticlibs/device/com/android/net/module/util/netlink/NetlinkMessage.java b/staticlibs/device/com/android/net/module/util/netlink/NetlinkMessage.java
index 111e0ba..781a04e 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/NetlinkMessage.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/NetlinkMessage.java
@@ -142,6 +142,7 @@
                 return (NetlinkMessage) RtNetlinkAddressMessage.parse(nlmsghdr, byteBuffer);
             case NetlinkConstants.RTM_NEWROUTE:
             case NetlinkConstants.RTM_DELROUTE:
+            case NetlinkConstants.RTM_GETROUTE:
                 return (NetlinkMessage) RtNetlinkRouteMessage.parse(nlmsghdr, byteBuffer);
             case NetlinkConstants.RTM_NEWNEIGH:
             case NetlinkConstants.RTM_DELNEIGH:
diff --git a/staticlibs/device/com/android/net/module/util/netlink/StructNlMsgHdr.java b/staticlibs/device/com/android/net/module/util/netlink/StructNlMsgHdr.java
index ff37639..5272366 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/StructNlMsgHdr.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/StructNlMsgHdr.java
@@ -71,13 +71,17 @@
             }
             sb.append("NLM_F_ECHO");
         }
-        if ((flags & NLM_F_ROOT) != 0) {
+        if ((flags & NLM_F_DUMP) == NLM_F_DUMP) {
+            if (sb.length() > 0) {
+                sb.append("|");
+            }
+            sb.append("NLM_F_DUMP");
+        } else if ((flags & NLM_F_ROOT) != 0) { // NLM_F_DUMP = NLM_F_ROOT | NLM_F_MATCH
             if (sb.length() > 0) {
                 sb.append("|");
             }
             sb.append("NLM_F_ROOT");
-        }
-        if ((flags & NLM_F_MATCH) != 0) {
+        } else if ((flags & NLM_F_MATCH) != 0) {
             if (sb.length() > 0) {
                 sb.append("|");
             }
diff --git a/staticlibs/tests/unit/Android.bp b/staticlibs/tests/unit/Android.bp
index 031e52f..0dfca57 100644
--- a/staticlibs/tests/unit/Android.bp
+++ b/staticlibs/tests/unit/Android.bp
@@ -33,7 +33,10 @@
         "//packages/modules/Connectivity/Tethering/tests:__subpackages__",
         "//packages/modules/NetworkStack/tests/integration",
     ],
-    lint: { strict_updatability_linting: true },
+    lint: {
+        strict_updatability_linting: true,
+        test: true
+    },
 }
 
 android_test {
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/StructNlMsgHdrTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/StructNlMsgHdrTest.java
index b7f68c6..a0d8b8c 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/StructNlMsgHdrTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/StructNlMsgHdrTest.java
@@ -16,6 +16,7 @@
 
 package com.android.net.module.util.netlink;
 
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP;
 import static org.junit.Assert.fail;
 
 import android.system.OsConstants;
@@ -48,10 +49,14 @@
     public static final String TEST_NLMSG_PID_STR = "nlmsg_pid{5678}";
 
     private StructNlMsgHdr makeStructNlMsgHdr(short type) {
+        return makeStructNlMsgHdr(type, TEST_NLMSG_FLAGS);
+    }
+
+    private StructNlMsgHdr makeStructNlMsgHdr(short type, short flags) {
         final StructNlMsgHdr struct = new StructNlMsgHdr();
         struct.nlmsg_len = TEST_NLMSG_LEN;
         struct.nlmsg_type = type;
-        struct.nlmsg_flags = TEST_NLMSG_FLAGS;
+        struct.nlmsg_flags = flags;
         struct.nlmsg_seq = TEST_NLMSG_SEQ;
         struct.nlmsg_pid = TEST_NLMSG_PID;
         return struct;
@@ -62,6 +67,11 @@
         fail("\"" + actualValue + "\" does not contain \"" + expectedSubstring + "\"");
     }
 
+    private static void assertNotContains(String actualValue, String unexpectedSubstring) {
+        if (!actualValue.contains(unexpectedSubstring)) return;
+        fail("\"" + actualValue + "\" contains \"" + unexpectedSubstring + "\"");
+    }
+
     @Test
     public void testToString() {
         StructNlMsgHdr struct = makeStructNlMsgHdr(NetlinkConstants.RTM_NEWADDR);
@@ -99,4 +109,31 @@
         assertContains(s, TEST_NLMSG_PID_STR);
         assertContains(s, "nlmsg_type{20(SOCK_DIAG_BY_FAMILY)}");
     }
+
+    @Test
+    public void testToString_flags_dumpRequest() {
+        final short flags = StructNlMsgHdr.NLM_F_REQUEST | StructNlMsgHdr.NLM_F_DUMP;
+        StructNlMsgHdr struct = makeStructNlMsgHdr(NetlinkConstants.RTM_GETROUTE, flags);
+
+        String s = struct.toString(OsConstants.NETLINK_ROUTE);
+
+        assertContains(s, "RTM_GETROUTE");
+        assertContains(s, "NLM_F_REQUEST");
+        assertContains(s, "NLM_F_DUMP");
+        // NLM_F_DUMP = NLM_F_ROOT | NLM_F_MATCH;
+        assertNotContains(s, "NLM_F_MATCH");
+        assertNotContains(s, "NLM_F_ROOT");
+    }
+
+    @Test
+    public void testToString_flags_root() {
+        final short flags = StructNlMsgHdr.NLM_F_ROOT;
+        StructNlMsgHdr struct = makeStructNlMsgHdr(NetlinkConstants.RTM_GETROUTE, flags);
+
+        String s = struct.toString(OsConstants.NETLINK_ROUTE);
+
+        assertContains(s, "NLM_F_ROOT");
+        // NLM_F_DUMP = NLM_F_ROOT | NLM_F_MATCH;
+        assertNotContains(s, "NLM_F_DUMP");
+    }
 }
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 46e9e45..c9cece0 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -2979,7 +2979,7 @@
                         null /* iface */, RTN_UNREACHABLE));
         assertEquals(expectedRoutes, lp.getRoutes());
 
-        verify(mMockNetworkAgent).unregister();
+        verify(mMockNetworkAgent, timeout(TEST_TIMEOUT_MS)).unregister();
     }
 
     @Test