#!/usr/bin/env python3.4
#
#   Copyright 2017 - 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.
"""
    Test Script for Telephony Pre Check In Sanity
"""

import re
import time
from acts import signals
from acts.utils import get_current_epoch_time
from acts_contrib.test_utils.tel.TelephonyBaseTest import TelephonyBaseTest
from acts_contrib.test_utils.tel.tel_bootloader_utils import reset_device_password
from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phone_default_state
from acts_contrib.test_utils.tel.tel_test_utils import abort_all_tests
from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
from acts_contrib.test_utils.tel.tel_test_utils import get_service_state_by_adb
from acts_contrib.test_utils.tel.tel_test_utils import is_sim_lock_enabled
from acts_contrib.test_utils.tel.tel_test_utils import toggle_airplane_mode_by_adb
from acts_contrib.test_utils.tel.tel_test_utils import unlock_sim
from acts_contrib.test_utils.tel.tel_test_utils import verify_internet_connection
from acts_contrib.test_utils.tel.tel_data_utils import wait_for_cell_data_connection
from acts_contrib.test_utils.tel.tel_voice_utils import call_setup_teardown
from acts_contrib.test_utils.tel.tel_voice_utils import dumpsys_last_call_info
from acts_contrib.test_utils.tel.tel_voice_utils import dumpsys_last_call_number
from acts_contrib.test_utils.tel.tel_voice_utils import dumpsys_new_call_info
from acts_contrib.test_utils.tel.tel_voice_utils import hangup_call_by_adb
from acts_contrib.test_utils.tel.tel_voice_utils import initiate_call
from acts_contrib.test_utils.tel.tel_voice_utils import initiate_emergency_dialer_call_by_adb
from acts_contrib.test_utils.tel.tel_voice_utils import last_call_drop_reason

CARRIER_OVERRIDE_CMD = (
    "am broadcast -a com.google.android.carrier.action.LOCAL_OVERRIDE -n "
    "com.google.android.carrier/.ConfigOverridingReceiver --ez")
IMS_FIRST = "carrier_use_ims_first_for_emergency_bool"
ALLOW_NON_EMERGENCY_CALL = "allow_non_emergency_calls_in_ecm_bool"
BLOCK_DURATION_CMD = "duration_blocking_disabled_after_emergency_int"
BLOCK_DURATION = 300


