Merge "Fix fd leak in clatStart() by closing in finally block" into main
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 375397b..4cf93a8 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -77,18 +77,6 @@
       "name": "libnetworkstats_test"
     },
     {
-      "name": "NetHttpCoverageTests",
-      "options": [
-        {
-          "exclude-annotation": "com.android.testutils.SkipPresubmit"
-        },
-        {
-          // These sometimes take longer than 1 min which is the presubmit timeout
-          "exclude-annotation": "androidx.test.filters.LargeTest"
-        }
-      ]
-    },
-    {
       "name": "CtsTetheringTestLatestSdk",
       "options": [
         {
diff --git a/netbpfload/NetBpfLoad.cpp b/netbpfload/NetBpfLoad.cpp
index 59986d4..f42e8a5 100644
--- a/netbpfload/NetBpfLoad.cpp
+++ b/netbpfload/NetBpfLoad.cpp
@@ -54,6 +54,7 @@
 namespace android {
 namespace bpf {
 
+using base::StartsWith;
 using base::EndsWith;
 using std::string;
 
@@ -228,6 +229,30 @@
     return !access("/metadata/gsi/dsu/booted", F_OK);
 }
 
+static bool hasGSM() {
+    static string ph = base::GetProperty("gsm.current.phone-type", "");
+    static bool gsm = (ph != "");
+    static bool logged = false;
+    if (!logged) {
+        logged = true;
+        ALOGI("hasGSM(gsm.current.phone-type='%s'): %s", ph.c_str(), gsm ? "true" : "false");
+    }
+    return gsm;
+}
+
+static bool isTV() {
+    if (hasGSM()) return false;  // TVs don't do GSM
+
+    static string key = base::GetProperty("ro.oem.key1", "");
+    static bool tv = StartsWith(key, "ATV00");
+    static bool logged = false;
+    if (!logged) {
+        logged = true;
+        ALOGI("isTV(ro.oem.key1='%s'): %s.", key.c_str(), tv ? "true" : "false");
+    }
+    return tv;
+}
+
 static int doLoad(char** argv, char * const envp[]) {
     const int device_api_level = android_get_device_api_level();
     const bool isAtLeastT = (device_api_level >= __ANDROID_API_T__);
@@ -278,7 +303,7 @@
 
     if (isAtLeastV && isX86() && !isKernel64Bit()) {
         ALOGE("Android V requires X86 kernel to be 64-bit.");
-        return 1;
+        if (!isTV()) return 1;
     }
 
     if (isAtLeastV) {
@@ -330,7 +355,7 @@
          * and 32-bit userspace on 64-bit kernel bpf ringbuffer compatibility is broken.
          */
         ALOGE("64-bit userspace required on 6.2+ kernels.");
-        return 1;
+        if (!isTV()) return 1;
     }
 
     // Ensure we can determine the Android build type.
diff --git a/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java b/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
index 385adc6..a92dfaf 100644
--- a/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
+++ b/service-t/src/com/android/metrics/NetworkNsdReportedMetrics.java
@@ -193,7 +193,8 @@
      * @param sentQueryCount The count of sent queries before stop discovery.
      */
     public void reportServiceDiscoveryStop(boolean isLegacy, int transactionId, long durationMs,
-            int foundCallbackCount, int lostCallbackCount, int servicesCount, int sentQueryCount) {
+            int foundCallbackCount, int lostCallbackCount, int servicesCount, int sentQueryCount,
+            boolean isServiceFromCache) {
         final Builder builder = makeReportedBuilder(isLegacy, transactionId);
         builder.setType(NsdEventType.NET_DISCOVER);
         builder.setQueryResult(MdnsQueryResult.MQR_SERVICE_DISCOVERY_STOP);
@@ -202,6 +203,7 @@
         builder.setLostCallbackCount(lostCallbackCount);
         builder.setFoundServiceCount(servicesCount);
         builder.setSentQueryCount(sentQueryCount);
+        builder.setIsKnownService(isServiceFromCache);
         mDependencies.statsWrite(builder.build());
     }
 
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index 0a8adf0..f8b0d53 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -1634,6 +1634,12 @@
                         NsdManager.nameOf(code), transactionId));
                 switch (code) {
                     case NsdManager.SERVICE_FOUND:
+                        // Set the ServiceFromCache flag only if the service is actually being
+                        // retrieved from the cache. This flag should not be overridden by later
+                        // service found event, which may not be cached.
+                        if (event.mIsServiceFromCache) {
+                            request.setServiceFromCache(true);
+                        }
                         clientInfo.onServiceFound(clientRequestId, info, request);
                         break;
                     case NsdManager.SERVICE_LOST:
@@ -2887,7 +2893,8 @@
                                 request.getFoundServiceCount(),
                                 request.getLostServiceCount(),
                                 request.getServicesCount(),
-                                request.getSentQueryCount());
+                                request.getSentQueryCount(),
+                                request.isServiceFromCache());
                     } else if (listener instanceof ResolutionListener) {
                         mMetrics.reportServiceResolutionStop(false /* isLegacy */, transactionId,
                                 request.calculateRequestDurationMs(mClock.elapsedRealtime()),
@@ -2927,7 +2934,8 @@
                                 request.getFoundServiceCount(),
                                 request.getLostServiceCount(),
                                 request.getServicesCount(),
-                                NO_SENT_QUERY_COUNT);
+                                NO_SENT_QUERY_COUNT,
+                                request.isServiceFromCache());
                         break;
                     case NsdManager.RESOLVE_SERVICE:
                         stopResolveService(transactionId);
@@ -3050,7 +3058,8 @@
                     request.getFoundServiceCount(),
                     request.getLostServiceCount(),
                     request.getServicesCount(),
-                    request.getSentQueryCount());
+                    request.getSentQueryCount(),
+                    request.isServiceFromCache());
             try {
                 mCb.onStopDiscoverySucceeded(listenerKey);
             } catch (RemoteException e) {
diff --git a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
index cd7ea94..90fb7a9 100644
--- a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
+++ b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
@@ -192,8 +192,8 @@
             futureReply!!.complete(recvbuf.sliceArray(8..<length))
         }
 
