Make ListeningPortsTest less flaky.

When a DNS lookup occurs, a new listening UDP socket will be
created to receive the DNS response.  This listening socket is
only temporary, and shouldn't count as a permanent open
socket.

Bug: 3276283

Change-Id: I45090c0e07d9b360cc26f4bce23833db8c399507
diff --git a/tests/tests/net/src/android/net/cts/ListeningPortsTest.java b/tests/tests/net/src/android/net/cts/ListeningPortsTest.java
index ff6b4e9..0a32bd7 100644
--- a/tests/tests/net/src/android/net/cts/ListeningPortsTest.java
+++ b/tests/tests/net/src/android/net/cts/ListeningPortsTest.java
@@ -23,6 +23,7 @@
 import java.util.Scanner;
 import java.util.regex.Pattern;
 
+import junit.framework.AssertionFailedError;
 import junit.framework.TestCase;
 
 public class ListeningPortsTest extends TestCase {
@@ -50,12 +51,40 @@
         EXCEPTION_PATTERNS.add("[0]{16}[F]{4}[0]{4}[0-9A-F]{6}7F:[0-9A-F]{4}"); // IPv4-6 Conversion
     }
 
-    public static void testNoListeningPorts() {
-        final boolean isTcp = true;
-        assertNoListeningPorts("/proc/net/tcp", isTcp);
-        assertNoListeningPorts("/proc/net/tcp6", isTcp);
-        assertNoListeningPorts("/proc/net/udp", !isTcp);
-        assertNoListeningPorts("/proc/net/udp6", !isTcp);
+    public void testNoListeningTcpPorts() {
+        assertNoListeningPorts("/proc/net/tcp", true);
+    }
+
+    public void testNoListeningTcp6Ports() {
+        assertNoListeningPorts("/proc/net/tcp6", true);
+    }
+
+    public void testNoListeningUdpPorts() throws Exception {
+        assertNoListeningUdpPorts("/proc/net/udp");
+    }
+
+    public void testNoListeningUdp6Ports() throws Exception {
+        assertNoListeningUdpPorts("/proc/net/udp6");
+    }
+
+    private static final int RETRIES_MAX = 4;
+
+    /**
+     * UDP tests can be flaky due to DNS lookups.  Compensate.
+     */
+    private static void assertNoListeningUdpPorts(String procFilePath) throws Exception {
+        for (int i = 0; i < RETRIES_MAX; i++) {
+            try {
+                assertNoListeningPorts(procFilePath, false);
+                return;
+            } catch (ListeningPortsAssertionError e) {
+                if (i == RETRIES_MAX - 1) {
+                    throw e;
+                }
+                Thread.sleep(2 * 1000 * i);
+            }
+        }
+        throw new IllegalStateException("unreachable");
     }
 
     private static void assertNoListeningPorts(String procFilePath, boolean isTcp) {
@@ -94,7 +123,8 @@
                         isAddress(localAddress));
 
                 if (!isException(localAddress) && isPortListening(state, isTcp)) {
-                    fail("Found port listening on " + localAddress + " in " + procFilePath);
+                    throw new ListeningPortsAssertionError(
+                            "Found port listening on " + localAddress + " in " + procFilePath);
                 }
             }
         } catch (FileNotFoundException notFound) {
@@ -128,4 +158,10 @@
         String listeningState = isTcp ? "0A" : "07";
         return listeningState.equals(state);
     }
+
+    private static class ListeningPortsAssertionError extends AssertionFailedError {
+        private ListeningPortsAssertionError(String msg) {
+            super(msg);
+        }
+    }
 }