Merge "Modify basic coex tests to be inline with new coexbasetest."
diff --git a/acts/framework/acts/controllers/android_device.py b/acts/framework/acts/controllers/android_device.py
index f66d6ce..1f8bbce 100755
--- a/acts/framework/acts/controllers/android_device.py
+++ b/acts/framework/acts/controllers/android_device.py
@@ -473,7 +473,8 @@
             'serial': self.serial,
             'model': self.model,
             'build_info': self.build_info,
-            'user_added_info': self._user_added_device_info
+            'user_added_info': self._user_added_device_info,
+            'flavor': self.flavor
         }
         return info
 
@@ -526,6 +527,11 @@
             return self.adb.getprop("ro.product.name").lower()
 
     @property
+    def flavor(self):
+        """Returns the specific flavor of Android build the device is using."""
+        return self.adb.getprop("ro.build.flavor").lower()
+
+    @property
     def droid(self):
         """Returns the RPC Service of the first Sl4aSession created."""
         if len(self._sl4a_manager.sessions) > 0:
diff --git a/acts/framework/acts/controllers/sl4a_lib/rpc_client.py b/acts/framework/acts/controllers/sl4a_lib/rpc_client.py
index e08ffb0..2f40bee 100644
--- a/acts/framework/acts/controllers/sl4a_lib/rpc_client.py
+++ b/acts/framework/acts/controllers/sl4a_lib/rpc_client.py
@@ -276,8 +276,9 @@
                     break
         except BrokenPipeError as e:
             if self.is_alive:
-                self._log.exception('Exception %s happened for sl4a call %s',
-                                    e, method)
+                self._log.exception('The device disconnected during RPC call '
+                                    '%s. Please check the logcat for a crash '
+                                    'or disconnect.', method)
                 self.on_error(connection)
             else:
                 self._log.warning('The connection was killed during cleanup:')