class TelLiveEmergencyBase(TelephonyBaseTest):
    def setup_class(self):
        TelephonyBaseTest.setup_class(self)
        self.number_of_devices = 1
        fake_number = self.user_params.get("fake_emergency_number", "411")
        self.fake_emergency_number = fake_number.strip("+").replace("-", "")
        self.my_devices = self.android_devices[:]

        for ad in self.android_devices:
            if not is_sim_lock_enabled(ad):
                self.setup_dut(ad)
                return True
        self.log.error("No device meets SIM READY or LOADED requirement")
        raise signals.TestAbortClass("No device meets SIM requirement")

    def setup_dut(self, ad):
        self.dut = ad
        output = self.dut.adb.shell("dumpsys carrier_config")
        self.default_settings = {}
        for setting in (IMS_FIRST, ALLOW_NON_EMERGENCY_CALL,
                        BLOCK_DURATION_CMD):
            values = re.findall(r"%s = (\S+)" % setting, output)
            if values:
                self.default_settings[setting] = values[-1]
            else:
                self.default_settings[setting] = ""
        self.dut.adb.shell(" ".join(
            [CARRIER_OVERRIDE_CMD, BLOCK_DURATION_CMD,
             "%s" % BLOCK_DURATION]))
        self.dut_operator = get_operator_name(self.log, ad)
        if self.dut_operator == "tmo":
            self.fake_emergency_number = "611"
        elif self.dut_operator == "vzw":
            self.fake_emergency_number = "922"
        elif self.dut_operator == "spt":
            self.fake_emergency_number = "526"
        if len(self.my_devices) > 1:
            self.android_devices.remove(ad)
            self.android_devices.insert(0, ad)

    def teardown_class(self):
        self.android_devices = self.my_devices
        TelephonyBaseTest.teardown_class(self)

    def setup_test(self):
        if not unlock_sim(self.dut):
            abort_all_tests(self.dut.log, "unable to unlock SIM")
        self.expected_call_result = True

    def teardown_test(self):
        self.dut.ensure_screen_on()
        self.dut.exit_setup_wizard()
        reset_device_password(self.dut, None)
        output = self.dut.adb.shell("dumpsys carrier_config")
        for setting, state in self.default_settings.items():
            values = re.findall(r"%s = (\S+)" % setting, output)
            if values and values[-1] != state:
                self.dut.adb.shell(" ".join(
                    [CARRIER_OVERRIDE_CMD, setting, state]))
        ensure_phone_default_state(self.log, self.dut)

    def change_emergency_number_list(self):
        test_number = "%s:%s" % (self.fake_emergency_number,
                                 self.fake_emergency_number)
        output = self.dut.adb.getprop("ril.test.emergencynumber")
        if output != test_number:
            cmd = "setprop ril.test.emergencynumber %s" % test_number
            self.dut.log.info(cmd)
            self.dut.adb.shell(cmd)
        for _ in range(5):
            existing = self.dut.adb.getprop("ril.ecclist")
            self.dut.log.info("Existing ril.ecclist is: %s", existing)
            if self.fake_emergency_number in existing:
                return True
            emergency_numbers = "%s,%s" % (existing,
                                           self.fake_emergency_number)
            cmd = "setprop ril.ecclist %s" % emergency_numbers
            self.dut.log.info(cmd)
            self.dut.adb.shell(cmd)
            # After some system events, ril.ecclist might change
            # wait sometime for it to settle
            time.sleep(10)
            if self.fake_emergency_number in existing:
                return True
        return False

    def change_qcril_emergency_source_mcc_table(self):
        # This will add the fake number into emergency number list for a mcc
        # in qcril. Please note, the fake number will be send as an emergency
        # number by modem and reach the real 911 by this
        qcril_database_path = self.dut.adb.shell("find /data -iname  qcril.db")
        if not qcril_database_path: return
        mcc = self.dut.droid.telephonyGetNetworkOperator()
        mcc = mcc[:3]
        self.dut.log.info("Add %s mcc %s in qcril_emergency_source_mcc_table")
        self.dut.adb.shell(
            "sqlite3 %s \"INSERT INTO qcril_emergency_source_mcc_table VALUES('%s','%s','','')\""
            % (qcril_database_path, mcc, self.fake_emergency_number))

    def fake_emergency_call_test(self, by_emergency_dialer=True, attemps=3):
        self.dut.log.info("ServiceState is in %s",
                          get_service_state_by_adb(self.log, self.dut))
        if by_emergency_dialer:
            dialing_func = initiate_emergency_dialer_call_by_adb
            callee = self.fake_emergency_number
        else:
            dialing_func = initiate_call
            # Initiate_call method has to have "+" in front
            # otherwise the number will be in dialer without dial out
            # with sl4a fascade. Need further investigation
            callee = "+%s" % self.fake_emergency_number
        for i in range(attemps):
            begin_time = get_current_epoch_time()
            result = True
            if not self.change_emergency_number_list():
                self.dut.log.error("Unable to add number to ril.ecclist")
                return False
            time.sleep(1)
            last_call_number = dumpsys_last_call_number(self.dut)
            call_result = dialing_func(self.log, self.dut, callee)
            time.sleep(3)
            hangup_call_by_adb(self.dut)
            if not call_result:
                last_call_drop_reason(self.dut, begin_time)
            self.dut.send_keycode("BACK")
            self.dut.send_keycode("BACK")
            if not dumpsys_new_call_info(self.dut, last_call_number):
                result = False
            if call_result == self.expected_call_result:
                self.dut.log.info("Call to %s returns %s as expected", callee,
                                  self.expected_call_result)
            else:
                self.dut.log.info("Call to %s returns %s", callee,
                                  not self.expected_call_result)
                result = False
            if result:
                return True
            ecclist = self.dut.adb.getprop("ril.ecclist")
            self.dut.log.info("ril.ecclist = %s", ecclist)
            if self.fake_emergency_number in ecclist:
                if i == attemps - 1:
                    self.dut.log.error("%s is in ril-ecclist, but call failed",
                                       self.fake_emergency_number)
                else:
                    self.dut.log.warning(
                        "%s is in ril-ecclist, but call failed, try again",
                        self.fake_emergency_number)
            else:
                if i == attemps - 1:
                    self.dut.log.error("Fail to write %s to ril-ecclist",
                                       self.fake_emergency_number)
                else:
                    self.dut.log.info("%s is not in ril-ecclist",
                                      self.fake_emergency_number)
        self.dut.log.info("fake_emergency_call_test result is %s", result)
        return result

    def set_ims_first(self, state):
        output = self.dut.adb.shell(
            "dumpsys carrier_config | grep %s | tail -1" % IMS_FIRST)
        self.dut.log.info(output)
        if state in output: return
        cmd = " ".join([CARRIER_OVERRIDE_CMD, IMS_FIRST, state])
        self.dut.log.info(cmd)
        self.dut.adb.shell(cmd)
        self.dut.log.info(
            self.dut.adb.shell(
                "dumpsys carrier_config | grep %s | tail -1" % IMS_FIRST))

    def set_allow_non_emergency_call(self, state):
        output = self.dut.adb.shell(
            "dumpsys carrier_config | grep %s | tail -1" %
            ALLOW_NON_EMERGENCY_CALL)
        self.dut.log.info(output)
        if state in output: return
        cmd = " ".join([CARRIER_OVERRIDE_CMD, ALLOW_NON_EMERGENCY_CALL, state])
        self.dut.log.info(cmd)
        self.dut.adb.shell(cmd)
        self.dut.log.info(
            self.dut.adb.shell("dumpsys carrier_config | grep %s | tail -1" %
                               ALLOW_NON_EMERGENCY_CALL))

    def check_emergency_call_back_mode(self,
                                       by_emergency_dialer=True,
                                       non_emergency_call_allowed=True,
                                       attemps=3):
        state = "true" if non_emergency_call_allowed else "false"
        self.set_allow_non_emergency_call(state)
        result = True
        for _ in range(attemps):
            if not self.change_emergency_number_list():
                self.dut.log.error("Unable to add number to ril.ecclist")
                return False
            last_call_number = dumpsys_last_call_number(self.dut)
            self.dut.log.info("Hung up fake emergency call in ringing")
            if by_emergency_dialer:
                initiate_emergency_dialer_call_by_adb(
                    self.log, self.dut, self.fake_emergency_number, timeout=0)
            else:
                callee = "+%s" % self.fake_emergency_number
                self.dut.droid.telecomCallNumber(callee)
            time.sleep(3)
            hangup_call_by_adb(self.dut)
            ecclist = self.dut.adb.getprop("ril.ecclist")
            self.dut.log.info("ril.ecclist = %s", ecclist)
            if self.fake_emergency_number in ecclist:
                break
        call_info = dumpsys_new_call_info(self.dut, last_call_number)
        if not call_info:
            return False
        call_tech = call_info.get("callTechnologies", "")
        if "CDMA" in call_tech:
            expected_ecbm = True
            expected_data = False
            expected_call = non_emergency_call_allowed
        elif "GSM" in call_tech:
            expected_ecbm = True
            expected_data = True
            expected_call = True
        else:
            expected_ecbm = False
            expected_data = True
            expected_call = True
        last_call_number = dumpsys_last_call_number(self.dut)
        begin_time = get_current_epoch_time()
        self.dut.ensure_screen_on()
        self.dut.exit_setup_wizard()
        reset_device_password(self.dut, None)
        call_check = call_setup_teardown(
            self.log, self.dut, self.android_devices[1], ad_hangup=self.dut)
        if call_check != expected_call:
            self.dut.log.error("Regular phone call is %s, expecting %s",
                               call_check, expected_call)
            result = False
        call_info = dumpsys_new_call_info(self.dut, last_call_number)
        if not call_info:
            self.dut.log.error("New call is not in dumpsys telecom")
            return False
        if expected_ecbm:
            if "ecbm" in call_info["callProperties"]:
                self.dut.log.info("New call is in ecbm.")
            else:
                self.dut.log.error(
                    "New call is not in emergency call back mode.")
                result = False
        else:
            if "ecbm" in call_info["callProperties"]:
                self.dut.log.error("New call is in emergency call back mode")
                result = False
        if not expected_data:
            if self.dut.droid.telephonyGetDataConnectionState():
                self.dut.log.info(
                    "Data connection is off as expected in ECB mode")
                self.dut.log.info("Wait for getting out of ecbm")
                if not wait_for_cell_data_connection(self.log, self.dut, True,
                                                     400):
                    self.dut.log.error(
                        "Data connection didn't come back after 5 minutes")
                    result = False
                #if not self.dut.droid.telephonyGetDataConnectionState():
                #    self.dut.log.error(
                #        "Data connection is not coming back")
                #    result = False
                elif not verify_internet_connection(self.log, self.dut):
                    self.dut.log.error(
                        "Internet connection check failed after getting out of ECB"
                    )
                    result = False

            else:
                self.dut.log.error("Data connection is not off in ECB mode")
                if not verify_internet_connection(self.log, self.dut, False):
                    self.dut.log.error("Internet connection is not off")
                    result = False
        else:
            if self.dut.droid.telephonyGetDataConnectionState():
                self.dut.log.info("Data connection is on as expected")
                if not verify_internet_connection(self.log, self.dut):
                    self.dut.log.error("Internet connection check failed")
                    result = False
            else:
                self.dut.log.error("Data connection is off, expecting on")
                result = False
        if expected_call:
            return result
        elapsed_time = (get_current_epoch_time() - begin_time) / 1000
        if elapsed_time < BLOCK_DURATION:
            time.sleep(BLOCK_DURATION - elapsed_time + 10)
        if not call_setup_teardown(
                self.log, self.dut, self.android_devices[1],
                ad_hangup=self.dut):
            self.dut.log.error("Regular phone call failed after out of ecbm")
            result = False
        return result

    def check_normal_call(self):
        result = True
        if "wfc" not in self.test_name:
            toggle_airplane_mode_by_adb(self.log, self.dut, False)
        self.dut.ensure_screen_on()
        self.dut.exit_setup_wizard()
        reset_device_password(self.dut, None)
        begin_time = get_current_epoch_time()
        if not call_setup_teardown(
                self.log,
                self.android_devices[1],
                self.dut,
                ad_hangup=self.android_devices[1]):
            self.dut.log.error("Regular MT phone call fails")
            self.dut.log.info("call_info = %s", dumpsys_last_call_info(
                self.dut))
            result = False
        if not call_setup_teardown(
                self.log, self.dut, self.android_devices[1],
                ad_hangup=self.dut):
            self.dut.log.error("Regular MO phone call fails")
            self.dut.log.info("call_info = %s", dumpsys_last_call_info(
                self.dut))
            result = False
        return result
