Merge changes from topic "conn-diags-skipped"

* changes:
  Update ConnDiags CTS test to expect validation result SKIPPED.
  Report result SKIPPED in ConnDiags if the network is not validated.
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 56dc69c..2c1fd29 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -1069,10 +1069,11 @@
             throw new AssertionError("IP address array not valid IPv4 address!");
         }
 
+        final String protoStr = (key.l4proto == OsConstants.IPPROTO_TCP) ? "tcp" : "udp";
         final String ageStr = (value.lastUsed == 0) ? "-"
                 : String.format("%dms", (now - value.lastUsed) / 1_000_000);
-        return String.format("[%s] %d(%s) %s:%d -> %d(%s) %s:%d -> %s:%d [%s] %s",
-                key.dstMac, key.iif, getIfName(key.iif), src4, key.srcPort,
+        return String.format("%s [%s] %d(%s) %s:%d -> %d(%s) %s:%d -> %s:%d [%s] %s",
+                protoStr, key.dstMac, key.iif, getIfName(key.iif), src4, key.srcPort,
                 value.oif, getIfName(value.oif),
                 public4, publicPort, dst4, value.dstPort, value.ethDstMac, ageStr);
     }
@@ -1095,12 +1096,14 @@
 
         try (BpfMap<Tether4Key, Tether4Value> upstreamMap = mDeps.getBpfUpstream4Map();
                 BpfMap<Tether4Key, Tether4Value> downstreamMap = mDeps.getBpfDownstream4Map()) {
-            pw.println("IPv4 Upstream: [inDstMac] iif(iface) src -> nat -> dst [outDstMac] age");
+            pw.println("IPv4 Upstream: proto [inDstMac] iif(iface) src -> nat -> "
+                    + "dst [outDstMac] age");
             pw.increaseIndent();
             dumpIpv4ForwardingRuleMap(now, UPSTREAM, upstreamMap, pw);
             pw.decreaseIndent();
 
-            pw.println("IPv4 Downstream: [inDstMac] iif(iface) src -> nat -> dst [outDstMac] age");
+            pw.println("IPv4 Downstream: proto [inDstMac] iif(iface) src -> nat -> "
+                    + "dst [outDstMac] age");
             pw.increaseIndent();
             dumpIpv4ForwardingRuleMap(now, DOWNSTREAM, downstreamMap, pw);
             pw.decreaseIndent();
diff --git a/Tethering/tests/Android.bp b/Tethering/tests/Android.bp
index 8f31c57..72ca666 100644
--- a/Tethering/tests/Android.bp
+++ b/Tethering/tests/Android.bp
@@ -22,7 +22,7 @@
     name: "TetheringTestsJarJarRules",
     srcs: ["jarjar-rules.txt"],
     visibility: [
-        "//frameworks/base/packages/Tethering/tests:__subpackages__",
+        "//packages/modules/Connectivity/tests:__subpackages__",
         "//packages/modules/Connectivity/Tethering/tests:__subpackages__",
     ]
 }
diff --git a/Tethering/tests/integration/Android.bp b/Tethering/tests/integration/Android.bp
index e807613..b93a969 100644
--- a/Tethering/tests/integration/Android.bp
+++ b/Tethering/tests/integration/Android.bp
@@ -78,9 +78,27 @@
     compile_multilib: "both",
 }
 
+android_library {
+    name: "TetheringCoverageTestsLib",
+    min_sdk_version: "30",
+    static_libs: [
+        "NetdStaticLibTestsLib",
+        "NetworkStaticLibTestsLib",
+        "NetworkStackTestsLib",
+        "TetheringTestsLatestSdkLib",
+        "TetheringIntegrationTestsLatestSdkLib",
+    ],
+    jarjar_rules: ":TetheringTestsJarJarRules",
+    manifest: "AndroidManifest_coverage.xml",
+    visibility: [
+        "//packages/modules/Connectivity/tests:__subpackages__"
+    ],
+}
+
 // Special version of the tethering tests that includes all tests necessary for code coverage
 // purposes. This is currently the union of TetheringTests, TetheringIntegrationTests and
 // NetworkStackTests.
+// TODO: remove in favor of ConnectivityCoverageTests, which includes below tests and more
 android_test {
     name: "TetheringCoverageTests",
     platform_apis: true,
@@ -91,11 +109,7 @@
     defaults: ["libnetworkstackutilsjni_deps"],
     static_libs: [
         "modules-utils-native-coverage-listener",
-        "NetdStaticLibTestsLib",
-        "NetworkStaticLibTestsLib",
-        "NetworkStackTestsLib",
-        "TetheringTestsLatestSdkLib",
-        "TetheringIntegrationTestsLatestSdkLib",
+        "TetheringCoverageTestsLib",
     ],
     jni_libs: [
         // For mockito extended
diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp
index 192a540..f6e29cd 100644
--- a/Tethering/tests/unit/Android.bp
+++ b/Tethering/tests/unit/Android.bp
@@ -75,7 +75,6 @@
         "libstaticjvmtiagent",
         "libtetherutilsjni",
     ],
-    jarjar_rules: ":TetheringTestsJarJarRules",
 }
 
 // Library containing the unit tests. This is used by the coverage test target to pull in the
@@ -100,4 +99,5 @@
     ],
     defaults: ["TetheringTestsDefaults"],
     compile_multilib: "both",
+    jarjar_rules: ":TetheringTestsJarJarRules",
 }
diff --git a/framework/Android.bp b/framework/Android.bp
index 93ef3bf..ee3a5d5 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -97,8 +97,6 @@
         ],
     },
     impl_only_libs: [
-        // TODO (b/183097033) remove once module_current includes core_platform
-        "stable.core.platform.api.stubs",
         "framework-tethering.stubs.module_lib",
         "framework-wifi.stubs.module_lib",
         "net-utils-device-common",
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 7e2f688..14ec608 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -4939,7 +4939,7 @@
                 Log.e(TAG, "Can't set proxy properties", e);
             }
             // Must flush DNS cache as new network may have different DNS resolutions.