-        fun sendPing(data: ByteArray) {
-            require(data.size == 56)
+        fun sendPing(data: ByteArray, payloadSize: Int) {
+            require(data.size == payloadSize)
 
             // rfc4443#section-4.1: Echo Request Message
             //   0                   1                   2                   3
@@ -415,8 +415,13 @@
         readProgram() // wait for install completion
 
         // Assert that initial ping does not get filtered.
-        val data = ByteArray(56).also { Random.nextBytes(it) }
-        packetReader.sendPing(data)
+        val payloadSize = if (getFirstApiLevel() >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            68
+        } else {
+            4
+        }
+        val data = ByteArray(payloadSize).also { Random.nextBytes(it) }
+        packetReader.sendPing(data, payloadSize)
         assertThat(packetReader.expectPingReply()).isEqualTo(data)
 
         // Generate an APF program that drops the next ping
@@ -436,7 +441,7 @@
         installProgram(program)
         readProgram() // wait for install completion
 
-        packetReader.sendPing(data)
+        packetReader.sendPing(data, payloadSize)
         packetReader.expectPingDropped()
     }
 
@@ -478,8 +483,13 @@
         readProgram() // wait for install completion
 
         // Trigger the program by sending a ping and waiting on the reply.
-        val data = ByteArray(56).also { Random.nextBytes(it) }
-        packetReader.sendPing(data)
+        val payloadSize = if (getFirstApiLevel() >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            68
+        } else {
+            4
+        }
+        val data = ByteArray(payloadSize).also { Random.nextBytes(it) }
+        packetReader.sendPing(data, payloadSize)
         packetReader.expectPingReply()
 
         val readResult = readProgram()
@@ -487,8 +497,8 @@
         expect.withMessage("PROGRAM_SIZE").that(buffer.getInt()).isEqualTo(program.size)
         expect.withMessage("RAM_LEN").that(buffer.getInt()).isEqualTo(caps.maximumApfProgramSize)
         expect.withMessage("IPV4_HEADER_SIZE").that(buffer.getInt()).isEqualTo(0)
-        // Ping packet (64) + IPv6 header (40) + ethernet header (14)
-        expect.withMessage("PACKET_SIZE").that(buffer.getInt()).isEqualTo(64 + 40 + 14)
+        // Ping packet payload + ICMPv6 header (8)  + IPv6 header (40) + ethernet header (14)
+        expect.withMessage("PACKET_SIZE").that(buffer.getInt()).isEqualTo(payloadSize + 8 + 40 + 14)
         expect.withMessage("FILTER_AGE_SECONDS").that(buffer.getInt()).isLessThan(5)
     }
 
