Merge "Add a CTS test for private DNS on VPNs." into qt-dev am: 17311d9af6 am: fe44dd21ba am: 523cc30322

Change-Id: If38d0c7e8b878e76ff4a52fcdd441b002ce9ddf0
diff --git a/tests/cts/hostside/Android.bp b/tests/cts/hostside/Android.bp
new file mode 100644
index 0000000..b6f5142
--- /dev/null
+++ b/tests/cts/hostside/Android.bp
@@ -0,0 +1,30 @@
+// Copyright (C) 2014 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.
+
+java_test_host {
+    name: "CtsHostsideNetworkTests",
+    defaults: ["cts_defaults"],
+    // Only compile source java files in this apk.
+    srcs: ["src/**/*.java"],
+    libs: [
+        "cts-tradefed",
+        "tradefed",
+    ],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+}
diff --git a/tests/cts/hostside/Android.mk b/tests/cts/hostside/Android.mk
deleted file mode 100644
index 7270580..0000000
--- a/tests/cts/hostside/Android.mk
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (C) 2014 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Only compile source java files in this apk.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_MODULE := CtsHostsideNetworkTests
-
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed
-
-LOCAL_CTS_TEST_PACKAGE := android.net.hostsidenetwork
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-include $(BUILD_CTS_HOST_JAVA_LIBRARY)
-
-# Build the test APKs using their own makefiles
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/cts/hostside/AndroidTest.xml b/tests/cts/hostside/AndroidTest.xml
index c7cab7b..dbff179 100644
--- a/tests/cts/hostside/AndroidTest.xml
+++ b/tests/cts/hostside/AndroidTest.xml
@@ -18,6 +18,7 @@
     <option name="config-descriptor:metadata" key="component" value="networking" />
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
 
     <target_preparer class="com.android.cts.net.NetPolicyTestsPreparer" />
 
diff --git a/tests/cts/hostside/aidl/Android.bp b/tests/cts/hostside/aidl/Android.bp
new file mode 100644
index 0000000..320a1fa
--- /dev/null
+++ b/tests/cts/hostside/aidl/Android.bp
@@ -0,0 +1,24 @@
+// Copyright (C) 2016 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.
+
+java_test_helper_library {
+    name: "CtsHostsideNetworkTestsAidl",
+    sdk_version: "current",
+    srcs: [
+        "com/android/cts/net/hostside/IMyService.aidl",
+        "com/android/cts/net/hostside/INetworkCallback.aidl",
+        "com/android/cts/net/hostside/INetworkStateObserver.aidl",
+        "com/android/cts/net/hostside/IRemoteSocketFactory.aidl",
+    ],
+}
diff --git a/tests/cts/hostside/aidl/Android.mk b/tests/cts/hostside/aidl/Android.mk
deleted file mode 100644
index 20dabc1..0000000
--- a/tests/cts/hostside/aidl/Android.mk
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright (C) 2016 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := \
-        com/android/cts/net/hostside/IMyService.aidl \
-        com/android/cts/net/hostside/INetworkCallback.aidl \
-        com/android/cts/net/hostside/INetworkStateObserver.aidl \
-        com/android/cts/net/hostside/IRemoteSocketFactory.aidl
-LOCAL_MODULE := CtsHostsideNetworkTestsAidl
-include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/cts/hostside/app/Android.bp b/tests/cts/hostside/app/Android.bp
new file mode 100644
index 0000000..d66b71b
--- /dev/null
+++ b/tests/cts/hostside/app/Android.bp
@@ -0,0 +1,39 @@
+//
+// Copyright (C) 2014 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.
+//
+
+android_test_helper_app {
+    name: "CtsHostsideNetworkTestsApp",
+    defaults: ["cts_support_defaults"],
+    //sdk_version: "current",
+    platform_apis: true,
+    static_libs: [
+        "compatibility-device-util-axt",
+        "ctstestrunner-axt",
+        "ub-uiautomator",
+        "CtsHostsideNetworkTestsAidl",
+    ],
+    libs: [
+        "android.test.runner.stubs",
+        "android.test.base.stubs",
+    ],
+    srcs: ["src/**/*.java"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+}
diff --git a/tests/cts/hostside/app/Android.mk b/tests/cts/hostside/app/Android.mk
deleted file mode 100644
index 11f6bb1..0000000
--- a/tests/cts/hostside/app/Android.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-# Copyright (C) 2014 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-#LOCAL_SDK_VERSION := current
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util-axt ctstestrunner-axt ub-uiautomator \
-        CtsHostsideNetworkTestsAidl
-
-LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := CtsHostsideNetworkTestsApp
-
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_DEX_PREOPT := false
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index fde8335..26397ef 100755
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -1008,6 +1008,32 @@
         assertTrue(mCM.isActiveNetworkMetered());
     }
 
