Merge "Reduce DnsResolverTest flaky rate"
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index b00455d..624d149 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-android_test {
-    name: "CtsNetTestCases",
+java_defaults {
+    name: "CtsNetTestCasesDefaults",
     defaults: ["cts_defaults"],
 
     // Include both the 32 and 64 bit versions
@@ -32,7 +32,6 @@
         "libnativehelper_compat_libc++",
     ],
 
-    // include CtsTestServer as a temporary hack to free net.cts from cts.stub.
     srcs: [
         "src/**/*.java",
         "src/**/*.kt",
@@ -54,13 +53,33 @@
     // uncomment when b/13249961 is fixed
     // sdk_version: "current",
     platform_apis: true,
+}
 
-    // Tag this module as a cts test artifact
+// Networking CTS tests for development and release. These tests always target the platform SDK
+// version, and are subject to all the restrictions appropriate to that version. Before SDK
+// finalization, these tests have a min_sdk_version of 10000, and cannot be installed on release
+// devices.
+android_test {
+    name: "CtsNetTestCases",
+    defaults: ["CtsNetTestCasesDefaults"],
     test_suites: [
         "cts",
         "vts",
         "general-tests",
+    ],
+    test_config_template: "AndroidTestTemplate.xml",
+}
+
+// Networking CTS tests that have a min_sdk_version of the latest released SDK. These tests can
+// be installed on release devices at any point in the release cycle and are useful for qualifying
+// mainline modules on release devices.
+android_test {
+    name: "CtsNetTestCasesLatestSdk",
+    defaults: ["CtsNetTestCasesDefaults"],
+    min_sdk_version: "29",
+    target_sdk_version: "29",
+    test_suites: [
         "mts",
     ],
-
+    test_config_template: "AndroidTestTemplate.xml",
 }
diff --git a/tests/cts/net/AndroidTest.xml b/tests/cts/net/AndroidTestTemplate.xml
similarity index 92%
rename from tests/cts/net/AndroidTest.xml
rename to tests/cts/net/AndroidTestTemplate.xml
index 3ff019e..1f75da1 100644
--- a/tests/cts/net/AndroidTest.xml
+++ b/tests/cts/net/AndroidTestTemplate.xml
@@ -12,7 +12,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<configuration description="Config for CTS Net test cases">
+<configuration description="Test config for {MODULE}">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="networking" />
     <option name="config-descriptor:metadata" key="token" value="SIM_CARD" />
@@ -22,7 +22,7 @@
     <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="CtsNetTestCases.apk" />
+        <option name="test-file-name" value="{MODULE}.apk" />
     </target_preparer>
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.net.cts" />
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
new file mode 100644
index 0000000..0a80047
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2020 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.cts;
+
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.net.ConnectivityDiagnosticsManager;
+import android.net.NetworkRequest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidJUnit4.class)
+public class ConnectivityDiagnosticsManagerTest {
+    private static final Executor INLINE_EXECUTOR = x -> x.run();
+    private static final NetworkRequest DEFAULT_REQUEST = new NetworkRequest.Builder().build();
+
+    private Context mContext;
+    private ConnectivityDiagnosticsManager mCdm;
+    private ConnectivityDiagnosticsCallback mCallback;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getContext();
+        mCdm = mContext.getSystemService(ConnectivityDiagnosticsManager.class);
+
+        mCallback = new ConnectivityDiagnosticsCallback() {};
+    }
+
+    @Test
+    public void testRegisterConnectivityDiagnosticsCallback() {
+        mCdm.registerConnectivityDiagnosticsCallback(DEFAULT_REQUEST, INLINE_EXECUTOR, mCallback);
+    }
+
+    @Test
+    public void testRegisterDuplicateConnectivityDiagnosticsCallback() {
+        mCdm.registerConnectivityDiagnosticsCallback(DEFAULT_REQUEST, INLINE_EXECUTOR, mCallback);
+
+        try {
+            mCdm.registerConnectivityDiagnosticsCallback(
+                    DEFAULT_REQUEST, INLINE_EXECUTOR, mCallback);
+            fail("Registering the same callback twice should throw an IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @Test
+    public void testUnregisterConnectivityDiagnosticsCallback() {
+        mCdm.registerConnectivityDiagnosticsCallback(DEFAULT_REQUEST, INLINE_EXECUTOR, mCallback);
+        mCdm.unregisterConnectivityDiagnosticsCallback(mCallback);
+    }
+
+    @Test
+    public void testUnregisterUnknownConnectivityDiagnosticsCallback() {
+        // Expected to silently ignore the unregister() call
+        mCdm.unregisterConnectivityDiagnosticsCallback(mCallback);
+    }
+}
diff --git a/tests/cts/net/src/android/net/wifi/cts/ConcurrencyTest.java b/tests/cts/net/src/android/net/wifi/cts/ConcurrencyTest.java
index c557338..ba0832f 100644
--- a/tests/cts/net/src/android/net/wifi/cts/ConcurrencyTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/ConcurrencyTest.java
@@ -299,7 +299,6 @@
         assertTrue(waitForBroadcasts(MySync.NETWORK_INFO));
         // wait for changing to EnabledState
         assertNotNull(mMySync.expectedNetworkInfo);
-        assertTrue(mMySync.expectedNetworkInfo.isAvailable());
 
         return true;
     }
diff --git a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
index 043d04f..98dbe52 100644
--- a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
+++ b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
@@ -15,13 +15,16 @@
  */
 package android.tethering.test;
 
+import static android.net.TetheringManager.TETHERING_WIFI;
+
 import static org.junit.Assert.fail;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.net.ConnectivityManager;
+import android.net.TetheringManager;
+import android.net.TetheringManager.TetheringRequest;
 import android.os.ConditionVariable;
 
 import androidx.test.InstrumentationRegistry;
@@ -35,6 +38,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Iterator;
+import java.util.concurrent.Executor;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
@@ -43,7 +47,7 @@
 
     private Context mContext;
 
-    private ConnectivityManager mCM;
+    private TetheringManager mTM;
 
     private TetherChangeReceiver mTetherChangeReceiver;
 
@@ -57,10 +61,10 @@
                 .getUiAutomation()
                 .adoptShellPermissionIdentity();
         mContext = InstrumentationRegistry.getContext();
-        mCM = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mTM = (TetheringManager) mContext.getSystemService(Context.TETHERING_SERVICE);
         mTetherChangeReceiver = new TetherChangeReceiver();
         final IntentFilter filter = new IntentFilter(
-                ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
+                TetheringManager.ACTION_TETHER_STATE_CHANGED);
         final Intent intent = mContext.registerReceiver(mTetherChangeReceiver, filter);
         if (intent != null) mTetherChangeReceiver.onReceive(null, intent);
     }