-            InetAddressCompat.clearDnsCache();
+            InetAddress.clearDnsCache();
             // Must flush socket pool as idle sockets will be bound to previous network and may
             // cause subsequent fetches to be performed on old network.
             NetworkEventDispatcher.getInstance().dispatchNetworkConfigurationChange();
diff --git a/framework/src/android/net/InetAddressCompat.java b/framework/src/android/net/InetAddressCompat.java
deleted file mode 100644
index 6b7e75c..0000000
--- a/framework/src/android/net/InetAddressCompat.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2021 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.
- */
-
-package android.net;
-
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
- * Compatibility utility for InetAddress core platform APIs.
- *
- * Connectivity has access to such APIs, but they are not part of the module_current stubs yet
- * (only core_current). Most stable core platform APIs are included manually in the connectivity
- * build rules, but because InetAddress is also part of the base java SDK that is earlier on the
- * classpath, the extra core platform APIs are not seen.
- *
- * TODO (b/183097033): remove this utility as soon as core_current is part of module_current
- * @hide
- */
-public class InetAddressCompat {
-
-    /**
-     * @see InetAddress#clearDnsCache()
-     */
-    public static void clearDnsCache() {
-        try {
-            InetAddress.class.getMethod("clearDnsCache").invoke(null);
-        } catch (InvocationTargetException e) {
-            if (e.getCause() instanceof RuntimeException) {
-                throw (RuntimeException) e.getCause();
-            }
-            throw new IllegalStateException("Unknown InvocationTargetException", e.getCause());
-        } catch (IllegalAccessException | NoSuchMethodException e) {
-            Log.wtf(InetAddressCompat.class.getSimpleName(), "Error clearing DNS cache", e);
-        }
-    }
-
-    /**
-     * @see InetAddress#getAllByNameOnNet(String, int)
-     */
-    public static InetAddress[] getAllByNameOnNet(String host, int netId) throws
-            UnknownHostException {
-        return (InetAddress[]) callGetByNameMethod("getAllByNameOnNet", host, netId);
-    }
-
-    /**
-     * @see InetAddress#getByNameOnNet(String, int)
-     */
-    public static InetAddress getByNameOnNet(String host, int netId) throws
-            UnknownHostException {
-        return (InetAddress) callGetByNameMethod("getByNameOnNet", host, netId);
-    }
-
-    private static Object callGetByNameMethod(String method, String host, int netId)
-            throws UnknownHostException {
-        try {
-            return InetAddress.class.getMethod(method, String.class, int.class)
-                    .invoke(null, host, netId);
-        } catch (InvocationTargetException e) {
-            if (e.getCause() instanceof UnknownHostException) {
-                throw (UnknownHostException) e.getCause();
-            }
-            if (e.getCause() instanceof RuntimeException) {
-                throw (RuntimeException) e.getCause();
-            }
-            throw new IllegalStateException("Unknown InvocationTargetException", e.getCause());
-        } catch (IllegalAccessException | NoSuchMethodException e) {
-            Log.wtf(InetAddressCompat.class.getSimpleName(), "Error calling " + method, e);
-            throw new IllegalStateException("Error querying via " + method, e);
-        }
-    }
-}
diff --git a/framework/src/android/net/Network.java b/framework/src/android/net/Network.java
index 1f49033..b3770ea 100644
--- a/framework/src/android/net/Network.java
+++ b/framework/src/android/net/Network.java
@@ -142,7 +142,7 @@
      * @throws UnknownHostException if the address lookup fails.
      */
     public InetAddress[] getAllByName(String host) throws UnknownHostException {
-        return InetAddressCompat.getAllByNameOnNet(host, getNetIdForResolv());
+        return InetAddress.getAllByNameOnNet(host, getNetIdForResolv());
     }
 
     /**
@@ -155,7 +155,7 @@
      *             if the address lookup fails.
      */
     public InetAddress getByName(String host) throws UnknownHostException {
-        return InetAddressCompat.getByNameOnNet(host, getNetIdForResolv());
+        return InetAddress.getByNameOnNet(host, getNetIdForResolv());
     }
 
     /**
diff --git a/service/Android.bp b/service/Android.bp
index 841e189..848de12 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -57,9 +57,6 @@
         ":net-module-utils-srcs",
     ],
     libs: [
-        // TODO (b/183097033) remove once system_server_current includes core_current
-        "stable.core.platform.api.stubs",
-        "android_system_server_stubs_current",
         "framework-annotations-lib",
         "framework-connectivity.impl",
         "framework-tethering.stubs.module_lib",
@@ -112,3 +109,9 @@
         "com.android.tethering",
     ],
 }
+
+filegroup {
+    name: "connectivity-jarjar-rules",
+    srcs: ["jarjar-rules.txt"],
+    visibility: ["//packages/modules/Connectivity:__subpackages__"],
+}
diff --git a/service/ServiceConnectivityResources/res/values/config.xml b/service/ServiceConnectivityResources/res/values/config.xml
index 70ddb9a..bf32ad5 100644
--- a/service/ServiceConnectivityResources/res/values/config.xml
+++ b/service/ServiceConnectivityResources/res/values/config.xml
@@ -111,4 +111,7 @@
          notification that can be dismissed. -->
     <bool name="config_ongoingSignInNotification">false</bool>
 
+    <!-- Whether to cancel network notifications automatically when tapped -->
+    <bool name="config_autoCancelNetworkNotifications">true</bool>
+
 </resources>
diff --git a/service/ServiceConnectivityResources/res/values/overlayable.xml b/service/ServiceConnectivityResources/res/values/overlayable.xml
index fd23566..6ac6a0e 100644
--- a/service/ServiceConnectivityResources/res/values/overlayable.xml
+++ b/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -31,7 +31,9 @@
             <item type="integer" name="config_networkNotifySwitchType"/>
             <item type="array" name="config_networkNotifySwitches"/>
             <item type="bool" name="config_ongoingSignInNotification"/>
-
+            <item type="bool" name="config_autoCancelNetworkNotifications"/>
+            <item type="drawable" name="stat_notify_wifi_in_range"/>
+            <item type="drawable" name="stat_notify_rssi_in_range"/>
         </policy>
     </overlayable>
 </resources>
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 0c4b6aa..1a9ff25 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -6307,7 +6307,8 @@
                 callingAttributionTag);
         if (VDBG) log("pendingListenForNetwork for " + nri);
 
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
+        mHandler.sendMessage(mHandler.obtainMessage(
+                    EVENT_REGISTER_NETWORK_LISTENER_WITH_INTENT, nri));
     }
 
     /** Returns the next Network provider ID. */