+    public void testB141603906() throws Exception {
+        final InetSocketAddress src = new InetSocketAddress(0);
+        final InetSocketAddress dst = new InetSocketAddress(0);
+        final int NUM_THREADS = 8;
+        final int NUM_SOCKETS = 5000;
+        final Thread[] threads = new Thread[NUM_THREADS];
+        startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+                 new String[] {"0.0.0.0/0", "::/0"},
+                 "", "", null, null /* underlyingNetworks */, false /* isAlwaysMetered */);
+
+        for (int i = 0; i < NUM_THREADS; i++) {
+            threads[i] = new Thread(() -> {
+                for (int j = 0; j < NUM_SOCKETS; j++) {
+                    mCM.getConnectionOwnerUid(IPPROTO_TCP, src, dst);
+                }
+            });
+        }
+        for (Thread thread : threads) {
+            thread.start();
+        }
+        for (Thread thread : threads) {
+            thread.join();
+        }
+        stopVpn();
+    }
+
     private boolean isNetworkMetered(Network network) {
         NetworkCapabilities nc = mCM.getNetworkCapabilities(network);
         return !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
diff --git a/tests/cts/hostside/app2/Android.bp b/tests/cts/hostside/app2/Android.bp
new file mode 100644
index 0000000..8a3c8e7
--- /dev/null
+++ b/tests/cts/hostside/app2/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright (C) 2016 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.
+//
+
+android_test_helper_app {
+    name: "CtsHostsideNetworkTestsApp2",
+    defaults: ["cts_support_defaults"],
+    sdk_version: "current",
+    static_libs: ["CtsHostsideNetworkTestsAidl"],
+    srcs: ["src/**/*.java"],
+    // Tag this module as a cts test artifact
+    test_suites: [
+        "cts",
+        "vts",
+        "general-tests",
+    ],
+    certificate: ":cts-net-app",
+}
diff --git a/tests/cts/hostside/app2/Android.mk b/tests/cts/hostside/app2/Android.mk
deleted file mode 100644
index 5c0bae1..0000000
--- a/tests/cts/hostside/app2/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# Copyright (C) 2016 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := CtsHostsideNetworkTestsAidl
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := CtsHostsideNetworkTestsApp2
-
-LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_DEX_PREOPT := false
-
-# Tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_CERTIFICATE := cts/hostsidetests/net/certs/cts-net-app
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/cts/hostside/certs/Android.bp b/tests/cts/hostside/certs/Android.bp
new file mode 100644
index 0000000..ab4cf34
--- /dev/null
+++ b/tests/cts/hostside/certs/Android.bp
@@ -0,0 +1,4 @@
+android_app_certificate {
+    name: "cts-net-app",
+    certificate: "cts-net-app",
+}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
index 6e37a24..62925ad 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
@@ -91,4 +91,8 @@
                 TEST_PKG + ".VpnTest",
                 "testAlwaysMeteredVpnWithNonNullUnderlyingNetwork");
     }
+
+    public void testB141603906() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testB141603906");
+    }
 }
diff --git a/tests/cts/net/AndroidTest.xml b/tests/cts/net/AndroidTest.xml
index 1807a6b..3ff019e 100644
--- a/tests/cts/net/AndroidTest.xml
+++ b/tests/cts/net/AndroidTest.xml
@@ -18,6 +18,7 @@
     <option name="config-descriptor:metadata" key="token" value="SIM_CARD" />
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/cts/net/api23Test/AndroidTest.xml b/tests/cts/net/api23Test/AndroidTest.xml
index 21f28fc..8042d50 100644
--- a/tests/cts/net/api23Test/AndroidTest.xml
+++ b/tests/cts/net/api23Test/AndroidTest.xml
@@ -17,6 +17,7 @@
     <option name="config-descriptor:metadata" key="component" value="networking" />
     <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
diff --git a/tests/cts/net/native/dns/AndroidTest.xml b/tests/cts/net/native/dns/AndroidTest.xml
index fe88cda..6d03c23 100644
--- a/tests/cts/net/native/dns/AndroidTest.xml
+++ b/tests/cts/net/native/dns/AndroidTest.xml
@@ -18,6 +18,7 @@
     <option name="config-descriptor:metadata" key="component" value="networking" />
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsNativeNetDnsTestCases->/data/local/tmp/CtsNativeNetDnsTestCases" />
diff --git a/tests/cts/net/native/qtaguid/AndroidTest.xml b/tests/cts/net/native/qtaguid/AndroidTest.xml
index a55afe7..fa4b2cf 100644
--- a/tests/cts/net/native/qtaguid/AndroidTest.xml
+++ b/tests/cts/net/native/qtaguid/AndroidTest.xml
@@ -18,6 +18,7 @@
     <option name="config-descriptor:metadata" key="component" value="networking" />
     <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
     <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsNativeNetTestCases->/data/local/tmp/CtsNativeNetTestCases" />
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index ca1f771..fa7e138 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -16,8 +16,10 @@
 
 package android.net.cts;
 
