| """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/<pattern>/<target>/g' <file_path> |
| 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 <key> 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 <file_pattern> |
| 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() |