Add CTS testing for ConnectivityDiagnostics Data Stall callback.

Verify that onDataStallSuspected() is invoked by the System when
expected.

ConnectivityDiagnosticsManager provides an API for registering callbacks
with the System. These callbacks allow the System to notify registered
and permissioned callbacks on Network validation, suspected data stalls,
and Network connectivity reported.

Bug: 148032944
Test: atest ConnectivityDiagnosticsManagerTest
Change-Id: If6ceae9d2bbcabf88298d2d8c39cad5275fbd1ef
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
index 6687fdc..8cacb43 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
@@ -23,6 +23,12 @@
 import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_VALIDATION_RESULT;
 import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.NETWORK_VALIDATION_RESULT_VALID;
 import static android.net.ConnectivityDiagnosticsManager.DataStallReport;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport.DETECTION_METHOD_DNS_EVENTS;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport.DETECTION_METHOD_TCP_METRICS;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_DNS_CONSECUTIVE_TIMEOUTS;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport.KEY_TCP_PACKET_FAIL_RATE;
+import static android.net.ConnectivityDiagnosticsManager.persistableBundleEquals;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
 import static android.net.NetworkCapabilities.TRANSPORT_TEST;
@@ -71,6 +77,10 @@
 public class ConnectivityDiagnosticsManagerTest {
     private static final int CALLBACK_TIMEOUT_MILLIS = 5000;
     private static final int NO_CALLBACK_INVOKED_TIMEOUT = 500;
+    private static final long TIMESTAMP = 123456789L;
+    private static final int DNS_CONSECUTIVE_TIMEOUTS = 5;
+    private static final int COLLECTION_PERIOD_MILLIS = 5000;
+    private static final int FAIL_RATE_PERCENTAGE = 100;
 
     private static final Executor INLINE_EXECUTOR = x -> x.run();
 
@@ -167,6 +177,46 @@
     }
 
     @Test
+    public void testOnDataStallSuspected_DnsEvents() throws Exception {
+        final PersistableBundle extras = new PersistableBundle();
+        extras.putInt(KEY_DNS_CONSECUTIVE_TIMEOUTS, DNS_CONSECUTIVE_TIMEOUTS);
+
+        verifyOnDataStallSuspected(DETECTION_METHOD_DNS_EVENTS, TIMESTAMP, extras);
+    }
+
+    @Test
+    public void testOnDataStallSuspected_TcpMetrics() throws Exception {
+        final PersistableBundle extras = new PersistableBundle();
+        extras.putInt(KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS, COLLECTION_PERIOD_MILLIS);
+        extras.putInt(KEY_TCP_PACKET_FAIL_RATE, FAIL_RATE_PERCENTAGE);
+
+        verifyOnDataStallSuspected(DETECTION_METHOD_TCP_METRICS, TIMESTAMP, extras);
+    }
+
+    private void verifyOnDataStallSuspected(
+            int detectionMethod, long timestampMillis, @NonNull PersistableBundle extras)
+            throws Exception {
+        mTestNetwork = setUpTestNetwork();
+
+        final TestConnectivityDiagnosticsCallback cb = new TestConnectivityDiagnosticsCallback();
+        mCdm.registerConnectivityDiagnosticsCallback(TEST_NETWORK_REQUEST, INLINE_EXECUTOR, cb);
+
+        final String interfaceName =
+                mConnectivityManager.getLinkProperties(mTestNetwork).getInterfaceName();
+
+        cb.expectOnConnectivityReportAvailable(mTestNetwork, interfaceName);
+
+        runWithShellPermissionIdentity(
+                () -> mConnectivityManager.simulateDataStall(
+                        detectionMethod, timestampMillis, mTestNetwork, extras),
+                android.Manifest.permission.MANAGE_TEST_NETWORKS);
+
+        cb.expectOnDataStallSuspected(
+                mTestNetwork, interfaceName, detectionMethod, timestampMillis, extras);
+        cb.assertNoCallback();
+    }
+
+    @Test
     public void testOnNetworkConnectivityReportedTrue() throws Exception {
         verifyOnNetworkConnectivityReported(true /* hasConnectivity */);
     }
@@ -278,6 +328,27 @@
             assertTrue("PROBES_ATTEMPTED mask not in expected range", probesAttempted >= 0);
         }
 
+        public void expectOnDataStallSuspected(
+                @NonNull Network network,
+                @NonNull String interfaceName,
+                int detectionMethod,
+                long timestampMillis,
+                @NonNull PersistableBundle extras) {
+            final DataStallReport result =
+                    (DataStallReport) mHistory.poll(CALLBACK_TIMEOUT_MILLIS, x -> true);
+            assertEquals(network, result.getNetwork());
+            assertEquals(detectionMethod, result.getDetectionMethod());
+            assertEquals(timestampMillis, result.getReportTimestamp());
+
+            final NetworkCapabilities nc = result.getNetworkCapabilities();
+            assertNotNull(nc);
+            assertTrue(nc.hasTransport(TRANSPORT_TEST));
+            assertNotNull(result.getLinkProperties());
+            assertEquals(interfaceName, result.getLinkProperties().getInterfaceName());
+
+            assertTrue(persistableBundleEquals(extras, result.getStallDetails()));
+        }
+
         public void expectOnNetworkConnectivityReported(
                 @NonNull Network network, boolean hasConnectivity) {
             final Pair<Network, Boolean> result =