bluetooth: Fix interleave scan test during active scan

The test is to verify if interleave scan pauses during active scan.

When BlueZ starts discovery, it initiates a 5 seconds break for every
10.24 seconds discovery, which restarts interleave scan during the
break.

Because there is no way for a client to know the underlying state
through dbus, we fix the test by ignoring records after a long
enough pause.

BUG=b:173969508
TEST=run bluetooth_AdapterAdvMonitor.advmon_interleaved_scan

Change-Id: Idd2d79ac642569424d5cdafe2c78bc6fdf94b301
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/2563140
Reviewed-by: Archie Pusaka <apusaka@chromium.org>
Commit-Queue: Yun-Hao Chung <howardchung@google.com>
Tested-by: Yun-Hao Chung <howardchung@google.com>
diff --git a/server/cros/bluetooth/bluetooth_adapter_adv_monitor_tests.py b/server/cros/bluetooth/bluetooth_adapter_adv_monitor_tests.py
index 9ebc3d9..fba45b9 100644
--- a/server/cros/bluetooth/bluetooth_adapter_adv_monitor_tests.py
+++ b/server/cros/bluetooth/bluetooth_adapter_adv_monitor_tests.py
@@ -144,6 +144,9 @@
     # Acceptable extra/missing cycles in interleave scan
     INTERLEAVE_SCAN_CYCLE_NUM_TOLERANCE = 2
 
+    # Duration of kernel perform 'start discovery', in sec
+    DISCOVERY_DURATION = 10.24
+
     test_case_log = bluetooth_adapter_tests.test_case_log
     test_retry_and_log = bluetooth_adapter_tests.test_retry_and_log
 
@@ -723,6 +726,58 @@
                 'Span within range': span_within_range
         }
 
+    def check_records_paused(self, records, cancel_event, expect_paused_time,
+                             expect_resume):
+        """ Check if the interleave scan is paused
+
+        @param records: a list of records
+        @param cancel_event: the timestamp interleave was canceled
+        @param expect_paused_time: minimum duration of interleave scan paused
+        @param expect_resume: True if interleave scan should restart,
+                              False if ***we don't care***
+
+        @returns: a dict of {'Cancel event': (bool),
+                             'Non-empty records before paused': (bool),
+                             'Non-empty records after paused': (bool),
+                             'Paused enough time': (bool)
+                            }
+                  Note: some entries might not exist if it doesn't make sense
+                        in that case.
+
+        """
+
+        result = {}
+
+        result.update({'Cancel event': cancel_event is not None})
+        if cancel_event is None:
+            return result
+
+        canceled_time = cancel_event + self.INTERLEAVE_SCAN_CANCEL_TOLERANCE
+
+        before_paused_rec = [r for r in records if r['time'] < canceled_time]
+        after_paused_rec = [r for r in records if r['time'] >= canceled_time]
+
+        result.update({
+                'Non-empty records before paused':
+                len(before_paused_rec) != 0
+        })
+
+        if expect_resume:
+            result.update({
+                    'Non-empty records after paused':
+                    len(after_paused_rec) != 0
+            })
+
+        if len(before_paused_rec) > 0 and len(after_paused_rec) > 0:
+            # Records are stored chronologically.
+            last_time_before_paused = before_paused_rec[-1]['time']
+            first_time_after_paused = after_paused_rec[0]['time']
+            paused_time = first_time_after_paused - last_time_before_paused
+            result.update(
+                    {'Paused enough time': paused_time >= expect_paused_time})
+
+        return result
+
     def get_interleave_scan_durations(self):
         """ Get interleave scan duration.
 
@@ -800,38 +855,45 @@
         logging.debug(records)
         logging.debug(cancel_event)
 
-        self.results = {}
-
-        if cancel_event is None:
-            self.results = {'Cancel event': cancel_event}
-            return False
-
-        canceled_time = cancel_event + self.INTERLEAVE_SCAN_CANCEL_TOLERANCE
-
-        suspend_records = [r for r in records if r['time'] < canceled_time]
-        resume_records = [r for r in records if r['time'] >= canceled_time]
-
-        if len(suspend_records) == 0:
-            self.results = {'non-empty records before suspended': False}
-            return False
-
-        if len(resume_records) == 0:
-            self.results = {'non-empty records after resumed': False}
-            return False
-
-        # Records are stored chronologically.
-        # The first record is the earliest, while the last one is the oldest.
-        last_suspend = suspend_records[-1]['time']
-        first_resume = resume_records[0]['time']
-        suspend_time = first_resume - last_suspend
-
         # Currently resume time is not very reliable. It is likely the actual
         # time in sleeping is less than expect_suspend_time.
         # Check the interleave scan paused for at least one cycle long instead.
-        self.results = {
-                'Paused enough time': suspend_time >= interleave_period
-        }
+        self.results = self.check_records_paused(records, cancel_event,
+                                                 interleave_period, True)
+        return all(self.results.values())
 
+    @test_retry_and_log(False)
+    def test_interleaving_active_scan_cycle(self):
+        """ Test for checking if kernel paused interleave scan during active
+            scan.
+
+        @returns: True on success, False otherwise.
+
+        """
+        durations = self.get_interleave_scan_durations()
+        interleave_period = sum(durations.values())
+
+        # make sure we'll get some records before/after active scan
+        extra_sleep_time = 2 * interleave_period
+
+        self.interleave_logger_start()
+        time.sleep(extra_sleep_time)
+        self.test_start_discovery()
+        time.sleep(extra_sleep_time + self.INTERLEAVE_SCAN_CANCEL_TOLERANCE)
+        self.interleave_logger_stop()
+        records = self.interleave_logger_get_records()
+        cancel_event = self.interleave_logger_get_cancel_event()
+
+        logging.debug(records)
+        logging.debug(cancel_event)
+
+        # BlueZ pauses discovery for every DISCOVERY_DURATION then restarts it
+        # 5 seconds later. Interleave scan also get restarted during the paused
+        # time.
+        self.results = self.check_records_paused(records, cancel_event,
+                                                 self.DISCOVERY_DURATION,
+                                                 False)
+        self.test_stop_discovery()
         return all(self.results.values())
 
     def advmon_test_monitor_creation(self):
@@ -1589,11 +1651,9 @@
         self.test_interleaving_state(True)
 
         # Interleaving with allowlist should get paused during active scan
-        self.test_start_discovery()
-        self.test_interleaving_state(False, cycles=EXPECT_FALSE_TEST_CYCLE)
+        self.test_interleaving_active_scan_cycle()
 
         # Interleaving with allowlist should get resumed after stopping scan
-        self.test_stop_discovery()
         self.test_interleaving_state(True)
 
         # Interleaving with allowlist should get paused during system suspend,