| #!/usr/bin/env python3.4 |
| # |
| # Copyright 2018 - Google |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| """ |
| Connectivity Monitor and Telephony Troubleshooter Tests |
| """ |
| |
| import os |
| import re |
| import time |
| |
| from acts import signals |
| from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest |
| from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VOLTE |
| from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VT |
| from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC |
| from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_FOR_STATE_CHANGE |
| from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED |
| from acts_contrib.test_utils.tel.tel_defines import VT_STATE_BIDIRECTIONAL |
| from acts_contrib.test_utils.tel.tel_bootloader_utils import fastboot_wipe |
| from acts_contrib.test_utils.tel.tel_ims_utils import toggle_volte |
| from acts_contrib.test_utils.tel.tel_ims_utils import toggle_wfc |
| from acts_contrib.test_utils.tel.tel_ims_utils import wait_for_wfc_enabled |
| from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_voice_2g |
| from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_voice_3g |
| from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_csfb |
| from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_iwlan |
| from acts_contrib.test_utils.tel.tel_phone_setup_utils import phone_setup_volte |
| from acts_contrib.test_utils.tel.tel_test_utils import bring_up_connectivity_monitor |
| from acts_contrib.test_utils.tel.tel_test_utils import get_device_epoch_time |
| from acts_contrib.test_utils.tel.tel_test_utils import get_model_name |
| from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name |
| from acts_contrib.test_utils.tel.tel_test_utils import get_outgoing_voice_sub_id |
| from acts_contrib.test_utils.tel.tel_test_utils import reboot_device |
| from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode |
| from acts_contrib.test_utils.tel.tel_test_utils import trigger_modem_crash |
| from acts_contrib.test_utils.tel.tel_test_utils import trigger_modem_crash_by_modem |
| from acts_contrib.test_utils.tel.tel_video_utils import video_call_setup_teardown |
| from acts_contrib.test_utils.tel.tel_video_utils import phone_setup_video |
| from acts_contrib.test_utils.tel.tel_video_utils import is_phone_in_call_video_bidirectional |
| from acts_contrib.test_utils.tel.tel_voice_utils import call_setup_teardown |
| from acts_contrib.test_utils.tel.tel_voice_utils import hangup_call |
| from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_2g |
| from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_3g |
| from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_csfb |
| from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_iwlan |
| from acts_contrib.test_utils.tel.tel_voice_utils import is_phone_in_call_volte |
| from acts_contrib.test_utils.tel.tel_voice_utils import last_call_drop_reason |
| from acts_contrib.test_utils.tel.tel_wifi_utils import ensure_wifi_connected |
| from acts_contrib.test_utils.tel.tel_wifi_utils import wifi_toggle_state |
| |
| CALL_DROP_CODE_MAPPING = { |
| 373: "Radio Internal Error", |
| 175: "Invalid Transaction Identifier V02", |
| 159: "Temporary Failure", |
| 135: "Rejected by Network V02", |
| 118: "SS Not Available", |
| 115: "Call Barred V02", |
| 42: "Access Block V02", |
| 41: "Incompatible V02" |
| } |
| |
| CONSECUTIVE_CALL_FAILS = 5 |
| CALL_TROUBLE_THRESHOLD = 25 |
| TROUBLES = { |
| 1: "WIFI_CALL_DROPS_IN_BAD_WIFI_SIGNAL", |
| 2: "WIFI_CALL_DROPS_IN_GOOD_WIFI_SIGNAL_ON_SPECIFIC_WIFI_NETWORK", |
| 3: "WIFI_CALL_DROPS_WITH_SPECIFIC_REASON_IN_GOOD_WIFI_SIGNAL", |
| 4: "WIFI_CALL_DROPS_WITH_RANDOM_FAILURES_IN_GOOD_WIFI_SIGNAL", |
| 5: "VOLTE_CALL_DROPS_IN_BAD_LTE_SIGNAL_AREAS", |
| 6: "VOLTE_CALL_DROPS_IN_GOOD_LTE_SIGNAL_AREAS", |
| 7: "CS_CALL_DROPS_IMS_DISABLED", |
| 8: "CS_CALL_DROPS_WFC_DISABLED", |
| 9: "CS_CALL_DROPS_IMS_REGISTRATION_FAILURES", |
| 10: "CS_CALL_DROPS_DURING_SRVCC", |
| 11: "CS_CALL_DROPS_IN_BAD_RF_CONDITIONS", |
| 12: "CS_CALL_DROPS_IN_GOOD_RF_CONDITIONS_WITH_SPECIFIC_REASON", |
| 13: "UNABLE_TO_TRIAGE" |
| } |
| |
| ACTIONS = { |
| 1: "CHECK_BLUETOOTH", |
| 2: "CHECK_HEADSET", |
| 3: "SWITCH_FROM_WIFI_PREFERRED_TO_CELLULAR_PREFERRED", |
| 4: "SWITCH_FROM_CELLULAR_PREFERRED_TO_WIFI_PREFERRED", |
| 5: "ENABLE_ADVANCED_4G_CALLING", |
| 6: "DISABLE_ADVANCED_4G_CALLING", |
| 7: "TOGGLE_AIRPLANE_MODE_TWICE", |
| 8: "REBOOT_THE_PHONE", |
| 9: "ENABLE_WIFI_CALLING", |
| 10: "DISABLE_WIFI_CALLING", |
| 11: "DISABLE_AIRPLANE_MODE", |
| 12: "NONE" |
| } |
| |
| IGNORED_CALL_DROP_REASONS = ["Radio Link Lost", "Media Timeout"] |
| |
| CALL_DATA_LOGS = ( |
| "/data/data/com.google.android.connectivitymonitor/databases") |
| |
| IGNORED_CALL_DROP_TRIGGERS = ["toggle_apm", "toggle_wifi"] |
| |
| |
| class TelLiveConnectivityMonitorBaseTest(TelephonyBaseTest): |
| def setup_class(self): |
| TelephonyBaseTest.setup_class(self) |
| self.user_params["enable_connectivity_metrics"] = False |
| self.user_params["telephony_auto_rerun"] = 0 |
| self.consecutive_failure_limit = 5 |
| |
| self.dut = self.android_devices[0] |
| self.ad_reference = self.android_devices[1] |
| self.dut_model = get_model_name(self.dut) |
| self.dut_operator = get_operator_name(self.log, self.dut) |
| self.dut_subID = get_outgoing_voice_sub_id(self.dut) |
| self.dut_capabilities = self.dut.telephony["subscription"][self.dut_subID].get("capabilities", []) |
| self.dut_wfc_modes = self.dut.telephony["subscription"][self.dut_subID].get("wfc_modes", []) |
| self.ad_reference_subID = get_outgoing_voice_sub_id(self.ad_reference) |
| self.reference_capabilities = self.ad_reference.telephony["subscription"][self.ad_reference_subID].get( |
| "capabilities", []) |
| self.dut.log.info("DUT capabilities: %s", self.dut_capabilities) |
| self.skip_reset_between_cases = False |
| self.user_params["telephony_auto_rerun"] = 0 |
| self.number_of_devices = 1 |
| self.call_drop_override_code = self.user_params.get( |
| "call_drop_override_code", 373) |
| |
| def setup_test(self): |
| TelephonyBaseTest.setup_test(self) |
| bring_up_connectivity_monitor(self.dut) |
| ## Work around for WFC not working issue on 2018 devices |
| if "Permissive" not in self.dut.adb.shell("su root getenforce"): |
| self.dut.adb.shell("su root setenforce 0") |
| |
| def on_fail(self, test_name, begin_time): |
| self.dut.log.info("Pulling %s", CALL_DATA_LOGS) |
| log_path = os.path.join(self.dut.log_path, test_name, |
| "ConnectivityMonitorLogs_%s" % self.dut.serial) |
| os.makedirs(log_path, exist_ok=True) |
| self.dut.pull_files([CALL_DATA_LOGS], log_path) |
| |
| self._take_bug_report(test_name, begin_time) |
| |
| def teardown_test(self): |
| self.set_drop_reason_override(override_code=None) |
| TelephonyBaseTest.teardown_test(self) |
| |
| def connect_to_wifi(self): |
| if not ensure_wifi_connected(self.log, self.dut, |
| self.wifi_network_ssid, |
| self.wifi_network_pass): |
| self.dut.log.error("Fail to connected to WiFi") |
| return False |
| else: |
| self.dut.log.info("Connected to WiFi") |
| return True |
| |
| def is_wfc_enabled(self): |
| return wait_for_wfc_enabled(self.log, self.dut) |
| |
| def enable_volte(self): |
| if CAPABILITY_VOLTE not in self.dut_capabilities: |
| raise signals.TestSkip("VoLTE is not supported, abort test.") |
| toggle_volte(self.log, self.dut, True) |
| |
| def enable_wfc(self): |
| if CAPABILITY_WFC not in self.dut_capabilities: |
| raise signals.TestSkip("WFC is not supported, abort test.") |
| toggle_wfc(self.log, self.dut, True) |
| |
| def disable_volte(self): |
| if CAPABILITY_VOLTE not in self.dut_capabilities: |
| raise signals.TestSkip("VoLTE is not supported, abort test.") |
| toggle_volte(self.log, self.dut, False) |
| |
| def disable_wfc(self): |
| if CAPABILITY_WFC not in self.dut_capabilities: |
| raise signals.TestSkip("WFC is not supported, abort test.") |
| toggle_wfc(self.log, self.dut, False) |
| |
| def setup_wfc_non_apm(self): |
| if CAPABILITY_WFC not in self.dut_capabilities and ( |
| WFC_MODE_WIFI_PREFERRED not in self.dut_wfc_modes): |
| raise signals.TestSkip( |
| "WFC in non-APM is not supported, abort test.") |
| if not phone_setup_iwlan( |
| self.log, self.dut, False, WFC_MODE_WIFI_PREFERRED, |
| self.wifi_network_ssid, self.wifi_network_pass): |
| self.dut.log.error("Failed to setup WFC.") |
| raise signals.TestFailure("Failed to setup WFC in non-APM") |
| self.dut.log.info("Phone is in WFC enabled state.") |
| return True |
| |
| def setup_wfc_apm(self): |
| if CAPABILITY_WFC not in self.dut_capabilities: |
| raise signals.TestSkip("WFC is not supported, abort test.") |
| if not phone_setup_iwlan(self.log, self.dut, True, |
| self.dut_wfc_modes[0], self.wifi_network_ssid, |
| self.wifi_network_pass): |
| self.dut.log.error("Failed to setup WFC.") |
| raise signals.TestFailure("Failed to setup WFC in APM") |
| self.dut.log.info("Phone is in WFC enabled state.") |
| return True |
| |
| def setup_volte(self): |
| if CAPABILITY_VOLTE not in self.dut_capabilities: |
| raise signals.TestSkip("VoLTE is not supported, abort test.") |
| if not phone_setup_volte(self.log, self.dut): |
| self.dut.log.error("Phone failed to enable VoLTE.") |
| raise signals.TestFailure("Failed to enable VoLTE") |
| self.dut.log.info("Phone VOLTE is enabled successfully.") |
| return True |
| |
| def setup_csfb(self): |
| if not phone_setup_csfb(self.log, self.dut): |
| self.dut.log.error("Phone failed to setup CSFB.") |
| raise signals.TestFailure("Failed to setup CSFB") |
| self.dut.log.info("Phone CSFB is enabled successfully.") |
| return True |
| |
| def setup_3g(self): |
| if not phone_setup_voice_3g(self.log, self.dut): |
| self.dut.log.error("Phone failed to setup 3G.") |
| raise signals.TestFailure("Faile to setup 3G") |
| self.dut.log.info("Phone RAT 3G is enabled successfully.") |
| return True |
| |
| def setup_2g(self): |
| if self.dut_operator not in ("tmo", "uk_ee"): |
| raise signals.TestSkip("2G is not supported, abort test.") |
| if not phone_setup_voice_2g(self.log, self.dut): |
| self.dut.log.error("Phone failed to setup 2G.") |
| raise signals.TestFailure("Failed to setup 2G") |
| self.dut.log.info("RAT 2G is enabled successfully.") |
| return True |
| |
| def setup_vt(self): |
| if CAPABILITY_VT not in self.dut_capabilities or ( |
| CAPABILITY_VT not in self.reference_capabilities): |
| raise signals.TestSkip("VT is not supported, abort test.") |
| for ad in (self.dut, self.ad_reference): |
| if not phone_setup_video(self.log, ad): |
| ad.log.error("Failed to setup VT.") |
| return False |
| return True |
| |
| def set_drop_reason_override(self, override_code=None): |
| if not override_code: |
| if self.dut.adb.shell("getprop vendor.radio.call_end_reason"): |
| self.dut.adb.shell("setprop vendor.radio.call_end_reason ''") |
| else: |
| if self.dut.adb.shell("getprop vendor.radio.call_end_reason" |
| ) != str(override_code): |
| cmd = "setprop vendor.radio.call_end_reason %s" \ |
| % override_code |
| self.dut.log.info("====== %s ======", cmd) |
| self.dut.adb.shell(cmd) |
| |
| def modem_crash(self): |
| # Modem SSR |
| self.user_params["check_crash"] = False |
| self.dut.log.info("Triggering ModemSSR") |
| try: |
| self.dut.droid.logI("======== Trigger modem crash ========") |
| except Exception: |
| pass |
| if (not self.dut.is_apk_installed("com.google.mdstest") |
| ) or self.dut.adb.getprop("ro.build.version.release")[0] in ( |
| "8", "O", "7", "N") or self.dut.model in ("angler", "bullhead", |
| "sailfish", |
| "marlin"): |
| trigger_modem_crash(self.dut) |
| else: |
| trigger_modem_crash_by_modem(self.dut) |
| |
| def call_drop_by_modem_crash(self, |
| call_verification_function=None, |
| vt=False): |
| if vt: |
| if not video_call_setup_teardown( |
| self.log, |
| self.dut, |
| self.ad_reference, |
| None, |
| video_state=VT_STATE_BIDIRECTIONAL, |
| verify_caller_func=is_phone_in_call_video_bidirectional, |
| verify_callee_func=is_phone_in_call_video_bidirectional): |
| self.dut.log.error("VT Call Failed.") |
| return False |
| else: |
| if not call_setup_teardown( |
| self.log, |
| self.dut, |
| self.ad_reference, |
| ad_hangup=None, |
| verify_caller_func=call_verification_function, |
| wait_time_in_call=10): |
| self.log.error("Call setup failed") |
| return False |
| |
| # Modem SSR |
| self.modem_crash() |
| |
| try: |
| if self.dut.droid.telecomIsInCall(): |
| self.dut.log.info("Still in call after trigger modem crash") |
| return False |
| else: |
| reasons = self.dut.search_logcat( |
| "qcril_qmi_voice_map_qmi_to_ril_last_call_failure_cause") |
| if reasons: |
| self.dut.log.info(reasons[-1]["log_message"]) |
| except Exception as e: |
| self.dut.log.error(e) |
| |
| def toggle_apm(self): |
| toggle_airplane_mode(self.log, self.dut, new_state=None) |
| |
| def toggle_wifi(self): |
| wifi_toggle_state(self.log, self.dut, None) |
| |
| def drop_reason_override(self): |
| hangup_call(self.log, self.ad_reference) |
| |
| def clearn_up_bugreport_database(self): |
| self.dut.adb.shell( |
| "rm /data/data/com.google.android.connectivitymonitor/" |
| "shared_prefs/ConnectivityMonitor_BugReport.xml") |
| |
| def clearn_up_troubleshooter_database(self): |
| self.dut.adb.shell( |
| "rm /data/data/com.google.android.connectivitymonitor/" |
| "shared_prefs/ConnectivityMonitor_TroubleshooterResult.xml") |
| |
| def parsing_bugreport_database(self): |
| output = self.dut.adb.shell( |
| "cat /data/data/com.google.android.connectivitymonitor/" |
| "shared_prefs/ConnectivityMonitor_BugReport.xml") |
| bugreport_database = re.findall(r">Call Drop:\s+(.*)<", output) |
| self.dut.log.info("bugreport_database = %s", bugreport_database) |
| return bugreport_database |
| |
| def parsing_troubleshooter_database(self): |
| output = self.dut.adb.shell( |
| "cat /data/data/com.google.android.connectivitymonitor/" |
| "shared_prefs/ConnectivityMonitor_TroubleshooterResult.xml") |
| results = re.findall(r"name=\"(\S+)\">(\S+)<", output) |
| troubleshooter_database = {} |
| for result in results: |
| if "count" in result[0] or "num_calls" in result[0]: |
| troubleshooter_database[result[0]] = int(result[1]) |
| else: |
| troubleshooter_database[result[0]] = result[1] |
| self.dut.log.info("TroubleshooterResult=%s", |
| sorted(troubleshooter_database.items())) |
| return troubleshooter_database |
| |
| def parsing_call_summary(self): |
| call_summary = self.dut.adb.shell( |
| "dumpsys activity service com.google.android.connectivitymonitor/" |
| ".ConnectivityMonitorService") |
| self.dut.log.info(call_summary) |
| call_summary_info = {} |
| results = re.findall( |
| r"(\S+): (\d+) out of (\d+) calls dropped, percentage=(\S+)", |
| call_summary) |
| for result in results: |
| call_summary_info[result[0]] = int(result[2]) |
| call_summary_info["%s_dropped" % result[0]] = int(result[1]) |
| if result[3] == "NaN": |
| call_summary_info["%s_dropped_percentage" % result[0]] = 0 |
| else: |
| call_summary_info["%s_dropped_percentage" % result[0]] = float( |
| result[3]) |
| results = re.findall(r"(\S+): predominant failure reason=(.+)", |
| call_summary) |
| for result in results: |
| call_summary_info["%s_failure_reason" % result[0]] = result[1] |
| self.dut.log.info("call summary dumpsys = %s", |
| sorted(call_summary_info.items())) |
| return call_summary_info |
| |
| def parsing_call_statistics(self): |
| call_statistics_info = {} |
| call_statistics = self.dut.adb.shell( |
| "content query --uri content://com.google.android." |
| "connectivitymonitor.troubleshooterprovider/call_statistics") |
| self.dut.log.info("troubleshooterprovider call_statistics:\n%s", |
| call_statistics) |
| results = re.findall(r"KEY=(\S+), VALUE=(\S+)", call_statistics) |
| for result in results: |
| if ("count" in result[0] or "num_calls" in result[0]): |
| if result[1] == "NULL": |
| call_statistics_info[result[0]] = 0 |
| else: |
| call_statistics_info[result[0]] = int(result[1]) |
| else: |
| call_statistics_info[result[0]] = result[1] |
| self.dut.log.info("troubleshooterprovider call_statistics: %s", |
| sorted(call_statistics_info.items())) |
| return call_statistics_info |
| |
| def parsing_diagnostics(self): |
| diagnostics_info = {} |
| diagnostics = self.dut.adb.shell( |
| "content query --uri content://com.google.android." |
| "connectivitymonitor.troubleshooterprovider/diagnostics") |
| self.dut.log.info("troubleshooterprovider diagnostics:\n%s", |
| diagnostics) |
| results = re.findall(r"KEY=(\S+), VALUE=(\S+)", diagnostics) |
| for result in results: |
| diagnostics_info[result[0]] = result[1] |
| self.dut.log.info("troubleshooterprovider diagnostics: %s", |
| sorted(diagnostics_info.items())) |
| return diagnostics_info |
| |
| def call_setup_and_connectivity_monitor_checking(self, |
| setup=None, |
| handover=None, |
| triggers=[], |
| expected_drop_reason="", |
| expected_trouble=None, |
| expected_action=None): |
| |
| call_verification_function = None |
| begin_time = get_device_epoch_time(self.dut) |
| call_data_summary_before = self.parsing_call_summary() |
| call_statistics_before = self.parsing_call_statistics() |
| self.parsing_diagnostics() |
| self.parsing_troubleshooter_database() |
| bugreport_database_before = self.parsing_bugreport_database() |
| |
| if expected_drop_reason: |
| expected_drop_reasons = set(expected_drop_reason.split("|")) |
| else: |
| expected_drop_reasons = set() |
| checking_counters = ["Calls"] |
| checking_reasons = [] |
| result = True |
| if setup in ("wfc_apm", "wfc_non_apm"): |
| call_verification_function = is_phone_in_call_iwlan |
| elif setup == "volte": |
| call_verification_function = is_phone_in_call_volte |
| elif setup == "csfb": |
| call_verification_function = is_phone_in_call_csfb |
| elif setup == "3g": |
| call_verification_function = is_phone_in_call_3g |
| elif setup == "2g": |
| call_verification_function = is_phone_in_call_2g |
| technology = handover or setup |
| if technology in ("wfc_apm", "wfc_non_apm"): |
| if triggers and triggers[0] not in IGNORED_CALL_DROP_TRIGGERS: |
| checking_counters.extend( |
| ["Calls_dropped", "VOWIFI", "VOWIFI_dropped"]) |
| checking_reasons.append("VOWIFI_failure_reason") |
| elif call_data_summary_before.get("Calls_dropped", 0): |
| checking_counters.append("VOWIFI") |
| elif technology == "volte": |
| if triggers and triggers[0] not in IGNORED_CALL_DROP_TRIGGERS: |
| checking_counters.extend( |
| ["Calls_dropped", "VOLTE", "VOLTE_dropped"]) |
| checking_reasons.append("VOLTE_failure_reason") |
| elif call_data_summary_before.get("Calls_dropped", 0): |
| checking_counters.append("VOLTE") |
| elif technology in ("csfb", "3g", "2g"): |
| if triggers and triggers[0] not in IGNORED_CALL_DROP_TRIGGERS: |
| checking_counters.extend(["Calls_dropped", "CS", "CS_dropped"]) |
| checking_reasons.append("CS_failure_reason") |
| elif call_data_summary_before.get("Calls_dropped", 0): |
| checking_counters.append("CS") |
| |
| if setup == "vt": |
| if not video_call_setup_teardown( |
| self.log, |
| self.dut, |
| self.ad_reference, |
| None, |
| video_state=VT_STATE_BIDIRECTIONAL, |
| verify_caller_func=is_phone_in_call_video_bidirectional, |
| verify_callee_func=is_phone_in_call_video_bidirectional): |
| raise signals.TestFailure("VT Call Failed.") |
| else: |
| if not call_setup_teardown( |
| self.log, |
| self.dut, |
| self.ad_reference, |
| ad_hangup=None, |
| verify_caller_func=call_verification_function, |
| wait_time_in_call=10): |
| raise signals.TestFailure("Call Setup Failed.") |
| |
| for trigger in triggers: |
| if self.dut.droid.telecomIsInCall(): |
| self.dut.log.info("Telecom is in call") |
| self.dut.log.info( |
| "Voice in RAT %s", |
| self.dut.droid.telephonyGetCurrentVoiceNetworkType()) |
| else: |
| self.dut.log.info("Not in call") |
| # Trigger in-call event |
| if trigger and getattr(self, trigger, None): |
| trigger_func = getattr(self, trigger) |
| trigger_func() |
| time.sleep(MAX_WAIT_TIME_FOR_STATE_CHANGE) |
| |
| if self.dut.droid.telecomIsInCall(): |
| self.dut.log.info("Telecom is in call") |
| self.dut.log.info( |
| "Voice in RAT %s", |
| self.dut.droid.telephonyGetCurrentVoiceNetworkType()) |
| else: |
| self.dut.log.info("Not in call") |
| |
| if self.dut.droid.telecomIsInCall(): |
| self.dut.log.info("Telecom is in call") |
| self.dut.log.info( |
| "Voice in RAT %s", |
| self.dut.droid.telephonyGetCurrentVoiceNetworkType()) |
| else: |
| self.dut.log.info("Not in call") |
| |
| drop_reason = last_call_drop_reason(self.dut, begin_time) |
| drop_reason = drop_reason.title() |
| if drop_reason: |
| expected_drop_reasons.add(drop_reason) |
| for ad in (self.ad_reference, self.dut): |
| try: |
| if ad.droid.telecomIsInCall(): |
| if triggers: |
| ad.log.info("Still in call after triggers %s", |
| triggers) |
| result = False |
| hangup_call(self.log, ad) |
| time.sleep(MAX_WAIT_TIME_FOR_STATE_CHANGE) |
| except Exception as e: |
| ad.log.error(e) |
| |
| call_data_summary_after = self.parsing_call_summary() |
| call_statistics_after = self.parsing_call_statistics() |
| diagnostics_after = self.parsing_diagnostics() |
| ts_database_after = self.parsing_troubleshooter_database() |
| |
| for counter in checking_counters: |
| if call_data_summary_after.get( |
| counter, |
| 0) != call_data_summary_before.get(counter, 0) + 1: |
| self.dut.log.error("Counter %s did not increase", counter) |
| result = False |
| else: |
| self.dut.log.info("Counter %s increased", counter) |
| if counter == "Calls": |
| if call_statistics_after.get("num_calls", |
| 0) - call_statistics_before.get( |
| "num_calls", 0) < 1: |
| self.dut.log.warning( |
| "call_statistics num_calls didn't increase") |
| # result = False |
| else: |
| self.dut.log.info("call_statistics num_calls increased") |
| if "_dropped" in counter and counter != "Calls_dropped": |
| desc = counter.split("_")[0] |
| if desc == "VOWIFI": |
| stat_key = "recent_wfc_fail_count" |
| else: |
| stat_key = "recent_%s_fail_count" % desc.lower() |
| before = call_statistics_after.get(stat_key, 0) |
| after = call_statistics_after.get(stat_key, 0) |
| most_failure_call_type = call_statistics_after.get( |
| "call_type_with_most_failures") |
| diagnosis = diagnostics_after.get("diagnosis") |
| actions = diagnostics_after.get("actions") |
| if after - before < 1: |
| self.dut.log.warning("call_statistics %s didn't increase, " |
| "before %s, after %s" % |
| (stat_key, before, after)) |
| # result = False |
| else: |
| self.dut.log.info("call_statistics %s increased", stat_key) |
| if most_failure_call_type != desc: |
| self.dut.log.warning( |
| "call_statistics call_type_with_most_failures " |
| "is %s, not %s", most_failure_call_type, desc) |
| else: |
| self.dut.log.info( |
| "call_statistics call_type_with_most_failures is %s", |
| most_failure_call_type) |
| dropped = call_data_summary_after.get("%s_dropped" % desc, 0) |
| drop_percentage = call_data_summary_after.get( |
| "%s_dropped_percentage" % desc, 0) |
| self.dut.log.info("%s_dropped = %s, percentage = %s", desc, |
| dropped, drop_percentage) |
| if expected_trouble and expected_trouble != diagnosis: |
| self.dut.log.warning("diagnoisis = %s, expecting %s", |
| diagnosis, expected_trouble) |
| if expected_action and expected_action != actions: |
| self.dut.log.error("actions = %s, expecting %s", actions, |
| expected_action) |
| result = False |
| if drop_percentage > CALL_TROUBLE_THRESHOLD and ( |
| dropped > CONSECUTIVE_CALL_FAILS): |
| if diagnosis == "UNABLE_TO_TRIAGE": |
| self.dut.log.error( |
| "troubleshooter diagnosis is %s with %s dropped " |
| "and %s drop_percentage", diagnosis, dropped, |
| drop_percentage) |
| result = False |
| if actions == "NONE": |
| self.dut.log.error( |
| "troubleshooter failed to provide suggestion, " |
| "actions = %s", actions) |
| result = False |
| if expected_drop_reasons: |
| expected_drop_reason = "|".join(expected_drop_reasons) |
| for reason_key in checking_reasons: |
| if call_data_summary_after.get(reason_key, None): |
| drop_reason = call_data_summary_after[reason_key] |
| if expected_drop_reason and drop_reason not in expected_drop_reason: |
| self.dut.log.error("%s is: %s, expecting %s", reason_key, |
| drop_reason, expected_drop_reason) |
| result = False |
| else: |
| self.dut.log.info("%s is: %s as expected", reason_key, |
| drop_reason) |
| else: |
| self.dut.log.error("%s is not provided in summary report", |
| reason_key) |
| result = False |
| |
| if not triggers or triggers[0] in IGNORED_CALL_DROP_TRIGGERS: |
| return result |
| if drop_reason in bugreport_database_before: |
| self.dut.log.info("%s is in bugreport database %s before call", |
| drop_reason, bugreport_database_before) |
| return result |
| else: |
| self.dut.log.info("%s is not in bugreport database %s before call", |
| drop_reason, bugreport_database_before) |
| if drop_reason in IGNORED_CALL_DROP_REASONS: |
| self.dut.log.info( |
| "Call drop with reason %s will skip bugreport notification", |
| drop_reason) |
| return result |
| else: |
| self.dut.log.info( |
| "Call drop %s should generate bugreport notification", |
| drop_reason) |
| # Parse logcat for UI notification only for the first failure |
| if self.dut.search_logcat("Bugreport notification title Call Drop:", |
| begin_time): |
| self.dut.log.info( |
| "Bugreport notification title Call Drop is seen in logcat") |
| return result |
| else: |
| self.dut.log.error( |
| "Bugreport notification title Call Drop is not seen in logcat") |
| return False |
| |
| def call_drop_test(self, |
| setup=None, |
| handover=None, |
| count=CONSECUTIVE_CALL_FAILS, |
| triggers=[], |
| expected_drop_reason=None, |
| expected_trouble=None, |
| expected_action=None): |
| if not triggers: |
| if self.dut.model in ("marlin", "sailfish", "walleye", "taimen"): |
| triggers = ["modem_crash"] |
| expected_drop_reason = "Error Unspecified" |
| else: |
| triggers = ["drop_reason_override"] |
| if "drop_reason_override" in triggers: |
| self.set_drop_reason_override( |
| override_code=self.call_drop_override_code) |
| expected_drop_reason = CALL_DROP_CODE_MAPPING[int( |
| self.call_drop_override_code)] |
| for iter in range(count): |
| self.dut.log.info("===== %s_iter_%s =====", self.test_name, |
| iter + 1) |
| if iter < count - 1: |
| action = None |
| trouble = None |
| else: |
| action = expected_action |
| trouble = expected_trouble |
| if not self.call_setup_and_connectivity_monitor_checking( |
| setup=setup, |
| handover=handover, |
| triggers=triggers, |
| expected_drop_reason=expected_drop_reason, |
| expected_trouble=trouble, |
| expected_action=action): |
| return False |
| return True |
| |
| def call_drop_triggered_suggestion_test(self, |
| setup=None, |
| handover=None, |
| triggers=[], |
| expected_drop_reason=None, |
| expected_trouble=None, |
| expected_action=None): |
| call_summary = self.parsing_call_summary() |
| diagnostics = self.parsing_diagnostics() |
| diagnosis = diagnostics.get("diagnosis") |
| actions = diagnostics.get("actions") |
| self.dut.log.info("Expected trouble = %s, action = %s", |
| expected_trouble, expected_action) |
| if expected_trouble and diagnosis == expected_trouble and not handover: |
| self.dut.log.info("Diagnosis is the expected %s", trouble) |
| if expected_action and expected_action != actions: |
| self.dut.log.error("Action is %s, expecting %s", actions, |
| expected_action) |
| result = False |
| if setup in ("wfc_apm", "wfc_non_apm"): |
| desc = "VOWIFI" |
| elif setup == "volte": |
| desc = "VOLTE" |
| elif setup in ("csfb", "3g", "2g"): |
| desc = "CS" |
| drops = call_summary.get("%s_dropped" % desc, 0) |
| drop_percentage = call_summary.get("%s_dropped_percentage" % desc, |
| 0) |
| if drops < CONSECUTIVE_CALL_FAILS or drop_percentage < 25: |
| self.dut.log.error( |
| "Should NOT get %s for %s %s_dropped and %s %s_dropped_percentage", |
| trouble, drops, desc, drop_percentage, desc) |
| return False |
| else: |
| return True |
| else: |
| self.dut.log.info("Generate %s consecutive call drops", |
| CONSECUTIVE_CALL_FAILS) |
| return self.call_drop_test( |
| setup=setup, |
| handover=handover, |
| count=CONSECUTIVE_CALL_FAILS, |
| triggers=triggers, |
| expected_drop_reason=expected_drop_reason, |
| expected_trouble=expected_trouble, |
| expected_action=expected_action) |
| |
| def healthy_call_test(self, |
| setup=None, |
| handover=None, |
| count=1, |
| triggers=[], |
| expected_trouble=None, |
| expected_action=None): |
| if self.dut.model not in ("marlin", "sailfish", "walleye", "taimen"): |
| self.set_drop_reason_override(override_code=25) |
| result = True |
| for iter in range(count): |
| if not self.call_setup_and_connectivity_monitor_checking( |
| setup=setup, |
| handover=handover, |
| triggers=triggers, |
| expected_trouble=expected_trouble, |
| expected_action=expected_action): |
| return False |
| return True |
| |
| def forced_call_drop_test(self, |
| setup=None, |
| handover=None, |
| triggers=None, |
| expected_drop_reason=None): |
| expected_trouble = None |
| expected_action = None |
| technology = handover or setup |
| if setup: |
| setup_func = getattr(self, "setup_%s" % setup) |
| if not setup_func(): return False |
| if technology == "volte": |
| expected_trouble = TROUBLES[6], |
| expected_action = ACTIONS[6] |
| elif technology == "csfb": |
| if CAPABILITY_VOLTE in self.dut_capabilities: |
| expected_action = ACTIONS[5] |
| else: |
| expected_action = ACTIONS[7] |
| expected_trouble = TROUBLES[7] |
| elif technology == "3g": |
| if CAPABILITY_VOLTE in self.dut_capabilities: |
| expected_action = ACTIONS[5] |
| else: |
| expected_action = ACTIONS[7] |
| expected_trouble = TROUBLES[7] |
| elif technology == "2g": |
| if CAPABILITY_VOLTE in self.dut_capabilities: |
| expected_action = ACTIONS[5] |
| else: |
| expected_action = ACTIONS[7] |
| expected_trouble = TROUBLES[7] |
| elif technology == "wfc_apm": |
| expected_trouble = TROUBLES[3] |
| expected_action = ACTIONS[11] |
| elif technology == "wfc_non_apm": |
| expected_trouble = TROUBLES[3] |
| expected_action = ACTIONS[3] |
| |
| return self.call_drop_triggered_suggestion_test( |
| setup=setup, |
| handover=handover, |
| triggers=triggers, |
| expected_drop_reason=expected_drop_reason, |
| expected_trouble=expected_trouble, |
| expected_action=expected_action) |
| |
| def call_drop_test_after_wipe(self, setup=None): |
| if setup: |
| setup_func = getattr(self, "setup_%s" % setup) |
| if not setup_func(): return False |
| fastboot_wipe(self.dut) |
| bring_up_connectivity_monitor(self.dut) |
| return self.forced_call_drop_test(setup=setup) |
| |
| def call_drop_test_after_reboot(self, setup=None): |
| self.forced_call_drop_test(setup=setup) |
| self.healthy_call_test(setup=setup, count=1) |
| reboot_device(self.dut) |
| return self.forced_call_drop_test(setup=setup) |