"""Make sure the user build configuration is working as expected. Although we can assume the features should be the same between user and user_debug build, the configuration difference between this two build are not tested. In this test suite, we modify the gps configuration to be the same as user build and check if the setting is working. For more details, please refer to : go/p22_user_build_verification """ import os import re import shutil import tempfile import time from acts import asserts from acts import signals from acts.base_test import BaseTestClass from acts_contrib.test_utils.gnss.testtracker_util import log_testtracker_uuid from acts.controllers.adb_lib.error import AdbCommandError from acts.libs.proc.job import TimeoutError from acts_contrib.test_utils.wifi import wifi_test_utils as wutils from acts_contrib.test_utils.gnss import gnss_test_utils as gutils class GpsConfig: def __init__(self, ad, name) -> None: self.ad = ad self.name = name self.folder = "/vendor/etc/gnss" self.full_path = os.path.join(self.folder, self.name) self.logenabled = "LogEnabled" self._log_enable = "true" self._log_disable = "false" def _change_file_content(self, pattern, target): """Modify file via sed command command will be sed -i 's///g' Args: pattern: a string will be used as search pattern target: string that will overwrite the matched result """ self.ad.adb.remount() command = f"sed -i s/{pattern}/{target}/g {self.full_path}" self.ad.adb.shell(command) def _get_setting_value(self, key): """Get setting value from config file command is grep self.full_path Args: key: a string will be used as search pattern Returns: string: grep result ("" for no grep result) """ command = f"grep {key} {self.full_path}" result = self.ad.adb.shell(command) return result def _adjust_log_enable_setting(self, key, enable): """Enable / Disable in self.full_path by setting key = true / false Args: key: The target will be changed enable: True to enable / False to disable """ src = self._log_disable if enable else self._log_enable target = self._log_enable if enable else self._log_disable pattern = f"{key}={src}" target = f"{key}={target}" self._change_file_content(pattern, target) result = self._get_setting_value(key) self.ad.log.debug("%s setting: %s", self.name, result) def _check_file_exist(self, file_pattern): """use command ls to check if file/dir exists command ls Args: file_pattern: A string represents the file or dir Returns: bool: True -> file exists / False -> file doesn't exist """ command = f"ls {file_pattern}" try: self.ad.adb.shell(command) result = True except AdbCommandError as e: result = False return result def enable_diagnostic_log(self): """Set LogEnabled=true in config file In gps.xml it will be LogEnabled=\"true\" """ self.ad.log.info("Enable diagnostic log in %s", self.name) self._adjust_log_enable_setting(key=self.logenabled, enable=True) def disable_diagnostic_log(self): """Set LogEnabled=false in config file In gps.xml it will be LogEnabled=\"false\" """ self.ad.log.info("Disable diagnostic log in %s", self.name) self._adjust_log_enable_setting(key=self.logenabled, enable=False) class ScdConf(GpsConfig): def __init__(self, ad) -> None: super().__init__(ad, "scd.conf") class GpsXml(GpsConfig): def __init__(self, ad) -> None: super().__init__(ad, "gps.xml") self.supllogenable = "SuplLogEnable" self.supl_log = "/data/vendor/gps/suplflow.txt" self._log_enable = "\\\"true\\\"" self._log_disable = "\\\"false\\\"" def enable_supl_log(self): """Set SuplLogEnable=\"true\" in gps.xml""" self.ad.log.info("Enable SUPL logs") self._adjust_log_enable_setting(key=self.supllogenable, enable=True) def disable_supl_log(self): """Set SuplLogEnable=\"false\" in gps.xml""" self.ad.log.info("Disable SUPL log") self._adjust_log_enable_setting(key=self.supllogenable, enable=False) def remove_supl_logs(self): """Remove /data/vendor/gps/suplflow.txt""" self.ad.log.info("Remove SUPL logs") command = f"rm -f {self.supl_log}" self.ad.adb.shell(command) def is_supl_log_file_exist(self): """Check if /data/vendor/gps/suplflow.txt exist Returns: bool: True -> supl log exists / False -> supl log doesn't exist """ result = self._check_file_exist(self.supl_log) self.ad.log.debug("Supl file exists?: %s", result) return result class LhdConf(GpsConfig): def __init__(self, ad) -> None: super().__init__(ad, "lhd.conf") self.lhefailsafe = "LheFailSafe" self.lheconsole = "LheConsole" self.lheconsole_hub = self.get_lheconsole_value() self.esw_crash_dump_pattern = self.get_esw_crash_dump_pattern() def _adjust_lhe_setting(self, key, enable): """Set lhe setting. Enable - uncomment out the setting Dissable - comment out the setting Args: key: A string will be used as search pattern enable: bool True to enable / False to disable """ pattern = f"#\ {key}" if enable else key target = key if enable else f"#\ {key}" self._change_file_content(pattern, target) def enable_lhefailsafe(self): """Uncomment out LheFailSafe""" self.ad.log.info("Enable %s", self.lhefailsafe) self._adjust_lhe_setting(key=self.lhefailsafe, enable=True) def disable_lhefailsafe(self): """Comment out LheFailSafe""" self.ad.log.info("Disable %s", self.lhefailsafe) self._adjust_lhe_setting(key=self.lhefailsafe, enable=False) def enable_lheconsole(self): """Uncomment out LheConsole""" self.ad.log.info("Enable %s", self.lheconsole) self._adjust_lhe_setting(key=self.lheconsole, enable=True) def disable_lheconsole(self): """Comment out LheConsole""" self.ad.log.info("Disable %s", self.lheconsole) self._adjust_lhe_setting(key=self.lheconsole, enable=False) def get_lhefailsafe_value(self): """Get the LheFailSafe value Returns: string: the LheFailSafe value in config Raises: ValueError: No LheFailSafe value """ result = self._get_setting_value(self.lhefailsafe) if not result: raise ValueError(("%s should exists in %s", self.lhefailsafe, self.name)) result = result.split("=")[1] self.ad.log.debug("%s is %s", self.lhefailsafe, result) return result def get_lheconsole_value(self): """Get the LheConsole value Returns: string: the LheConsole value in config Raises: ValueError: No LheConsole value """ result = self._get_setting_value(self.lheconsole) if not result: raise ValueError(("%s should exists in %s", self.lheconsole, self.name)) result = result.split("=")[1] self.ad.log.debug("%s is %s", self.lheconsole, result) return result def get_esw_crash_dump_pattern(self): """Get the esw crash dump file pattern The value is set in LheFailSafe, but we need to add wildcard. Returns: string: esw crash dump pattern Raises: ValueError: No LheFailSafe value """ value = self.get_lhefailsafe_value() value = value.replace(".txt", "*.txt") self.ad.log.debug("Dump file pattern is %s", value) return value def remove_esw_crash_dump_file(self): """Remove crash dump file""" self.ad.log.info("Remove esw crash file") command = f"rm -f {self.esw_crash_dump_pattern}" self.ad.adb.shell(command) def trigger_firmware_crash(self): """Send command to LheConsole to trigger firmware crash""" self.ad.log.info("Trigger firmware crash") command = f"echo Lhe:write=0xFFFFFFFF,4 > {self.lheconsole_hub}.toAsic" self.ad.adb.shell(command, timeout=10) def is_esw_crash_dump_file_exist(self): """Check if esw_crash_dump_pattern exists Will try 3 times, 1 second interval for each attempt Returns: bool: True -> file exists / False -> file doesn't exist """ for attempt in range(1, 4): result = self._check_file_exist(self.esw_crash_dump_pattern) self.ad.log.debug("(Attempt %s)esw dump file exists?: %s", attempt, result) if result: return result time.sleep(1) return False class GnssBroadcomConfigurationTest(BaseTestClass): """ GNSS configuration Tests on Broadcom device.""" def setup_class(self): super().setup_class() self.ad = self.android_devices[0] req_params = ["standalone_cs_criteria"] self.unpack_userparams(req_param_names=req_params) if not gutils.check_chipset_vendor_by_qualcomm(self.ad): self.init_device() self.gps_config_path = tempfile.mkdtemp() self.gps_xml = GpsXml(self.ad) self.lhd_conf = LhdConf(self.ad) self.scd_conf = ScdConf(self.ad) self.enable_testing_setting() self.backup_gps_config() def init_device(self): gutils._init_device(self.ad) gutils.enable_supl_mode(self.ad) gutils.enable_vendor_orbit_assistance_data(self.ad) wutils.wifi_toggle_state(self.ad, True) gutils.set_mobile_data(self.ad, state=True) def teardown_class(self): if hasattr(self, "gps_config_path") and os.path.isdir(self.gps_config_path): shutil.rmtree(self.gps_config_path) def setup_test(self): gutils.log_current_epoch_time(self.ad, "test_start_time") log_testtracker_uuid(self.ad, self.current_test_name) if gutils.check_chipset_vendor_by_qualcomm(self.ad): raise signals.TestSkip("Device is Qualcomm, skip the test") gutils.get_baseband_and_gms_version(self.ad) gutils.clear_logd_gnss_qxdm_log(self.ad) def teardown_test(self): if not gutils.check_chipset_vendor_by_qualcomm(self.ad): self.revert_gps_config() self.ad.reboot() gutils.log_current_epoch_time(self.ad, "test_end_time") def on_fail(self, test_name, begin_time): self.ad.take_bug_report(test_name, begin_time) gutils.get_gnss_qxdm_log(self.ad) def enable_testing_setting(self): """Enable setting to the testing target Before backing up config, enable all the testing target To ensure the teardown_test can bring the device back to the desired state """ self.set_gps_logenabled(enable=True) self.gps_xml.enable_supl_log() self.lhd_conf.enable_lheconsole() self.lhd_conf.enable_lhefailsafe() def backup_gps_config(self): """Copy the gps config config file will be copied: gps.xml / lhd.conf / scd.conf """ for conf in [self.gps_xml, self.scd_conf, self.lhd_conf]: self.ad.log.debug("Backup %s", conf.full_path) self.ad.adb.pull(conf.full_path, self.gps_config_path) def revert_gps_config(self): """Revert the gps config from the one we backup in the setup_class config file will be reverted: gps.xml / lhd.conf / scd.conf """ self.ad.adb.remount() for conf in [self.gps_xml, self.scd_conf, self.lhd_conf]: file_path = os.path.join(self.gps_config_path, conf.name) self.ad.log.debug("Revert %s", conf.full_path) self.ad.adb.push(file_path, conf.full_path) def run_gps_and_capture_log(self): """Enable GPS via gps tool for 15s and capture pixel log""" gutils.start_pixel_logger(self.ad) gutils.gnss_tracking_via_gtw_gpstool(self.ad, self.standalone_cs_criteria, testtime=1) def set_gps_logenabled(self, enable): """Set LogEnabled in gps.xml / lhd.conf / scd.conf Args: enable: True to enable / False to disable """ if enable: self.gps_xml.enable_diagnostic_log() self.scd_conf.enable_diagnostic_log() self.lhd_conf.enable_diagnostic_log() else: self.gps_xml.disable_diagnostic_log() self.scd_conf.disable_diagnostic_log() self.lhd_conf.disable_diagnostic_log() def test_gps_logenabled_setting(self): """Verify the LogEnabled setting in gps.xml / scd.conf / lhd.conf Steps: 1. default setting is on in user_debug build 2. run gps tracking for 1 min 3. should find slog in pixel logger log files 4. disable LogEnabled in all the gps conf 5. run gps tracking for 1 min 6. should not find slog in pixel logger log files """ self.run_gps_and_capture_log() pattern = re.compile(f".*slog\s+:.*") result, _ = gutils.parse_brcm_nmea_log(self.ad, pattern, []) asserts.assert_true(bool(result), "LogEnabled is set to true, but no gps log was found") self.set_gps_logenabled(enable=False) gutils.clear_logd_gnss_qxdm_log(self.ad) # Removes pixel logger path again in case pixel logger still writes log unexpectedly. gutils.remove_pixel_logger_folder(self.ad) self.run_gps_and_capture_log() try: result, _ = gutils.parse_brcm_nmea_log(self.ad, pattern, []) asserts.assert_false( bool(result), ("LogEnabled is set to False but still found %d slog" % len(result))) except FileNotFoundError: self.ad.log.info("Test pass because no BRCM log files/folders was found") def test_gps_supllogenable_setting(self): """Verify SuplLogEnable in gps.xml Steps: 1. default setting is on in user_debug build 2. remove existing supl log 3. run gps tracking for 1 min 4. supl log should exist 5. disable SuplLogEnable in gps.xml 6. remove existing supl log 7. run gps tracking for 1 min 8. supl log should not exist """ def is_supl_log_exist_after_supl_request(): self.gps_xml.remove_supl_logs() self.ad.reboot() self.run_gps_and_capture_log() return self.gps_xml.is_supl_log_file_exist() result = is_supl_log_exist_after_supl_request() asserts.assert_true(result, "SuplLogEnable is enable, should find supl log file") self.gps_xml.disable_supl_log() result = is_supl_log_exist_after_supl_request() asserts.assert_false(result, "SuplLogEnable is disable, should not find supl log file") def test_lhe_setting(self): """Verify lhefailsafe / lheconsole setting in lhd.conf Steps: 1. both setting is enabled 2. trigger firmware crash and check if dump file exist 3. disable lhefailsafe 4. trigger firmware crash and check if dump file exist 5. disable lheconsle 6. trigger firmware crash and check if command timeout """ def is_dump_file_exist_after_firmware_crash(): self.lhd_conf.remove_esw_crash_dump_file() self.lhd_conf.trigger_firmware_crash() return self.lhd_conf.is_esw_crash_dump_file_exist() result = is_dump_file_exist_after_firmware_crash() asserts.assert_true(result, "LheFailSafe is enabled, but no crash file was found") self.lhd_conf.disable_lhefailsafe() self.ad.reboot() result = is_dump_file_exist_after_firmware_crash() asserts.assert_false(result, "LheFailSafe is disabled, but still found crash file") self.lhd_conf.disable_lheconsole() self.ad.reboot() with asserts.assert_raises(TimeoutError): self.lhd_conf.trigger_firmware_crash()