Merge changes I95352711,I27bdb5d8,I7e1acc65,I7c95ff19

* changes:
  BpfCoordinator: dump IPv6 BPF downstream rules
  BpfCoordinator: change function name and format for dumping IPv6 rules
  add header on ipv6 forwarding rulesdump
  add header on ipv6 upstream bpf map dump
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 75f63c8..5191bb7 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -1975,6 +1975,10 @@
                 return;
             }
 
+            if (arg1 == UpstreamNetworkMonitor.NOTIFY_TEST_NETWORK_AVAILABLE) {
+                chooseUpstreamType(false);
+            }
+
             if (ns == null || !pertainsToCurrentUpstream(ns)) {
                 // TODO: In future, this is where upstream evaluation and selection
                 // could be handled for notifications which include sufficient data.
diff --git a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
index 16c031b..15df0c6 100644
--- a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
+++ b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
@@ -85,11 +85,12 @@
     private static final boolean DBG = false;
     private static final boolean VDBG = false;
 
-    public static final int EVENT_ON_CAPABILITIES   = 1;
-    public static final int EVENT_ON_LINKPROPERTIES = 2;
-    public static final int EVENT_ON_LOST           = 3;
-    public static final int EVENT_DEFAULT_SWITCHED  = 4;
-    public static final int NOTIFY_LOCAL_PREFIXES   = 10;
+    public static final int EVENT_ON_CAPABILITIES         = 1;
+    public static final int EVENT_ON_LINKPROPERTIES       = 2;
+    public static final int EVENT_ON_LOST                 = 3;
+    public static final int EVENT_DEFAULT_SWITCHED        = 4;
+    public static final int NOTIFY_LOCAL_PREFIXES         = 10;
+    public static final int NOTIFY_TEST_NETWORK_AVAILABLE = 11;
     // This value is used by deprecated preferredUpstreamIfaceTypes selection which is default
     // disabled.
     @VisibleForTesting
@@ -467,6 +468,17 @@
         notifyTarget(EVENT_DEFAULT_SWITCHED, ns);
     }
 
