Merge "Add aconfig flags for Connectivity FlaggedAPI annotations" into main
diff --git a/OWNERS_core_networking b/OWNERS_core_networking
index 6d17476..83f798a 100644
--- a/OWNERS_core_networking
+++ b/OWNERS_core_networking
@@ -1,16 +1,13 @@
 chiachangwang@google.com
 cken@google.com
-huangaaron@google.com
 jchalard@google.com
 junyulai@google.com
 lifr@google.com
 lorenzo@google.com
-lucaslin@google.com
 markchien@google.com
 martinwu@google.com
 maze@google.com
 motomuman@google.com
-nuccachen@google.com
 paulhu@google.com
 prohr@google.com
 reminv@google.com
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index 5ec4d15..544ba01 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -33,6 +33,7 @@
 
 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,7 +317,6 @@
 
     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.
@@ -326,7 +326,7 @@
             @Nullable LateSdk<RoutingCoordinatorManager> routingCoordinator, Callback callback,
             TetheringConfiguration config, PrivateAddressCoordinator addressCoordinator,
             TetheringMetrics tetheringMetrics, Dependencies deps) {
-        super(ifaceName, config.isSyncSM() ? null : handler.getLooper());
+        super(ifaceName, USE_SYNC_SM ? null : handler.getLooper());
         mHandler = handler;
         mLog = log.forSubComponent(ifaceName);
         mNetd = netd;
@@ -339,7 +339,6 @@
         mLinkProperties = new LinkProperties();
         mUsingLegacyDhcp = config.useLegacyDhcpServer();
         mP2pLeasesSubnetPrefixLength = config.getP2pLeasesSubnetPrefixLength();
-        mIsSyncSM = config.isSyncSM();
         mPrivateAddressCoordinator = addressCoordinator;
         mDeps = deps;
         mTetheringMetrics = tetheringMetrics;
@@ -517,7 +516,7 @@
 
         private void handleError() {
             mLastError = TETHER_ERROR_DHCPSERVER_ERROR;
-            if (mIsSyncSM) {
+            if (USE_SYNC_SM) {
                 sendMessage(CMD_SERVICE_FAILED_TO_START, TETHER_ERROR_DHCPSERVER_ERROR);
             } else {
                 sendMessageAtFrontOfQueueToAsyncSM(CMD_SERVICE_FAILED_TO_START,
@@ -1172,7 +1171,7 @@
                 // in previous versions of the mainline module.
                 // TODO : remove sendMessageAtFrontOfQueueToAsyncSM after migrating to the Sync
                 // StateMachine.
-                if (mIsSyncSM) {
+                if (USE_SYNC_SM) {
                     sendSelfMessageToSyncSM(CMD_SERVICE_FAILED_TO_START, mLastError);
                 } else {
                     sendMessageAtFrontOfQueueToAsyncSM(CMD_SERVICE_FAILED_TO_START, mLastError);
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 9f542f4..81e18ab 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -379,7 +379,7 @@
             if (!isAtLeastS()) return null;
             try {
                 return new BpfMap<>(TETHER_DOWNSTREAM4_MAP_PATH,
-                    BpfMap.BPF_F_RDWR, Tether4Key.class, Tether4Value.class);
+                    Tether4Key.class, Tether4Value.class);
             } catch (ErrnoException e) {
                 Log.e(TAG, "Cannot create downstream4 map: " + e);
                 return null;
@@ -391,7 +391,7 @@
             if (!isAtLeastS()) return null;
             try {
                 return new BpfMap<>(TETHER_UPSTREAM4_MAP_PATH,
-                    BpfMap.BPF_F_RDWR, Tether4Key.class, Tether4Value.class);
+                    Tether4Key.class, Tether4Value.class);
             } catch (ErrnoException e) {
                 Log.e(TAG, "Cannot create upstream4 map: " + e);
                 return null;
@@ -403,7 +403,7 @@
             if (!isAtLeastS()) return null;
             try {
                 return new BpfMap<>(TETHER_DOWNSTREAM6_FS_PATH,
-                    BpfMap.BPF_F_RDWR, TetherDownstream6Key.class, Tether6Value.class);
+                    TetherDownstream6Key.class, Tether6Value.class);
             } catch (ErrnoException e) {
                 Log.e(TAG, "Cannot create downstream6 map: " + e);
                 return null;
@@ -414,7 +414,7 @@
         @Nullable public IBpfMap<TetherUpstream6Key, Tether6Value> getBpfUpstream6Map() {
             if (!isAtLeastS()) return null;
             try {
-                return new BpfMap<>(TETHER_UPSTREAM6_FS_PATH, BpfMap.BPF_F_RDWR,
+                return new BpfMap<>(TETHER_UPSTREAM6_FS_PATH,
                         TetherUpstream6Key.class, Tether6Value.class);
             } catch (ErrnoException e) {
                 Log.e(TAG, "Cannot create upstream6 map: " + e);
@@ -427,7 +427,7 @@
             if (!isAtLeastS()) return null;
             try {
                 return new BpfMap<>(TETHER_STATS_MAP_PATH,
-                    BpfMap.BPF_F_RDWR, TetherStatsKey.class, TetherStatsValue.class);
+                    TetherStatsKey.class, TetherStatsValue.class);
             } catch (ErrnoException e) {
                 Log.e(TAG, "Cannot create stats map: " + e);
                 return null;
@@ -439,7 +439,7 @@
             if (!isAtLeastS()) return null;
             try {
                 return new BpfMap<>(TETHER_LIMIT_MAP_PATH,
-                    BpfMap.BPF_F_RDWR, TetherLimitKey.class, TetherLimitValue.class);
+                    TetherLimitKey.class, TetherLimitValue.class);
             } catch (ErrnoException e) {
                 Log.e(TAG, "Cannot create limit map: " + e);
                 return null;
@@ -451,7 +451,7 @@
             if (!isAtLeastS()) return null;
             try {
                 return new BpfMap<>(TETHER_DEV_MAP_PATH,
-                    BpfMap.BPF_F_RDWR, TetherDevKey.class, TetherDevValue.class);
+                    TetherDevKey.class, TetherDevValue.class);
             } catch (ErrnoException e) {
                 Log.e(TAG, "Cannot create dev map: " + e);
                 return null;
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index d09183a..0678525 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,7 +174,6 @@
 
     private final boolean mEnableWearTethering;
     private final boolean mRandomPrefixBase;
-    private final boolean mEnableSyncSm;
 
     private final int mUsbTetheringFunction;
     protected final ContentResolver mContentResolver;
@@ -293,7 +292,6 @@
         mEnableWearTethering = shouldEnableWearTethering(ctx);
 
         mRandomPrefixBase = mDeps.isFeatureEnabled(ctx, TETHER_FORCE_RANDOM_PREFIX_BASE_SELECTION);
-        mEnableSyncSm = mDeps.isFeatureEnabled(ctx, TETHER_ENABLE_SYNC_SM);
 
         configLog.log(toString());
     }
@@ -387,10 +385,6 @@
         return mRandomPrefixBase;
     }
 
-    public boolean isSyncSM() {
-        return mEnableSyncSm;
-    }
-
     /** Does the dumping.*/
     public void dump(PrintWriter pw) {
         pw.print("activeDataSubId: ");
@@ -444,9 +438,6 @@
 
         pw.print("mRandomPrefixBase: ");
         pw.println(mRandomPrefixBase);
-
-        pw.print("mEnableSyncSm: ");
-        pw.println(mEnableSyncSm);
     }
 
     /** Returns the string representation of this object.*/
diff --git a/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java b/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
index 0e8b044..d5d71bc 100644
--- a/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
+++ b/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
@@ -84,7 +84,7 @@
 
     private void initTestMap() throws Exception {
         mTestMap = new BpfMap<>(
-                TETHER_DOWNSTREAM6_FS_PATH, BpfMap.BPF_F_RDWR,
+                TETHER_DOWNSTREAM6_FS_PATH,
                 TetherDownstream6Key.class, Tether6Value.class);
 
         mTestMap.forEach((key, value) -> {
@@ -135,7 +135,7 @@
                 assertEquals(OsConstants.EPERM, expected.errno);
             }
         }
-        try (BpfMap readWriteMap = new BpfMap<>(TETHER_DOWNSTREAM6_FS_PATH, BpfMap.BPF_F_RDWR,
+        try (BpfMap readWriteMap = new BpfMap<>(TETHER_DOWNSTREAM6_FS_PATH,
                 TetherDownstream6Key.class, Tether6Value.class)) {
             assertNotNull(readWriteMap);
         }
@@ -389,7 +389,7 @@
     public void testOpenNonexistentMap() throws Exception {
         try {
             final BpfMap<TetherDownstream6Key, Tether6Value> nonexistentMap = new BpfMap<>(
-                    "/sys/fs/bpf/tethering/nonexistent", BpfMap.BPF_F_RDWR,
+                    "/sys/fs/bpf/tethering/nonexistent",
                     TetherDownstream6Key.class, Tether6Value.class);
         } catch (ErrnoException expected) {
             assertEquals(OsConstants.ENOENT, expected.errno);
@@ -409,8 +409,8 @@
         final int before = getNumOpenFds();
         for (int i = 0; i < iterations; i++) {
             try (BpfMap<TetherDownstream6Key, Tether6Value> map = new BpfMap<>(
-                TETHER_DOWNSTREAM6_FS_PATH, BpfMap.BPF_F_RDWR,
-                TetherDownstream6Key.class, Tether6Value.class)) {
+                    TETHER_DOWNSTREAM6_FS_PATH,
+                    TetherDownstream6Key.class, Tether6Value.class)) {
                 // do nothing
             }
         }
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 19c6e5a..aa322dc 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -754,26 +754,4 @@
                 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/framework-t/api/system-current.txt b/framework-t/api/system-current.txt
index 3513573..d346af3 100644
--- a/framework-t/api/system-current.txt
+++ b/framework-t/api/system-current.txt
@@ -532,6 +532,7 @@
     field public static final int ERROR_RESPONSE_BAD_FORMAT = 9; // 0x9
     field public static final int ERROR_TIMEOUT = 3; // 0x3
     field public static final int ERROR_UNAVAILABLE = 4; // 0x4
+    field public static final int ERROR_UNKNOWN = 11; // 0xb
     field public static final int ERROR_UNSUPPORTED_CHANNEL = 7; // 0x7
   }
 
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java b/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
index e7b0eaa..869ac9b 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClient.java
@@ -96,7 +96,8 @@
         @Override
         public void onInterfaceDestroyed(@NonNull SocketKey socketKey,
                 @NonNull MdnsInterfaceSocket socket) {
-            notifySocketDestroyed(socketKey);
+            mActiveSockets.remove(socketKey);
+            mSocketCreationCallback.onSocketDestroyed(socketKey);
             maybeCleanupPacketHandler(socketKey);
         }
 
diff --git a/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java b/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java
index 27c0f9f..4ec1562 100644
--- a/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java
+++ b/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java
@@ -66,7 +66,7 @@
         /** Create BpfMap for updating interface and index mapping. */
         public IBpfMap<S32, InterfaceMapValue> getInterfaceMap() {
             try {
-                return new BpfMap<>(IFACE_INDEX_NAME_MAP_PATH, BpfMap.BPF_F_RDWR,
+                return new BpfMap<>(IFACE_INDEX_NAME_MAP_PATH,
                     S32.class, InterfaceMapValue.class);
             } catch (ErrnoException e) {
                 Log.e(TAG, "Cannot create interface map: " + e);
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 7cf6293..78c4504 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -481,6 +481,8 @@
     @Nullable
     private final SkDestroyListener mSkDestroyListener;
 
+    private static final int MAX_SOCKET_DESTROY_LISTENER_LOGS = 20;
+
     private static @NonNull Clock getDefaultClock() {
         return new BestClock(ZoneOffset.UTC, SystemClock.currentNetworkTimeClock(),
                 Clock.systemUTC());
@@ -806,7 +808,7 @@
         /** Get counter sets map for each UID. */
         public IBpfMap<S32, U8> getUidCounterSetMap() {
             try {
-                return new BpfMap<S32, U8>(UID_COUNTERSET_MAP_PATH, BpfMap.BPF_F_RDWR,
+                return new BpfMap<S32, U8>(UID_COUNTERSET_MAP_PATH,
                         S32.class, U8.class);
             } catch (ErrnoException e) {
                 Log.wtf(TAG, "Cannot open uid counter set map: " + e);
@@ -818,7 +820,7 @@
         public IBpfMap<CookieTagMapKey, CookieTagMapValue> getCookieTagMap() {
             try {
                 return new BpfMap<CookieTagMapKey, CookieTagMapValue>(COOKIE_TAG_MAP_PATH,
-                        BpfMap.BPF_F_RDWR, CookieTagMapKey.class, CookieTagMapValue.class);
+                        CookieTagMapKey.class, CookieTagMapValue.class);
             } catch (ErrnoException e) {
                 Log.wtf(TAG, "Cannot open cookie tag map: " + e);
                 return null;
@@ -829,7 +831,7 @@
         public IBpfMap<StatsMapKey, StatsMapValue> getStatsMapA() {
             try {
                 return new BpfMap<StatsMapKey, StatsMapValue>(STATS_MAP_A_PATH,
-                        BpfMap.BPF_F_RDWR, StatsMapKey.class, StatsMapValue.class);
+                        StatsMapKey.class, StatsMapValue.class);
             } catch (ErrnoException e) {
                 Log.wtf(TAG, "Cannot open stats map A: " + e);
                 return null;
@@ -840,7 +842,7 @@
         public IBpfMap<StatsMapKey, StatsMapValue> getStatsMapB() {
             try {
                 return new BpfMap<StatsMapKey, StatsMapValue>(STATS_MAP_B_PATH,
-                        BpfMap.BPF_F_RDWR, StatsMapKey.class, StatsMapValue.class);
+                        StatsMapKey.class, StatsMapValue.class);
             } catch (ErrnoException e) {
                 Log.wtf(TAG, "Cannot open stats map B: " + e);
                 return null;
@@ -851,7 +853,7 @@
         public IBpfMap<UidStatsMapKey, StatsMapValue> getAppUidStatsMap() {
             try {
                 return new BpfMap<UidStatsMapKey, StatsMapValue>(APP_UID_STATS_MAP_PATH,
-                        BpfMap.BPF_F_RDWR, UidStatsMapKey.class, StatsMapValue.class);
+                       UidStatsMapKey.class, StatsMapValue.class);
             } catch (ErrnoException e) {
                 Log.wtf(TAG, "Cannot open app uid stats map: " + e);
                 return null;
@@ -862,7 +864,7 @@
         public IBpfMap<S32, StatsMapValue> getIfaceStatsMap() {
             try {
                 return new BpfMap<S32, StatsMapValue>(IFACE_STATS_MAP_PATH,
-                        BpfMap.BPF_F_RDWR, S32.class, StatsMapValue.class);
+                       S32.class, StatsMapValue.class);
             } catch (ErrnoException e) {
                 throw new IllegalStateException("Failed to open interface stats map", e);
             }
@@ -881,7 +883,8 @@
         /** Create a new SkDestroyListener. */
         public SkDestroyListener makeSkDestroyListener(
                 IBpfMap<CookieTagMapKey, CookieTagMapValue> cookieTagMap, Handler handler) {
-            return new SkDestroyListener(cookieTagMap, handler, new SharedLog(TAG));
+            return new SkDestroyListener(
+                    cookieTagMap, handler, new SharedLog(MAX_SOCKET_DESTROY_LISTENER_LOGS, TAG));
         }
 
         /**
@@ -2912,6 +2915,12 @@
             dumpStatsMapLocked(mStatsMapB, pw, "mStatsMapB");
             dumpIfaceStatsMapLocked(pw);
             pw.decreaseIndent();
+
+            pw.println();
+            pw.println("SkDestroyListener logs:");
+            pw.increaseIndent();
+            mSkDestroyListener.dump(pw);
+            pw.decreaseIndent();
         }
     }
 
diff --git a/service-t/src/com/android/server/net/SkDestroyListener.java b/service-t/src/com/android/server/net/SkDestroyListener.java
index 7b68f89..a6cc2b5 100644
--- a/service-t/src/com/android/server/net/SkDestroyListener.java
+++ b/service-t/src/com/android/server/net/SkDestroyListener.java
@@ -30,6 +30,8 @@
 import com.android.net.module.util.netlink.NetlinkMessage;
 import com.android.net.module.util.netlink.StructInetDiagSockId;
 
+import java.io.PrintWriter;
+
 /**
  * Monitor socket destroy and delete entry from cookie tag bpf map.
  */
@@ -72,4 +74,11 @@
             mLog.e("Failed to delete CookieTagMap entry for " + sockId.cookie  + ": " + e);
         }
     }
+
+    /**
+     * Dump the contents of SkDestroyListener log.
+     */
+    public void dump(PrintWriter pw) {
+        mLog.reverseDump(pw);
+    }
 }
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index ad9cfbe..aa40285 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -187,7 +187,7 @@
     private static IBpfMap<S32, U32> getConfigurationMap() {
         try {
             return new BpfMap<>(
-                    CONFIGURATION_MAP_PATH, BpfMap.BPF_F_RDWR, S32.class, U32.class);
+                    CONFIGURATION_MAP_PATH, S32.class, U32.class);
         } catch (ErrnoException e) {
             throw new IllegalStateException("Cannot open netd configuration map", e);
         }
@@ -196,7 +196,7 @@
     private static IBpfMap<S32, UidOwnerValue> getUidOwnerMap() {
         try {
             return new BpfMap<>(
-                    UID_OWNER_MAP_PATH, BpfMap.BPF_F_RDWR, S32.class, UidOwnerValue.class);
+                    UID_OWNER_MAP_PATH, S32.class, UidOwnerValue.class);
         } catch (ErrnoException e) {
             throw new IllegalStateException("Cannot open uid owner map", e);
         }
@@ -205,7 +205,7 @@
     private static IBpfMap<S32, U8> getUidPermissionMap() {
         try {
             return new BpfMap<>(
-                    UID_PERMISSION_MAP_PATH, BpfMap.BPF_F_RDWR, S32.class, U8.class);
+                    UID_PERMISSION_MAP_PATH, S32.class, U8.class);
         } catch (ErrnoException e) {
             throw new IllegalStateException("Cannot open uid permission map", e);
         }
@@ -213,7 +213,7 @@
 
     private static IBpfMap<CookieTagMapKey, CookieTagMapValue> getCookieTagMap() {
         try {
-            return new BpfMap<>(COOKIE_TAG_MAP_PATH, BpfMap.BPF_F_RDWR,
+            return new BpfMap<>(COOKIE_TAG_MAP_PATH,
                     CookieTagMapKey.class, CookieTagMapValue.class);
         } catch (ErrnoException e) {
             throw new IllegalStateException("Cannot open cookie tag map", e);
@@ -223,7 +223,7 @@
     private static IBpfMap<S32, U8> getDataSaverEnabledMap() {
         try {
             return new BpfMap<>(
-                    DATA_SAVER_ENABLED_MAP_PATH, BpfMap.BPF_F_RDWR, S32.class, U8.class);
+                    DATA_SAVER_ENABLED_MAP_PATH, S32.class, U8.class);
         } catch (ErrnoException e) {
             throw new IllegalStateException("Cannot open data saver enabled map", e);
         }
@@ -231,7 +231,7 @@
 
     private static IBpfMap<IngressDiscardKey, IngressDiscardValue> getIngressDiscardMap() {
         try {
-            return new BpfMap<>(INGRESS_DISCARD_MAP_PATH, BpfMap.BPF_F_RDWR,
+            return new BpfMap<>(INGRESS_DISCARD_MAP_PATH,
                     IngressDiscardKey.class, IngressDiscardValue.class);
         } catch (ErrnoException e) {
             throw new IllegalStateException("Cannot open ingress discard map", e);
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 1836140..a995439 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -65,6 +65,7 @@
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_SKIPPED;
 import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
+import static android.net.MulticastRoutingConfig.FORWARD_NONE;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
@@ -4050,6 +4051,10 @@
         pw.increaseIndent();
         mNetworkActivityTracker.dump(pw);
         pw.decreaseIndent();
+
+        pw.println();
+        pw.println("Multicast routing supported: " +
+                (mMulticastRoutingCoordinatorService != null));
     }
 
     private void dumpNetworks(IndentingPrintWriter pw) {
@@ -9151,7 +9156,15 @@
     private void applyMulticastRoutingConfig(@NonNull String localNetworkInterfaceName,
             @NonNull String upstreamNetworkInterfaceName,
             @NonNull final LocalNetworkConfig config) {
-        if (mMulticastRoutingCoordinatorService == null) return;
+        if (mMulticastRoutingCoordinatorService == null) {
+            if (config.getDownstreamMulticastRoutingConfig().getForwardingMode() != FORWARD_NONE ||
+                config.getUpstreamMulticastRoutingConfig().getForwardingMode() != FORWARD_NONE) {
+                loge("Multicast routing is not supported, failed to configure " + config
+                        + " for " + localNetworkInterfaceName + " to "
+                        +  upstreamNetworkInterfaceName);
+            }
+            return;
+        }
 
         mMulticastRoutingCoordinatorService.applyMulticastRoutingConfig(localNetworkInterfaceName,
                 upstreamNetworkInterfaceName, config.getUpstreamMulticastRoutingConfig());
@@ -9162,7 +9175,9 @@
 
     private void disableMulticastRouting(@NonNull String localNetworkInterfaceName,
             @NonNull String upstreamNetworkInterfaceName) {
-        if (mMulticastRoutingCoordinatorService == null) return;
+        if (mMulticastRoutingCoordinatorService == null) {
+            return;
+        }
 
         mMulticastRoutingCoordinatorService.applyMulticastRoutingConfig(localNetworkInterfaceName,
                 upstreamNetworkInterfaceName, MulticastRoutingConfig.CONFIG_FORWARD_NONE);
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
index 17de146..daaf91d 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -256,7 +256,7 @@
         public IBpfMap<ClatIngress6Key, ClatIngress6Value> getBpfIngress6Map() {
             try {
                 return new BpfMap<>(CLAT_INGRESS6_MAP_PATH,
-                    BpfMap.BPF_F_RDWR, ClatIngress6Key.class, ClatIngress6Value.class);
+                       ClatIngress6Key.class, ClatIngress6Value.class);
             } catch (ErrnoException e) {
                 Log.e(TAG, "Cannot create ingress6 map: " + e);
                 return null;
@@ -268,7 +268,7 @@
         public IBpfMap<ClatEgress4Key, ClatEgress4Value> getBpfEgress4Map() {
             try {
                 return new BpfMap<>(CLAT_EGRESS4_MAP_PATH,
-                    BpfMap.BPF_F_RDWR, ClatEgress4Key.class, ClatEgress4Value.class);
+                       ClatEgress4Key.class, ClatEgress4Value.class);
             } catch (ErrnoException e) {
                 Log.e(TAG, "Cannot create egress4 map: " + e);
                 return null;
@@ -280,7 +280,7 @@
         public IBpfMap<CookieTagMapKey, CookieTagMapValue> getBpfCookieTagMap() {
             try {
                 return new BpfMap<>(COOKIE_TAG_MAP_PATH,
-                        BpfMap.BPF_F_RDWR, CookieTagMapKey.class, CookieTagMapValue.class);
+                       CookieTagMapKey.class, CookieTagMapValue.class);
             } catch (ErrnoException e) {
                 Log.wtf(TAG, "Cannot open cookie tag map: " + e);
                 return null;
diff --git a/service/src/com/android/server/connectivity/DscpPolicyTracker.java b/service/src/com/android/server/connectivity/DscpPolicyTracker.java
index 8d566b6..ea3878f 100644
--- a/service/src/com/android/server/connectivity/DscpPolicyTracker.java
+++ b/service/src/com/android/server/connectivity/DscpPolicyTracker.java
@@ -86,9 +86,9 @@
         mAttachedIfaces = new HashSet<String>();
         mIfaceIndexToPolicyIdBpfMapIndex = new HashMap<Integer, SparseIntArray>();
         mBpfDscpIpv4Policies = new BpfMap<Struct.S32, DscpPolicyValue>(IPV4_POLICY_MAP_PATH,
-                BpfMap.BPF_F_RDWR, Struct.S32.class, DscpPolicyValue.class);
+                Struct.S32.class, DscpPolicyValue.class);
         mBpfDscpIpv6Policies = new BpfMap<Struct.S32, DscpPolicyValue>(IPV6_POLICY_MAP_PATH,
-                BpfMap.BPF_F_RDWR, Struct.S32.class, DscpPolicyValue.class);
+                Struct.S32.class, DscpPolicyValue.class);
     }
 
     private boolean isUnusedIndex(int index) {
diff --git a/staticlibs/device/com/android/net/module/util/BpfBitmap.java b/staticlibs/device/com/android/net/module/util/BpfBitmap.java
index acb3ca5..b62a430 100644
--- a/staticlibs/device/com/android/net/module/util/BpfBitmap.java
+++ b/staticlibs/device/com/android/net/module/util/BpfBitmap.java
@@ -38,8 +38,7 @@
      * @param path The path of the BPF map.
      */
     public BpfBitmap(@NonNull String path) throws ErrnoException {
-        mBpfMap = new BpfMap<Struct.S32, Struct.S64>(path, BpfMap.BPF_F_RDWR,
-                Struct.S32.class, Struct.S64.class);
+        mBpfMap = new BpfMap<>(path, Struct.S32.class, Struct.S64.class);
     }
 
     /**
diff --git a/staticlibs/device/com/android/net/module/util/BpfMap.java b/staticlibs/device/com/android/net/module/util/BpfMap.java
index e3ef0f0..da77ae8 100644
--- a/staticlibs/device/com/android/net/module/util/BpfMap.java
+++ b/staticlibs/device/com/android/net/module/util/BpfMap.java
@@ -27,7 +27,6 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 
-import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 import java.util.NoSuchElementException;
@@ -110,6 +109,17 @@
     }
 
     /**
+     * Create a R/W BpfMap map wrapper with "path" of filesystem.
+     *
+     * @throws ErrnoException if the BPF map associated with {@code path} cannot be retrieved.
+     * @throws NullPointerException if {@code path} is null.
+     */
+    public BpfMap(@NonNull final String path, final Class<K> key,
+            final Class<V> value) throws ErrnoException, NullPointerException {
+        this(path, BPF_F_RDWR, key, value);
+    }
+
+    /**
      * Update an existing or create a new key -> value entry in an eBbpf map.
      * (use insertOrReplaceEntry() if you need to know whether insert or replace happened)
      */
diff --git a/staticlibs/testutils/Android.bp b/staticlibs/testutils/Android.bp
index a5c4fea..43853ee 100644
--- a/staticlibs/testutils/Android.bp
+++ b/staticlibs/testutils/Android.bp
@@ -91,6 +91,8 @@
         "cts",
         "mts-networking",
         "mcts-networking",
+        "mts-tethering",
+        "mcts-tethering",
     ],
     data: [":ConnectivityTestPreparer"],
 }
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java
index ad30ce0..9474464 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsMultinetworkSocketClientTest.java
@@ -101,11 +101,17 @@
 
     private SocketCallback expectSocketCallback(MdnsServiceBrowserListener listener,
             Network requestedNetwork) {
+        return expectSocketCallback(listener, requestedNetwork, mSocketCreationCallback,
+                1 /* requestSocketCount */);
+    }
+
+    private SocketCallback expectSocketCallback(MdnsServiceBrowserListener listener,
+                Network requestedNetwork, SocketCreationCallback callback, int requestSocketCount) {
         final ArgumentCaptor<SocketCallback> callbackCaptor =
                 ArgumentCaptor.forClass(SocketCallback.class);
         mHandler.post(() -> mSocketClient.notifyNetworkRequested(
-                listener, requestedNetwork, mSocketCreationCallback));
-        verify(mProvider, timeout(DEFAULT_TIMEOUT))
+                listener, requestedNetwork, callback));
+        verify(mProvider, timeout(DEFAULT_TIMEOUT).times(requestSocketCount))
                 .requestSocket(eq(requestedNetwork), callbackCaptor.capture());
         return callbackCaptor.getValue();
     }
@@ -365,4 +371,40 @@
         callback.onInterfaceDestroyed(otherSocketKey, otherSocket);
         verify(mSocketCreationCallback).onSocketDestroyed(otherSocketKey);
     }
+
+    @Test
+    public void testSocketDestroyed_MultipleCallbacks() {
+        final MdnsInterfaceSocket socket2 = mock(MdnsInterfaceSocket.class);
+        final SocketKey socketKey2 = new SocketKey(1001 /* interfaceIndex */);
+        final SocketCreationCallback creationCallback1 = mock(SocketCreationCallback.class);
+        final SocketCreationCallback creationCallback2 = mock(SocketCreationCallback.class);
+        final SocketCreationCallback creationCallback3 = mock(SocketCreationCallback.class);
+        final SocketCallback callback1 = expectSocketCallback(
+                mock(MdnsServiceBrowserListener.class), mNetwork, creationCallback1,
+                1 /* requestSocketCount */);
+        final SocketCallback callback2 = expectSocketCallback(
+                mock(MdnsServiceBrowserListener.class), mNetwork, creationCallback2,
+                2 /* requestSocketCount */);
+        final SocketCallback callback3 = expectSocketCallback(
+                mock(MdnsServiceBrowserListener.class), null /* requestedNetwork */,
+                creationCallback3, 1 /* requestSocketCount */);
+
+        doReturn(createEmptyNetworkInterface()).when(mSocket).getInterface();
+        callback1.onSocketCreated(mSocketKey, mSocket, List.of());
+        callback2.onSocketCreated(mSocketKey, mSocket, List.of());
+        callback3.onSocketCreated(mSocketKey, mSocket, List.of());
+        callback3.onSocketCreated(socketKey2, socket2, List.of());
+        verify(creationCallback1).onSocketCreated(mSocketKey);
+        verify(creationCallback2).onSocketCreated(mSocketKey);
+        verify(creationCallback3).onSocketCreated(mSocketKey);
+        verify(creationCallback3).onSocketCreated(socketKey2);
+
+        callback1.onInterfaceDestroyed(mSocketKey, mSocket);
+        callback2.onInterfaceDestroyed(mSocketKey, mSocket);
+        callback3.onInterfaceDestroyed(mSocketKey, mSocket);
+        verify(creationCallback1).onSocketDestroyed(mSocketKey);
+        verify(creationCallback2).onSocketDestroyed(mSocketKey);
+        verify(creationCallback3).onSocketDestroyed(mSocketKey);
+        verify(creationCallback3, never()).onSocketDestroyed(socketKey2);
+    }
 }
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index 1ee3f9d..a5fee5b 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -85,6 +85,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -130,6 +131,7 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
 import android.util.Pair;
 
 import androidx.annotation.Nullable;
@@ -2802,4 +2804,16 @@
         final String dump = getDump();
         assertDumpContains(dump, pollReasonNameOf(POLL_REASON_RAT_CHANGED));
     }
+
+    @Test
+    public void testDumpSkDestroyListenerLogs() throws ErrnoException {
+        doAnswer((invocation) -> {
+            final IndentingPrintWriter ipw = (IndentingPrintWriter) invocation.getArgument(0);
+            ipw.println("Log for testing");
+            return null;
+        }).when(mSkDestroyListener).dump(any());
+
+        final String dump = getDump();
+        assertDumpContains(dump, "Log for testing");
+    }
 }
diff --git a/thread/framework/java/android/net/thread/ThreadNetworkException.java b/thread/framework/java/android/net/thread/ThreadNetworkException.java
index c5e1e97..4fd445b 100644
--- a/thread/framework/java/android/net/thread/ThreadNetworkException.java
+++ b/thread/framework/java/android/net/thread/ThreadNetworkException.java
@@ -47,6 +47,7 @@
         ERROR_REJECTED_BY_PEER,
         ERROR_RESPONSE_BAD_FORMAT,
         ERROR_RESOURCE_EXHAUSTED,
+        ERROR_UNKNOWN,
     })
     public @interface ErrorCode {}
 
@@ -122,6 +123,12 @@
      */
     public static final int ERROR_RESOURCE_EXHAUSTED = 10;
 
+    /**
+     * The operation failed because of an unknown error in the system. This typically indicates
+     * that the caller doesn't understand error codes added in newer Android versions.
+     */
+    public static final int ERROR_UNKNOWN = 11;
+
     private final int mErrorCode;
 
     /** Creates a new {@link ThreadNetworkException} object with given error code and message. */