@@ -10044,7 +10045,7 @@
         // - The request for the mobile network preferred.
         // - The request for the default network, for fallback.
         requests.add(createDefaultInternetRequestForTransport(
-                TRANSPORT_CELLULAR, NetworkRequest.Type.LISTEN));
+                TRANSPORT_CELLULAR, NetworkRequest.Type.REQUEST));
         requests.add(createDefaultInternetRequestForTransport(
                 TYPE_NONE, NetworkRequest.Type.TRACK_DEFAULT));
         final Set<UidRange> ranges = new ArraySet<>();
diff --git a/service/src/com/android/server/connectivity/KeepaliveTracker.java b/service/src/com/android/server/connectivity/KeepaliveTracker.java
index acf39f0..ee1538a 100644
--- a/service/src/com/android/server/connectivity/KeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/KeepaliveTracker.java
@@ -373,12 +373,10 @@
                     Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
                 }
             }
-            // Ignore the case when the network disconnects immediately after stop() has been
-            // called and the keepalive code is waiting for the response from the modem. This
-            // might happen when the caller listens for a lower-layer network disconnect
-            // callback and stop the keepalive at that time. But the stop() races with the
-            // stop() generated in ConnectivityService network disconnection code path.
-            if (mStartedState == STOPPING && reason == ERROR_INVALID_NETWORK) return;
+            // To prevent races from re-entrance of stop(), return if the state is already stopping.
+            // This might happen if multiple event sources stop keepalive in a short time. Such as
+            // network disconnect after user calls stop(), or tear down socket after binder died.
+            if (mStartedState == STOPPING) return;
 
             // Store the reason of stopping, and report it after the keepalive is fully stopped.
             if (mStopReason != ERROR_STOP_REASON_UNINITIALIZED) {
diff --git a/service/src/com/android/server/connectivity/NetworkNotificationManager.java b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
index 3dc79c5..ae98d92 100644
--- a/service/src/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
@@ -274,7 +274,7 @@
                 .setWhen(System.currentTimeMillis())
                 .setShowWhen(notifyType == NotificationType.NETWORK_SWITCH)
                 .setSmallIcon(icon)
-                .setAutoCancel(true)
+                .setAutoCancel(r.getBoolean(R.bool.config_autoCancelNetworkNotifications))
                 .setTicker(title)
                 .setColor(mContext.getColor(android.R.color.system_notification_accent_color))
                 .setContentTitle(title)
diff --git a/service/src/com/android/server/connectivity/OsCompat.java b/service/src/com/android/server/connectivity/OsCompat.java
deleted file mode 100644
index 57e3dcd..0000000
--- a/service/src/com/android/server/connectivity/OsCompat.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2021 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.
- */
-
-package com.android.server.connectivity;
-
-import android.system.ErrnoException;
-import android.system.Os;
-
-import java.io.FileDescriptor;
-
-/**
- * Compatibility utility for android.system.Os core platform APIs.
- *
- * Connectivity has access to such APIs, but they are not part of the module_current stubs yet
- * (only core_current). Most stable core platform APIs are included manually in the connectivity
- * build rules, but because Os is also part of the base java SDK that is earlier on the
- * classpath, the extra core platform APIs are not seen.
- *
- * TODO (b/157639992, b/183097033): remove as soon as core_current is part of system_server_current
- * @hide
- */
-public class OsCompat {
-    // This value should be correct on all architectures supported by Android, but hardcoding ioctl
-    // numbers should be avoided.
-    /**
-     * @see android.system.OsConstants#TIOCOUTQ
-     */
-    public static final int TIOCOUTQ = 0x5411;
-
-    /**
-     * @see android.system.Os#getsockoptInt(FileDescriptor, int, int)
-     */
-    public static int getsockoptInt(FileDescriptor fd, int level, int option) throws
-            ErrnoException {
-        try {
-            return (int) Os.class.getMethod(
-                    "getsockoptInt", FileDescriptor.class, int.class, int.class)
-                    .invoke(null, fd, level, option);
-        } catch (ReflectiveOperationException e) {
-            if (e.getCause() instanceof ErrnoException) {
-                throw (ErrnoException) e.getCause();
-            }
-            throw new IllegalStateException("Error calling getsockoptInt", e);
-        }
-    }
-
-    /**
-     * @see android.system.Os#ioctlInt(FileDescriptor, int)
-     */
-    public static int ioctlInt(FileDescriptor fd, int cmd) throws
-            ErrnoException {
-        try {
-            return (int) Os.class.getMethod(
-                    "ioctlInt", FileDescriptor.class, int.class).invoke(null, fd, cmd);
-        } catch (ReflectiveOperationException e) {
-            if (e.getCause() instanceof ErrnoException) {
-                throw (ErrnoException) e.getCause();
-            }
-            throw new IllegalStateException("Error calling ioctlInt", e);
-        }
-    }
-}
diff --git a/service/src/com/android/server/connectivity/TcpKeepaliveController.java b/service/src/com/android/server/connectivity/TcpKeepaliveController.java
index 73f3475..c480594 100644
--- a/service/src/com/android/server/connectivity/TcpKeepaliveController.java
+++ b/service/src/com/android/server/connectivity/TcpKeepaliveController.java
@@ -27,8 +27,7 @@
 import static android.system.OsConstants.IPPROTO_TCP;
 import static android.system.OsConstants.IP_TOS;
 import static android.system.OsConstants.IP_TTL;
-
-import static com.android.server.connectivity.OsCompat.TIOCOUTQ;
+import static android.system.OsConstants.TIOCOUTQ;
 
 import android.annotation.NonNull;
 import android.net.InvalidPacketException;
@@ -176,10 +175,10 @@
             }
             // Query write sequence number from SEND_QUEUE.
             Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_SEND_QUEUE);
