1#!/usr/bin/env python3
2#
3#   Copyright 2016 - Google
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16"""
17    Base Class for Defining Common Telephony Test Functionality
18"""
19
20import logging
21import os
22import re
23import shutil
24import time
25
26from acts import asserts
27from acts import logger as acts_logger
28from acts import signals
29from acts.base_test import BaseTestClass
30from acts.controllers.android_device import DEFAULT_QXDM_LOG_PATH
31from acts.controllers.android_device import DEFAULT_SDM_LOG_PATH
32from acts.keys import Config
33from acts import records
34from acts import utils
35
36from acts.test_utils.tel.tel_subscription_utils import \
37    initial_set_up_for_subid_infomation
38from acts.test_utils.tel.tel_subscription_utils import \
39    set_default_sub_for_all_services
40from acts.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
41from acts.test_utils.tel.tel_test_utils import build_id_override
42from acts.test_utils.tel.tel_test_utils import disable_qxdm_logger
43from acts.test_utils.tel.tel_test_utils import enable_connectivity_metrics
44from acts.test_utils.tel.tel_test_utils import enable_radio_log_on
45from acts.test_utils.tel.tel_test_utils import ensure_phone_default_state
46from acts.test_utils.tel.tel_test_utils import ensure_phone_idle
47from acts.test_utils.tel.tel_test_utils import ensure_wifi_connected
48from acts.test_utils.tel.tel_test_utils import force_connectivity_metrics_upload
49from acts.test_utils.tel.tel_test_utils import get_operator_name
50from acts.test_utils.tel.tel_test_utils import get_screen_shot_log
51from acts.test_utils.tel.tel_test_utils import get_sim_state
52from acts.test_utils.tel.tel_test_utils import get_tcpdump_log
53from acts.test_utils.tel.tel_test_utils import multithread_func
54from acts.test_utils.tel.tel_test_utils import print_radio_info
55from acts.test_utils.tel.tel_test_utils import reboot_device
56from acts.test_utils.tel.tel_test_utils import recover_build_id
57from acts.test_utils.tel.tel_test_utils import run_multithread_func
58from acts.test_utils.tel.tel_test_utils import setup_droid_properties
59from acts.test_utils.tel.tel_test_utils import set_phone_screen_on
60from acts.test_utils.tel.tel_test_utils import set_phone_silent_mode
61from acts.test_utils.tel.tel_test_utils import set_qxdm_logger_command
62from acts.test_utils.tel.tel_test_utils import start_qxdm_logger
63from acts.test_utils.tel.tel_test_utils import start_qxdm_loggers
64from acts.test_utils.tel.tel_test_utils import start_sdm_loggers
65from acts.test_utils.tel.tel_test_utils import start_sdm_logger
66from acts.test_utils.tel.tel_test_utils import start_tcpdumps
67from acts.test_utils.tel.tel_test_utils import stop_qxdm_logger
68from acts.test_utils.tel.tel_test_utils import stop_sdm_loggers
69from acts.test_utils.tel.tel_test_utils import stop_sdm_logger
70from acts.test_utils.tel.tel_test_utils import stop_tcpdumps
71from acts.test_utils.tel.tel_test_utils import synchronize_device_time
72from acts.test_utils.tel.tel_test_utils import unlock_sim
73from acts.test_utils.tel.tel_test_utils import wait_for_sim_ready_by_adb
74from acts.test_utils.tel.tel_test_utils import wait_for_sims_ready_by_adb
75from acts.test_utils.tel.tel_test_utils import activate_wfc_on_device
76from acts.test_utils.tel.tel_test_utils import install_googleaccountutil_apk
77from acts.test_utils.tel.tel_test_utils import add_google_account
78from acts.test_utils.tel.tel_test_utils import install_googlefi_apk
79from acts.test_utils.tel.tel_test_utils import activate_google_fi_account
80from acts.test_utils.tel.tel_test_utils import check_google_fi_activated
81from acts.test_utils.tel.tel_test_utils import check_fi_apk_installed
82from acts.test_utils.tel.tel_test_utils import phone_switch_to_msim_mode
83from acts.test_utils.tel.tel_test_utils import activate_esim_using_suw
84from acts.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND
85from acts.test_utils.tel.tel_defines import SINGLE_SIM_CONFIG, MULTI_SIM_CONFIG
86from acts.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND
87from acts.test_utils.tel.tel_defines import PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING
88from acts.test_utils.tel.tel_defines import SIM_STATE_ABSENT
89from acts.test_utils.tel.tel_defines import SIM_STATE_UNKNOWN
90from acts.test_utils.tel.tel_defines import WIFI_VERBOSE_LOGGING_ENABLED
91from acts.test_utils.tel.tel_defines import WIFI_VERBOSE_LOGGING_DISABLED
92from acts.test_utils.tel.tel_defines import INVALID_SUB_ID
93
94
95class TelephonyBaseTest(BaseTestClass):
96    # Use for logging in the test cases to facilitate
97    # faster log lookup and reduce ambiguity in logging.
98    @staticmethod
99    def tel_test_wrap(fn):
100        def _safe_wrap_test_case(self, *args, **kwargs):
101            test_id = "%s:%s:%s" % (self.__class__.__name__, self.test_name,
102                                    self.log_begin_time.replace(' ', '-'))
103            self.test_id = test_id
104            self.result_detail = ""
105            self.testsignal_details = ""
106            self.testsignal_extras = {}
107            tries = int(self.user_params.get("telephony_auto_rerun", 1))
108            for ad in self.android_devices:
109                ad.log_path = self.log_path
110            for i in range(tries + 1):
111                result = True
112                if i > 0:
113                    log_string = "[Test Case] RERUN %s" % self.test_name
114                    self.log.info(log_string)
115                    self._teardown_test(self.test_name)
116                    self._setup_test(self.test_name)
117                try:
118                    result = fn(self, *args, **kwargs)
119                except signals.TestFailure as e:
120                    self.testsignal_details = e.details
121                    self.testsignal_extras = e.extras
122                    result = False
123                except signals.TestSignal:
124                    raise
125                except Exception as e:
126                    self.log.exception(e)
127                    asserts.fail(self.result_detail)
128                if result is False:
129                    if i < tries:
130                        continue
131                else:
132                    break
133            if self.user_params.get("check_crash", True):
134                new_crash = ad.check_crash_report(self.test_name,
135                                                  self.begin_time, True)
136                if new_crash:
137                    msg = "Find new crash reports %s" % new_crash
138                    ad.log.error(msg)
139                    self.result_detail = "%s %s %s" % (self.result_detail,
140                                                       ad.serial, msg)
141                    result = False
142            if result is not False:
143                asserts.explicit_pass(self.result_detail)
144            else:
145                if self.result_detail:
146                    asserts.fail(self.result_detail)
147                else:
148                    asserts.fail(self.testsignal_details, self.testsignal_extras)
149
150        return _safe_wrap_test_case
151
152    def setup_class(self):
153        super().setup_class()
154        self.wifi_network_ssid = self.user_params.get(
155            "wifi_network_ssid") or self.user_params.get(
156                "wifi_network_ssid_2g") or self.user_params.get(
157                    "wifi_network_ssid_5g")
158        self.wifi_network_pass = self.user_params.get(
159            "wifi_network_pass") or self.user_params.get(
160                "wifi_network_pass_2g") or self.user_params.get(
161                    "wifi_network_ssid_5g")
162
163        self.log_path = getattr(logging, "log_path", None)
164        self.qxdm_log = self.user_params.get("qxdm_log", True)
165        self.sdm_log = self.user_params.get("sdm_log", False)
166        self.enable_radio_log_on = self.user_params.get(
167            "enable_radio_log_on", False)
168        self.cbrs_esim = self.user_params.get("cbrs_esim", False)
169        self.account_util = self.user_params.get("account_util", None)
170        self.save_passing_logs = self.user_params.get("save_passing_logs", False)
171        if isinstance(self.account_util, list):
172            self.account_util = self.account_util[0]
173        self.fi_util = self.user_params.get("fi_util", None)
174        if isinstance(self.fi_util, list):
175            self.fi_util = self.fi_util[0]
176        tasks = [(self._init_device, [ad]) for ad in self.android_devices]
177        multithread_func(self.log, tasks)
178        self.skip_reset_between_cases = self.user_params.get(
179            "skip_reset_between_cases", True)
180        self.log_path = getattr(logging, "log_path", None)
181        self.sim_config = {
182                            "config":SINGLE_SIM_CONFIG,
183                            "number_of_sims":1
184                        }
185
186        for ad in self.android_devices:
187            if hasattr(ad, "dsds"):
188                self.sim_config = {
189                                    "config":MULTI_SIM_CONFIG,
190                                    "number_of_sims":2
191                                }
192                break
193        if "anritsu_md8475a_ip_address" in self.user_params:
194            return
195        qxdm_log_mask_cfg = self.user_params.get("qxdm_log_mask_cfg", None)
196        if isinstance(qxdm_log_mask_cfg, list):
197            qxdm_log_mask_cfg = qxdm_log_mask_cfg[0]
198        if qxdm_log_mask_cfg and "dev/null" in qxdm_log_mask_cfg:
199            qxdm_log_mask_cfg = None
200        sim_conf_file = self.user_params.get("sim_conf_file")
201        if not sim_conf_file:
202            self.log.info("\"sim_conf_file\" is not provided test bed config!")
203        else:
204            if isinstance(sim_conf_file, list):
205                sim_conf_file = sim_conf_file[0]
206            # If the sim_conf_file is not a full path, attempt to find it
207            # relative to the config file.
208            if not os.path.isfile(sim_conf_file):
209                sim_conf_file = os.path.join(
210                    self.user_params[Config.key_config_path.value],
211                    sim_conf_file)
212                if not os.path.isfile(sim_conf_file):
213                    self.log.error("Unable to load user config %s ",
214                                   sim_conf_file)
215
216        tasks = [(self._setup_device, [ad, sim_conf_file, qxdm_log_mask_cfg])
217                 for ad in self.android_devices]
218        return multithread_func(self.log, tasks)
219
220    def _init_device(self, ad):
221        synchronize_device_time(ad)
222        ad.log_path = self.log_path
223        print_radio_info(ad)
224        unlock_sim(ad)
225        ad.wakeup_screen()
226        ad.adb.shell("input keyevent 82")
227
228    def wait_for_sim_ready(self,ad):
229        wait_for_sim_ready_on_sim_config = {
230              SINGLE_SIM_CONFIG : lambda:wait_for_sim_ready_by_adb(self.log,ad),
231              MULTI_SIM_CONFIG : lambda:wait_for_sims_ready_by_adb(self.log,ad)
232              }
233        if not wait_for_sim_ready_on_sim_config[self.sim_config["config"]]:
234            raise signals.TestAbortClass("unable to load the SIM")
235
236    def _setup_device(self, ad, sim_conf_file, qxdm_log_mask_cfg=None):
237        ad.qxdm_log = getattr(ad, "qxdm_log", self.qxdm_log)
238        ad.sdm_log = getattr(ad, "sdm_log", self.sdm_log)
239        if self.user_params.get("enable_connectivity_metrics", False):
240            enable_connectivity_metrics(ad)
241        if self.user_params.get("build_id_override", False):
242            build_postfix = self.user_params.get("build_id_postfix",
243                                                 "LAB_TEST")
244            build_id_override(
245                ad,
246                new_build_id=self.user_params.get("build_id_override_with",
247                                                  None),
248                postfix=build_postfix)
249        if self.enable_radio_log_on:
250            enable_radio_log_on(ad)
251        list_of_models = ["sdm", "msm", "kon", "lit"]
252        if any(model in ad.model for model in list_of_models):
253            phone_mode = "ssss"
254            if hasattr(ad, "mtp_dsds"):
255                phone_mode = "dsds"
256            if ad.adb.getprop("persist.radio.multisim.config") != phone_mode:
257                ad.adb.shell("setprop persist.radio.multisim.config %s" \
258                             % phone_mode)
259                reboot_device(ad)
260
261        stop_qxdm_logger(ad)
262        if ad.qxdm_log:
263            qxdm_log_mask = getattr(ad, "qxdm_log_mask", None)
264            if qxdm_log_mask_cfg:
265                qxdm_mask_path = self.user_params.get("qxdm_log_path",
266                                                      DEFAULT_QXDM_LOG_PATH)
267                ad.adb.shell("mkdir %s" % qxdm_mask_path)
268                ad.log.info("Push %s to %s", qxdm_log_mask_cfg, qxdm_mask_path)
269                ad.adb.push("%s %s" % (qxdm_log_mask_cfg, qxdm_mask_path))
270                mask_file_name = os.path.split(qxdm_log_mask_cfg)[-1]
271                qxdm_log_mask = os.path.join(qxdm_mask_path, mask_file_name)
272            set_qxdm_logger_command(ad, mask=qxdm_log_mask)
273            start_qxdm_logger(ad, utils.get_current_epoch_time())
274        elif ad.sdm_log:
275            start_sdm_logger(ad)
276        else:
277            disable_qxdm_logger(ad)
278        if not unlock_sim(ad):
279            raise signals.TestAbortClass("unable to unlock the SIM")
280
281        # eSIM enablement
282        if hasattr(ad, "fi_esim"):
283            if not ensure_wifi_connected(self.log, ad, self.wifi_network_ssid,
284                                         self.wifi_network_pass):
285                ad.log.error("Failed to connect to wifi")
286            if check_google_fi_activated(ad):
287                ad.log.info("Google Fi is already Activated")
288            else:
289                install_googleaccountutil_apk(ad, self.account_util)
290                add_google_account(ad)
291                install_googlefi_apk(ad, self.fi_util)
292                if not activate_google_fi_account(ad):
293                    ad.log.error("Failed to activate Fi")
294                check_google_fi_activated(ad)
295        if hasattr(ad, "dsds"):
296            sim_mode = ad.droid.telephonyGetPhoneCount()
297            if sim_mode == 1:
298                ad.log.info("Phone in Single SIM Mode")
299                if not phone_switch_to_msim_mode(ad):
300                    ad.log.error("Failed to switch to Dual SIM Mode")
301                    return False
302            elif sim_mode == 2:
303                ad.log.info("Phone already in Dual SIM Mode")
304        if get_sim_state(ad) in (SIM_STATE_ABSENT, SIM_STATE_UNKNOWN):
305            ad.log.info("Device has no or unknown SIM in it")
306            # eSIM needs activation
307            activate_esim_using_suw(ad)
308            ensure_phone_idle(self.log, ad)
309        elif self.user_params.get("Attenuator"):
310            ad.log.info("Device in chamber room")
311            ensure_phone_idle(self.log, ad)
312            setup_droid_properties(self.log, ad, sim_conf_file)
313        else:
314            self.wait_for_sim_ready(ad)
315            ensure_phone_default_state(self.log, ad)
316            setup_droid_properties(self.log, ad, sim_conf_file)
317
318        default_slot = getattr(ad, "default_slot", 0)
319        if get_subid_from_slot_index(ad.log, ad, default_slot) != INVALID_SUB_ID:
320            ad.log.info("Slot %s is the default slot.", default_slot)
321            set_default_sub_for_all_services(ad, default_slot)
322        else:
323            ad.log.warning("Slot %s is NOT a valid slot. Slot %s will be used by default.",
324                default_slot, 1-default_slot)
325            set_default_sub_for_all_services(ad, 1-default_slot)
326
327        # Activate WFC on Verizon, AT&T and Canada operators as per # b/33187374 &
328        # b/122327716
329        activate_wfc_on_device(self.log, ad)
330
331        # Sub ID setup
332        initial_set_up_for_subid_infomation(self.log, ad)
333
334        # If device is setup already, skip the following setup procedures
335        if getattr(ad, "telephony_test_setup", None):
336            return True
337
338        try:
339            ad.droid.wifiEnableVerboseLogging(WIFI_VERBOSE_LOGGING_ENABLED)
340        except Exception:
341            pass
342
343        # Disable Emergency alerts
344        # Set chrome browser start with no-first-run verification and
345        # disable-fre. Give permission to read from and write to storage.
346        for cmd in ("pm disable com.android.cellbroadcastreceiver",
347                    "pm grant com.android.chrome "
348                    "android.permission.READ_EXTERNAL_STORAGE",
349                    "pm grant com.android.chrome "
350                    "android.permission.WRITE_EXTERNAL_STORAGE",
351                    "rm /data/local/chrome-command-line",
352                    "am set-debug-app --persistent com.android.chrome",
353                    'echo "chrome --no-default-browser-check --no-first-run '
354                    '--disable-fre" > /data/local/tmp/chrome-command-line'):
355            ad.adb.shell(cmd, ignore_status=True)
356
357        # Curl for 2016/7 devices
358        if not getattr(ad, "curl_capable", False):
359            try:
360                out = ad.adb.shell("/data/curl --version")
361                if not out or "not found" in out:
362                    if int(ad.adb.getprop("ro.product.first_api_level")) >= 25:
363                        tel_data = self.user_params.get("tel_data", "tel_data")
364                        if isinstance(tel_data, list):
365                            tel_data = tel_data[0]
366                        curl_file_path = os.path.join(tel_data, "curl")
367                        if not os.path.isfile(curl_file_path):
368                            curl_file_path = os.path.join(
369                                self.user_params[Config.key_config_path.value],
370                                curl_file_path)
371                        if os.path.isfile(curl_file_path):
372                            ad.log.info("Pushing Curl to /data dir")
373                            ad.adb.push("%s /data" % (curl_file_path))
374                            ad.adb.shell(
375                                "chmod 777 /data/curl", ignore_status=True)
376                else:
377                    setattr(ad, "curl_capable", True)
378            except Exception:
379                ad.log.info("Failed to push curl on this device")
380
381        # Ensure that a test class starts from a consistent state that
382        # improves chances of valid network selection and facilitates
383        # logging.
384        try:
385            if not set_phone_screen_on(self.log, ad):
386                self.log.error("Failed to set phone screen-on time.")
387                return False
388            if not set_phone_silent_mode(self.log, ad):
389                self.log.error("Failed to set phone silent mode.")
390                return False
391            ad.droid.telephonyAdjustPreciseCallStateListenLevel(
392                PRECISE_CALL_STATE_LISTEN_LEVEL_FOREGROUND, True)
393            ad.droid.telephonyAdjustPreciseCallStateListenLevel(
394                PRECISE_CALL_STATE_LISTEN_LEVEL_RINGING, True)
395            ad.droid.telephonyAdjustPreciseCallStateListenLevel(
396                PRECISE_CALL_STATE_LISTEN_LEVEL_BACKGROUND, True)
397        except Exception as e:
398            self.log.error("Failure with %s", e)
399        setattr(ad, "telephony_test_setup", True)
400        return True
401
402    def _teardown_device(self, ad):
403        try:
404            stop_qxdm_logger(ad)
405            stop_sdm_logger(ad)
406        except Exception as e:
407            self.log.error("Failure with %s", e)
408        try:
409            ad.droid.disableDevicePassword()
410        except Exception as e:
411            self.log.error("Failure with %s", e)
412        if self.user_params.get("enable_connectivity_metrics", False):
413            if not ensure_wifi_connected(self.log, ad, self.wifi_network_ssid,
414                                         self.wifi_network_pass):
415                ad.log.error("Failed to connect to wifi")
416            force_connectivity_metrics_upload(ad)
417            time.sleep(30)
418        try:
419            ad.droid.wifiEnableVerboseLogging(WIFI_VERBOSE_LOGGING_DISABLED)
420        except Exception as e:
421            self.log.error("Failure with %s", e)
422        try:
423            if self.user_params.get("build_id_override",
424                                    False) and self.user_params.get(
425                                        "recover_build_id", False):
426                recover_build_id(ad)
427        except Exception as e:
428            self.log.error("Failure with %s", e)
429
430    def teardown_class(self):
431        tasks = [(self._teardown_device, [ad]) for ad in self.android_devices]
432        multithread_func(self.log, tasks)
433        return True
434
435    def setup_test(self):
436        if getattr(self, "qxdm_log", True):
437            if not self.user_params.get("qxdm_log_mask_cfg", None):
438                if "wfc" in self.test_name:
439                    for ad in self.android_devices:
440                        if not getattr(ad, "qxdm_logger_command", None) or (
441                                "IMS_DS_CNE_LnX_Golden.cfg" not in getattr(
442                                    ad, "qxdm_logger_command", "")):
443                            set_qxdm_logger_command(
444                                ad, "IMS_DS_CNE_LnX_Golden.cfg")
445                else:
446                    for ad in self.android_devices:
447                        if not getattr(ad, "qxdm_logger_command", None) or (
448                                "IMS_DS_CNE_LnX_Golden.cfg" in getattr(
449                                    ad, "qxdm_logger_command", "")):
450                            set_qxdm_logger_command(ad, None)
451            start_qxdm_loggers(self.log, self.android_devices, self.begin_time)
452        if getattr(self, "sdm_log", False):
453            start_sdm_loggers(self.log, self.android_devices)
454        if getattr(self, "tcpdump_log", False) or "wfc" in self.test_name:
455            mask = getattr(self, "tcpdump_mask", "all")
456            interface = getattr(self, "tcpdump_interface", "wlan0")
457            start_tcpdumps(
458                self.android_devices,
459                begin_time=self.begin_time,
460                interface=interface,
461                mask=mask)
462        else:
463            stop_tcpdumps(self.android_devices)
464        for ad in self.android_devices:
465            if self.skip_reset_between_cases:
466                ensure_phone_idle(self.log, ad)
467            else:
468                ensure_phone_default_state(self.log, ad)
469            for session in ad._sl4a_manager.sessions.values():
470                ed = session.get_event_dispatcher()
471                ed.clear_all_events()
472            output = ad.adb.logcat("-t 1")
473            match = re.search(r"\d+-\d+\s\d+:\d+:\d+.\d+", output)
474            if match:
475                ad.test_log_begin_time = match.group(0)
476
477    def teardown_test(self):
478        stop_tcpdumps(self.android_devices)
479
480    def on_fail(self, test_name, begin_time):
481        self._take_bug_report(test_name, begin_time)
482
483    def on_pass(self, test_name, begin_time):
484        if self.save_passing_logs:
485            self._take_bug_report(test_name, begin_time)
486
487    def _ad_take_extra_logs(self, ad, test_name, begin_time):
488        ad.adb.wait_for_device()
489        result = True
490
491        try:
492            # get tcpdump and screen shot log
493            get_tcpdump_log(ad, test_name, begin_time)
494            get_screen_shot_log(ad, test_name, begin_time)
495        except Exception as e:
496            ad.log.error("Exception error %s", e)
497            result = False
498
499        try:
500            ad.check_crash_report(test_name, begin_time, log_crash_report=True)
501        except Exception as e:
502            ad.log.error("Failed to check crash report for %s with error %s",
503                         test_name, e)
504            result = False
505
506        extra_qxdm_logs_in_seconds = self.user_params.get(
507            "extra_qxdm_logs_in_seconds", 60 * 3)
508        if getattr(ad, "qxdm_log", True):
509            # Gather qxdm log modified 3 minutes earlier than test start time
510            if begin_time:
511                qxdm_begin_time = begin_time - 1000 * extra_qxdm_logs_in_seconds
512            else:
513                qxdm_begin_time = None
514            try:
515                time.sleep(10)
516                ad.get_qxdm_logs(test_name, qxdm_begin_time)
517            except Exception as e:
518                ad.log.error("Failed to get QXDM log for %s with error %s",
519                             test_name, e)
520                result = False
521        if getattr(ad, "sdm_log", False):
522            # Gather sdm log modified 3 minutes earlier than test start time
523            if begin_time:
524                sdm_begin_time = begin_time - 1000 * extra_qxdm_logs_in_seconds
525            else:
526                sdm_begin_time = None
527            try:
528                time.sleep(10)
529                ad.get_sdm_logs(test_name, sdm_begin_time)
530            except Exception as e:
531                ad.log.error("Failed to get SDM log for %s with error %s",
532                             test_name, e)
533                result = False
534
535        return result
536
537    def _take_bug_report(self, test_name, begin_time):
538        if self._skip_bug_report(test_name):
539            return
540        dev_num = getattr(self, "number_of_devices", None) or len(
541            self.android_devices)
542        tasks = [(self._ad_take_bugreport, (ad, test_name, begin_time))
543                 for ad in self.android_devices[:dev_num]]
544        tasks.extend([(self._ad_take_extra_logs, (ad, test_name, begin_time))
545                      for ad in self.android_devices[:dev_num]])
546        run_multithread_func(self.log, tasks)
547        for ad in self.android_devices[:dev_num]:
548            if getattr(ad, "reboot_to_recover", False):
549                reboot_device(ad)
550                ad.reboot_to_recover = False
551        # Zip log folder
552        if not self.user_params.get("zip_log", False): return
553        src_dir = os.path.join(self.log_path, test_name)
554        os.makedirs(src_dir, exist_ok=True)
555        file_name = "%s_%s" % (src_dir, begin_time)
556        self.log.info("Zip folder %s to %s.zip", src_dir, file_name)
557        shutil.make_archive(file_name, "zip", src_dir)
558        shutil.rmtree(src_dir)
559
560    def _block_all_test_cases(self, tests, reason='Failed class setup'):
561        """Over-write _block_all_test_cases in BaseTestClass."""
562        for (i, (test_name, test_func)) in enumerate(tests):
563            signal = signals.TestFailure(reason)
564            record = records.TestResultRecord(test_name, self.TAG)
565            record.test_begin()
566            # mark all test cases as FAIL
567            record.test_fail(signal)
568            self.results.add_record(record)
569            # only gather bug report for the first test case
570            if i == 0:
571                self.on_fail(test_name, record.begin_time)
572
573    def get_stress_test_number(self):
574        """Gets the stress_test_number param from user params.
575
576        Gets the stress_test_number param. If absent, returns default 100.
577        """
578        return int(self.user_params.get("stress_test_number", 100))
579