+import static android.content.pm.PackageManager.FEATURE_ETHERNET;
 import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
 import static android.content.pm.PackageManager.FEATURE_WIFI;
+import static android.content.pm.PackageManager.FEATURE_USB_HOST;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
@@ -111,9 +113,7 @@
     public static final int TYPE_WIFI = ConnectivityManager.TYPE_WIFI;
 
     private static final int HOST_ADDRESS = 0x7f000001;// represent ip 127.0.0.1
-    private static final int CONNECT_TIMEOUT_MS = 2000;
     private static final int KEEPALIVE_CALLBACK_TIMEOUT_MS = 2000;
-    private static final int KEEPALIVE_SOCKET_TIMEOUT_MS = 5000;
     private static final int INTERVAL_KEEPALIVE_RETRY_MS = 500;
     private static final int MAX_KEEPALIVE_RETRY_COUNT = 3;
     private static final int MIN_KEEPALIVE_INTERVAL = 10;
@@ -259,7 +259,7 @@
 
     public void testGetNetworkInfo() {
         for (int type = -1; type <= ConnectivityManager.MAX_NETWORK_TYPE+1; type++) {
-            if (isSupported(type)) {
+            if (shouldBeSupported(type)) {
                 NetworkInfo ni = mCm.getNetworkInfo(type);
                 assertTrue("Info shouldn't be null for " + type, ni != null);
                 State state = ni.getState();
@@ -279,7 +279,7 @@
         NetworkInfo[] ni = mCm.getAllNetworkInfo();
         assertTrue(ni.length >= MIN_NUM_NETWORK_TYPES);
         for (int type = 0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
-            int desiredFoundCount = (isSupported(type) ? 1 : 0);
+            int desiredFoundCount = (shouldBeSupported(type) ? 1 : 0);
             int foundCount = 0;
             for (NetworkInfo i : ni) {
                 if (i.getType() == type) foundCount++;
@@ -387,20 +387,32 @@
         assertStartUsingNetworkFeatureUnsupported(TYPE_WIFI, mmsFeature);
     }
 
-    private boolean isSupported(int networkType) {
+    private boolean shouldEthernetBeSupported() {
+        // Instant mode apps aren't allowed to query the Ethernet service due to selinux policies.
+        // When in instant mode, don't fail if the Ethernet service is available. Instead, rely on
+        // the fact that Ethernet should be supported if the device has a hardware Ethernet port, or
+        // if the device can be a USB host and thus can use USB Ethernet adapters.
+        //
+        // Note that this test this will still fail in instant mode if a device supports Ethernet
+        // via other hardware means. We are not currently aware of any such device.
+        return (mContext.getSystemService(Context.ETHERNET_SERVICE) != null) ||
+            mPackageManager.hasSystemFeature(FEATURE_ETHERNET) ||
+            mPackageManager.hasSystemFeature(FEATURE_USB_HOST);
+    }
+
+    private boolean shouldBeSupported(int networkType) {
         return mNetworks.containsKey(networkType) ||
                (networkType == ConnectivityManager.TYPE_VPN) ||
-               (networkType == ConnectivityManager.TYPE_ETHERNET &&
-                       mContext.getSystemService(Context.ETHERNET_SERVICE) != null);
+               (networkType == ConnectivityManager.TYPE_ETHERNET && shouldEthernetBeSupported());
     }
 
     public void testIsNetworkSupported() {
         for (int type = -1; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
             boolean supported = mCm.isNetworkSupported(type);
-            if (isSupported(type)) {
-                assertTrue(supported);
+            if (shouldBeSupported(type)) {
+                assertTrue("Network type " + type + " should be supported", supported);
             } else {
-                assertFalse(supported);
+                assertFalse("Network type " + type + " should not be supported", supported);
             }
         }
     }
@@ -834,15 +846,14 @@
     }
 
     private Socket getConnectedSocket(final Network network, final String host, final int port,
-            final int socketTimeOut, final int family) throws Exception {
+            final int family) throws Exception {
         final Socket s = network.getSocketFactory().createSocket();
         try {
             final InetAddress addr = getAddrByName(host, family);
             if (addr == null) fail("Fail to get destination address for " + family);
 
             final InetSocketAddress sockAddr = new InetSocketAddress(addr, port);
-            s.setSoTimeout(socketTimeOut);
-            s.connect(sockAddr, CONNECT_TIMEOUT_MS);
+            s.connect(sockAddr);
         } catch (Exception e) {
             s.close();
             throw e;
@@ -967,8 +978,7 @@
         final byte[] requestBytes = CtsNetUtils.HTTP_REQUEST.getBytes("UTF-8");
         // So far only ipv4 tcp keepalive offload is supported.
         // TODO: add test case for ipv6 tcp keepalive offload when it is supported.
-        try (Socket s = getConnectedSocket(network, TEST_HOST, HTTP_PORT,
-                KEEPALIVE_SOCKET_TIMEOUT_MS, AF_INET)) {
+        try (Socket s = getConnectedSocket(network, TEST_HOST, HTTP_PORT, AF_INET)) {
 
             // Should able to start keep alive offload when socket is idle.
             final Executor executor = mContext.getMainExecutor();
@@ -1102,7 +1112,7 @@
             // sockets will be duplicated and kept valid in service side if the keepalives are
             // successfully started.
             try (Socket tcpSocket = getConnectedSocket(network, TEST_HOST, HTTP_PORT,
-                    0 /* Unused */, AF_INET)) {
+                    AF_INET)) {
                 return mCm.createSocketKeepalive(network, tcpSocket, executor, callback);
             } catch (Exception e) {
                 fail("Unexpected error when creating TCP socket: " + e);
diff --git a/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java b/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
index 88e86f4..766c55e 100644
--- a/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
+++ b/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
@@ -25,6 +25,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkUtils;
 import android.net.cts.util.CtsNetUtils;
+import android.platform.test.annotations.AppModeFull;
 import android.provider.Settings;
 import android.system.ErrnoException;
 import android.system.OsConstants;
@@ -73,7 +74,6 @@
 
     @Override
     protected void tearDown() throws Exception {
-        restorePrivateDnsSetting();
         super.tearDown();
     }
 
@@ -214,7 +214,7 @@
         } catch (IllegalArgumentException e) {}
     }
 
-    public void testResNApi() throws InterruptedException {
+    public void testResNApi() throws Exception {
         final Network[] testNetworks = getTestableNetworks();
 
         for (Network network : testNetworks) {
@@ -232,16 +232,24 @@
                 runResNnxDomainCheck(network.getNetworkHandle());
             }
         }
+    }
+
+    @AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
+    public void testResNApiNXDomainPrivateDns() throws InterruptedException {
         // Enable private DNS strict mode and set server to dns.google before doing NxDomain test.
         // b/144521720
-        Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, "hostname");
-        Settings.Global.putString(mCR,
-                Settings.Global.PRIVATE_DNS_SPECIFIER, GOOGLE_PRIVATE_DNS_SERVER);
-        for (Network network : testNetworks) {
-          // Wait for private DNS setting to propagate.
-          mCtsNetUtils.awaitPrivateDnsSetting("NxDomain test wait private DNS setting timeout",
-                    network, GOOGLE_PRIVATE_DNS_SERVER, PRIVATE_DNS_SETTING_TIMEOUT_MS);
-          runResNnxDomainCheck(network.getNetworkHandle());
+        try {
+            Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, "hostname");
+            Settings.Global.putString(mCR,
+                    Settings.Global.PRIVATE_DNS_SPECIFIER, GOOGLE_PRIVATE_DNS_SERVER);
+            for (Network network : getTestableNetworks()) {
+              // Wait for private DNS setting to propagate.
+              mCtsNetUtils.awaitPrivateDnsSetting("NxDomain test wait private DNS setting timeout",
+                        network, GOOGLE_PRIVATE_DNS_SERVER, PRIVATE_DNS_SETTING_TIMEOUT_MS);
+              runResNnxDomainCheck(network.getNetworkHandle());
+            }
+        } finally {
+            restorePrivateDnsSetting();
         }
     }
 }
diff --git a/tests/cts/net/src/android/net/cts/NetworkWatchlistTest.java b/tests/cts/net/src/android/net/cts/NetworkWatchlistTest.java
index e4e350c..81a9e30 100644
--- a/tests/cts/net/src/android/net/cts/NetworkWatchlistTest.java
+++ b/tests/cts/net/src/android/net/cts/NetworkWatchlistTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assume.assumeTrue;
 
 import android.content.Context;
@@ -72,6 +73,7 @@
         setWatchlistConfig(TEST_EMPTY_WATCHLIST_XML);
         // Verify test watchlist config is not set before testing
         byte[] result = mConnectivityManager.getNetworkWatchlistConfigHash();
+        assertNotNull("Watchlist config does not exist", result);
         assertNotEquals(TEST_WATCHLIST_CONFIG_HASH, byteArrayToHexString(result));
     }
 
diff --git a/tests/cts/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java b/tests/cts/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java
index 01ac3fd..cbe54f8 100644
--- a/tests/cts/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java
+++ b/tests/cts/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java
@@ -16,135 +16,333 @@
 
 package android.net.cts;
 
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.Socket;
-
-import javax.net.SocketFactory;
-import javax.net.ssl.SSLPeerUnverifiedException;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.net.SSLCertificateSocketFactory;
 import android.platform.test.annotations.AppModeFull;
-import android.test.AndroidTestCase;
-
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.SSLSession;
 import libcore.javax.net.ssl.SSLConfigurationAsserts;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
-public class SSLCertificateSocketFactoryTest extends AndroidTestCase {
-    private SSLCertificateSocketFactory mFactory;
-    private int mTimeout;
+@RunWith(JUnit4.class)
+public class SSLCertificateSocketFactoryTest {
+    // TEST_HOST should point to a web server with a valid TLS certificate.
+    private static final String TEST_HOST = "www.google.com";
+    private static final int HTTPS_PORT = 443;
+    private HostnameVerifier mDefaultVerifier;
+    private SSLCertificateSocketFactory mSocketFactory;
+    private InetAddress mLocalAddress;
+    // InetAddress obtained by resolving TEST_HOST.
+    private InetAddress mTestHostAddress;
+    // SocketAddress combining mTestHostAddress and HTTPS_PORT.
+    private List<SocketAddress> mTestSocketAddresses;
 
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mTimeout = 1000;
-        mFactory = (SSLCertificateSocketFactory) SSLCertificateSocketFactory.getDefault(mTimeout);
+    @Before
+    public void setUp() {
+        // Expected state before each test method is that
+        // HttpsURLConnection.getDefaultHostnameVerifier() will return the system default.
+        mDefaultVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
+        mSocketFactory = (SSLCertificateSocketFactory)
+            SSLCertificateSocketFactory.getDefault(1000 /* handshakeTimeoutMillis */);
+        assertNotNull(mSocketFactory);
+        InetAddress[] addresses;
+        try {
+            addresses = InetAddress.getAllByName(TEST_HOST);
+            mTestHostAddress = addresses[0];
+        } catch (UnknownHostException uhe) {
+            throw new AssertionError(
+                "Unable to test SSLCertificateSocketFactory: cannot resolve " + TEST_HOST, uhe);
+        }
+
+        mTestSocketAddresses = Arrays.stream(addresses)
+            .map(addr -> new InetSocketAddress(addr, HTTPS_PORT))
+            .collect(Collectors.toList());
+
+        // Find the local IP address which will be used to connect to TEST_HOST.
+        try {
+            Socket testSocket = new Socket(TEST_HOST, HTTPS_PORT);
+            mLocalAddress = testSocket.getLocalAddress();
+            testSocket.close();
+        } catch (IOException ioe) {
+            throw new AssertionError(""
+                + "Unable to test SSLCertificateSocketFactory: cannot connect to "
+                + TEST_HOST, ioe);
+        }
     }
 
+    // Restore the system default hostname verifier after each test.
+    @After
+    public void restoreDefaultHostnameVerifier() {
+        HttpsURLConnection.setDefaultHostnameVerifier(mDefaultVerifier);
+    }
+
+    @Test
     public void testDefaultConfiguration() throws Exception {
-        SSLConfigurationAsserts.assertSSLSocketFactoryDefaultConfiguration(mFactory);
+        SSLConfigurationAsserts.assertSSLSocketFactoryDefaultConfiguration(mSocketFactory);
     }
 
-    public void testAccessProperties() throws Exception {
-        mFactory.getSupportedCipherSuites();
-        mFactory.getDefaultCipherSuites();
-        SocketFactory sf = SSLCertificateSocketFactory.getDefault(mTimeout);
-        assertNotNull(sf);
+    @Test
+    public void testAccessProperties() {
+        mSocketFactory.getSupportedCipherSuites();
+        mSocketFactory.getDefaultCipherSuites();
     }
 
-    public void testCreateSocket() throws Exception {
-        new SSLCertificateSocketFactory(100);
-        int port = 443;
-        String host = "www.google.com";
-        InetAddress inetAddress = null;
-        inetAddress = InetAddress.getLocalHost();
+    /**
+     * Tests the {@code createSocket()} cases which are expected to fail with {@code IOException}.
+     */
+    @Test
+    @AppModeFull(reason = "Socket cannot bind in instant app mode")
+    public void createSocket_io_error_expected() {
+        // Connect to the localhost HTTPS port. Should result in connection refused IOException
+        // because no service should be listening on that port.
+        InetAddress localhostAddress = InetAddress.getLoopbackAddress();
         try {
-            mFactory.createSocket(inetAddress, port);
-            fail("should throw exception!");
+            mSocketFactory.createSocket(localhostAddress, HTTPS_PORT);
+            fail();
         } catch (IOException e) {
             // expected
         }
 
+        // Same, but also binding to a local address.
         try {
-            InetAddress inetAddress1 = InetAddress.getLocalHost();
-            InetAddress inetAddress2 = InetAddress.getLocalHost();
-            mFactory.createSocket(inetAddress1, port, inetAddress2, port);
-            fail("should throw exception!");
+            mSocketFactory.createSocket(localhostAddress, HTTPS_PORT, localhostAddress, 0);
+            fail();
         } catch (IOException e) {
             // expected
         }
 
+        // Same, wrapping an existing plain socket which is in an unconnected state.
         try {
             Socket socket = new Socket();
-            mFactory.createSocket(socket, host, port, true);
-            fail("should throw exception!");
+            mSocketFactory.createSocket(socket, "localhost", HTTPS_PORT, true);
+            fail();
         } catch (IOException e) {
             // expected
         }
-        Socket socket = null;
-        socket = mFactory.createSocket(host, port);
+    }
+
+    /**
+     * Tests hostname verification for
+     * {@link SSLCertificateSocketFactory#createSocket(String, int)}.
+     *
+     * <p>This method should return a socket which is fully connected (i.e. TLS handshake complete)
+     * and whose peer TLS certificate has been verified to have the correct hostname.
+     *
+     * <p>{@link SSLCertificateSocketFactory} is documented to verify hostnames using
+     * the {@link HostnameVerifier} returned by
+     * {@link HttpsURLConnection#getDefaultHostnameVerifier}, so this test connects twice,
+     * once with the system default {@link HostnameVerifier} which is expected to succeed,
+     * and once after installing a {@link NegativeHostnameVerifier} which will cause
+     * {@link SSLCertificateSocketFactory#verifyHostname} to throw a
+     * {@link SSLPeerUnverifiedException}.
+     *
+     * <p>These tests only test the hostname verification logic in SSLCertificateSocketFactory,
+     * other TLS failure modes and the default HostnameVerifier are tested elsewhere, see
+     * {@link com.squareup.okhttp.internal.tls.HostnameVerifierTest} and
+     * https://android.googlesource.com/platform/external/boringssl/+/refs/heads/master/src/ssl/test
+     *
+     * <p>Tests the following behaviour:-
+     * <ul>
+     * <li>TEST_SERVER is available and has a valid TLS certificate
+     * <li>{@code createSocket()} verifies the remote hostname is correct using
+     *     {@link HttpsURLConnection#getDefaultHostnameVerifier}
+     * <li>{@link SSLPeerUnverifiedException} is thrown when the remote hostname is invalid
+     * </ul>
+     *
+     * <p>See also http://b/2807618.
+     */
+    @Test
+    public void createSocket_simple_with_hostname_verification() throws Exception {
+        Socket socket = mSocketFactory.createSocket(TEST_HOST, HTTPS_PORT);
+        assertConnectedSocket(socket);
+        socket.close();
+
+        HttpsURLConnection.setDefaultHostnameVerifier(new NegativeHostnameVerifier());
+        try {
+            mSocketFactory.createSocket(TEST_HOST, HTTPS_PORT);
+            fail();
+        } catch (SSLPeerUnverifiedException expected) {
+            // expected
+        }
+    }
+
+    /**
+     * Tests hostname verification for
+     * {@link SSLCertificateSocketFactory#createSocket(Socket, String, int, boolean)}.
+     *
+     * <p>This method should return a socket which is fully connected (i.e. TLS handshake complete)
+     * and whose peer TLS certificate has been verified to have the correct hostname.
+     *
+     * <p>The TLS socket returned is wrapped around the plain socket passed into
+     * {@code createSocket()}.
+     *
+     * <p>See {@link #createSocket_simple_with_hostname_verification()} for test methodology.
+     */
+    @Test
+    public void createSocket_wrapped_with_hostname_verification() throws Exception {
+        Socket underlying = new Socket(TEST_HOST, HTTPS_PORT);
+        Socket socket = mSocketFactory.createSocket(underlying, TEST_HOST, HTTPS_PORT, true);
+        assertConnectedSocket(socket);
+        socket.close();
+
+        HttpsURLConnection.setDefaultHostnameVerifier(new NegativeHostnameVerifier());
+        try {
+            underlying = new Socket(TEST_HOST, HTTPS_PORT);
+            mSocketFactory.createSocket(underlying, TEST_HOST, HTTPS_PORT, true);
+            fail();
+        } catch (SSLPeerUnverifiedException expected) {
+            // expected
+        }
+    }
+
+    /**
+     * Tests hostname verification for
+     * {@link SSLCertificateSocketFactory#createSocket(String, int, InetAddress, int)}.
+     *
+     * <p>This method should return a socket which is fully connected (i.e. TLS handshake complete)
+     * and whose peer TLS certificate has been verified to have the correct hostname.
+     *
+     * <p>The TLS socket returned is also bound to the local address determined in {@link #setUp} to
+     * be used for connections to TEST_HOST, and a wildcard port.
+     *
+     * <p>See {@link #createSocket_simple_with_hostname_verification()} for test methodology.
+     */
+    @Test
+    @AppModeFull(reason = "Socket cannot bind in instant app mode")
+    public void createSocket_bound_with_hostname_verification() throws Exception {
+        Socket socket = mSocketFactory.createSocket(TEST_HOST, HTTPS_PORT, mLocalAddress, 0);
+        assertConnectedSocket(socket);
+        socket.close();
+
+        HttpsURLConnection.setDefaultHostnameVerifier(new NegativeHostnameVerifier());
+        try {
+            mSocketFactory.createSocket(TEST_HOST, HTTPS_PORT, mLocalAddress, 0);
+            fail();
+        } catch (SSLPeerUnverifiedException expected) {
+            // expected
+        }
+    }
+
+    /**
+     * Tests hostname verification for
+     * {@link SSLCertificateSocketFactory#createSocket(InetAddress, int)}.
+     *
+     * <p>This method should return a socket which the documentation describes as "unconnected",
+     * which actually means that the socket is fully connected at the TCP layer but TLS handshaking
+     * and hostname verification have not yet taken place.
+     *
+     * <p>Behaviour is tested by installing a {@link NegativeHostnameVerifier} and by calling
+     * {@link #assertConnectedSocket} to ensure TLS handshaking but no hostname verification takes
+     * place.  Next, {@link SSLCertificateSocketFactory#verifyHostname} is called to ensure
+     * that hostname verification is using the {@link HostnameVerifier} returned by
+     * {@link HttpsURLConnection#getDefaultHostnameVerifier} as documented.
+     *
+     * <p>Tests the following behaviour:-
+     * <ul>
+     * <li>TEST_SERVER is available and has a valid TLS certificate
+     * <li>{@code createSocket()} does not verify the remote hostname
+     * <li>Calling {@link SSLCertificateSocketFactory#verifyHostname} on the returned socket
+     *     throws {@link SSLPeerUnverifiedException} if the remote hostname is invalid
+     * </ul>
+     */
+    @Test
+    public void createSocket_simple_no_hostname_verification() throws Exception{
+        HttpsURLConnection.setDefaultHostnameVerifier(new NegativeHostnameVerifier());
+        Socket socket = mSocketFactory.createSocket(mTestHostAddress, HTTPS_PORT);
+        // Need to provide the expected hostname here or the TLS handshake will
+        // be unable to supply SNI to the remote host.
+        mSocketFactory.setHostname(socket, TEST_HOST);
+        assertConnectedSocket(socket);
+        try {
+          SSLCertificateSocketFactory.verifyHostname(socket, TEST_HOST);
+          fail();
+        } catch (SSLPeerUnverifiedException expected) {
+            // expected
+        }
+        HttpsURLConnection.setDefaultHostnameVerifier(mDefaultVerifier);
+        SSLCertificateSocketFactory.verifyHostname(socket, TEST_HOST);
+        socket.close();
+    }
+
+    /**
+     * Tests hostname verification for
+     * {@link SSLCertificateSocketFactory#createSocket(InetAddress, int, InetAddress, int)}.
+     *
+     * <p>This method should return a socket which the documentation describes as "unconnected",
+     * which actually means that the socket is fully connected at the TCP layer but TLS handshaking
+     * and hostname verification have not yet taken place.
+     *
+     * <p>The TLS socket returned is also bound to the local address determined in {@link #setUp} to
+     * be used for connections to TEST_HOST, and a wildcard port.
+     *
+     * <p>See {@link #createSocket_simple_no_hostname_verification()} for test methodology.
+     */
+    @Test
+    @AppModeFull(reason = "Socket cannot bind in instant app mode")
+    public void createSocket_bound_no_hostname_verification() throws Exception{
+        HttpsURLConnection.setDefaultHostnameVerifier(new NegativeHostnameVerifier());
+        Socket socket =
+            mSocketFactory.createSocket(mTestHostAddress, HTTPS_PORT, mLocalAddress, 0);
+        // Need to provide the expected hostname here or the TLS handshake will
+        // be unable to supply SNI to the peer.
+        mSocketFactory.setHostname(socket, TEST_HOST);
+        assertConnectedSocket(socket);
+        try {
+          SSLCertificateSocketFactory.verifyHostname(socket, TEST_HOST);
+          fail();
+        } catch (SSLPeerUnverifiedException expected) {
+            // expected
+        }
+        HttpsURLConnection.setDefaultHostnameVerifier(mDefaultVerifier);
+        SSLCertificateSocketFactory.verifyHostname(socket, TEST_HOST);
+        socket.close();
+    }
+
+    /**
+     * Asserts a socket is fully connected to the expected peer.
+     *
+     * <p>For the variants of createSocket which verify the remote hostname,
+     * {@code socket} should already be fully connected.
+     *
+     * <p>For the non-verifying variants, retrieving the input stream will trigger a TLS handshake
+     * and so may throw an exception, for example if the peer's certificate is invalid.
+     *
+     * <p>Does no hostname verification.
+     */
+    private void assertConnectedSocket(Socket socket) throws Exception {
         assertNotNull(socket);
-        assertNotNull(socket.getOutputStream());
+        assertTrue(socket.isConnected());
         assertNotNull(socket.getInputStream());
-
-        // it throw exception when calling createSocket(String, int, InetAddress, int)
-        // The socket level is invalid.
-    }
-
-    // a host and port that are expected to be available but have
-    // a cert with a different CN, in this case CN=mail.google.com
-    private static String TEST_CREATE_SOCKET_HOST = "www3.l.google.com";
-    private static int TEST_CREATE_SOCKET_PORT = 443;
-
-    /**
-     * b/2807618 Make sure that hostname verifcation in cases were it
-     * is documented to be included by various
-     * SSLCertificateSocketFactory.createSocket messages.
-     *
-     * NOTE: Test will fail if external server is not available.
-     */
-    @AppModeFull(reason = "Socket cannot bind in instant app mode")
-    public void test_createSocket_simple() throws Exception {
-        try {
-            mFactory.createSocket(TEST_CREATE_SOCKET_HOST, TEST_CREATE_SOCKET_PORT);
-            fail();
-        } catch (SSLPeerUnverifiedException expected) {
-            // expected
-        }
+        assertNotNull(socket.getOutputStream());
+        assertTrue(mTestSocketAddresses.contains(socket.getRemoteSocketAddress()));
     }
 
     /**
-     * b/2807618 Make sure that hostname verifcation in cases were it
-     * is documented to be included by various
-     * SSLCertificateSocketFactory.createSocket messages.
-     *
-     * NOTE: Test will fail if external server is not available.
+     * A HostnameVerifier which always returns false to simulate a server returning a
+     * certificate which does not match the expected hostname.
      */
-    @AppModeFull(reason = "Socket cannot bind in instant app mode")
-    public void test_createSocket_wrapping() throws Exception {
-        try {
-            Socket underlying = new Socket(TEST_CREATE_SOCKET_HOST, TEST_CREATE_SOCKET_PORT);
-            mFactory.createSocket(
-                    underlying, TEST_CREATE_SOCKET_HOST, TEST_CREATE_SOCKET_PORT, true);
-            fail();
-        } catch (SSLPeerUnverifiedException expected) {
-            // expected
-        }
-    }
-
-    /**
-     * b/2807618 Make sure that hostname verifcation in cases were it
-     * is documented to be included by various
-     * SSLCertificateSocketFactory.createSocket messages.
-     *
-     * NOTE: Test will fail if external server is not available.
-     */
-    @AppModeFull(reason = "Socket cannot bind in instant app mode")
-    public void test_createSocket_bind() throws Exception {
-        try {
-            mFactory.createSocket(TEST_CREATE_SOCKET_HOST, TEST_CREATE_SOCKET_PORT, null, 0);
-            fail();
-        } catch (SSLPeerUnverifiedException expected) {
-            // expected
+    private static class NegativeHostnameVerifier implements HostnameVerifier {
+        @Override
+        public boolean verify(String hostname, SSLSession sslSession) {
+            return false;
         }
     }
 }
diff --git a/tests/cts/net/src/android/net/rtp/cts/AudioGroupTest.java b/tests/cts/net/src/android/net/rtp/cts/AudioGroupTest.java
index 1bd7fad..fee8621 100644
--- a/tests/cts/net/src/android/net/rtp/cts/AudioGroupTest.java
+++ b/tests/cts/net/src/android/net/rtp/cts/AudioGroupTest.java
@@ -62,7 +62,7 @@
         mSocketB.connect(mStreamB.getLocalAddress(), mStreamB.getLocalPort());
         mStreamB.associate(mSocketB.getLocalAddress(), mSocketB.getLocalPort());
 
-        mGroup = new AudioGroup();
+        mGroup = new AudioGroup(mContext);
     }
 
     @Override
diff --git a/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java b/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java
index 0ef5cd9..3e47764 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -921,9 +921,9 @@
                 .distinct()
                 .collect(Collectors.toList());
 
-        if (uniquePackageNames.size() > 1) {
-            fail("The NETWORK_CARRIER_PROVISIONING permission must not be held by more than one "
-                    + "application, but is held by " + uniquePackageNames.size() + " applications: "
+        if (uniquePackageNames.size() > 2) {
+            fail("The NETWORK_CARRIER_PROVISIONING permission must not be held by more than two "
+                    + "applications, but is held by " + uniquePackageNames.size() + " applications: "
                     + String.join(", ", uniquePackageNames));
         }
     }