-            tcpDetails.seq = OsCompat.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
+            tcpDetails.seq = Os.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
             // Query read sequence number from RECV_QUEUE.
             Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_RECV_QUEUE);
-            tcpDetails.ack = OsCompat.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
+            tcpDetails.ack = Os.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
             // Switch to NO_QUEUE to prevent illegal socket read/write in repair mode.
             Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_NO_QUEUE);
             // Finally, check if socket is still idle. TODO : this check needs to move to
@@ -199,9 +198,9 @@
             tcpDetails.rcvWndScale = trw.rcvWndScale;
             if (tcpDetails.srcAddress.length == 4 /* V4 address length */) {
                 // Query TOS.
-                tcpDetails.tos = OsCompat.getsockoptInt(fd, IPPROTO_IP, IP_TOS);
+                tcpDetails.tos = Os.getsockoptInt(fd, IPPROTO_IP, IP_TOS);
                 // Query TTL.
-                tcpDetails.ttl = OsCompat.getsockoptInt(fd, IPPROTO_IP, IP_TTL);
+                tcpDetails.ttl = Os.getsockoptInt(fd, IPPROTO_IP, IP_TTL);
             }
         } catch (ErrnoException e) {
             Log.e(TAG, "Exception reading TCP state from socket", e);
@@ -306,7 +305,7 @@
 
     private static boolean isReceiveQueueEmpty(FileDescriptor fd)
             throws ErrnoException {
-        final int result = OsCompat.ioctlInt(fd, SIOCINQ);
+        final int result = Os.ioctlInt(fd, SIOCINQ);
         if (result != 0) {
             Log.e(TAG, "Read queue has data");
             return false;
@@ -316,7 +315,7 @@
 
     private static boolean isSendQueueEmpty(FileDescriptor fd)
             throws ErrnoException {
-        final int result = OsCompat.ioctlInt(fd, SIOCOUTQ);
+        final int result = Os.ioctlInt(fd, SIOCOUTQ);
         if (result != 0) {
             Log.e(TAG, "Write queue has data");
             return false;
diff --git a/tests/common/Android.bp b/tests/common/Android.bp
index 8be8ea1..e1fab09 100644
--- a/tests/common/Android.bp
+++ b/tests/common/Android.bp
@@ -43,6 +43,23 @@
     ],
 }
 
+// Connectivity coverage tests combines Tethering and Connectivity tests, each with their
+// respective jarjar rules applied.
+// Some tests may be duplicated (in particular static lib tests), as they need to be run under both
+// jarjared packages to cover both usages.
+android_library {
+    name: "ConnectivityCoverageTestsLib",
+    min_sdk_version: "30",
+    static_libs: [
+        "FrameworksNetTestsLib",
+        "NetdStaticLibTestsLib",
+        "NetworkStaticLibTestsLib",
+    ],
+    jarjar_rules: ":connectivity-jarjar-rules",
+    manifest: "AndroidManifest_coverage.xml",
+    visibility: ["//visibility:private"],
+}
+
 android_test {
     name: "ConnectivityCoverageTests",
     // Tethering started on SDK 30
@@ -62,13 +79,9 @@
         // (some tests would fail).
         // TODO: consider removing extended mockito usage in tests that use it, for performance
         "mockito-target-extended-minus-junit4",
-        "FrameworksNetTestsLib",
         "modules-utils-native-coverage-listener",
-        "NetdStaticLibTestsLib",
-        "NetworkStaticLibTestsLib",
-        "NetworkStackTestsLib",
-        "TetheringTestsLatestSdkLib",
-        "TetheringIntegrationTestsLatestSdkLib",
+        "ConnectivityCoverageTestsLib",
+        "TetheringCoverageTestsLib",
     ],
     jni_libs: [
         // For mockito extended
diff --git a/tests/common/java/android/net/metrics/IpConnectivityLogTest.java b/tests/common/java/android/net/metrics/IpConnectivityLogTest.java
index d4780d3..ab97f2d 100644
--- a/tests/common/java/android/net/metrics/IpConnectivityLogTest.java
+++ b/tests/common/java/android/net/metrics/IpConnectivityLogTest.java
@@ -19,6 +19,8 @@
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
+import static com.android.net.module.util.NetworkCapabilitiesUtils.unpackBits;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.timeout;
@@ -31,8 +33,6 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.util.BitUtils;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -49,7 +49,7 @@
 @SmallTest
 public class IpConnectivityLogTest {
     private static final int FAKE_NET_ID = 100;
-    private static final int[] FAKE_TRANSPORT_TYPES = BitUtils.unpackBits(TRANSPORT_WIFI);
+    private static final int[] FAKE_TRANSPORT_TYPES = unpackBits(TRANSPORT_WIFI);
     private static final long FAKE_TIME_STAMP = System.currentTimeMillis();
     private static final String FAKE_INTERFACE_NAME = "test";
     private static final IpReachabilityEvent FAKE_EV =
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyManagerTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyManagerTest.java
index ddc5fd4..ad7ec9e 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyManagerTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyManagerTest.java
@@ -23,6 +23,8 @@
 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isUidNetworkingBlocked;
 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isUidRestrictedOnMeteredNetworks;
 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -79,6 +81,7 @@
         assertFalse(isUidNetworkingBlocked(mUid, NON_METERED)); // Match NTWK_ALLOWED_NON_METERED
     }
 
+    @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE})
     @Test
     public void testIsUidNetworkingBlocked_withSystemUid() throws Exception {
         // Refer to NetworkPolicyManagerService#isUidNetworkingBlockedInternal(), this test is to
@@ -103,6 +106,7 @@
         }
     }
 
+    @RequiredProperties({DATA_SAVER_MODE})
     @Test
     public void testIsUidNetworkingBlocked_withDataSaverMode() throws Exception {
         // Refer to NetworkPolicyManagerService#isUidNetworkingBlockedInternal(), this test is to
@@ -182,6 +186,7 @@
         }
     }
 
