1"""Make sure the user build configuration is working as expected. 2 3Although we can assume the features should be the same between user and user_debug build, 4the configuration difference between this two build are not tested. 5 6In this test suite, we modify the gps configuration to be the same as user build 7and check if the setting is working. 8For more details, please refer to : go/p22_user_build_verification 9""" 10import os 11import re 12import shutil 13import tempfile 14import time 15 16from acts import asserts 17from acts import signals 18from acts.base_test import BaseTestClass 19from acts_contrib.test_utils.gnss.testtracker_util import log_testtracker_uuid 20from acts.controllers.adb_lib.error import AdbCommandError 21from acts.libs.proc.job import TimeoutError 22from acts_contrib.test_utils.wifi import wifi_test_utils as wutils 23from acts_contrib.test_utils.gnss import gnss_test_utils as gutils 24 25 26class GpsConfig: 27 def __init__(self, ad, name) -> None: 28 self.ad = ad 29 self.name = name 30 self.folder = "/vendor/etc/gnss" 31 self.full_path = os.path.join(self.folder, self.name) 32 self.logenabled = "LogEnabled" 33 self._log_enable = "true" 34 self._log_disable = "false" 35 36 def _change_file_content(self, pattern, target): 37 """Modify file via sed command 38 39 command will be sed -i 's/<pattern>/<target>/g' <file_path> 40 Args: 41 pattern: a string will be used as search pattern 42 target: string that will overwrite the matched result 43 """ 44 self.ad.adb.remount() 45 command = f"sed -i s/{pattern}/{target}/g {self.full_path}" 46 self.ad.adb.shell(command) 47 48 def _get_setting_value(self, key): 49 """Get setting value from config file 50 51 command is grep <key> self.full_path 52 Args: 53 key: a string will be used as search pattern 54 Returns: 55 string: grep result ("" for no grep result) 56 """ 57 command = f"grep {key} {self.full_path}" 58 result = self.ad.adb.shell(command) 59 return result 60 61 def _adjust_log_enable_setting(self, key, enable): 62 """Enable / Disable in self.full_path by setting key = true / false 63 Args: 64 key: The target will be changed 65 enable: True to enable / False to disable 66 """ 67 src = self._log_disable if enable else self._log_enable 68 target = self._log_enable if enable else self._log_disable 69 pattern = f"{key}={src}" 70 target = f"{key}={target}" 71 self._change_file_content(pattern, target) 72 result = self._get_setting_value(key) 73 self.ad.log.debug("%s setting: %s", self.name, result) 74 75 def _check_file_exist(self, file_pattern): 76 """use command ls to check if file/dir exists 77 command ls <file_pattern> 78 Args: 79 file_pattern: A string represents the file or dir 80 Returns: 81 bool: True -> file exists / False -> file doesn't exist 82 """ 83 command = f"ls {file_pattern}" 84 try: 85 self.ad.adb.shell(command) 86 result = True 87 except AdbCommandError as e: 88 result = False 89 return result 90 91 def enable_diagnostic_log(self): 92 """Set LogEnabled=true in config file 93 In gps.xml it will be LogEnabled=\"true\" 94 """ 95 self.ad.log.info("Enable diagnostic log in %s", self.name) 96 self._adjust_log_enable_setting(key=self.logenabled, enable=True) 97 98 def disable_diagnostic_log(self): 99 """Set LogEnabled=false in config file 100 In gps.xml it will be LogEnabled=\"false\" 101 """ 102 self.ad.log.info("Disable diagnostic log in %s", self.name) 103 self._adjust_log_enable_setting(key=self.logenabled, enable=False) 104 105 106class ScdConf(GpsConfig): 107 def __init__(self, ad) -> None: 108 super().__init__(ad, "scd.conf") 109 110 111class GpsXml(GpsConfig): 112 def __init__(self, ad) -> None: 113 super().__init__(ad, "gps.xml") 114 self.supllogenable = "SuplLogEnable" 115 self.supl_log = "/data/vendor/gps/suplflow.txt" 116 self._log_enable = "\\\"true\\\"" 117 self._log_disable = "\\\"false\\\"" 118 119 def enable_supl_log(self): 120 """Set SuplLogEnable=\"true\" in gps.xml""" 121 self.ad.log.info("Enable SUPL logs") 122 self._adjust_log_enable_setting(key=self.supllogenable, enable=True) 123 124 def disable_supl_log(self): 125 """Set SuplLogEnable=\"false\" in gps.xml""" 126 self.ad.log.info("Disable SUPL log") 127 self._adjust_log_enable_setting(key=self.supllogenable, enable=False) 128 129 def remove_supl_logs(self): 130 """Remove /data/vendor/gps/suplflow.txt""" 131 self.ad.log.info("Remove SUPL logs") 132 command = f"rm -f {self.supl_log}" 133 self.ad.adb.shell(command) 134 135 def is_supl_log_file_exist(self): 136 """Check if /data/vendor/gps/suplflow.txt exist 137 Returns: 138 bool: True -> supl log exists / False -> supl log doesn't exist 139 """ 140 result = self._check_file_exist(self.supl_log) 141 self.ad.log.debug("Supl file exists?: %s", result) 142 return result 143 144 145class LhdConf(GpsConfig): 146 def __init__(self, ad) -> None: 147 super().__init__(ad, "lhd.conf") 148 self.lhefailsafe = "LheFailSafe" 149 self.lheconsole = "LheConsole" 150 self.lheconsole_hub = self.get_lheconsole_value() 151 self.esw_crash_dump_pattern = self.get_esw_crash_dump_pattern() 152 153 def _adjust_lhe_setting(self, key, enable): 154 """Set lhe setting. 155 Enable - uncomment out the setting 156 Dissable - comment out the setting 157 Args: 158 key: A string will be used as search pattern 159 enable: bool True to enable / False to disable 160 """ 161 pattern = f"#\ {key}" if enable else key 162 target = key if enable else f"#\ {key}" 163 self._change_file_content(pattern, target) 164 165 def enable_lhefailsafe(self): 166 """Uncomment out LheFailSafe""" 167 self.ad.log.info("Enable %s", self.lhefailsafe) 168 self._adjust_lhe_setting(key=self.lhefailsafe, enable=True) 169 170 def disable_lhefailsafe(self): 171 """Comment out LheFailSafe""" 172 self.ad.log.info("Disable %s", self.lhefailsafe) 173 self._adjust_lhe_setting(key=self.lhefailsafe, enable=False) 174 175 def enable_lheconsole(self): 176 """Uncomment out LheConsole""" 177 self.ad.log.info("Enable %s", self.lheconsole) 178 self._adjust_lhe_setting(key=self.lheconsole, enable=True) 179 180 def disable_lheconsole(self): 181 """Comment out LheConsole""" 182 self.ad.log.info("Disable %s", self.lheconsole) 183 self._adjust_lhe_setting(key=self.lheconsole, enable=False) 184 185 def get_lhefailsafe_value(self): 186 """Get the LheFailSafe value 187 188 Returns: 189 string: the LheFailSafe value in config 190 Raises: 191 ValueError: No LheFailSafe value 192 """ 193 result = self._get_setting_value(self.lhefailsafe) 194 if not result: 195 raise ValueError(("%s should exists in %s", self.lhefailsafe, self.name)) 196 result = result.split("=")[1] 197 self.ad.log.debug("%s is %s", self.lhefailsafe, result) 198 return result 199 200 def get_lheconsole_value(self): 201 """Get the LheConsole value 202 203 Returns: 204 string: the LheConsole value in config 205 Raises: 206 ValueError: No LheConsole value 207 """ 208 result = self._get_setting_value(self.lheconsole) 209 if not result: 210 raise ValueError(("%s should exists in %s", self.lheconsole, self.name)) 211 result = result.split("=")[1] 212 self.ad.log.debug("%s is %s", self.lheconsole, result) 213 return result 214 215 def get_esw_crash_dump_pattern(self): 216 """Get the esw crash dump file pattern 217 The value is set in LheFailSafe, but we need to add wildcard. 218 Returns: 219 string: esw crash dump pattern 220 Raises: 221 ValueError: No LheFailSafe value 222 """ 223 value = self.get_lhefailsafe_value() 224 value = value.replace(".txt", "*.txt") 225 self.ad.log.debug("Dump file pattern is %s", value) 226 return value 227 228 def remove_esw_crash_dump_file(self): 229 """Remove crash dump file""" 230 self.ad.log.info("Remove esw crash file") 231 command = f"rm -f {self.esw_crash_dump_pattern}" 232 self.ad.adb.shell(command) 233 234 def trigger_firmware_crash(self): 235 """Send command to LheConsole to trigger firmware crash""" 236 self.ad.log.info("Trigger firmware crash") 237 command = f"echo Lhe:write=0xFFFFFFFF,4 > {self.lheconsole_hub}.toAsic" 238 self.ad.adb.shell(command, timeout=10) 239 240 def is_esw_crash_dump_file_exist(self): 241 """Check if esw_crash_dump_pattern exists 242 Will try 3 times, 1 second interval for each attempt 243 Returns: 244 bool: True -> file exists / False -> file doesn't exist 245 """ 246 for attempt in range(1, 4): 247 result = self._check_file_exist(self.esw_crash_dump_pattern) 248 self.ad.log.debug("(Attempt %s)esw dump file exists?: %s", attempt, result) 249 if result: 250 return result 251 time.sleep(1) 252 return False 253 254 255class GnssBroadcomConfigurationTest(BaseTestClass): 256 """ GNSS configuration Tests on Broadcom device.""" 257 def setup_class(self): 258 super().setup_class() 259 self.ad = self.android_devices[0] 260 req_params = ["standalone_cs_criteria"] 261 self.unpack_userparams(req_param_names=req_params) 262 263 if not gutils.check_chipset_vendor_by_qualcomm(self.ad): 264 self.init_device() 265 self.gps_config_path = tempfile.mkdtemp() 266 self.gps_xml = GpsXml(self.ad) 267 self.lhd_conf = LhdConf(self.ad) 268 self.scd_conf = ScdConf(self.ad) 269 self.enable_testing_setting() 270 self.backup_gps_config() 271 272 def init_device(self): 273 gutils._init_device(self.ad) 274 gutils.enable_supl_mode(self.ad) 275 gutils.enable_vendor_orbit_assistance_data(self.ad) 276 wutils.wifi_toggle_state(self.ad, True) 277 gutils.set_mobile_data(self.ad, state=True) 278 279 280 def teardown_class(self): 281 if hasattr(self, "gps_config_path") and os.path.isdir(self.gps_config_path): 282 shutil.rmtree(self.gps_config_path) 283 284 def setup_test(self): 285 gutils.log_current_epoch_time(self.ad, "test_start_time") 286 log_testtracker_uuid(self.ad, self.current_test_name) 287 if gutils.check_chipset_vendor_by_qualcomm(self.ad): 288 raise signals.TestSkip("Device is Qualcomm, skip the test") 289 gutils.get_baseband_and_gms_version(self.ad) 290 gutils.clear_logd_gnss_qxdm_log(self.ad) 291 292 def teardown_test(self): 293 if not gutils.check_chipset_vendor_by_qualcomm(self.ad): 294 self.revert_gps_config() 295 self.ad.reboot() 296 gutils.log_current_epoch_time(self.ad, "test_end_time") 297 298 def on_fail(self, test_name, begin_time): 299 self.ad.take_bug_report(test_name, begin_time) 300 gutils.get_gnss_qxdm_log(self.ad) 301 302 def enable_testing_setting(self): 303 """Enable setting to the testing target 304 Before backing up config, enable all the testing target 305 To ensure the teardown_test can bring the device back to the desired state 306 """ 307 self.set_gps_logenabled(enable=True) 308 self.gps_xml.enable_supl_log() 309 self.lhd_conf.enable_lheconsole() 310 self.lhd_conf.enable_lhefailsafe() 311 312 def backup_gps_config(self): 313 """Copy the gps config 314 315 config file will be copied: gps.xml / lhd.conf / scd.conf 316 """ 317 for conf in [self.gps_xml, self.scd_conf, self.lhd_conf]: 318 self.ad.log.debug("Backup %s", conf.full_path) 319 self.ad.adb.pull(conf.full_path, self.gps_config_path) 320 321 def revert_gps_config(self): 322 """Revert the gps config from the one we backup in the setup_class 323 324 config file will be reverted: gps.xml / lhd.conf / scd.conf 325 """ 326 self.ad.adb.remount() 327 for conf in [self.gps_xml, self.scd_conf, self.lhd_conf]: 328 file_path = os.path.join(self.gps_config_path, conf.name) 329 self.ad.log.debug("Revert %s", conf.full_path) 330 self.ad.adb.push(file_path, conf.full_path) 331 332 def run_gps_and_capture_log(self): 333 """Enable GPS via gps tool for 15s and capture pixel log""" 334 gutils.start_pixel_logger(self.ad) 335 gutils.gnss_tracking_via_gtw_gpstool(self.ad, self.standalone_cs_criteria, testtime=1) 336 337 def set_gps_logenabled(self, enable): 338 """Set LogEnabled in gps.xml / lhd.conf / scd.conf 339 340 Args: 341 enable: True to enable / False to disable 342 """ 343 if enable: 344 self.gps_xml.enable_diagnostic_log() 345 self.scd_conf.enable_diagnostic_log() 346 self.lhd_conf.enable_diagnostic_log() 347 else: 348 self.gps_xml.disable_diagnostic_log() 349 self.scd_conf.disable_diagnostic_log() 350 self.lhd_conf.disable_diagnostic_log() 351 352 def test_gps_logenabled_setting(self): 353 """Verify the LogEnabled setting in gps.xml / scd.conf / lhd.conf 354 Steps: 355 1. default setting is on in user_debug build 356 2. run gps tracking for 1 min 357 3. should find slog in pixel logger log files 358 4. disable LogEnabled in all the gps conf 359 5. run gps tracking for 1 min 360 6. should not find slog in pixel logger log files 361 """ 362 self.run_gps_and_capture_log() 363 pattern = re.compile(f".*slog\s+:.*") 364 result, _ = gutils.parse_brcm_nmea_log(self.ad, pattern, []) 365 asserts.assert_true(bool(result), "LogEnabled is set to true, but no gps log was found") 366 367 self.set_gps_logenabled(enable=False) 368 gutils.clear_logd_gnss_qxdm_log(self.ad) 369 # Removes pixel logger path again in case pixel logger still writes log unexpectedly. 370 gutils.remove_pixel_logger_folder(self.ad) 371 372 self.run_gps_and_capture_log() 373 try: 374 result, _ = gutils.parse_brcm_nmea_log(self.ad, pattern, []) 375 asserts.assert_false( 376 bool(result), 377 ("LogEnabled is set to False but still found %d slog" % len(result))) 378 except FileNotFoundError: 379 self.ad.log.info("Test pass because no BRCM log files/folders was found") 380 381 def test_gps_supllogenable_setting(self): 382 """Verify SuplLogEnable in gps.xml 383 Steps: 384 1. default setting is on in user_debug build 385 2. remove existing supl log 386 3. run gps tracking for 1 min 387 4. supl log should exist 388 5. disable SuplLogEnable in gps.xml 389 6. remove existing supl log 390 7. run gps tracking for 1 min 391 8. supl log should not exist 392 """ 393 def is_supl_log_exist_after_supl_request(): 394 self.gps_xml.remove_supl_logs() 395 self.ad.reboot() 396 self.run_gps_and_capture_log() 397 return self.gps_xml.is_supl_log_file_exist() 398 399 result = is_supl_log_exist_after_supl_request() 400 asserts.assert_true(result, "SuplLogEnable is enable, should find supl log file") 401 402 self.gps_xml.disable_supl_log() 403 404 result = is_supl_log_exist_after_supl_request() 405 asserts.assert_false(result, "SuplLogEnable is disable, should not find supl log file") 406 407 def test_lhe_setting(self): 408 """Verify lhefailsafe / lheconsole setting in lhd.conf 409 Steps: 410 1. both setting is enabled 411 2. trigger firmware crash and check if dump file exist 412 3. disable lhefailsafe 413 4. trigger firmware crash and check if dump file exist 414 5. disable lheconsle 415 6. trigger firmware crash and check if command timeout 416 """ 417 def is_dump_file_exist_after_firmware_crash(): 418 self.lhd_conf.remove_esw_crash_dump_file() 419 self.lhd_conf.trigger_firmware_crash() 420 return self.lhd_conf.is_esw_crash_dump_file_exist() 421 422 result = is_dump_file_exist_after_firmware_crash() 423 asserts.assert_true(result, "LheFailSafe is enabled, but no crash file was found") 424 425 self.lhd_conf.disable_lhefailsafe() 426 self.ad.reboot() 427 428 result = is_dump_file_exist_after_firmware_crash() 429 asserts.assert_false(result, "LheFailSafe is disabled, but still found crash file") 430 431 self.lhd_conf.disable_lheconsole() 432 self.ad.reboot() 433 434 with asserts.assert_raises(TimeoutError): 435 self.lhd_conf.trigger_firmware_crash() 436