@@ -496,6 +506,10 @@
     @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
     @Test
     fun testFilterAgeIncreasesBetweenPackets() {
+        // VSR-14 mandates APF to be turned on when the screen is off and the Wi-Fi link
+        // is idle or traffic is less than 10 Mbps. Before that, we don't mandate when the APF
+        // should be turned on.
+        assume().that(getVsrApiLevel()).isAtLeast(34)
         assumeApfVersionSupportAtLeast(4)
         clearApfMemory()
         val gen = ApfV4Generator(4)
@@ -512,8 +526,9 @@
         installProgram(gen.generate())
         readProgram() // wait for install completion
 
-        val data = ByteArray(56).also { Random.nextBytes(it) }
-        packetReader.sendPing(data)
+        val payloadSize = 56
+        val data = ByteArray(payloadSize).also { Random.nextBytes(it) }
+        packetReader.sendPing(data, payloadSize)
         packetReader.expectPingReply()
 
         var buffer = ByteBuffer.wrap(readProgram(), counterRegion, 4 /* length */)
@@ -521,7 +536,7 @@
 
         Thread.sleep(5100)
 
-        packetReader.sendPing(data)
+        packetReader.sendPing(data, payloadSize)
         packetReader.expectPingReply()
 
         buffer = ByteBuffer.wrap(readProgram(), counterRegion, 4 /* length */)
diff --git a/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt b/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt
index aa28e5a..10ba6a4 100644
--- a/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt
+++ b/tests/unit/java/com/android/metrics/NetworkNsdReportedMetricsTest.kt
@@ -163,7 +163,8 @@
         val sentQueryCount = 150
         val metrics = NetworkNsdReportedMetrics(clientId, deps)
         metrics.reportServiceDiscoveryStop(true /* isLegacy */, transactionId, durationMs,
-                foundCallbackCount, lostCallbackCount, servicesCount, sentQueryCount)
+                foundCallbackCount, lostCallbackCount, servicesCount, sentQueryCount,
+                true /* isServiceFromCache */)
 
         val eventCaptor = ArgumentCaptor.forClass(NetworkNsdReported::class.java)
         verify(deps).statsWrite(eventCaptor.capture())
@@ -179,6 +180,7 @@
             assertEquals(servicesCount, it.foundServiceCount)
             assertEquals(durationMs, it.eventDurationMillisec)
             assertEquals(sentQueryCount, it.sentQueryCount)
+            assertTrue(it.isKnownService)
         }
     }
 
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index aece3f7..979e0a1 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -1196,7 +1196,7 @@
                 Instant.MAX /* expirationTime */);
 
         // Verify onServiceNameDiscovered callback
-        listener.onServiceNameDiscovered(foundInfo, false /* isServiceFromCache */);
+        listener.onServiceNameDiscovered(foundInfo, true /* isServiceFromCache */);
         verify(discListener, timeout(TIMEOUT_MS)).onServiceFound(argThat(info ->
                 info.getServiceName().equals(SERVICE_NAME)
                         // Service type in discovery callbacks has a dot at the end
@@ -1232,7 +1232,7 @@
         verify(mSocketProvider, timeout(CLEANUP_DELAY_MS + TIMEOUT_MS)).requestStopWhenInactive();
         verify(mMetrics).reportServiceDiscoveryStop(false /* isLegacy */, discId,
                 10L /* durationMs */, 1 /* foundCallbackCount */, 1 /* lostCallbackCount */,
-                1 /* servicesCount */, 3 /* sentQueryCount */);
+                1 /* servicesCount */, 3 /* sentQueryCount */, true /* isServiceFromCache */);
     }
 
     @Test
diff --git a/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java b/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java
index 0e76930..996d22d 100644
--- a/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java
@@ -30,6 +30,8 @@
 import android.net.thread.ActiveOperationalDataset.Builder;
 import android.net.thread.ActiveOperationalDataset.SecurityPolicy;
 import android.net.thread.OperationalDatasetTimestamp;
+import android.net.thread.utils.ThreadFeatureCheckerRule;
+import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
 import android.util.SparseArray;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -38,6 +40,7 @@
 import com.google.common.primitives.Bytes;
 import com.google.common.testing.EqualsTester;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -46,6 +49,7 @@
 
 /** CTS tests for {@link ActiveOperationalDataset}. */
 @SmallTest