+    @RequiredProperties({BATTERY_SAVER_MODE})
     @Test
     public void testIsUidNetworkingBlocked_withPowerSaverMode() throws Exception {
         // Refer to NetworkPolicyManagerService#isUidNetworkingBlockedInternal(), this test is to
@@ -209,6 +214,7 @@
         }
     }
 
+    @RequiredProperties({DATA_SAVER_MODE})
     @Test
     public void testIsUidRestrictedOnMeteredNetworks() throws Exception {
         try {
diff --git a/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java b/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
index a54fd64..86642ea 100644
--- a/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
@@ -35,6 +35,7 @@
 import android.os.Build;
 import android.os.connectivity.CellularBatteryStats;
 import android.os.connectivity.WifiBatteryStats;
+import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 
 import androidx.test.runner.AndroidJUnit4;
@@ -80,6 +81,7 @@
     }
 
     @Test
+    @AppModeFull(reason = "Cannot get CHANGE_NETWORK_STATE to request wifi/cell in instant mode")
     @SkipPresubmit(reason = "Virtual hardware does not support wifi battery stats")
     public void testReportNetworkInterfaceForTransports() throws Exception {
         try {
@@ -132,6 +134,7 @@
     }
 
     @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+    @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
     @Test
     public void testReportNetworkInterfaceForTransports_throwsSecurityException()
             throws Exception {
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index a19c7a6..cd5281f 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -72,6 +72,7 @@
 import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity;
 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
 import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_LOCKDOWN_VPN;
 import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_NONE;
 import static com.android.testutils.MiscAsserts.assertThrows;
@@ -499,6 +500,7 @@
     @Test
     public void testGetAllNetworkStateSnapshots()
             throws InterruptedException {
+        assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
         // Make sure cell is active to retrieve IMSI for verification in later step.
         final Network cellNetwork = mCtsNetUtils.connectToCell();
         final String subscriberId = getSubscriberIdForCellNetwork(cellNetwork);
@@ -936,8 +938,8 @@
             // noticeably flaky.
             Thread.sleep(NO_CALLBACK_TIMEOUT_MS);
 
-            // TODO: BUG (b/189868426): this should also apply to listens
-            if (!useListen) {
+            // For R- frameworks, listens will receive duplicated callbacks. See b/189868426.
+            if (isAtLeastS() || !useListen) {
                 assertEquals("PendingIntent should only be received once", 1, receivedCount.get());
             }
         } finally {
@@ -952,8 +954,8 @@
             boolean useListen) {
         assertArrayEquals(filed.networkCapabilities.getCapabilities(),
                 broadcasted.networkCapabilities.getCapabilities());
-        // TODO: BUG (b/189868426): this should also apply to listens
-        if (useListen) return;
+        // For R- frameworks, listens will receive duplicated callbacks. See b/189868426.
+        if (!isAtLeastS() && useListen) return;
         assertArrayEquals(filed.networkCapabilities.getTransportTypes(),
                 broadcasted.networkCapabilities.getTransportTypes());
     }
@@ -1077,7 +1079,7 @@
         final Matcher m = Pattern.compile("^" + ssid + ";(true|false|none)$",
                 Pattern.MULTILINE | Pattern.UNIX_LINES).matcher(policyString);
         if (!m.find()) {
-            fail("Unexpected format from cmd netpolicy");
+            fail("Unexpected format from cmd netpolicy, policyString = " + policyString);
         }
         return m.group(1);
     }
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index ccc9416..7c380e3 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -101,6 +101,7 @@
 import com.android.testutils.TestableNetworkCallback
 import org.junit.After
 import org.junit.Assert.assertArrayEquals
+import org.junit.Assume.assumeFalse
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -1034,6 +1035,9 @@
 
     @Test
     fun testQosCallbackRegisterWithUnregister() {
+        // Instant apps can't bind sockets to localhost
+        // TODO: use @AppModeFull when supported by DevSdkIgnoreRunner
+        assumeFalse(realContext.packageManager.isInstantApp())
         val (agent, socket) = setupForQosCallbackTesting()
 
         val qosCallback = TestableQosCallback()
@@ -1060,6 +1064,9 @@
 
     @Test
     fun testQosCallbackOnQosSession() {
+        // Instant apps can't bind sockets to localhost
+        // TODO: use @AppModeFull when supported by DevSdkIgnoreRunner
+        assumeFalse(realContext.packageManager.isInstantApp())
         val (agent, socket) = setupForQosCallbackTesting()
         val qosCallback = TestableQosCallback()
         Executors.newSingleThreadExecutor().let { executor ->
@@ -1104,6 +1111,9 @@
 
     @Test
     fun testQosCallbackOnError() {
+        // Instant apps can't bind sockets to localhost
+        // TODO: use @AppModeFull when supported by DevSdkIgnoreRunner
+        assumeFalse(realContext.packageManager.isInstantApp())
         val (agent, socket) = setupForQosCallbackTesting()
         val qosCallback = TestableQosCallback()
         Executors.newSingleThreadExecutor().let { executor ->
@@ -1142,6 +1152,9 @@
 
     @Test
     fun testQosCallbackIdsAreMappedCorrectly() {
+        // Instant apps can't bind sockets to localhost
+        // TODO: use @AppModeFull when supported by DevSdkIgnoreRunner
+        assumeFalse(realContext.packageManager.isInstantApp())
         val (agent, socket) = setupForQosCallbackTesting()
         val qosCallback1 = TestableQosCallback()
         val qosCallback2 = TestableQosCallback()
@@ -1182,6 +1195,9 @@
 
     @Test
     fun testQosCallbackWhenNetworkReleased() {
+        // Instant apps can't bind sockets to localhost
+        // TODO: use @AppModeFull when supported by DevSdkIgnoreRunner
+        assumeFalse(realContext.packageManager.isInstantApp())
         val (agent, socket) = setupForQosCallbackTesting()
         Executors.newSingleThreadExecutor().let { executor ->
             try {
diff --git a/tests/cts/net/src/android/net/cts/PacProxyManagerTest.java b/tests/cts/net/src/android/net/cts/PacProxyManagerTest.java
index 7d5e9ff..a20f1cc 100644
--- a/tests/cts/net/src/android/net/cts/PacProxyManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/PacProxyManagerTest.java
@@ -35,6 +35,7 @@
 import android.net.ProxyInfo;
 import android.net.Uri;
 import android.os.Build;
+import android.platform.test.annotations.AppModeFull;
 import android.util.Log;
 import android.util.Range;
 
@@ -145,6 +146,7 @@
         }
     }
 
+    @AppModeFull(reason = "Instant apps can't bind sockets to localhost for a test proxy server")
     @Test
     public void testSetCurrentProxyScriptUrl() throws Exception {
         // Register a PacProxyInstalledListener
diff --git a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
index 0a5e506..bd1b74a 100644
--- a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
+++ b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
@@ -361,6 +361,7 @@
     @Test
     public void testRequestLatestEntitlementResult() throws Exception {
         assumeTrue(mTM.isTetheringSupported());
+        assumeTrue(mPm.hasSystemFeature(FEATURE_TELEPHONY));
         // Verify that requestLatestTetheringEntitlementResult() can get entitlement
         // result(TETHER_ERROR_ENTITLEMENT_UNKNOWN due to invalid downstream type) via listener.
         assertEntitlementResult(listener -> mTM.requestLatestTetheringEntitlementResult(
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 78fbc4a..6d7d3d2 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -5450,6 +5450,8 @@
             // the follow-up network disconnection will be processed first.
             mWiFiNetworkAgent.setKeepaliveResponseDelay(3 * TIMEOUT_MS);
             ka.stop();
+            // Call stop() twice shouldn't result in crash, b/182586681.
+            ka.stop();
 
             // Make sure the stop has been processed. Wait for executor idle is needed to prevent
             // flaky since the actual stop call to the service is delegated to executor thread.
@@ -5747,37 +5749,59 @@
     @Test
     public void testNetworkCallbackMaximum() throws Exception {
         final int MAX_REQUESTS = 100;
-        final int CALLBACKS = 89;
-        final int INTENTS = 11;
+        final int CALLBACKS = 87;
+        final int DIFF_INTENTS = 10;
+        final int SAME_INTENTS = 10;
         final int SYSTEM_ONLY_MAX_REQUESTS = 250;
-        assertEquals(MAX_REQUESTS, CALLBACKS + INTENTS);
+        // Assert 1 (Default request filed before testing) + CALLBACKS + DIFF_INTENTS +
+        // 1 (same intent) = MAX_REQUESTS - 1, since the capacity is MAX_REQUEST - 1.
+        assertEquals(MAX_REQUESTS - 1, 1 + CALLBACKS + DIFF_INTENTS + 1);
 
         NetworkRequest networkRequest = new NetworkRequest.Builder().build();
         ArrayList<Object> registered = new ArrayList<>();
 
-        int j = 0;
-        while (j++ < CALLBACKS / 2) {
-            NetworkCallback cb = new NetworkCallback();
-            mCm.requestNetwork(networkRequest, cb);
+        for (int j = 0; j < CALLBACKS; j++) {
+            final NetworkCallback cb = new NetworkCallback();
+            if (j < CALLBACKS / 2) {
+                mCm.requestNetwork(networkRequest, cb);
+            } else {
+                mCm.registerNetworkCallback(networkRequest, cb);
+            }
             registered.add(cb);
         }
-        while (j++ < CALLBACKS) {
-            NetworkCallback cb = new NetworkCallback();
-            mCm.registerNetworkCallback(networkRequest, cb);
-            registered.add(cb);
+
+        // Since ConnectivityService will de-duplicate the request with the same intent,
+        // register multiple times does not really increase multiple requests.
+        final PendingIntent same_pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
+                new Intent("same"), FLAG_IMMUTABLE);
+        for (int j = 0; j < SAME_INTENTS; j++) {
+            mCm.registerNetworkCallback(networkRequest, same_pi);
+            // Wait for the requests with the same intent to be de-duplicated. Because
+            // ConnectivityService side incrementCountOrThrow in binder, decrementCount in handler
+            // thread, waitForIdle is needed to ensure decrementCount being invoked for same intent
+            // requests before doing further tests.
+            waitForIdle();
         }
-        j = 0;
-        while (j++ < INTENTS / 2) {
-            final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
-                    new Intent("a" + j), FLAG_IMMUTABLE);
-            mCm.requestNetwork(networkRequest, pi);
-            registered.add(pi);
+        for (int j = 0; j < SAME_INTENTS; j++) {
+            mCm.requestNetwork(networkRequest, same_pi);
+            // Wait for the requests with the same intent to be de-duplicated.
+            // Refer to the reason above.
+            waitForIdle();
         }
-        while (j++ < INTENTS) {
-            final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
-                    new Intent("b" + j), FLAG_IMMUTABLE);
-            mCm.registerNetworkCallback(networkRequest, pi);
-            registered.add(pi);
+        registered.add(same_pi);
+
+        for (int j = 0; j < DIFF_INTENTS; j++) {
+            if (j < DIFF_INTENTS / 2) {
+                final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
+                        new Intent("a" + j), FLAG_IMMUTABLE);
+                mCm.requestNetwork(networkRequest, pi);
+                registered.add(pi);
+            } else {
+                final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
+                        new Intent("b" + j), FLAG_IMMUTABLE);
+                mCm.registerNetworkCallback(networkRequest, pi);
+                registered.add(pi);
+            }
         }
 
         // Test that the limit is enforced when MAX_REQUESTS simultaneous requests are added.
@@ -5827,10 +5851,10 @@
 
         for (Object o : registered) {
             if (o instanceof NetworkCallback) {
-                mCm.unregisterNetworkCallback((NetworkCallback)o);
+                mCm.unregisterNetworkCallback((NetworkCallback) o);
             }
             if (o instanceof PendingIntent) {
-                mCm.unregisterNetworkCallback((PendingIntent)o);
+                mCm.unregisterNetworkCallback((PendingIntent) o);
             }
         }
         waitForIdle();
@@ -13414,43 +13438,43 @@
     }
 
     @Test
-    public void testSetMobileDataPreferredUids_noIssueToFactory() throws Exception {
-        // First set mobile data preferred uid to create a multi-layer requests: 1. listen for
+    public void testMultilayerRequestsOfSetMobileDataPreferredUids() throws Exception {
+        // First set mobile data preferred uid to create a multi-layer requests: 1. request for
         // cellular, 2. track the default network for fallback.
         setAndUpdateMobileDataPreferredUids(
                 Set.of(PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID)));
 
         final HandlerThread handlerThread = new HandlerThread("MockFactory");
         handlerThread.start();
-        NetworkCapabilities internetFilter = new NetworkCapabilities()
+        final NetworkCapabilities cellFilter = new NetworkCapabilities()
+                .addTransportType(TRANSPORT_CELLULAR)
                 .addCapability(NET_CAPABILITY_INTERNET)
                 .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
-        final MockNetworkFactory internetFactory = new MockNetworkFactory(handlerThread.getLooper(),
-                mServiceContext, "internetFactory", internetFilter, mCsHandlerThread);
-        internetFactory.setScoreFilter(40);
+        final MockNetworkFactory cellFactory = new MockNetworkFactory(handlerThread.getLooper(),
+                mServiceContext, "cellFactory", cellFilter, mCsHandlerThread);
+        cellFactory.setScoreFilter(40);
 
         try {
-            internetFactory.register();
-            // Default internet request only. The first request is listen for cellular network,
-            // which is never sent to factories (it's a LISTEN, not requestable). The second
-            // fallback request is TRACK_DEFAULT which is also not sent to factories.
-            internetFactory.expectRequestAdds(1);
-            internetFactory.assertRequestCountEquals(1);
+            cellFactory.register();
+            // Default internet request and the mobile data preferred request.
+            cellFactory.expectRequestAdds(2);
+            cellFactory.assertRequestCountEquals(2);
 
-            mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
-            mCellNetworkAgent.connect(true);
+            mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+            mWiFiNetworkAgent.connect(true);
 
-            // The internet factory however is outscored, and should lose its requests.
-            internetFactory.expectRequestRemove();
-            internetFactory.assertRequestCountEquals(0);
+            // The cellFactory however is outscored, and should lose default internet request.
+            // But it should still see mobile data preferred request.
+            cellFactory.expectRequestRemove();
+            cellFactory.assertRequestCountEquals(1);
 
-            mCellNetworkAgent.disconnect();
+            mWiFiNetworkAgent.disconnect();
             // The network satisfying the default internet request has disconnected, so the
-            // internetFactory sees the default request again.
-            internetFactory.expectRequestAdds(1);
-            internetFactory.assertRequestCountEquals(1);
+            // cellFactory sees the default internet requests again.
+            cellFactory.expectRequestAdd();
+            cellFactory.assertRequestCountEquals(2);
         } finally {
-            internetFactory.terminate();
+            cellFactory.terminate();
             handlerThread.quitSafely();
         }
     }
diff --git a/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index c924535..07deeef 100644
--- a/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.connectivity;
 
+import static android.app.Notification.FLAG_AUTO_CANCEL;
 import static android.app.Notification.FLAG_ONGOING_EVENT;
 
 import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.LOST_INTERNET;
@@ -80,6 +81,8 @@
 
     private static final String TEST_SSID = "Test SSID";
     private static final String TEST_EXTRA_INFO = "extra";
+    private static final int TEST_NOTIF_ID = 101;
+    private static final String TEST_NOTIF_TAG = NetworkNotificationManager.tagFor(TEST_NOTIF_ID);
     static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities();
     static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities();
     static final NetworkCapabilities VPN_CAPABILITIES = new NetworkCapabilities();
@@ -141,6 +144,7 @@
         }
         when(mResources.getStringArray(R.array.network_switch_type_name))
             .thenReturn(transportNames);
+        when(mResources.getBoolean(R.bool.config_autoCancelNetworkNotifications)).thenReturn(true);
 
         mManager = new NetworkNotificationManager(mCtx, mTelephonyManager);
     }
@@ -237,57 +241,65 @@
         verify(mNotificationManager, never()).notify(any(), anyInt(), any());
     }
 
-    private void assertNotification(NotificationType type, boolean ongoing) {
-        final int id = 101;
-        final String tag = NetworkNotificationManager.tagFor(id);
+    private void assertNotification(NotificationType type, boolean ongoing, boolean autoCancel) {
         final ArgumentCaptor<Notification> noteCaptor = ArgumentCaptor.forClass(Notification.class);
-        mManager.showNotification(id, type, mWifiNai, mCellNai, null, false);
-        verify(mNotificationManager, times(1)).notify(eq(tag), eq(type.eventId),
+        mManager.showNotification(TEST_NOTIF_ID, type, mWifiNai, mCellNai, null, false);
+        verify(mNotificationManager, times(1)).notify(eq(TEST_NOTIF_TAG), eq(type.eventId),
                 noteCaptor.capture());
 
         assertEquals("Notification ongoing flag should be " + (ongoing ? "set" : "unset"),
                 ongoing, (noteCaptor.getValue().flags & FLAG_ONGOING_EVENT) != 0);
+        assertEquals("Notification autocancel flag should be " + (autoCancel ? "set" : "unset"),
+                autoCancel, (noteCaptor.getValue().flags & FLAG_AUTO_CANCEL) != 0);
     }
 
     @Test
     public void testDuplicatedNotificationsNoInternetThenSignIn() {
-        final int id = 101;
-        final String tag = NetworkNotificationManager.tagFor(id);
-
         // Show first NO_INTERNET
-        assertNotification(NO_INTERNET, false /* ongoing */);
+        assertNotification(NO_INTERNET, false /* ongoing */, true /* autoCancel */);
 
         // Captive portal detection triggers SIGN_IN a bit later, clearing the previous NO_INTERNET
-        assertNotification(SIGN_IN, false /* ongoing */);
-        verify(mNotificationManager, times(1)).cancel(eq(tag), eq(NO_INTERNET.eventId));
+        assertNotification(SIGN_IN, false /* ongoing */, true /* autoCancel */);
+        verify(mNotificationManager, times(1)).cancel(eq(TEST_NOTIF_TAG), eq(NO_INTERNET.eventId));
 
         // Network disconnects
-        mManager.clearNotification(id);
-        verify(mNotificationManager, times(1)).cancel(eq(tag), eq(SIGN_IN.eventId));
+        mManager.clearNotification(TEST_NOTIF_ID);
+        verify(mNotificationManager, times(1)).cancel(eq(TEST_NOTIF_TAG), eq(SIGN_IN.eventId));
     }
 
     @Test
     public void testOngoingSignInNotification() {
         doReturn(true).when(mResources).getBoolean(R.bool.config_ongoingSignInNotification);
-        final int id = 101;
-        final String tag = NetworkNotificationManager.tagFor(id);
 
         // Show first NO_INTERNET
-        assertNotification(NO_INTERNET, false /* ongoing */);
+        assertNotification(NO_INTERNET, false /* ongoing */, true /* autoCancel */);
 
         // Captive portal detection triggers SIGN_IN a bit later, clearing the previous NO_INTERNET
-        assertNotification(SIGN_IN, true /* ongoing */);
-        verify(mNotificationManager, times(1)).cancel(eq(tag), eq(NO_INTERNET.eventId));
+        assertNotification(SIGN_IN, true /* ongoing */, true /* autoCancel */);
+        verify(mNotificationManager, times(1)).cancel(eq(TEST_NOTIF_TAG), eq(NO_INTERNET.eventId));
 
         // Network disconnects
-        mManager.clearNotification(id);
-        verify(mNotificationManager, times(1)).cancel(eq(tag), eq(SIGN_IN.eventId));
+        mManager.clearNotification(TEST_NOTIF_ID);
+        verify(mNotificationManager, times(1)).cancel(eq(TEST_NOTIF_TAG), eq(SIGN_IN.eventId));
+    }
+
+    @Test
+    public void testNoAutoCancelNotification() {
+        doReturn(false).when(mResources).getBoolean(R.bool.config_autoCancelNetworkNotifications);
+
+        // Show NO_INTERNET, then SIGN_IN
+        assertNotification(NO_INTERNET, false /* ongoing */, false /* autoCancel */);
+        assertNotification(SIGN_IN, false /* ongoing */, false /* autoCancel */);
+        verify(mNotificationManager, times(1)).cancel(eq(TEST_NOTIF_TAG), eq(NO_INTERNET.eventId));
+
+        mManager.clearNotification(TEST_NOTIF_ID);
+        verify(mNotificationManager, times(1)).cancel(eq(TEST_NOTIF_TAG), eq(SIGN_IN.eventId));
     }
 
     @Test
     public void testDuplicatedNotificationsSignInThenNoInternet() {
-        final int id = 101;
-        final String tag = NetworkNotificationManager.tagFor(id);
+        final int id = TEST_NOTIF_ID;
+        final String tag = TEST_NOTIF_TAG;
 
         // Show first SIGN_IN
         mManager.showNotification(id, SIGN_IN, mWifiNai, mCellNai, null, false);
@@ -306,8 +318,8 @@
 
     @Test
     public void testClearNotificationByType() {
-        final int id = 101;
-        final String tag = NetworkNotificationManager.tagFor(id);
+        final int id = TEST_NOTIF_ID;
+        final String tag = TEST_NOTIF_TAG;
 
         // clearNotification(int id, NotificationType notifyType) will check if given type is equal
         // to previous type or not. If they are equal then clear the notification; if they are not