+    private void maybeHandleTestNetwork(@NonNull Network network) {
+        if (!mPreferTestNetworks) return;
+
+        final UpstreamNetworkState ns = mNetworkMap.get(network);
+        if (network.equals(mTetheringUpstreamNetwork) || !isTestNetwork(ns)) return;
+
+        // Test network is available. Notify tethering.
+        Log.d(TAG, "Handle test network: " + network);
+        notifyTarget(NOTIFY_TEST_NETWORK_AVAILABLE, ns);
+    }
+
     private void recomputeLocalPrefixes() {
         final HashSet<IpPrefix> localPrefixes = allLocalPrefixes(mNetworkMap.values());
         if (!mLocalPrefixes.equals(localPrefixes)) {
@@ -549,6 +561,12 @@
             // So it's not useful to do this work for non-LISTEN_ALL callbacks.
             if (mCallbackType == CALLBACK_LISTEN_ALL) {
                 recomputeLocalPrefixes();
+
+                // When the LISTEN_ALL network callback calls onLinkPropertiesChanged, it means that
+                // all the network information for the network is known (because
+                // onLinkPropertiesChanged is called after onAvailable and onCapabilitiesChanged).
+                // Inform tethering that the test network might have changed.
+                maybeHandleTestNetwork(network);
             }
         }
 
diff --git a/Tethering/tests/integration/AndroidManifest.xml b/Tethering/tests/integration/AndroidManifest.xml
index c89c556..9303d0a 100644
--- a/Tethering/tests/integration/AndroidManifest.xml
+++ b/Tethering/tests/integration/AndroidManifest.xml
@@ -16,12 +16,13 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.android.networkstack.tethering.tests.integration">
 
-    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <!-- The test need CHANGE_NETWORK_STATE permission to use requestNetwork API to setup test
          network. Since R shell application don't have such permission, grant permission to the test
          here. TODO: Remove CHANGE_NETWORK_STATE permission here and use adopt shell perssion to
          obtain CHANGE_NETWORK_STATE for testing once R device is no longer supported. -->
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
 
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index 06586e2..c9b3686 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -16,8 +16,6 @@
 
 package android.net;
 
-import static android.Manifest.permission.ACCESS_NETWORK_STATE;
-import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
 import static android.Manifest.permission.DUMP;
 import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
 import static android.Manifest.permission.NETWORK_SETTINGS;
@@ -42,6 +40,7 @@
 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
 import static com.android.testutils.DeviceInfoUtils.KVersion;
 import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
+import static com.android.testutils.TestPermissionUtil.runAsShell;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -129,6 +128,10 @@
 
     private static final String TAG = EthernetTetheringTest.class.getSimpleName();
     private static final int TIMEOUT_MS = 5000;
+    // Used to check if any tethering interface is available. Choose 200ms to be request timeout
+    // because the average interface requested time on cuttlefish@acloud is around 10ms.
+    // See TetheredInterfaceRequester.getInterface, isInterfaceForTetheringAvailable.
+    private static final int AVAILABLE_TETHER_IFACE_REQUEST_TIMEOUT_MS = 200;
     private static final int TETHER_REACHABILITY_ATTEMPTS = 20;
     private static final int DUMP_POLLING_MAX_RETRY = 100;
     private static final int DUMP_POLLING_INTERVAL_MS = 50;
@@ -186,30 +189,25 @@
 
     @Before
     public void setUp() throws Exception {
-        // Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive
-        // tethered client callbacks. The restricted networks permission is needed to ensure that
-        // EthernetManager#isAvailable will correctly return true on devices where Ethernet is
-        // marked restricted, like cuttlefish. The dump permission is needed to verify bpf related
-        // functions via dumpsys output.
-        mUiAutomation.adoptShellPermissionIdentity(
-                MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED, ACCESS_NETWORK_STATE,
-                CONNECTIVITY_USE_RESTRICTED_NETWORKS, DUMP);
         mHandlerThread = new HandlerThread(getClass().getSimpleName());
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
 
-        mRunTests = isEthernetTetheringSupported();
+        mRunTests = runAsShell(NETWORK_SETTINGS, TETHER_PRIVILEGED, () ->
+                mTm.isTetheringSupported());
         assumeTrue(mRunTests);
 
         mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm);
     }
 
     private void cleanUp() throws Exception {
-        mTm.setPreferTestNetworks(false);
+        setPreferTestNetworks(false);
 
         if (mUpstreamTracker != null) {
-            mUpstreamTracker.teardown();
-            mUpstreamTracker = null;
+            runAsShell(MANAGE_TEST_NETWORKS, () -> {
+                mUpstreamTracker.teardown();
+                mUpstreamTracker = null;
+            });
         }
         if (mUpstreamReader != null) {
             TapPacketReader reader = mUpstreamReader;
@@ -217,19 +215,25 @@
             mUpstreamReader = null;
         }
 
-        mTm.stopTethering(TETHERING_ETHERNET);
-        if (mTetheringEventCallback != null) {
-            mTetheringEventCallback.awaitInterfaceUntethered();
-            mTetheringEventCallback.unregister();
-            mTetheringEventCallback = null;
-        }
+        runAsShell(TETHER_PRIVILEGED, () -> {
+            mTm.stopTethering(TETHERING_ETHERNET);
+            // Binder call is an async call. Need to hold the shell permission until tethering
+            // stopped. This helps to avoid the test become flaky.
+            if (mTetheringEventCallback != null) {
+                mTetheringEventCallback.awaitInterfaceUntethered();
+                mTetheringEventCallback.unregister();
+                mTetheringEventCallback = null;
+            }
+        });
         if (mDownstreamReader != null) {
             TapPacketReader reader = mDownstreamReader;
             mHandler.post(() -> reader.stop());
             mDownstreamReader = null;
         }
-        mTetheredInterfaceRequester.release();
-        mEm.setIncludeTestInterfaces(false);
+        runAsShell(NETWORK_SETTINGS, () -> {
+            mTetheredInterfaceRequester.release();
+        });
+        setIncludeTestInterfaces(false);
         maybeDeleteTestInterface();
     }
 