+@RequiresThreadFeature
 @RunWith(AndroidJUnit4.class)
 public final class ActiveOperationalDatasetTest {
     private static final int TYPE_ACTIVE_TIMESTAMP = 14;
@@ -81,6 +85,8 @@
     private static final ActiveOperationalDataset DEFAULT_DATASET =
             ActiveOperationalDataset.fromThreadTlvs(VALID_DATASET_TLVS);
 
+    @Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
+
     private static byte[] removeTlv(byte[] dataset, int type) {
         ByteArrayOutputStream os = new ByteArrayOutputStream(dataset.length);
         int i = 0;
diff --git a/thread/tests/cts/src/android/net/thread/cts/OperationalDatasetTimestampTest.java b/thread/tests/cts/src/android/net/thread/cts/OperationalDatasetTimestampTest.java
index 9be3d56..4d7c7f1 100644
--- a/thread/tests/cts/src/android/net/thread/cts/OperationalDatasetTimestampTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/OperationalDatasetTimestampTest.java
@@ -21,12 +21,15 @@
 import static org.junit.Assert.assertThrows;
 
 import android.net.thread.OperationalDatasetTimestamp;
+import android.net.thread.utils.ThreadFeatureCheckerRule;
+import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.google.common.testing.EqualsTester;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -34,8 +37,11 @@
 
 /** Tests for {@link OperationalDatasetTimestamp}. */
 @SmallTest
+@RequiresThreadFeature
 @RunWith(AndroidJUnit4.class)
 public final class OperationalDatasetTimestampTest {
+    @Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
+
     @Test
     public void fromInstant_tooLargeInstant_throwsIllegalArgument() {
         assertThrows(
diff --git a/thread/tests/cts/src/android/net/thread/cts/PendingOperationalDatasetTest.java b/thread/tests/cts/src/android/net/thread/cts/PendingOperationalDatasetTest.java
index 0bb18ce..76be054 100644
--- a/thread/tests/cts/src/android/net/thread/cts/PendingOperationalDatasetTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/PendingOperationalDatasetTest.java
@@ -28,6 +28,8 @@
 import android.net.thread.ActiveOperationalDataset.SecurityPolicy;
 import android.net.thread.OperationalDatasetTimestamp;
 import android.net.thread.PendingOperationalDataset;
+import android.net.thread.utils.ThreadFeatureCheckerRule;
+import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
 import android.util.SparseArray;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -36,6 +38,7 @@
 import com.google.common.primitives.Bytes;
 import com.google.common.testing.EqualsTester;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -44,8 +47,11 @@
 
 /** Tests for {@link PendingOperationalDataset}. */
 @SmallTest
+@RequiresThreadFeature
 @RunWith(AndroidJUnit4.class)
 public final class PendingOperationalDatasetTest {
+    @Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
+
     private static ActiveOperationalDataset createActiveDataset() throws Exception {
         SparseArray<byte[]> channelMask = new SparseArray<>(1);
         channelMask.put(0, new byte[] {0x00, 0x1f, (byte) 0xff, (byte) 0xe0});
diff --git a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkExceptionTest.java b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkExceptionTest.java
index 7d9ae81..4de2e13 100644
--- a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkExceptionTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkExceptionTest.java
@@ -25,17 +25,23 @@
 import static org.junit.Assert.assertThrows;
 
 import android.net.thread.ThreadNetworkException;
+import android.net.thread.utils.ThreadFeatureCheckerRule;
+import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 /** CTS tests for {@link ThreadNetworkException}. */
 @SmallTest
+@RequiresThreadFeature
 @RunWith(AndroidJUnit4.class)
 public final class ThreadNetworkExceptionTest {
+    @Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
+
     @Test
     public void constructor_validValues_valuesAreConnectlySet() throws Exception {
         ThreadNetworkException errorThreadDisabled =
diff --git a/thread/tests/utils/src/android/net/thread/utils/ThreadFeatureCheckerRule.java b/thread/tests/utils/src/android/net/thread/utils/ThreadFeatureCheckerRule.java
index bee9ceb..38a6e90 100644
--- a/thread/tests/utils/src/android/net/thread/utils/ThreadFeatureCheckerRule.java
+++ b/thread/tests/utils/src/android/net/thread/utils/ThreadFeatureCheckerRule.java
@@ -21,7 +21,6 @@
 import static org.junit.Assume.assumeTrue;
 
 import android.content.Context;
-import android.net.thread.ThreadNetworkManager;
 import android.os.SystemProperties;
 import android.os.VintfRuntimeInfo;
 
@@ -122,7 +121,10 @@
     /** Returns {@code true} if this device has the Thread feature supported. */
     private static boolean hasThreadFeature() {
         final Context context = ApplicationProvider.getApplicationContext();
-        return context.getSystemService(ThreadNetworkManager.class) != null;
+
+        // Use service name rather than `ThreadNetworkManager.class` to avoid
+        // `ClassNotFoundException` on U- devices.
+        return context.getSystemService("thread_network") != null;
     }
 
     /**