@@ -81,36 +85,37 @@
 
             TetherState(Intent intent) {
                 mAvailable = intent.getStringArrayListExtra(
-                        ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+                        TetheringManager.EXTRA_AVAILABLE_TETHER);
                 mActive = intent.getStringArrayListExtra(
-                        ConnectivityManager.EXTRA_ACTIVE_TETHER);
+                        TetheringManager.EXTRA_ACTIVE_TETHER);
                 mErrored = intent.getStringArrayListExtra(
-                        ConnectivityManager.EXTRA_ERRORED_TETHER);
+                        TetheringManager.EXTRA_ERRORED_TETHER);
             }
         }
 
         @Override
         public void onReceive(Context content, Intent intent) {
             String action = intent.getAction();
-            if (action.equals(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)) {
+            if (action.equals(TetheringManager.ACTION_TETHER_STATE_CHANGED)) {
                 mResult.add(new TetherState(intent));
             }
         }
 
         public final LinkedBlockingQueue<TetherState> mResult = new LinkedBlockingQueue<>();
 
-        // This method expects either an event where one of the interfaces is active, or an event
-        // where one of the interface is available followed by one where one of the interfaces is
-        // active.
+        // This method expects either an event where one of the interfaces is active, or events
+        // where the interfaces are available followed by one event where one of the interfaces is
+        // active. Here is a typical example for wifi tethering:
+        // AVAILABLE(wlan0) -> AVAILABLE(wlan1) -> ACTIVATE(wlan1).
         public void expectActiveTethering(String[] ifaceRegexs) {
-            TetherState state = pollAndAssertNoError(DEFAULT_TIMEOUT_MS);
-            if (state == null) fail("Do not receive tethering state change broadcast");
-
-            if (isIfaceActive(ifaceRegexs, state)) return;
-
-            if (isIfaceAvailable(ifaceRegexs, state)) {
+            TetherState state = null;
+            while (true) {
                 state = pollAndAssertNoError(DEFAULT_TIMEOUT_MS);
+                if (state == null) fail("Do not receive active state change broadcast");
+
                 if (isIfaceActive(ifaceRegexs, state)) return;
+
+                if (!isIfaceAvailable(ifaceRegexs, state)) break;
             }
 
             fail("Tethering is not actived, available ifaces: " + state.mAvailable.toString()
@@ -175,16 +180,15 @@
         }
     }
 
-    private class OnStartTetheringCallback extends
-            ConnectivityManager.OnStartTetheringCallback {
+    private class StartTetheringCallback extends TetheringManager.StartTetheringCallback {
         @Override
         public void onTetheringStarted() {
             // Do nothing, TetherChangeReceiver will wait until it receives the broadcast.
         }
 
         @Override
-        public void onTetheringFailed() {
-            fail("startTethering fail");
+        public void onTetheringFailed(final int resultCode) {
+            fail("startTethering fail: " + resultCode);
         }
     }
 
@@ -206,18 +210,19 @@
 
     @Test
     public void testStartTetheringWithStateChangeBroadcast() throws Exception {
-        if (!mCM.isTetheringSupported()) return;
+        if (!mTM.isTetheringSupported()) return;
 
-        final String[] wifiRegexs = mCM.getTetherableWifiRegexs();
+        final String[] wifiRegexs = mTM.getTetherableWifiRegexs();
         if (wifiRegexs.length == 0) return;
 
         mTetherChangeReceiver.expectNoActiveTethering(0 /** timeout */);
 
-        final OnStartTetheringCallback startTetheringCallback = new OnStartTetheringCallback();
-        mCM.startTethering(ConnectivityManager.TETHERING_WIFI, true, startTetheringCallback);
+        final StartTetheringCallback startTetheringCallback = new StartTetheringCallback();
+        mTM.startTethering(new TetheringRequest.Builder(TETHERING_WIFI).build(), c -> c.run(),
+                startTetheringCallback);
         mTetherChangeReceiver.expectActiveTethering(wifiRegexs);
 
-        mCM.stopTethering(ConnectivityManager.TETHERING_WIFI);
+        mTM.stopTethering(TETHERING_WIFI);
         mTetherChangeReceiver.expectNoActiveTethering(DEFAULT_TIMEOUT_MS);
     }
 }