1#!/usr/bin/env python3.4 2# 3# Copyright 2018 - 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 Connectivity Monitor and Telephony Troubleshooter Tests 18""" 19 20import os 21import re 22import time 23 24from acts import signals 25from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest 26from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VOLTE 27from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VT 28from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC 29from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_FOR_STATE_CHANGE 30from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED 31from acts_contrib.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL 32from acts_contrib.test_utils.tel.tel_bootloader_utils import fastboot_wipe 33from acts_contrib.test_utils.tel.tel_ims_utils import toggle_volte 34from acts_contrib.test_utils.tel.tel_ims_utils import toggle_wfc 35from acts_contrib.test_utils.tel.tel_ims_utils import wait_for_wfc_enabled 36from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_voice_2g 37from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_voice_3g 38from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_csfb 39from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_iwlan 40from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_volte 41from acts_contrib.test_utils.tel.tel_test_utils import bring_up_connectivity_monitor 42from acts_contrib.test_utils.tel.tel_test_utils import get_device_epoch_time 43from acts_contrib.test_utils.tel.tel_test_utils import get_model_name 44from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name 45from acts_contrib.test_utils.tel.tel_test_utils import get_outgoing_voice_sub_id 46from acts_contrib.test_utils.tel.tel_test_utils import reboot_device 47from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode 48from acts_contrib.test_utils.tel.tel_test_utils import trigger_modem_crash 49from acts_contrib.test_utils.tel.tel_test_utils import trigger_modem_crash_by_modem 50from acts_contrib.test_utils.tel.tel_video_utils import video_call_setup_teardown 51from acts_contrib.test_utils.tel.tel_video_utils import phone_setup_video 52from acts_contrib.test_utils.tel.tel_video_utils import is_phone_in_call_video_bidirectional 53from acts_contrib.test_utils.tel.tel_voice_utils import call_setup_teardown 54from acts_contrib.test_utils.tel.tel_voice_utils import hangup_call 55from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_2g 56from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_3g 57from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb 58from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan 59from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte 60from acts_contrib.test_utils.tel.tel_voice_utils import last_call_drop_reason 61from acts_contrib.test_utils.tel.tel_wifi_utils import ensure_wifi_connected 62from acts_contrib.test_utils.tel.tel_wifi_utils import wifi_toggle_state 63 64CALL_DROP_CODE_MAPPING = { 65 373: "Radio Internal Error", 66 175: "Invalid Transaction Identifier V02", 67 159: "Temporary Failure", 68 135: "Rejected by Network V02", 69 118: "SS Not Available", 70 115: "Call Barred V02", 71 42: "Access Block V02", 72 41: "Incompatible V02" 73} 74 75CONSECUTIVE_CALL_FAILS = 5 76CALL_TROUBLE_THRESHOLD = 25 77TROUBLES = { 78 1: "WIFI_CALL_DROPS_IN_BAD_WIFI_SIGNAL", 79 2: "WIFI_CALL_DROPS_IN_GOOD_WIFI_SIGNAL_ON_SPECIFIC_WIFI_NETWORK", 80 3: "WIFI_CALL_DROPS_WITH_SPECIFIC_REASON_IN_GOOD_WIFI_SIGNAL", 81 4: "WIFI_CALL_DROPS_WITH_RANDOM_FAILURES_IN_GOOD_WIFI_SIGNAL", 82 5: "VOLTE_CALL_DROPS_IN_BAD_LTE_SIGNAL_AREAS", 83 6: "VOLTE_CALL_DROPS_IN_GOOD_LTE_SIGNAL_AREAS", 84 7: "CS_CALL_DROPS_IMS_DISABLED", 85 8: "CS_CALL_DROPS_WFC_DISABLED", 86 9: "CS_CALL_DROPS_IMS_REGISTRATION_FAILURES", 87 10: "CS_CALL_DROPS_DURING_SRVCC", 88 11: "CS_CALL_DROPS_IN_BAD_RF_CONDITIONS", 89 12: "CS_CALL_DROPS_IN_GOOD_RF_CONDITIONS_WITH_SPECIFIC_REASON", 90 13: "UNABLE_TO_TRIAGE" 91} 92 93ACTIONS = { 94 1: "CHECK_BLUETOOTH", 95 2: "CHECK_HEADSET", 96 3: "SWITCH_FROM_WIFI_PREFERRED_TO_CELLULAR_PREFERRED", 97 4: "SWITCH_FROM_CELLULAR_PREFERRED_TO_WIFI_PREFERRED", 98 5: "ENABLE_ADVANCED_4G_CALLING", 99 6: "DISABLE_ADVANCED_4G_CALLING", 100 7: "TOGGLE_AIRPLANE_MODE_TWICE", 101 8: "REBOOT_THE_PHONE", 102 9: "ENABLE_WIFI_CALLING", 103 10: "DISABLE_WIFI_CALLING", 104 11: "DISABLE_AIRPLANE_MODE", 105 12: "NONE" 106} 107 108IGNORED_CALL_DROP_REASONS = ["Radio Link Lost", "Media Timeout"] 109 110CALL_DATA_LOGS = ( 111 "/data/data/com.google.android.connectivitymonitor/databases") 112 113IGNORED_CALL_DROP_TRIGGERS = ["toggle_apm", "toggle_wifi"] 114 115 116class TelLiveConnectivityMonitorBaseTest(TelephonyBaseTest): 117 def setup_class(self): 118 TelephonyBaseTest.setup_class(self) 119 self.user_params["enable_connectivity_metrics"] = False 120 self.user_params["telephony_auto_rerun"] = 0 121 self.consecutive_failure_limit = 5 122 123 self.dut = self.android_devices[0] 124 self.ad_reference = self.android_devices[1] 125 self.dut_model = get_model_name(self.dut) 126 self.dut_operator = get_operator_name(self.log, self.dut) 127 self.dut_subID = get_outgoing_voice_sub_id(self.dut) 128 self.dut_capabilities = self.dut.telephony["subscription"][self.dut_subID].get("capabilities", []) 129 self.dut_wfc_modes = self.dut.telephony["subscription"][self.dut_subID].get("wfc_modes", []) 130 self.ad_reference_subID = get_outgoing_voice_sub_id(self.ad_reference) 131 self.reference_capabilities = self.ad_reference.telephony["subscription"][self.ad_reference_subID].get( 132 "capabilities", []) 133 self.dut.log.info("DUT capabilities: %s", self.dut_capabilities) 134 self.skip_reset_between_cases = False 135 self.user_params["telephony_auto_rerun"] = 0 136 self.number_of_devices = 1 137 self.call_drop_override_code = self.user_params.get( 138 "call_drop_override_code", 373) 139 140 def setup_test(self): 141 TelephonyBaseTest.setup_test(self) 142 bring_up_connectivity_monitor(self.dut) 143 ## Work around for WFC not working issue on 2018 devices 144 if "Permissive" not in self.dut.adb.shell("su root getenforce"): 145 self.dut.adb.shell("su root setenforce 0") 146 147 def on_fail(self, test_name, begin_time): 148 self.dut.log.info("Pulling %s", CALL_DATA_LOGS) 149 log_path = os.path.join(self.dut.log_path, test_name, 150 "ConnectivityMonitorLogs_%s" % self.dut.serial) 151 os.makedirs(log_path, exist_ok=True) 152 self.dut.pull_files([CALL_DATA_LOGS], log_path) 153 154 self._take_bug_report(test_name, begin_time) 155 156 def teardown_test(self): 157 self.set_drop_reason_override(override_code=None) 158 TelephonyBaseTest.teardown_test(self) 159 160 def connect_to_wifi(self): 161 if not ensure_wifi_connected(self.log, self.dut, 162 self.wifi_network_ssid, 163 self.wifi_network_pass): 164 self.dut.log.error("Fail to connected to WiFi") 165 return False 166 else: 167 self.dut.log.info("Connected to WiFi") 168 return True 169 170 def is_wfc_enabled(self): 171 return wait_for_wfc_enabled(self.log, self.dut) 172 173 def enable_volte(self): 174 if CAPABILITY_VOLTE not in self.dut_capabilities: 175 raise signals.TestSkip("VoLTE is not supported, abort test.") 176 toggle_volte(self.log, self.dut, True) 177 178 def enable_wfc(self): 179 if CAPABILITY_WFC not in self.dut_capabilities: 180 raise signals.TestSkip("WFC is not supported, abort test.") 181 toggle_wfc(self.log, self.dut, True) 182 183 def disable_volte(self): 184 if CAPABILITY_VOLTE not in self.dut_capabilities: 185 raise signals.TestSkip("VoLTE is not supported, abort test.") 186 toggle_volte(self.log, self.dut, False) 187 188 def disable_wfc(self): 189 if CAPABILITY_WFC not in self.dut_capabilities: 190 raise signals.TestSkip("WFC is not supported, abort test.") 191 toggle_wfc(self.log, self.dut, False) 192 193 def setup_wfc_non_apm(self): 194 if CAPABILITY_WFC not in self.dut_capabilities and ( 195 WFC_MODE_WIFI_PREFERRED not in self.dut_wfc_modes): 196 raise signals.TestSkip( 197 "WFC in non-APM is not supported, abort test.") 198 if not phone_setup_iwlan( 199 self.log, self.dut, False, WFC_MODE_WIFI_PREFERRED, 200 self.wifi_network_ssid, self.wifi_network_pass): 201 self.dut.log.error("Failed to setup WFC.") 202 raise signals.TestFailure("Failed to setup WFC in non-APM") 203 self.dut.log.info("Phone is in WFC enabled state.") 204 return True 205 206 def setup_wfc_apm(self): 207 if CAPABILITY_WFC not in self.dut_capabilities: 208 raise signals.TestSkip("WFC is not supported, abort test.") 209 if not phone_setup_iwlan(self.log, self.dut, True, 210 self.dut_wfc_modes[0], self.wifi_network_ssid, 211 self.wifi_network_pass): 212 self.dut.log.error("Failed to setup WFC.") 213 raise signals.TestFailure("Failed to setup WFC in APM") 214 self.dut.log.info("Phone is in WFC enabled state.") 215 return True 216 217 def setup_volte(self): 218 if CAPABILITY_VOLTE not in self.dut_capabilities: 219 raise signals.TestSkip("VoLTE is not supported, abort test.") 220 if not phone_setup_volte(self.log, self.dut): 221 self.dut.log.error("Phone failed to enable VoLTE.") 222 raise signals.TestFailure("Failed to enable VoLTE") 223 self.dut.log.info("Phone VOLTE is enabled successfully.") 224 return True 225 226 def setup_csfb(self): 227 if not phone_setup_csfb(self.log, self.dut): 228 self.dut.log.error("Phone failed to setup CSFB.") 229 raise signals.TestFailure("Failed to setup CSFB") 230 self.dut.log.info("Phone CSFB is enabled successfully.") 231 return True 232 233 def setup_3g(self): 234 if not phone_setup_voice_3g(self.log, self.dut): 235 self.dut.log.error("Phone failed to setup 3G.") 236 raise signals.TestFailure("Faile to setup 3G") 237 self.dut.log.info("Phone RAT 3G is enabled successfully.") 238 return True 239 240 def setup_2g(self): 241 if self.dut_operator not in ("tmo", "uk_ee"): 242 raise signals.TestSkip("2G is not supported, abort test.") 243 if not phone_setup_voice_2g(self.log, self.dut): 244 self.dut.log.error("Phone failed to setup 2G.") 245 raise signals.TestFailure("Failed to setup 2G") 246 self.dut.log.info("RAT 2G is enabled successfully.") 247 return True 248 249 def setup_vt(self): 250 if CAPABILITY_VT not in self.dut_capabilities or ( 251 CAPABILITY_VT not in self.reference_capabilities): 252 raise signals.TestSkip("VT is not supported, abort test.") 253 for ad in (self.dut, self.ad_reference): 254 if not phone_setup_video(self.log, ad): 255 ad.log.error("Failed to setup VT.") 256 return False 257 return True 258 259 def set_drop_reason_override(self, override_code=None): 260 if not override_code: 261 if self.dut.adb.shell("getprop vendor.radio.call_end_reason"): 262 self.dut.adb.shell("setprop vendor.radio.call_end_reason ''") 263 else: 264 if self.dut.adb.shell("getprop vendor.radio.call_end_reason" 265 ) != str(override_code): 266 cmd = "setprop vendor.radio.call_end_reason %s" \ 267 % override_code 268 self.dut.log.info("====== %s ======", cmd) 269 self.dut.adb.shell(cmd) 270 271 def modem_crash(self): 272 # Modem SSR 273 self.user_params["check_crash"] = False 274 self.dut.log.info("Triggering ModemSSR") 275 try: 276 self.dut.droid.logI("======== Trigger modem crash ========") 277 except Exception: 278 pass 279 if (not self.dut.is_apk_installed("com.google.mdstest") 280 ) or self.dut.adb.getprop("ro.build.version.release")[0] in ( 281 "8", "O", "7", "N") or self.dut.model in ("angler", "bullhead", 282 "sailfish", 283 "marlin"): 284 trigger_modem_crash(self.dut) 285 else: 286 trigger_modem_crash_by_modem(self.dut) 287 288 def call_drop_by_modem_crash(self, 289 call_verification_function=None, 290 vt=False): 291 if vt: 292 if not video_call_setup_teardown( 293 self.log, 294 self.dut, 295 self.ad_reference, 296 None, 297 video_state=VT_STATE_BIDIRECTIONAL, 298 verify_caller_func=is_phone_in_call_video_bidirectional, 299 verify_callee_func=is_phone_in_call_video_bidirectional): 300 self.dut.log.error("VT Call Failed.") 301 return False 302 else: 303 if not call_setup_teardown( 304 self.log, 305 self.dut, 306 self.ad_reference, 307 ad_hangup=None, 308 verify_caller_func=call_verification_function, 309 wait_time_in_call=10): 310 self.log.error("Call setup failed") 311 return False 312 313 # Modem SSR 314 self.modem_crash() 315 316 try: 317 if self.dut.droid.telecomIsInCall(): 318 self.dut.log.info("Still in call after trigger modem crash") 319 return False 320 else: 321 reasons = self.dut.search_logcat( 322 "qcril_qmi_voice_map_qmi_to_ril_last_call_failure_cause") 323 if reasons: 324 self.dut.log.info(reasons[-1]["log_message"]) 325 except Exception as e: 326 self.dut.log.error(e) 327 328 def toggle_apm(self): 329 toggle_airplane_mode(self.log, self.dut, new_state=None) 330 331 def toggle_wifi(self): 332 wifi_toggle_state(self.log, self.dut, None) 333 334 def drop_reason_override(self): 335 hangup_call(self.log, self.ad_reference) 336 337 def clearn_up_bugreport_database(self): 338 self.dut.adb.shell( 339 "rm /data/data/com.google.android.connectivitymonitor/" 340 "shared_prefs/ConnectivityMonitor_BugReport.xml") 341 342 def clearn_up_troubleshooter_database(self): 343 self.dut.adb.shell( 344 "rm /data/data/com.google.android.connectivitymonitor/" 345 "shared_prefs/ConnectivityMonitor_TroubleshooterResult.xml") 346 347 def parsing_bugreport_database(self): 348 output = self.dut.adb.shell( 349 "cat /data/data/com.google.android.connectivitymonitor/" 350 "shared_prefs/ConnectivityMonitor_BugReport.xml") 351 bugreport_database = re.findall(r">Call Drop:\s+(.*)<", output) 352 self.dut.log.info("bugreport_database = %s", bugreport_database) 353 return bugreport_database 354 355 def parsing_troubleshooter_database(self): 356 output = self.dut.adb.shell( 357 "cat /data/data/com.google.android.connectivitymonitor/" 358 "shared_prefs/ConnectivityMonitor_TroubleshooterResult.xml") 359 results = re.findall(r"name=\"(\S+)\">(\S+)<", output) 360 troubleshooter_database = {} 361 for result in results: 362 if "count" in result[0] or "num_calls" in result[0]: 363 troubleshooter_database[result[0]] = int(result[1]) 364 else: 365 troubleshooter_database[result[0]] = result[1] 366 self.dut.log.info("TroubleshooterResult=%s", 367 sorted(troubleshooter_database.items())) 368 return troubleshooter_database 369 370 def parsing_call_summary(self): 371 call_summary = self.dut.adb.shell( 372 "dumpsys activity service com.google.android.connectivitymonitor/" 373 ".ConnectivityMonitorService") 374 self.dut.log.info(call_summary) 375 call_summary_info = {} 376 results = re.findall( 377 r"(\S+): (\d+) out of (\d+) calls dropped, percentage=(\S+)", 378 call_summary) 379 for result in results: 380 call_summary_info[result[0]] = int(result[2]) 381 call_summary_info["%s_dropped" % result[0]] = int(result[1]) 382 if result[3] == "NaN": 383 call_summary_info["%s_dropped_percentage" % result[0]] = 0 384 else: 385 call_summary_info["%s_dropped_percentage" % result[0]] = float( 386 result[3]) 387 results = re.findall(r"(\S+): predominant failure reason=(.+)", 388 call_summary) 389 for result in results: 390 call_summary_info["%s_failure_reason" % result[0]] = result[1] 391 self.dut.log.info("call summary dumpsys = %s", 392 sorted(call_summary_info.items())) 393 return call_summary_info 394 395 def parsing_call_statistics(self): 396 call_statistics_info = {} 397 call_statistics = self.dut.adb.shell( 398 "content query --uri content://com.google.android." 399 "connectivitymonitor.troubleshooterprovider/call_statistics") 400 self.dut.log.info("troubleshooterprovider call_statistics:\n%s", 401 call_statistics) 402 results = re.findall(r"KEY=(\S+), VALUE=(\S+)", call_statistics) 403 for result in results: 404 if ("count" in result[0] or "num_calls" in result[0]): 405 if result[1] == "NULL": 406 call_statistics_info[result[0]] = 0 407 else: 408 call_statistics_info[result[0]] = int(result[1]) 409 else: 410 call_statistics_info[result[0]] = result[1] 411 self.dut.log.info("troubleshooterprovider call_statistics: %s", 412 sorted(call_statistics_info.items())) 413 return call_statistics_info 414 415 def parsing_diagnostics(self): 416 diagnostics_info = {} 417 diagnostics = self.dut.adb.shell( 418 "content query --uri content://com.google.android." 419 "connectivitymonitor.troubleshooterprovider/diagnostics") 420 self.dut.log.info("troubleshooterprovider diagnostics:\n%s", 421 diagnostics) 422 results = re.findall(r"KEY=(\S+), VALUE=(\S+)", diagnostics) 423 for result in results: 424 diagnostics_info[result[0]] = result[1] 425 self.dut.log.info("troubleshooterprovider diagnostics: %s", 426 sorted(diagnostics_info.items())) 427 return diagnostics_info 428 429 def call_setup_and_connectivity_monitor_checking(self, 430 setup=None, 431 handover=None, 432 triggers=[], 433 expected_drop_reason="", 434 expected_trouble=None, 435 expected_action=None): 436 437 call_verification_function = None 438 begin_time = get_device_epoch_time(self.dut) 439 call_data_summary_before = self.parsing_call_summary() 440 call_statistics_before = self.parsing_call_statistics() 441 self.parsing_diagnostics() 442 self.parsing_troubleshooter_database() 443 bugreport_database_before = self.parsing_bugreport_database() 444 445 if expected_drop_reason: 446 expected_drop_reasons = set(expected_drop_reason.split("|")) 447 else: 448 expected_drop_reasons = set() 449 checking_counters = ["Calls"] 450 checking_reasons = [] 451 result = True 452 if setup in ("wfc_apm", "wfc_non_apm"): 453 call_verification_function = is_phone_in_call_iwlan 454 elif setup == "volte": 455 call_verification_function = is_phone_in_call_volte 456 elif setup == "csfb": 457 call_verification_function = is_phone_in_call_csfb 458 elif setup == "3g": 459 call_verification_function = is_phone_in_call_3g 460 elif setup == "2g": 461 call_verification_function = is_phone_in_call_2g 462 technology = handover or setup 463 if technology in ("wfc_apm", "wfc_non_apm"): 464 if triggers and triggers[0] not in IGNORED_CALL_DROP_TRIGGERS: 465 checking_counters.extend( 466 ["Calls_dropped", "VOWIFI", "VOWIFI_dropped"]) 467 checking_reasons.append("VOWIFI_failure_reason") 468 elif call_data_summary_before.get("Calls_dropped", 0): 469 checking_counters.append("VOWIFI") 470 elif technology == "volte": 471 if triggers and triggers[0] not in IGNORED_CALL_DROP_TRIGGERS: 472 checking_counters.extend( 473 ["Calls_dropped", "VOLTE", "VOLTE_dropped"]) 474 checking_reasons.append("VOLTE_failure_reason") 475 elif call_data_summary_before.get("Calls_dropped", 0): 476 checking_counters.append("VOLTE") 477 elif technology in ("csfb", "3g", "2g"): 478 if triggers and triggers[0] not in IGNORED_CALL_DROP_TRIGGERS: 479 checking_counters.extend(["Calls_dropped", "CS", "CS_dropped"]) 480 checking_reasons.append("CS_failure_reason") 481 elif call_data_summary_before.get("Calls_dropped", 0): 482 checking_counters.append("CS") 483 484 if setup == "vt": 485 if not video_call_setup_teardown( 486 self.log, 487 self.dut, 488 self.ad_reference, 489 None, 490 video_state=VT_STATE_BIDIRECTIONAL, 491 verify_caller_func=is_phone_in_call_video_bidirectional, 492 verify_callee_func=is_phone_in_call_video_bidirectional): 493 raise signals.TestFailure("VT Call Failed.") 494 else: 495 if not call_setup_teardown( 496 self.log, 497 self.dut, 498 self.ad_reference, 499 ad_hangup=None, 500 verify_caller_func=call_verification_function, 501 wait_time_in_call=10): 502 raise signals.TestFailure("Call Setup Failed.") 503 504 for trigger in triggers: 505 if self.dut.droid.telecomIsInCall(): 506 self.dut.log.info("Telecom is in call") 507 self.dut.log.info( 508 "Voice in RAT %s", 509 self.dut.droid.telephonyGetCurrentVoiceNetworkType()) 510 else: 511 self.dut.log.info("Not in call") 512 # Trigger in-call event 513 if trigger and getattr(self, trigger, None): 514 trigger_func = getattr(self, trigger) 515 trigger_func() 516 time.sleep(MAX_WAIT_TIME_FOR_STATE_CHANGE) 517 518 if self.dut.droid.telecomIsInCall(): 519 self.dut.log.info("Telecom is in call") 520 self.dut.log.info( 521 "Voice in RAT %s", 522 self.dut.droid.telephonyGetCurrentVoiceNetworkType()) 523 else: 524 self.dut.log.info("Not in call") 525 526 if self.dut.droid.telecomIsInCall(): 527 self.dut.log.info("Telecom is in call") 528 self.dut.log.info( 529 "Voice in RAT %s", 530 self.dut.droid.telephonyGetCurrentVoiceNetworkType()) 531 else: 532 self.dut.log.info("Not in call") 533 534 drop_reason = last_call_drop_reason(self.dut, begin_time) 535 drop_reason = drop_reason.title() 536 if drop_reason: 537 expected_drop_reasons.add(drop_reason) 538 for ad in (self.ad_reference, self.dut): 539 try: 540 if ad.droid.telecomIsInCall(): 541 if triggers: 542 ad.log.info("Still in call after triggers %s", 543 triggers) 544 result = False 545 hangup_call(self.log, ad) 546 time.sleep(MAX_WAIT_TIME_FOR_STATE_CHANGE) 547 except Exception as e: 548 ad.log.error(e) 549 550 call_data_summary_after = self.parsing_call_summary() 551 call_statistics_after = self.parsing_call_statistics() 552 diagnostics_after = self.parsing_diagnostics() 553 ts_database_after = self.parsing_troubleshooter_database() 554 555 for counter in checking_counters: 556 if call_data_summary_after.get( 557 counter, 558 0) != call_data_summary_before.get(counter, 0) + 1: 559 self.dut.log.error("Counter %s did not increase", counter) 560 result = False 561 else: 562 self.dut.log.info("Counter %s increased", counter) 563 if counter == "Calls": 564 if call_statistics_after.get("num_calls", 565 0) - call_statistics_before.get( 566 "num_calls", 0) < 1: 567 self.dut.log.warning( 568 "call_statistics num_calls didn't increase") 569 # result = False 570 else: 571 self.dut.log.info("call_statistics num_calls increased") 572 if "_dropped" in counter and counter != "Calls_dropped": 573 desc = counter.split("_")[0] 574 if desc == "VOWIFI": 575 stat_key = "recent_wfc_fail_count" 576 else: 577 stat_key = "recent_%s_fail_count" % desc.lower() 578 before = call_statistics_after.get(stat_key, 0) 579 after = call_statistics_after.get(stat_key, 0) 580 most_failure_call_type = call_statistics_after.get( 581 "call_type_with_most_failures") 582 diagnosis = diagnostics_after.get("diagnosis") 583 actions = diagnostics_after.get("actions") 584 if after - before < 1: 585 self.dut.log.warning("call_statistics %s didn't increase, " 586 "before %s, after %s" % 587 (stat_key, before, after)) 588 # result = False 589 else: 590 self.dut.log.info("call_statistics %s increased", stat_key) 591 if most_failure_call_type != desc: 592 self.dut.log.warning( 593 "call_statistics call_type_with_most_failures " 594 "is %s, not %s", most_failure_call_type, desc) 595 else: 596 self.dut.log.info( 597 "call_statistics call_type_with_most_failures is %s", 598 most_failure_call_type) 599 dropped = call_data_summary_after.get("%s_dropped" % desc, 0) 600 drop_percentage = call_data_summary_after.get( 601 "%s_dropped_percentage" % desc, 0) 602 self.dut.log.info("%s_dropped = %s, percentage = %s", desc, 603 dropped, drop_percentage) 604 if expected_trouble and expected_trouble != diagnosis: 605 self.dut.log.warning("diagnoisis = %s, expecting %s", 606 diagnosis, expected_trouble) 607 if expected_action and expected_action != actions: 608 self.dut.log.error("actions = %s, expecting %s", actions, 609 expected_action) 610 result = False 611 if drop_percentage > CALL_TROUBLE_THRESHOLD and ( 612 dropped > CONSECUTIVE_CALL_FAILS): 613 if diagnosis == "UNABLE_TO_TRIAGE": 614 self.dut.log.error( 615 "troubleshooter diagnosis is %s with %s dropped " 616 "and %s drop_percentage", diagnosis, dropped, 617 drop_percentage) 618 result = False 619 if actions == "NONE": 620 self.dut.log.error( 621 "troubleshooter failed to provide suggestion, " 622 "actions = %s", actions) 623 result = False 624 if expected_drop_reasons: 625 expected_drop_reason = "|".join(expected_drop_reasons) 626 for reason_key in checking_reasons: 627 if call_data_summary_after.get(reason_key, None): 628 drop_reason = call_data_summary_after[reason_key] 629 if expected_drop_reason and drop_reason not in expected_drop_reason: 630 self.dut.log.error("%s is: %s, expecting %s", reason_key, 631 drop_reason, expected_drop_reason) 632 result = False 633 else: 634 self.dut.log.info("%s is: %s as expected", reason_key, 635 drop_reason) 636 else: 637 self.dut.log.error("%s is not provided in summary report", 638 reason_key) 639 result = False 640 641 if not triggers or triggers[0] in IGNORED_CALL_DROP_TRIGGERS: 642 return result 643 if drop_reason in bugreport_database_before: 644 self.dut.log.info("%s is in bugreport database %s before call", 645 drop_reason, bugreport_database_before) 646 return result 647 else: 648 self.dut.log.info("%s is not in bugreport database %s before call", 649 drop_reason, bugreport_database_before) 650 if drop_reason in IGNORED_CALL_DROP_REASONS: 651 self.dut.log.info( 652 "Call drop with reason %s will skip bugreport notification", 653 drop_reason) 654 return result 655 else: 656 self.dut.log.info( 657 "Call drop %s should generate bugreport notification", 658 drop_reason) 659 # Parse logcat for UI notification only for the first failure 660 if self.dut.search_logcat("Bugreport notification title Call Drop:", 661 begin_time): 662 self.dut.log.info( 663 "Bugreport notification title Call Drop is seen in logcat") 664 return result 665 else: 666 self.dut.log.error( 667 "Bugreport notification title Call Drop is not seen in logcat") 668 return False 669 670 def call_drop_test(self, 671 setup=None, 672 handover=None, 673 count=CONSECUTIVE_CALL_FAILS, 674 triggers=[], 675 expected_drop_reason=None, 676 expected_trouble=None, 677 expected_action=None): 678 if not triggers: 679 if self.dut.model in ("marlin", "sailfish", "walleye", "taimen"): 680 triggers = ["modem_crash"] 681 expected_drop_reason = "Error Unspecified" 682 else: 683 triggers = ["drop_reason_override"] 684 if "drop_reason_override" in triggers: 685 self.set_drop_reason_override( 686 override_code=self.call_drop_override_code) 687 expected_drop_reason = CALL_DROP_CODE_MAPPING[int( 688 self.call_drop_override_code)] 689 for iter in range(count): 690 self.dut.log.info("===== %s_iter_%s =====", self.test_name, 691 iter + 1) 692 if iter < count - 1: 693 action = None 694 trouble = None 695 else: 696 action = expected_action 697 trouble = expected_trouble 698 if not self.call_setup_and_connectivity_monitor_checking( 699 setup=setup, 700 handover=handover, 701 triggers=triggers, 702 expected_drop_reason=expected_drop_reason, 703 expected_trouble=trouble, 704 expected_action=action): 705 return False 706 return True 707 708 def call_drop_triggered_suggestion_test(self, 709 setup=None, 710 handover=None, 711 triggers=[], 712 expected_drop_reason=None, 713 expected_trouble=None, 714 expected_action=None): 715 call_summary = self.parsing_call_summary() 716 diagnostics = self.parsing_diagnostics() 717 diagnosis = diagnostics.get("diagnosis") 718 actions = diagnostics.get("actions") 719 self.dut.log.info("Expected trouble = %s, action = %s", 720 expected_trouble, expected_action) 721 if expected_trouble and diagnosis == expected_trouble and not handover: 722 self.dut.log.info("Diagnosis is the expected %s", trouble) 723 if expected_action and expected_action != actions: 724 self.dut.log.error("Action is %s, expecting %s", actions, 725 expected_action) 726 result = False 727 if setup in ("wfc_apm", "wfc_non_apm"): 728 desc = "VOWIFI" 729 elif setup == "volte": 730 desc = "VOLTE" 731 elif setup in ("csfb", "3g", "2g"): 732 desc = "CS" 733 drops = call_summary.get("%s_dropped" % desc, 0) 734 drop_percentage = call_summary.get("%s_dropped_percentage" % desc, 735 0) 736 if drops < CONSECUTIVE_CALL_FAILS or drop_percentage < 25: 737 self.dut.log.error( 738 "Should NOT get %s for %s %s_dropped and %s %s_dropped_percentage", 739 trouble, drops, desc, drop_percentage, desc) 740 return False 741 else: 742 return True 743 else: 744 self.dut.log.info("Generate %s consecutive call drops", 745 CONSECUTIVE_CALL_FAILS) 746 return self.call_drop_test( 747 setup=setup, 748 handover=handover, 749 count=CONSECUTIVE_CALL_FAILS, 750 triggers=triggers, 751 expected_drop_reason=expected_drop_reason, 752 expected_trouble=expected_trouble, 753 expected_action=expected_action) 754 755 def healthy_call_test(self, 756 setup=None, 757 handover=None, 758 count=1, 759 triggers=[], 760 expected_trouble=None, 761 expected_action=None): 762 if self.dut.model not in ("marlin", "sailfish", "walleye", "taimen"): 763 self.set_drop_reason_override(override_code=25) 764 result = True 765 for iter in range(count): 766 if not self.call_setup_and_connectivity_monitor_checking( 767 setup=setup, 768 handover=handover, 769 triggers=triggers, 770 expected_trouble=expected_trouble, 771 expected_action=expected_action): 772 return False 773 return True 774 775 def forced_call_drop_test(self, 776 setup=None, 777 handover=None, 778 triggers=None, 779 expected_drop_reason=None): 780 expected_trouble = None 781 expected_action = None 782 technology = handover or setup 783 if setup: 784 setup_func = getattr(self, "setup_%s" % setup) 785 if not setup_func(): return False 786 if technology == "volte": 787 expected_trouble = TROUBLES[6], 788 expected_action = ACTIONS[6] 789 elif technology == "csfb": 790 if CAPABILITY_VOLTE in self.dut_capabilities: 791 expected_action = ACTIONS[5] 792 else: 793 expected_action = ACTIONS[7] 794 expected_trouble = TROUBLES[7] 795 elif technology == "3g": 796 if CAPABILITY_VOLTE in self.dut_capabilities: 797 expected_action = ACTIONS[5] 798 else: 799 expected_action = ACTIONS[7] 800 expected_trouble = TROUBLES[7] 801 elif technology == "2g": 802 if CAPABILITY_VOLTE in self.dut_capabilities: 803 expected_action = ACTIONS[5] 804 else: 805 expected_action = ACTIONS[7] 806 expected_trouble = TROUBLES[7] 807 elif technology == "wfc_apm": 808 expected_trouble = TROUBLES[3] 809 expected_action = ACTIONS[11] 810 elif technology == "wfc_non_apm": 811 expected_trouble = TROUBLES[3] 812 expected_action = ACTIONS[3] 813 814 return self.call_drop_triggered_suggestion_test( 815 setup=setup, 816 handover=handover, 817 triggers=triggers, 818 expected_drop_reason=expected_drop_reason, 819 expected_trouble=expected_trouble, 820 expected_action=expected_action) 821 822 def call_drop_test_after_wipe(self, setup=None): 823 if setup: 824 setup_func = getattr(self, "setup_%s" % setup) 825 if not setup_func(): return False 826 fastboot_wipe(self.dut) 827 bring_up_connectivity_monitor(self.dut) 828 return self.forced_call_drop_test(setup=setup) 829 830 def call_drop_test_after_reboot(self, setup=None): 831 self.forced_call_drop_test(setup=setup) 832 self.healthy_call_test(setup=setup, count=1) 833 reboot_device(self.dut) 834 return self.forced_call_drop_test(setup=setup) 835