diff --git a/acts/framework/acts/test_utils/coex/audio_capture.py b/acts/framework/acts/test_utils/coex/audio_capture.py
deleted file mode 100644
index bb29dcc..0000000
--- a/acts/framework/acts/test_utils/coex/audio_capture.py
+++ /dev/null
@@ -1,159 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2018 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may not
-# use this file except in compliance with the License. You may obtain a copy of
-# the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-
-import argparse
-import json
-import logging
-import os
-import pyaudio
-import wave
-
-RECORD_FILE_TEMPLATE = 'recorded_audio_%s.wav'
-
-
-class DeviceNotFound(Exception):
-    """Raises exception if audio capture device is not found."""
-
-# TODO: (@sairamganesh) This class will be deprecated for
-# ../acts/test_utils/coex/audio_capture_device.py
-
-
-class AudioCapture:
-
-    def __init__(self, test_params, path):
-        """Creates object to pyaudio and defines audio parameters.
-
-        Args:
-            test_params: Audio parameters fetched from config.
-            path: Result path.
-        """
-        self.audio = pyaudio.PyAudio()
-        self.audio_format = pyaudio.paInt16
-        self.audio_params = test_params
-        self.channels = self.audio_params["channel"]
-        self.chunk = self.audio_params["chunk"]
-        self.sample_rate = self.audio_params["sample_rate"]
-        self.file_counter = 0
-        self.__input_device = None
-        self.record_file_template = os.path.join(path, RECORD_FILE_TEMPLATE)
-        if not self.audio_params["ssh_config"]:
-            self.__input_device = self.__get_input_device()
-
-    @property
-    def name(self):
-        try:
-            return self.audio_params["ssh_config"]["host"]
-        except KeyError:
-            return self.__input_device["name"]
-
-    def __get_input_device(self):
-        """Checks for the audio capture device."""
-        if self.__input_device is None:
-            for i in range(self.audio.get_device_count()):
-                device_info = self.audio.get_device_info_by_index(i)
-                logging.info("Device Information {}".format(device_info))
-                if self.audio_params['input_device'] in device_info['name']:
-                    self.__input_device = device_info
-                    break
-            else:
-                logging.error("Audio Capture device {} not found.".format(
-                    self.audio_params["input_device"]))
-                raise DeviceNotFound("Audio Capture Input device not found")
-        return self.__input_device
-
-    def capture_and_store_audio(self, trim_beginning=0, trim_end=0):
-        """Records the A2DP streaming.
-
-        Args:
-            trim_beginning: how many seconds to trim from the beginning
-            trim_end: how many seconds to trim from the end
-        """
-        if self.audio_params['ssh_config']:
-            self.__input_device = self.__get_input_device()
-        stream = self.audio.open(
-            format=self.audio_format,
-            channels=self.channels,
-            rate=self.sample_rate,
-            input=True,
-            frames_per_buffer=self.chunk,
-            input_device_index=self.__input_device['index'])
-        frames = []
-        b_chunks = trim_beginning * (self.sample_rate // self.chunk)
-        e_chunks = trim_end * (self.sample_rate // self.chunk)
-        total_chunks = self.sample_rate // self.chunk * self.audio_params[
-            'record_duration']
-        for i in range(total_chunks):
-            try:
-                data = stream.read(self.chunk, exception_on_overflow=False)
-            except IOError as ex:
-                logging.error("Cannot record audio :{}".format(ex))
-                return False
-            if b_chunks <= i < total_chunks - e_chunks:
-                frames.append(data)
-
-        stream.stop_stream()
-        stream.close()
-        status = self.write_record_file(frames)
-        return status
-
-    def last_fileno(self):
-        return self.next_fileno() - 1
-
-    def next_fileno(self):
-        counter = 0
-        while os.path.exists(self.record_file_template % counter):
-            counter += 1
-        return counter
-
-    def write_record_file(self, frames):
-        """Writes the recorded audio into the file.
-
-        Args:
-            frames: Recorded audio frames.
-        """
-        file_name = self.record_file_template % self.next_fileno()
-        logging.info('writing to %s' % file_name)
-        wf = wave.open(file_name, 'wb')
-        wf.setnchannels(self.channels)
-        wf.setsampwidth(self.audio.get_sample_size(self.audio_format))
-        wf.setframerate(self.sample_rate)
-        wf.writeframes(b''.join(frames))
-        wf.close()
-        return True
-
-    def terminate_audio(self):
-        """Terminates the pulse audio instance."""
-        self.audio.terminate()
-
-
-if __name__ == '__main__':
-    parser = argparse.ArgumentParser()
-    parser.add_argument(
-        '-p',
-        '--path',
-        type=str,
-        help="Contains path where the recorded files to be stored")
-    parser.add_argument(
-        '-t',
-        '--test_params',
-        type=json.loads,
-        help="Contains sample rate, channels,"
-             " chunk and device index for recording.")
-    args = parser.parse_args()
-    audio = AudioCapture(args.test_params, args.path)
-    audio.capture_and_store_audio(args.test_params['trim_beginning'],
-                                  args.test_params['trim_end'])
-    audio.terminate_audio()
diff --git a/acts/framework/acts/test_utils/coex/audio_capture_device.py b/acts/framework/acts/test_utils/coex/audio_capture_device.py
index 924bf4a..f99f6a8 100644
--- a/acts/framework/acts/test_utils/coex/audio_capture_device.py
+++ b/acts/framework/acts/test_utils/coex/audio_capture_device.py
@@ -57,6 +57,21 @@
     def last_fileno(self):
         return self.next_fileno - 1
 
+    @property
+    def get_last_record_duration_millis(self):
+        """Get duration of most recently recorded file.
+
+        Returns:
+            duration (float): duration of recorded file in milliseconds.
+        """
+        latest_file_path = self.wave_file % self.last_fileno
+        print (latest_file_path)
+        with wave.open(latest_file_path, 'r') as f:
+            frames = f.getnframes()
+            rate = f.getframerate()
+            duration = (frames / float(rate)) * 1000
+        return duration
+
     def write_record_file(self, audio_params, frames):
         """Writes the recorded audio into the file.
 
diff --git a/acts/framework/acts/test_utils/coex/audio_test_utils.py b/acts/framework/acts/test_utils/coex/audio_test_utils.py
index b514712..02b99ca 100644
--- a/acts/framework/acts/test_utils/coex/audio_test_utils.py
+++ b/acts/framework/acts/test_utils/coex/audio_test_utils.py
@@ -16,16 +16,12 @@
 
 import logging
 import os
-import wave
 
+from acts.test_utils.coex.audio_capture_device import AudioCaptureBase
 from acts.test_utils.coex.audio_capture_device import CaptureAudioOverAdb
 from acts.test_utils.coex.audio_capture_device import CaptureAudioOverLocal
-from acts.controllers.utils_lib.ssh import connection
-from acts.controllers.utils_lib.ssh import settings
 from acts.test_utils.audio_analysis_lib import audio_analysis
 from acts.test_utils.audio_analysis_lib.check_quality import quality_analysis
-from acts.test_utils.coex.audio_capture import AudioCapture
-from acts.test_utils.coex.audio_capture import RECORD_FILE_TEMPLATE
 
 ANOMALY_DETECTION_BLOCK_SIZE = audio_analysis.ANOMALY_DETECTION_BLOCK_SIZE
 ANOMALY_GROUPING_TOLERANCE = audio_analysis.ANOMALY_GROUPING_TOLERANCE
@@ -67,58 +63,18 @@
 class FileNotFound(Exception):
     """Raises Exception if file is not present"""
 
-# TODO @sairamganesh Rename this class to AudioCaptureResult and
-# remove duplicates which are in ../test_utils/coex/audio_capture_device.py.
 
+class AudioCaptureResult(AudioCaptureBase):
 
-class SshAudioCapture(AudioCapture):
-
-    def __init__(self, test_params, path):
-        super(SshAudioCapture, self).__init__(test_params, path)
-        self.remote_path = path
-        self.ssh_session = None
-
-    def capture_audio(self, trim_beginning=0, trim_end=0):
-        """Captures audio and store results.
+    def __init__(self, path):
+        """Initializes Audio Capture Result class.
 
         Args:
-            trim_beginning: To trim audio at the start in seconds.
-            trim_end: To trim audio at the end in seconds.
-
-        Returns:
-            Returns exit status of ssh connection.
+            path: Path of audio capture result.
         """
-        if not trim_beginning:
-            trim_beginning = self.audio_params.get('trim_beginning', 0)
-        if not trim_end:
-            trim_end = self.audio_params.get('trim_end', 0)
-        if self.audio_params["ssh_config"]:
-            ssh_settings = settings.from_config(
-                self.audio_params["ssh_config"])
-            self.ssh_session = connection.SshConnection(ssh_settings)
-            cur_path = os.path.dirname(os.path.realpath(__file__))
-            local_path = os.path.join(cur_path, "audio_capture.py")
-            self.ssh_session.send_file(local_path,
-                                       self.audio_params["dest_path"])
-            path = self.audio_params["dest_path"]
-            test_params = str(self.audio_params).replace("\'", "\"")
-            self.cmd = "python3 audio_capture.py -p '{}' -t '{}'".format(
-                path, test_params)
-            job_result = self.ssh_session.run(self.cmd)
-            logging.debug("Job Result {}".format(job_result.stdout))
-            self.ssh_session.pull_file(
-                self.remote_path, os.path.join(
-                    self.audio_params["dest_path"], "*.wav"))
-            return bool(not job_result.exit_status)
-        else:
-            return self.capture_and_store_audio(trim_beginning, trim_end)
-
-    def terminate_and_store_audio_results(self):
-        """Terminates audio and stores audio files."""
-        if self.audio_params["ssh_config"]:
-            self.ssh_session.run('rm *.wav', ignore_status=True)
-        else:
-            self.terminate_audio()
+        super().__init__()
+        self.path = path
+        self.analysis_path = os.path.join(self.log_path, ANALYSIS_FILE_TEMPLATE)
 
     def THDN(self, win_size=None, step_size=None, q=1, freq=None):
         """Calculate THD+N value for most recently recorded file.
@@ -137,13 +93,12 @@
             channel_results (list): THD+N value for each channel's signal.
                 List index corresponds to channel index.
         """
-        latest_file_path = self.record_file_template % self.last_fileno()
         if not (win_size and step_size):
-            return audio_analysis.get_file_THDN(filename=latest_file_path,
+            return audio_analysis.get_file_THDN(filename=self.path,
                                                 q=q,
                                                 freq=freq)
         else:
-            return audio_analysis.get_file_max_THDN(filename=latest_file_path,
+            return audio_analysis.get_file_max_THDN(filename=self.path,
                                                     step_size=step_size,
                                                     window_size=win_size,
                                                     q=q,
@@ -172,28 +127,22 @@
             channel_results (list): anomaly durations for each channel's signal.
                 List index corresponds to channel index.
         """
-        latest_file_path = self.record_file_template % self.last_fileno()
         return audio_analysis.get_file_anomaly_durations(
-            filename=latest_file_path,
+            filename=self.path,
             freq=freq,
             block_size=block_size,
             threshold=threshold,
             tolerance=tolerance)
 
-    def get_last_record_duration_millis(self):
-        """Get duration of most recently recorded file.
+    @property
+    def analysis_fileno(self):
+        """Returns the file number to dump audio analysis results."""
+        counter = 0
+        while os.path.exists(self.analysis_path % counter):
+            counter += 1
+        return counter
 
-        Returns:
-            duration (float): duration of recorded file in milliseconds.
-        """
-        latest_file_path = self.record_file_template % self.last_fileno()
-        with wave.open(latest_file_path, 'r') as f:
-            frames = f.getnframes()
-            rate = f.getframerate()
-            duration = (frames / float(rate)) * 1000
-        return duration
-
-    def audio_quality_analysis(self, path):
+    def audio_quality_analysis(self, audio_params):
         """Measures audio quality based on the audio file given as input.
 
         Args:
@@ -202,19 +151,16 @@
         Returns:
             analysis_path on success.
         """
-        dest_file_path = os.path.join(path,
-                RECORD_FILE_TEMPLATE % self.last_fileno())
-        analysis_path = os.path.join(path,
-                ANALYSIS_FILE_TEMPLATE % self.last_fileno())
-        if not os.path.exists(dest_file_path):
+        analysis_path = self.analysis_path % self.analysis_fileno
+        if not os.path.exists(self.path):
             raise FileNotFound("Recorded file not found")
         try:
             quality_analysis(
-                filename=dest_file_path,
+                filename=self.path,
                 output_file=analysis_path,
                 bit_width=bits_per_sample,
-                rate=self.audio_params["sample_rate"],
-                channel=self.audio_params["channel"],
+                rate=audio_params["sample_rate"],
+                channel=audio_params["channel"],
                 spectral_only=False)
         except Exception as err:
             logging.exception("Failed to analyze raw audio: %s" % err)
diff --git a/acts/framework/acts/test_utils/power/PowerBaseTest.py b/acts/framework/acts/test_utils/power/PowerBaseTest.py
index 2cd8509..53fbc7a 100644
--- a/acts/framework/acts/test_utils/power/PowerBaseTest.py
+++ b/acts/framework/acts/test_utils/power/PowerBaseTest.py
@@ -189,6 +189,14 @@
         self.power_logger.set_avg_power(self.power_result.metric_value)
         self.power_logger.set_testbed(self.testbed_name)
 
+        build_id = self.dut.build_info.get('incremental_build_id')
+        branch = self.user_params.get('branch')
+        target = self.dut.device_info.get('flavor')
+
+        self.power_logger.set_branch(branch)
+        self.power_logger.set_build_id(build_id)
+        self.power_logger.set_target(target)
+
         # Take Bugreport
         if self.bug_report:
             begin_time = utils.get_current_epoch_time()
diff --git a/acts/framework/acts/test_utils/power/loggers/power_metric_logger.py b/acts/framework/acts/test_utils/power/loggers/power_metric_logger.py
index 6738e09..09cf399 100644
--- a/acts/framework/acts/test_utils/power/loggers/power_metric_logger.py
+++ b/acts/framework/acts/test_utils/power/loggers/power_metric_logger.py
@@ -48,6 +48,15 @@
     def set_testbed(self, testbed):
         self.proto.testbed = testbed
 
+    def set_branch(self, branch):
+        self.proto.branch = branch
+
+    def set_build_id(self, build_id):
+        self.proto.build_id = build_id
+
+    def set_target(self, target):
+        self.proto.target = target
+
     def end(self, event):
         metric = ProtoMetric(name='spanner_power_metric', data=self.proto)
         return self.publisher.publish(metric)
diff --git a/acts/framework/acts/test_utils/power/loggers/protos/power_metric.proto b/acts/framework/acts/test_utils/power/loggers/protos/power_metric.proto
index a3c81ba..5cd9bf9 100644
--- a/acts/framework/acts/test_utils/power/loggers/protos/power_metric.proto
+++ b/acts/framework/acts/test_utils/power/loggers/protos/power_metric.proto
@@ -19,6 +19,9 @@
   optional float avg_power = 1; // Required
   optional string testbed = 2; // Required
   optional PowerCellularMetric cellular_metric = 3;
+  optional string branch = 4;
+  optional string build_id = 5;
+  optional string target = 6;
 }
 
 message PowerCellularMetric {
diff --git a/acts/framework/acts/test_utils/power/loggers/protos/power_metric_pb2.py b/acts/framework/acts/test_utils/power/loggers/protos/power_metric_pb2.py
index 0d205dd..6f29e20 100644
--- a/acts/framework/acts/test_utils/power/loggers/protos/power_metric_pb2.py
+++ b/acts/framework/acts/test_utils/power/loggers/protos/power_metric_pb2.py
@@ -19,7 +19,7 @@
   name='power_metric.proto',
   package='wireless.android.platform.testing.power.metrics',
   syntax='proto2',
-  serialized_pb=_b('\n\x12power_metric.proto\x12/wireless.android.platform.testing.power.metrics\"\x90\x01\n\x0bPowerMetric\x12\x11\n\tavg_power\x18\x01 \x01(\x02\x12\x0f\n\x07testbed\x18\x02 \x01(\t\x12]\n\x0f\x63\x65llular_metric\x18\x03 \x01(\x0b\x32\x44.wireless.android.platform.testing.power.metrics.PowerCellularMetric\"?\n\x13PowerCellularMetric\x12\x13\n\x0b\x61vg_dl_tput\x18\x01 \x01(\x02\x12\x13\n\x0b\x61vg_ul_tput\x18\x02 \x01(\x02')
+  serialized_pb=_b('\n\x12power_metric.proto\x12/wireless.android.platform.testing.power.metrics\"\xc2\x01\n\x0bPowerMetric\x12\x11\n\tavg_power\x18\x01 \x01(\x02\x12\x0f\n\x07testbed\x18\x02 \x01(\t\x12]\n\x0f\x63\x65llular_metric\x18\x03 \x01(\x0b\x32\x44.wireless.android.platform.testing.power.metrics.PowerCellularMetric\x12\x0e\n\x06\x62ranch\x18\x04 \x01(\t\x12\x10\n\x08\x62uild_id\x18\x05 \x01(\t\x12\x0e\n\x06target\x18\x06 \x01(\t\"?\n\x13PowerCellularMetric\x12\x13\n\x0b\x61vg_dl_tput\x18\x01 \x01(\x02\x12\x13\n\x0b\x61vg_ul_tput\x18\x02 \x01(\x02')
 )
 
 
@@ -53,6 +53,27 @@
       message_type=None, enum_type=None, containing_type=None,
       is_extension=False, extension_scope=None,
       options=None),
+    _descriptor.FieldDescriptor(
+      name='branch', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.branch', index=3,
+      number=4, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='build_id', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.build_id', index=4,
+      number=5, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
+    _descriptor.FieldDescriptor(
+      name='target', full_name='wireless.android.platform.testing.power.metrics.PowerMetric.target', index=5,
+      number=6, type=9, cpp_type=9, label=1,
+      has_default_value=False, default_value=_b("").decode('utf-8'),
+      message_type=None, enum_type=None, containing_type=None,
+      is_extension=False, extension_scope=None,
+      options=None),
   ],
   extensions=[
   ],
@@ -66,7 +87,7 @@
   oneofs=[
   ],
   serialized_start=72,
-  serialized_end=216,
+  serialized_end=266,
 )
 
 
@@ -103,8 +124,8 @@
   extension_ranges=[],
   oneofs=[
   ],
-  serialized_start=218,
-  serialized_end=281,
+  serialized_start=268,
+  serialized_end=331,
 )
 
 _POWERMETRIC.fields_by_name['cellular_metric'].message_type = _POWERCELLULARMETRIC
diff --git a/acts/tests/google/net/DataCostTest.py b/acts/tests/google/net/DataCostTest.py
index 2b7bd50..617626f 100644
--- a/acts/tests/google/net/DataCostTest.py
+++ b/acts/tests/google/net/DataCostTest.py
@@ -65,6 +65,34 @@
 
     """ Helper functions """
 
+    def _clear_netstats(self, ad):
+        """ Clear netstats stored on device
+
+        Args:
+            ad: Android device object
+        """
+        ad.log.info("Clear netstats record.")
+        ad.adb.shell("rm /data/system/netstats/*")
+        asserts.assert_equal("", ad.adb.shell("ls /data/system/netstats/"),
+                             "Fail to clear netstats.")
+        ad.reboot()
+        time.sleep(10)
+        self._check_multipath_preference_from_dumpsys(ad)
+
+    def _check_multipath_preference_from_dumpsys(self, ad):
+        """ Check cell multipath_preference from dumpsys
+
+        Args:
+            ad: Android device object
+        """
+        out = ad.adb.shell("dumpsys connectivity | grep budget")
+        asserts.assert_true(out, "Fail to get status from dumpsys.")
+        ad.log.info("MultipathPolicyTracker: %s" % out)
+        asserts.assert_true(
+            "HANDOVER|RELIABILITY" in out,
+            "Cell multipath preference should be HANDOVER|RELIABILITY."
+        )
+
     def _get_total_data_usage_for_device(self, ad, conn_type, sub_id):
         """ Get total data usage in MB for device
 
@@ -138,6 +166,8 @@
         """
         # set vars
         ad = self.android_devices[0]
+        self._clear_netstats(ad)
+
         sub_id = str(ad.droid.telephonyGetSubscriberId())
         cell_network = ad.droid.connectivityGetActiveNetwork()
         self.log.info("cell network %s" % cell_network)
@@ -182,6 +212,8 @@
         """
         # set vars
         ad = self.android_devices[1]
+        self._clear_netstats(ad)
+
         cell_network = ad.droid.connectivityGetActiveNetwork()
         self.log.info("cell network %s" % cell_network)
         wutils.wifi_connect(ad, self.wifi_network)
diff --git a/acts/tests/google/tel/live/TelLiveStressTest.py b/acts/tests/google/tel/live/TelLiveStressTest.py
index ec364ce..5d55883 100644
--- a/acts/tests/google/tel/live/TelLiveStressTest.py
+++ b/acts/tests/google/tel/live/TelLiveStressTest.py
@@ -96,6 +96,7 @@
 from acts.test_utils.tel.tel_subscription_utils import set_subid_for_data
 from acts.test_utils.tel.tel_subscription_utils import set_subid_for_message
 from acts.test_utils.tel.tel_subscription_utils import set_subid_for_outgoing_call
+from acts.test_utils.tel.tel_subscription_utils import set_slways_allow_mms_data
 from acts.utils import get_current_epoch_time
 from acts.utils import rand_ascii_str
 
@@ -960,6 +961,7 @@
         if not call_verification_func:
             call_verification_func = is_phone_in_call
         self.finishing_time = time.time() + self.max_run_time
+        # CBRS setup
         if self.cbrs_esim:
             cbrs_sub_count = 0
             for ad in self.android_devices:
@@ -976,6 +978,15 @@
             if cbrs_sub_count != 2:
                 self.log.error("Expecting - 2 CBRS subs, found - %d", cbrs_sub_count)
                 raise signals.TestAbortClass("Cannot find all expected CBRS subs")
+        # DSDS setup
+        if self.dsds_esim:
+            for ad in self.android_devices:
+                for i in range(0, 2):
+                    sub_id = get_subid_from_slot_index(ad.log, ad, i)
+                    set_slways_allow_mms_data(ad, sub_id)
+                    operator = get_operatorname_from_slot_index(ad, i)
+                    ad.log.info("Slot %d - Sub %s - %s", i, sub_id, operator)
+        # Actual test trigger
         if not self.dsds_esim and self.check_incall_data():
             self.log.info(
                 "==== Start parallel voice/message/data stress test ====")