@@ -243,10 +247,44 @@
         }
     }
 
+    private boolean isInterfaceForTetheringAvailable() throws Exception {
+        // If previous test case doesn't release tethering interface successfully, the other tests
+        // after that test may be skipped as unexcepted.
+        // TODO: figure out a better way to check default tethering interface existenion.
+        final TetheredInterfaceRequester requester = new TetheredInterfaceRequester(mHandler, mEm);
+        try {
+            // Use short timeout (200ms) for requesting an existing interface, if any, because
+            // it should reurn faster than requesting a new tethering interface. Using default
+            // timeout (5000ms, TIMEOUT_MS) may make that total testing time is over 1 minute
+            // test module timeout on internal testing.
+            // TODO: if this becomes flaky, consider using default timeout (5000ms) and moving
+            // this check into #setUpOnce.
+            return requester.getInterface(AVAILABLE_TETHER_IFACE_REQUEST_TIMEOUT_MS) != null;
+        } catch (TimeoutException e) {
+            return false;
+        } finally {
+            runAsShell(NETWORK_SETTINGS, () -> {
+                requester.release();
+            });
+        }
+    }
+
+    private void setIncludeTestInterfaces(boolean include) {
+        runAsShell(NETWORK_SETTINGS, () -> {
+            mEm.setIncludeTestInterfaces(include);
+        });
+    }
+
+    private void setPreferTestNetworks(boolean prefer) {
+        runAsShell(NETWORK_SETTINGS, () -> {
+            mTm.setPreferTestNetworks(prefer);
+        });
+    }
+
     @Test
     public void testVirtualEthernetAlreadyExists() throws Exception {
         // This test requires manipulating packets. Skip if there is a physical Ethernet connected.
-        assumeFalse(mEm.isAvailable());
+        assumeFalse(isInterfaceForTetheringAvailable());
 
         mDownstreamIface = createTestInterface();
         // This must be done now because as soon as setIncludeTestInterfaces(true) is called, the
@@ -256,7 +294,7 @@
         int mtu = getMTU(mDownstreamIface);
 
         Log.d(TAG, "Including test interfaces");
-        mEm.setIncludeTestInterfaces(true);
+        setIncludeTestInterfaces(true);
 
         final String iface = mTetheredInterfaceRequester.getInterface();
         assertEquals("TetheredInterfaceCallback for unexpected interface",
@@ -268,11 +306,11 @@
     @Test
     public void testVirtualEthernet() throws Exception {
         // This test requires manipulating packets. Skip if there is a physical Ethernet connected.
-        assumeFalse(mEm.isAvailable());
+        assumeFalse(isInterfaceForTetheringAvailable());
 
         CompletableFuture<String> futureIface = mTetheredInterfaceRequester.requestInterface();
 
-        mEm.setIncludeTestInterfaces(true);
+        setIncludeTestInterfaces(true);
 
         mDownstreamIface = createTestInterface();
 
@@ -285,9 +323,9 @@
 
     @Test
     public void testStaticIpv4() throws Exception {
-        assumeFalse(mEm.isAvailable());
+        assumeFalse(isInterfaceForTetheringAvailable());
 
-        mEm.setIncludeTestInterfaces(true);
+        setIncludeTestInterfaces(true);
 
         mDownstreamIface = createTestInterface();
 
@@ -363,9 +401,9 @@
 
     @Test
     public void testLocalOnlyTethering() throws Exception {
-        assumeFalse(mEm.isAvailable());
+        assumeFalse(isInterfaceForTetheringAvailable());
 
-        mEm.setIncludeTestInterfaces(true);
+        setIncludeTestInterfaces(true);
 
         mDownstreamIface = createTestInterface();
 
@@ -397,7 +435,7 @@
 
     @Test
     public void testPhysicalEthernet() throws Exception {
-        assumeTrue(mEm.isAvailable());
+        assumeTrue(isInterfaceForTetheringAvailable());
         // Do not run this test if adb is over network and ethernet is connected.
         // It is likely the adb run over ethernet, the adb would break when ethernet is switching
         // from client mode to server mode. See b/160389275.
@@ -438,6 +476,7 @@
         private final CountDownLatch mLocalOnlyStoppedLatch = new CountDownLatch(1);
         private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1);
         private final CountDownLatch mUpstreamLatch = new CountDownLatch(1);
+        private final CountDownLatch mCallbackRegisteredLatch = new CountDownLatch(1);
         private final TetheringInterface mIface;
         private final Network mExpectedUpstream;
 
@@ -516,6 +555,22 @@
                     mLocalOnlyStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
         }
 
+        // Used to check if the callback has registered. When the callback is registered,
+        // onSupportedTetheringTypes is celled in onCallbackStarted(). After
+        // onSupportedTetheringTypes called, drop the permission for registering callback.
+        // See MyTetheringEventCallback#register, TetheringManager#onCallbackStarted.
+        @Override
+        public void onSupportedTetheringTypes(Set<Integer> supportedTypes) {
+            // Used to check callback registered.
+            mCallbackRegisteredLatch.countDown();
+        }
+
+        public void awaitCallbackRegistered() throws Exception {
+            if (!mCallbackRegisteredLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                fail("Did not receive callback registered signal after " + TIMEOUT_MS + "ms");
+            }
+        }
+
         public void awaitInterfaceUntethered() throws Exception {
             // Don't block teardown if the interface was never tethered.
             // This is racy because the interface might become tethered right after this check, but
@@ -591,16 +646,34 @@
         } else {
             callback = new MyTetheringEventCallback(mTm, iface);
         }
-        mTm.registerTetheringEventCallback(mHandler::post, callback);
-
+        runAsShell(NETWORK_SETTINGS, () -> {
+            mTm.registerTetheringEventCallback(mHandler::post, callback);
+            // Need to hold the shell permission until callback is registered. This helps to avoid
+            // the test become flaky.
+            callback.awaitCallbackRegistered();
+        });
+        final CountDownLatch tetheringStartedLatch = new CountDownLatch(1);
         StartTetheringCallback startTetheringCallback = new StartTetheringCallback() {
             @Override
+            public void onTetheringStarted() {
+                Log.d(TAG, "Ethernet tethering started");
+                tetheringStartedLatch.countDown();
+            }
+
+            @Override
             public void onTetheringFailed(int resultCode) {
                 fail("Unexpectedly got onTetheringFailed");
             }
         };
         Log.d(TAG, "Starting Ethernet tethering");
-        mTm.startTethering(request, mHandler::post /* executor */,  startTetheringCallback);
+        runAsShell(TETHER_PRIVILEGED, () -> {
+            mTm.startTethering(request, mHandler::post /* executor */, startTetheringCallback);
+            // Binder call is an async call. Need to hold the shell permission until tethering
+            // started. This helps to avoid the test become flaky.
+            if (!tetheringStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+                fail("Did not receive tethering started callback after " + TIMEOUT_MS + "ms");
+            }
+        });
 
         final int connectivityType = request.getConnectivityScope();
         switch (connectivityType) {
@@ -708,12 +781,17 @@
         public CompletableFuture<String> requestInterface() {
             assertNull("BUG: more than one tethered interface request", mRequest);
             Log.d(TAG, "Requesting tethered interface");
-            mRequest = mEm.requestTetheredInterface(mHandler::post, this);
+            mRequest = runAsShell(NETWORK_SETTINGS, () ->
+                    mEm.requestTetheredInterface(mHandler::post, this));
             return mFuture;
         }
 
+        public String getInterface(int timeout) throws Exception {
+            return requestInterface().get(timeout, TimeUnit.MILLISECONDS);
+        }
+
         public String getInterface() throws Exception {
-            return requestInterface().get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            return getInterface(TIMEOUT_MS);
         }
 
         public void release() {
@@ -764,8 +842,10 @@
     }
 
     private TestNetworkInterface createTestInterface() throws Exception {
-        TestNetworkManager tnm = mContext.getSystemService(TestNetworkManager.class);
-        TestNetworkInterface iface = tnm.createTapInterface();
+        TestNetworkManager tnm = runAsShell(MANAGE_TEST_NETWORKS, () ->
+                mContext.getSystemService(TestNetworkManager.class));
+        TestNetworkInterface iface = runAsShell(MANAGE_TEST_NETWORKS, () ->
+                tnm.createTapInterface());
         Log.d(TAG, "Created test interface " + iface.getInterfaceName());
         return iface;
     }
@@ -780,14 +860,14 @@
 
     private TestNetworkTracker createTestUpstream(final List<LinkAddress> addresses,
             final List<InetAddress> dnses) throws Exception {
-        mTm.setPreferTestNetworks(true);
+        setPreferTestNetworks(true);
 
         final LinkProperties lp = new LinkProperties();
         lp.setLinkAddresses(addresses);
         lp.setDnsServers(dnses);
         lp.setNat64Prefix(TEST_NAT64PREFIX);
 
-        return initTestNetwork(mContext, lp, TIMEOUT_MS);
+        return runAsShell(MANAGE_TEST_NETWORKS, () -> initTestNetwork(mContext, lp, TIMEOUT_MS));
     }
 
     @Test
@@ -1091,7 +1171,7 @@
 
     private TetheringTester initTetheringTester(List<LinkAddress> upstreamAddresses,
             List<InetAddress> upstreamDnses) throws Exception {
-        assumeFalse(mEm.isAvailable());
+        assumeFalse(isInterfaceForTetheringAvailable());
 
         // MyTetheringEventCallback currently only support await first available upstream. Tethering
         // may select internet network as upstream if test network is not available and not be
@@ -1099,7 +1179,7 @@
         mUpstreamTracker = createTestUpstream(upstreamAddresses, upstreamDnses);
 
         mDownstreamIface = createTestInterface();
-        mEm.setIncludeTestInterfaces(true);
+        setIncludeTestInterfaces(true);
 
         // Make sure EtherentTracker use "mDownstreamIface" as server mode interface.
         assertEquals("TetheredInterfaceCallback for unexpected interface",
@@ -1224,7 +1304,8 @@
             Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)
             throws Exception {
         final String[] args = new String[] {DUMPSYS_TETHERING_RAWMAP_ARG, mapArg};
-        final String rawMapStr = DumpTestUtils.dumpService(Context.TETHERING_SERVICE, args);
+        final String rawMapStr = runAsShell(DUMP, () ->
+                DumpTestUtils.dumpService(Context.TETHERING_SERVICE, args));
         final HashMap<K, V> map = new HashMap<>();
 
         for (final String line : rawMapStr.split(LINE_DELIMITER)) {
@@ -1250,7 +1331,8 @@
     }
 
     private boolean isTetherConfigBpfOffloadEnabled() throws Exception {
-        final String dumpStr = DumpTestUtils.dumpService(Context.TETHERING_SERVICE, "--short");
+        final String dumpStr = runAsShell(DUMP, () ->
+                DumpTestUtils.dumpService(Context.TETHERING_SERVICE, "--short"));
 
         // BPF offload tether config can be overridden by "config_tether_enable_bpf_offload" in
         // packages/modules/Connectivity/Tethering/res/values/config.xml. OEM may disable config by
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java
index b2cbf75..e0d77ee 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java
@@ -19,6 +19,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
 
 import static com.android.networkstack.apishim.common.ShimUtils.isAtLeastS;
 
@@ -329,6 +330,28 @@
             this.legacyType = toLegacyType(networkCapabilities);
         }
 
+        // Used for test network only because ConnectivityManager.networkCapabilitiesForType
+        // doesn't support "TRANSPORT_TEST -> TYPE_TEST" in #matchesLegacyType. Beware of
+        // satisfiedByNetworkCapabilities doesn't check on new |networkCapabilities| as
+        // #matchesLegacyType.
+        // TODO: refactor when tethering no longer uses CONNECTIVITY_ACTION.
+        private TestNetworkAgent(TestConnectivityManager cm) {
+            this.cm = cm;
+            networkId = new Network(cm.getNetworkId());
+            networkCapabilities = new NetworkCapabilities.Builder()
+                    .addTransportType(TRANSPORT_TEST)
+                    .addCapability(NET_CAPABILITY_INTERNET)
+                    .build();
+            linkProperties = new LinkProperties();
+            legacyType = TYPE_TEST;
+        }
+
+        // TODO: refactor when tethering no longer uses CONNECTIVITY_ACTION.
+        public static TestNetworkAgent buildTestNetworkAgentForTestNetwork(
+                TestConnectivityManager cm) {
+            return new TestNetworkAgent(cm);
+        }
+
         private static int toLegacyType(NetworkCapabilities nc) {
             for (int type = 0; type < ConnectivityManager.TYPE_TEST; type++) {
                 if (matchesLegacyType(nc, type)) return type;
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
index 9b9507b..b0cb7f2 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
@@ -449,6 +449,36 @@
     }
 
     @Test
+    public void testGetCurrentPreferredUpstream_TestNetworkPreferred() throws Exception {
+        mUNM.startTrackDefaultNetwork(mEntitleMgr);
+        mUNM.startObserveAllNetworks();
+        mUNM.setUpstreamConfig(true /* autoUpstream */, false /* dunRequired */);
+        mUNM.setTryCell(true);
+        mUNM.setPreferTestNetworks(true);
+
+        // [1] Mobile connects, DUN not required -> mobile selected.
+        final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, CELL_CAPABILITIES);
+        cellAgent.fakeConnect();
+        mCM.makeDefaultNetwork(cellAgent);
+        mLooper.dispatchAll();
+        assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
+        assertEquals(0, mCM.mRequested.size());
+
+        // [2] Test network connects -> test network selected.
+        final TestNetworkAgent testAgent =
+                TestNetworkAgent.buildTestNetworkAgentForTestNetwork(mCM);
+        testAgent.fakeConnect();
+        mLooper.dispatchAll();
+        assertEquals(testAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
+        assertEquals(0, mCM.mRequested.size());
+
+        // [3] Disable test networks preferred -> mobile selected.
+        mUNM.setPreferTestNetworks(false);
+        assertEquals(cellAgent.networkId, mUNM.getCurrentPreferredUpstream().network);
+        assertEquals(0, mCM.mRequested.size());
+    }
+
+    @Test
     public void testLocalPrefixes() throws Exception {
         mUNM.startTrackDefaultNetwork(mEntitleMgr);
         mUNM.startObserveAllNetworks();
diff --git a/netd/BpfHandler.cpp b/netd/BpfHandler.cpp
index 2810d80..994db1d 100644
--- a/netd/BpfHandler.cpp
+++ b/netd/BpfHandler.cpp
@@ -134,18 +134,16 @@
 
 int BpfHandler::tagSocket(int sockFd, uint32_t tag, uid_t chargeUid, uid_t realUid) {
     std::lock_guard guard(mMutex);
-    if (chargeUid != realUid && !hasUpdateDeviceStatsPermission(realUid)) {
-        return -EPERM;
-    }
+    if (!mCookieTagMap.isValid()) return -EPERM;
+
+    if (chargeUid != realUid && !hasUpdateDeviceStatsPermission(realUid)) return -EPERM;
 
     // Note that tagging the socket to AID_CLAT is only implemented in JNI ClatCoordinator.
     // The process is not allowed to tag socket to AID_CLAT via tagSocket() which would cause
     // process data usage accounting to be bypassed. Tagging AID_CLAT is used for avoiding counting
     // CLAT traffic data usage twice. See packages/modules/Connectivity/service/jni/
     // com_android_server_connectivity_ClatCoordinator.cpp
-    if (chargeUid == AID_CLAT) {
-        return -EPERM;
-    }
+    if (chargeUid == AID_CLAT) return -EPERM;
 
     // The socket destroy listener only monitors on the group {INET_TCP, INET_UDP, INET6_TCP,
     // INET6_UDP}. Tagging listener unsupported socket causes that the tag can't be removed from
@@ -180,6 +178,7 @@
 
     uint64_t sock_cookie = getSocketCookie(sockFd);
     if (sock_cookie == NONEXISTENT_COOKIE) return -errno;
+
     UidTagValue newKey = {.uid = (uint32_t)chargeUid, .tag = tag};
 
     uint32_t totalEntryCount = 0;
@@ -242,9 +241,11 @@
 
 int BpfHandler::untagSocket(int sockFd) {
     std::lock_guard guard(mMutex);
-    uint64_t sock_cookie = getSocketCookie(sockFd);
 
+    uint64_t sock_cookie = getSocketCookie(sockFd);
     if (sock_cookie == NONEXISTENT_COOKIE) return -errno;
+
+    if (!mCookieTagMap.isValid()) return -EPERM;
     base::Result<void> res = mCookieTagMap.deleteValue(sock_cookie);
     if (!res.ok()) {
         ALOGE("Failed to untag socket: %s", strerror(res.error().code()));
diff --git a/netd/NetdUpdatable.cpp b/netd/NetdUpdatable.cpp
index f0997fc..41b1fdb 100644
--- a/netd/NetdUpdatable.cpp
+++ b/netd/NetdUpdatable.cpp
@@ -16,19 +16,20 @@
 
 #define LOG_TAG "NetdUpdatable"
 
-#include "NetdUpdatable.h"
+#include "BpfHandler.h"
 
 #include <android-base/logging.h>
 #include <netdutils/Status.h>
 
 #include "NetdUpdatablePublic.h"
 
+static android::net::BpfHandler sBpfHandler;
+
 int libnetd_updatable_init(const char* cg2_path) {
     android::base::InitLogging(/*argv=*/nullptr);
     LOG(INFO) << __func__ << ": Initializing";
 
-    android::net::gNetdUpdatable = android::net::NetdUpdatable::getInstance();
-    android::netdutils::Status ret = android::net::gNetdUpdatable->mBpfHandler.init(cg2_path);
+    android::netdutils::Status ret = sBpfHandler.init(cg2_path);
     if (!android::netdutils::isOk(ret)) {
         LOG(ERROR) << __func__ << ": BPF handler init failed";
         return -ret.code();
@@ -37,25 +38,9 @@
 }
 
 int libnetd_updatable_tagSocket(int sockFd, uint32_t tag, uid_t chargeUid, uid_t realUid) {
-    if (android::net::gNetdUpdatable == nullptr) return -EPERM;
-    return android::net::gNetdUpdatable->mBpfHandler.tagSocket(sockFd, tag, chargeUid, realUid);
+    return sBpfHandler.tagSocket(sockFd, tag, chargeUid, realUid);
 }
 
 int libnetd_updatable_untagSocket(int sockFd) {
-    if (android::net::gNetdUpdatable == nullptr) return -EPERM;
-    return android::net::gNetdUpdatable->mBpfHandler.untagSocket(sockFd);
+    return sBpfHandler.untagSocket(sockFd);
 }
-
-namespace android {
-namespace net {
-
-NetdUpdatable* gNetdUpdatable = nullptr;
-
-NetdUpdatable* NetdUpdatable::getInstance() {
-    // Instantiated on first use.
-    static NetdUpdatable instance;
-    return &instance;
-}
-
-}  // namespace net
-}  // namespace android
diff --git a/netd/NetdUpdatable.h b/netd/NetdUpdatable.h
deleted file mode 100644
index 333037f..0000000
--- a/netd/NetdUpdatable.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * Copyright (c) 2022, The Android Open Source Project
- *
- * 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.
- */
-
-#pragma once
-
-#include "BpfHandler.h"
-
-namespace android {
-namespace net {
-
-class NetdUpdatable {
-  public:
-    NetdUpdatable() = default;
-    NetdUpdatable(const NetdUpdatable&) = delete;
-    NetdUpdatable& operator=(const NetdUpdatable&) = delete;
-    static NetdUpdatable* getInstance();
-
-    BpfHandler mBpfHandler;
-};
-
-extern NetdUpdatable* gNetdUpdatable;
-
-}  // namespace net
-}  // namespace android
\ No newline at end of file
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
index 6c4a021..e1c7b64 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -17,6 +17,7 @@
 package com.android.server.connectivity;
 
 import static android.net.INetd.IF_STATE_UP;
+import static android.net.INetd.PERMISSION_NETWORK;
 import static android.net.INetd.PERMISSION_SYSTEM;
 import static android.system.OsConstants.ETH_P_IP;
 import static android.system.OsConstants.ETH_P_IPV6;
@@ -386,9 +387,9 @@
     static int getFwmark(int netId) {
         // See union Fwmark in system/netd/include/Fwmark.h
         return (netId & 0xffff)
-                | 0x1 << 16  // protectedFromVpn: true
-                | 0x1 << 17  // explicitlySelected: true
-                | (PERMISSION_SYSTEM & 0x3) << 18;
+                | 0x1 << 16  // explicitlySelected: true
+                | 0x1 << 17  // protectedFromVpn: true
+                | ((PERMISSION_NETWORK | PERMISSION_SYSTEM) & 0x3) << 18;  // 2 permission bits = 3
     }
 
     @VisibleForTesting
diff --git a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
index bbb61cd..7646c19 100644
--- a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
@@ -94,10 +94,10 @@
     private static final int GOOGLE_DNS_4 = 0x08080808;  // 8.8.8.8
     private static final int NETID = 42;
 
-    // The test fwmark means: PERMISSION_SYSTEM (0x2), protectedFromVpn: true,
+    // The test fwmark means: PERMISSION_NETWORK | PERMISSION_SYSTEM (0x3), protectedFromVpn: true,
     // explicitlySelected: true, netid: 42. For bit field structure definition, see union Fwmark in
     // system/netd/include/Fwmark.h
-    private static final int MARK = 0xb002a;
+    private static final int MARK = 0xf002a;
 
     private static final String XLAT_LOCAL_IPV4ADDR_STRING = "192.0.0.46";
     private static final String XLAT_LOCAL_IPV6ADDR_STRING = "2001:db8:0:b11::464";
@@ -493,10 +493,10 @@
 
     @Test
     public void testGetFwmark() throws Exception {
-        assertEquals(0xb0064, ClatCoordinator.getFwmark(100));
-        assertEquals(0xb03e8, ClatCoordinator.getFwmark(1000));
-        assertEquals(0xb2710, ClatCoordinator.getFwmark(10000));
-        assertEquals(0xbffff, ClatCoordinator.getFwmark(65535));
+        assertEquals(0xf0064, ClatCoordinator.getFwmark(100));
+        assertEquals(0xf03e8, ClatCoordinator.getFwmark(1000));
+        assertEquals(0xf2710, ClatCoordinator.getFwmark(10000));
+        assertEquals(0xfffff, ClatCoordinator.getFwmark(65535));
     }
 
     @Test