• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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