Improve stability of APFv{4,6} multi-device tests

This commit enhances the stability of the APFv{4,6} multi-device tests by addressing several sources of flakiness and improving the test setup and validation.

Specifically, this commit:

* Increased sleep time in the test setup. This ensures APF is activated before each test, preventing unexpected behavior.
* Added sleep time in `ApfV4Test` to ensure APF remains enabled and is not disabled due to high throughput scenarios.
* Changed the validation order to first check received packets and then APF counters. This helps to more easily identify the root cause of test failures.
* Changed the Ethernet destination address from multicast to unicast in `ApfV6Test` to prevent packets from being filtered due to DTIM skip.
* Removed the `test_broadcast_arp_request_offload()` test, which was flaky due to DTIM skip and is adequately covered by the `test_unicast_arp_request_offload()` test.

Bug: 406570694
Flag: TEST_ONLY
Test: atest CtsConnectivityMultiDevicesTestCases:ApfV4Test
Test: atest CtsConnectivityMultiDevicesTestCases:ApfV6Test
Test: run cts -m CtsConnectivityMultiDevicesTestCases --skip-preconditions -t ApfV4Test --shard-count 2
Test: run cts -m CtsConnectivityMultiDevicesTestCases --skip-preconditions -t ApfV6Test --shard-count 2
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:744a17443a425e3320219ff02f9dbfa78216464e)
Merged-In: I053509459078d0bd94e16a6bf7349b948f930e6c
Change-Id: I053509459078d0bd94e16a6bf7349b948f930e6c
diff --git a/staticlibs/testutils/host/python/apf_test_base.py b/staticlibs/testutils/host/python/apf_test_base.py
index 6a62e21..9d1ea02 100644
--- a/staticlibs/testutils/host/python/apf_test_base.py
+++ b/staticlibs/testutils/host/python/apf_test_base.py
@@ -70,8 +70,8 @@
     # Enable doze mode to activate APF.
     adb_utils.set_doze_mode(self.clientDevice, True)
 
-    # wait for APF to become active.
-    time.sleep(3)
+    # Longer wait time is required for APF to become active in CTS test suite.
+    time.sleep(5)
 
   def teardown_class(self):
     adb_utils.set_doze_mode(self.clientDevice, False)
@@ -106,7 +106,15 @@
     try:
         apf_utils.start_capture_packets(self.serverDevice, self.server_iface_name)
 
-        self.send_packet_and_expect_counter_increased(send_packet, counter_name)
+        count_before_test = apf_utils.get_apf_counter(
+            self.clientDevice,
+            self.client_iface_name,
+            counter_name,
+        )
+
+        apf_utils.send_raw_packet_downstream(
+            self.serverDevice, self.server_iface_name, send_packet
+        )
 
         assert_utils.expect_with_retry(
             lambda: apf_utils.get_matched_packet_counts(
@@ -114,5 +122,15 @@
             )
             == 1
         )
+
+        assert_utils.expect_with_retry(
+            lambda: apf_utils.get_apf_counter(
+                self.clientDevice,
+                self.client_iface_name,
+                counter_name,
+            )
+            > count_before_test
+        )
+
     finally:
         apf_utils.stop_capture_packets(self.serverDevice, self.server_iface_name)
diff --git a/tests/cts/multidevices/apfv4_test.py b/tests/cts/multidevices/apfv4_test.py
index 0fc1093..2f212fc 100644
--- a/tests/cts/multidevices/apfv4_test.py
+++ b/tests/cts/multidevices/apfv4_test.py
@@ -16,6 +16,7 @@
 from mobly import asserts
 from net_tests_utils.host.python import apf_test_base, apf_utils
 from scapy.layers.l2 import Ether
+import time
 
 # Constants.
 COUNTER_DROPPED_ETHERTYPE_NOT_ALLOWED = "DROPPED_ETHERTYPE_NOT_ALLOWED"
@@ -59,6 +60,9 @@
 
     # Add zero padding up to minimum ethernet frame length
     packet = packet.ljust(MIN_PACKET_SIZE * 2, "0")
+
+    # Pause packet sending between tests to avoid APF disablement due to high throughput.
+    time.sleep(3)
     self.send_packet_and_expect_counter_increased(
         packet, COUNTER_DROPPED_ETHERTYPE_NOT_ALLOWED
     )
diff --git a/tests/cts/multidevices/apfv6_test.py b/tests/cts/multidevices/apfv6_test.py
index 9b2e309..c6e5f8d 100644
--- a/tests/cts/multidevices/apfv6_test.py
+++ b/tests/cts/multidevices/apfv6_test.py
@@ -79,33 +79,6 @@
             arp_request, "DROPPED_ARP_REQUEST_REPLIED", expected_arp_reply
         )
 
-    def test_broadcast_arp_request_offload(self):
-        eth = Ether(src=self.server_mac_address, dst='ff:ff:ff:ff:ff:ff')
-        arp = ARP(
-            op=1,
-            psrc=self.server_ipv4_addresses[0],
-            pdst=self.client_ipv4_addresses[0],
-            hwsrc=self.server_mac_address
-        )
-        arp_request = bytes(eth/arp).hex()
-
-        eth = Ether(src=self.client_mac_address, dst=self.server_mac_address)
-        arp = ARP(
-            op=2,
-            psrc=self.client_ipv4_addresses[0],
-            pdst=self.server_ipv4_addresses[0],
-            hwsrc=self.client_mac_address,
-            hwdst=self.server_mac_address
-        )
-        expected_arp_reply = bytes(eth/arp).hex()
-
-        # Add zero padding up to 60 bytes, since APFv6 ARP offload always sent out 60 bytes reply
-        expected_arp_reply = expected_arp_reply.ljust(ARP_OFFLOAD_REPLY_LEN * 2, "0")
-
-        self.send_packet_and_expect_reply_received(
-            arp_request, "DROPPED_ARP_REQUEST_REPLIED", expected_arp_reply
-        )
-
     def test_non_dad_ipv6_neighbor_solicitation_offload(self):
         eth = Ether(src=self.server_mac_address, dst=self.client_mac_address)
         ip = IPv6(src=self.server_ipv6_addresses[0], dst=self.client_ipv6_addresses[0])
@@ -158,7 +131,8 @@
 
     @apf_utils.at_least_B()
     def test_igmpv3_general_query_offload(self):
-        ether = Ether(src=self.server_mac_address, dst='01:00:5e:00:00:01')
+        # use unicast to replace multicast ether dst to prevent flaky due to DTIM skip
+        ether = Ether(src=self.server_mac_address, dst=self.client_mac_address)
         ip = IP(
             src=self.server_ipv4_addresses[0],
             dst='224.0.0.1',
@@ -206,7 +180,8 @@
     @apf_utils.at_least_B()
     @apf_utils.apf_ram_at_least(3000)
     def test_mldv2_general_query_offload(self):
-        ether = Ether(src=self.server_mac_address, dst='33:33:00:00:00:01')
+        # use unicast to replace multicast ether dst to prevent flaky due to DTIM skip
+        ether = Ether(src=self.server_mac_address, dst=self.client_mac_address)
         ip = IPv6(src=self.server_ipv6_addresses[0], dst='ff02::1', hlim=1)
         hopOpts = IPv6ExtHdrHopByHop(options=[RouterAlert(otype=5)])
         mld = ICMPv6MLQuery2()