blob: 09722c4dc0cc67ee5998f698c60d1ea3aad6c89d [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright 2021 - 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.
import re
import time
from queue import Empty
from acts import signals
from acts.logger import epoch_to_log_line_timestamp
from acts.utils import get_current_epoch_time
from acts_contrib.test_utils.tel.loggers.protos.telephony_metric_pb2 import TelephonyVoiceTestResult
from acts_contrib.test_utils.tel.tel_defines import CarrierConfigs
from acts_contrib.test_utils.tel.tel_defines import CARRIER_NTT_DOCOMO, CARRIER_KDDI, CARRIER_RAKUTEN, CARRIER_SBM
from acts_contrib.test_utils.tel.tel_defines import CALL_PROPERTY_HIGH_DEF_AUDIO
from acts_contrib.test_utils.tel.tel_defines import CALL_STATE_ACTIVE
from acts_contrib.test_utils.tel.tel_defines import CALL_STATE_HOLDING
from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VOLTE
from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC
from acts_contrib.test_utils.tel.tel_defines import DIRECTION_MOBILE_ORIGINATED
from acts_contrib.test_utils.tel.tel_defines import GEN_2G
from acts_contrib.test_utils.tel.tel_defines import GEN_3G
from acts_contrib.test_utils.tel.tel_defines import INCALL_UI_DISPLAY_BACKGROUND
from acts_contrib.test_utils.tel.tel_defines import INCALL_UI_DISPLAY_FOREGROUND
from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
from acts_contrib.test_utils.tel.tel_defines import MAX_SAVED_VOICE_MAIL
from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT
from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_DROP
from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_IDLE_EVENT
from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALL_INITIATION
from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_CALLEE_RINGING
from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_TELECOM_RINGING
from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_VOICE_MAIL_COUNT
from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_DATA
from acts_contrib.test_utils.tel.tel_defines import NETWORK_SERVICE_VOICE
from acts_contrib.test_utils.tel.tel_defines import RAT_1XRTT
from acts_contrib.test_utils.tel.tel_defines import RAT_IWLAN
from acts_contrib.test_utils.tel.tel_defines import RAT_LTE
from acts_contrib.test_utils.tel.tel_defines import RAT_UMTS
from acts_contrib.test_utils.tel.tel_defines import RAT_UNKNOWN
from acts_contrib.test_utils.tel.tel_defines import TELEPHONY_STATE_IDLE
from acts_contrib.test_utils.tel.tel_defines import TELEPHONY_STATE_OFFHOOK
from acts_contrib.test_utils.tel.tel_defines import TELEPHONY_STATE_RINGING
from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_ANDROID_STATE_SETTLING
from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_REG_AND_CALL
from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_BETWEEN_STATE_CHECK
from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_IN_CALL
from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_LEAVE_VOICE_MAIL
from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_REJECT_CALL
from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_VOICE_MAIL_SERVER_RESPONSE
from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
from acts_contrib.test_utils.tel.tel_defines import EventCallStateChanged
from acts_contrib.test_utils.tel.tel_defines import EventMessageWaitingIndicatorChanged
from acts_contrib.test_utils.tel.tel_defines import CallStateContainer
from acts_contrib.test_utils.tel.tel_defines import MessageWaitingIndicatorContainer
from acts_contrib.test_utils.tel.tel_ims_utils import is_wfc_enabled
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 set_wfc_mode
from acts_contrib.test_utils.tel.tel_ims_utils import wait_for_volte_enabled
from acts_contrib.test_utils.tel.tel_ims_utils import wait_for_wfc_enabled
from acts_contrib.test_utils.tel.tel_ims_utils import wait_for_wfc_disabled
from acts_contrib.test_utils.tel.tel_lookup_tables import get_voice_mail_delete_digit
from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phones_idle
from acts_contrib.test_utils.tel.tel_phone_setup_utils import wait_for_network_rat
from acts_contrib.test_utils.tel.tel_phone_setup_utils import ensure_phone_subscription
from acts_contrib.test_utils.tel.tel_phone_setup_utils import wait_for_not_network_rat
from acts_contrib.test_utils.tel.tel_phone_setup_utils import wait_for_voice_attach
from acts_contrib.test_utils.tel.tel_subscription_utils import get_incoming_voice_sub_id
from acts_contrib.test_utils.tel.tel_subscription_utils import get_outgoing_voice_sub_id
from acts_contrib.test_utils.tel.tel_subscription_utils import set_subid_for_outgoing_call
from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
from acts_contrib.test_utils.tel.tel_test_utils import _wait_for_droid_in_state
from acts_contrib.test_utils.tel.tel_test_utils import check_call_state_connected_by_adb
from acts_contrib.test_utils.tel.tel_test_utils import check_call_state_idle_by_adb
from acts_contrib.test_utils.tel.tel_test_utils import check_phone_number_match
from acts_contrib.test_utils.tel.tel_test_utils import check_voice_mail_count
from acts_contrib.test_utils.tel.tel_test_utils import check_voice_network_type
from acts_contrib.test_utils.tel.tel_test_utils import get_call_uri
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_network_gen_for_subscription
from acts_contrib.test_utils.tel.tel_test_utils import get_network_rat
from acts_contrib.test_utils.tel.tel_test_utils import get_network_rat_for_subscription
from acts_contrib.test_utils.tel.tel_test_utils import get_number_from_tel_uri
from acts_contrib.test_utils.tel.tel_test_utils import get_operator_name
from acts_contrib.test_utils.tel.tel_test_utils import get_user_config_profile
from acts_contrib.test_utils.tel.tel_test_utils import get_voice_mail_number
from acts_contrib.test_utils.tel.tel_test_utils import is_event_match
from acts_contrib.test_utils.tel.tel_test_utils import is_event_match_for_list
from acts_contrib.test_utils.tel.tel_test_utils import num_active_calls
from acts_contrib.test_utils.tel.tel_test_utils import TelResultWrapper
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 verify_incall_state
from acts_contrib.test_utils.tel.tel_test_utils import wait_for_state
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
CallResult = TelephonyVoiceTestResult.CallResult.Value
result_dict ={}
voice_call_type = {}
def check_call_status(ad, voice_type_init=None, voice_type_in_call=None):
""""
Args:
ad: Android device object
voice_type_init: Voice network type before initiate call
voice_type_in_call: Voice network type in call state
Return:
voice_call_type_dict: Voice call status
"""
dut = str(ad.serial)
network_type = voice_type_init + "_" + voice_type_in_call
if network_type == "NR_NR":
voice_call_type_dict = update_voice_call_type_dict(dut, "VoNR")
elif network_type == "NR_LTE":
voice_call_type_dict = update_voice_call_type_dict(dut, "EPSFB")
elif network_type == "LTE_LTE":
voice_call_type_dict = update_voice_call_type_dict(dut, "VoLTE")
elif (network_type == "LTE_WCDMA" or network_type == "LTE_EDGE" or
network_type == "LTE_GSM"):
voice_call_type_dict = update_voice_call_type_dict(dut, "CSFB")
elif network_type == "EDGE_EDGE":
voice_call_type_dict = update_voice_call_type_dict(dut, "EDGE")
elif network_type == "GSM_GSM":
voice_call_type_dict = update_voice_call_type_dict(dut, "GSM")
else:
voice_call_type_dict = update_voice_call_type_dict(dut, "UNKNOWN")
return voice_call_type_dict
def update_voice_call_type_dict(dut, key):
"""
Args:
dut: Serial Number of android device object
key: Network subscription parameter (VoNR or EPSFB or VoLTE or CSFB or UNKNOWN)
Return:
voice_call_type: Voice call status
"""
if dut not in voice_call_type.keys():
voice_call_type[dut] = {key:0}
if key not in voice_call_type[dut].keys():
voice_call_type[dut].update({key:0})
voice_call_type[dut][key] += 1
return voice_call_type
def dial_phone_number(ad, callee_number):
for number in str(callee_number):
if number == "#":
ad.send_keycode("POUND")
elif number == "*":
ad.send_keycode("STAR")
elif number in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]:
ad.send_keycode("%s" % number)
def disconnect_call_by_id(log, ad, call_id):
"""Disconnect call by call id.
"""
ad.droid.telecomCallDisconnect(call_id)
return True
def dumpsys_last_call_info(ad):
""" Get call information by dumpsys telecom. """
num = dumpsys_last_call_number(ad)
output = ad.adb.shell("dumpsys telecom")
result = re.search(r"Call TC@%s: {(.*?)}" % num, output, re.DOTALL)
call_info = {"TC": num}
if result:
result = result.group(1)
for attr in ("startTime", "endTime", "direction", "isInterrupted",
"callTechnologies", "callTerminationsReason",
"isVideoCall", "callProperties"):
match = re.search(r"%s: (.*)" % attr, result)
if match:
if attr in ("startTime", "endTime"):
call_info[attr] = epoch_to_log_line_timestamp(
int(match.group(1)))
else:
call_info[attr] = match.group(1)
ad.log.debug("call_info = %s", call_info)
return call_info
def dumpsys_last_call_number(ad):
output = ad.adb.shell("dumpsys telecom")
call_nums = re.findall("Call TC@(\d+):", output)
if not call_nums:
return 0
else:
return int(call_nums[-1])
def dumpsys_new_call_info(ad, last_tc_number, retries=3, interval=5):
for i in range(retries):
if dumpsys_last_call_number(ad) > last_tc_number:
call_info = dumpsys_last_call_info(ad)
ad.log.info("New call info = %s", sorted(call_info.items()))
return call_info
else:
time.sleep(interval)
ad.log.error("New call is not in sysdump telecom")
return {}
def emergency_dialer_call_by_keyevent(ad, callee_number):
for i in range(3):
if "EmergencyDialer" in ad.get_my_current_focus_window():
ad.log.info("EmergencyDialer is the current focus window")
break
elif i <= 2:
ad.adb.shell("am start -a com.android.phone.EmergencyDialer.DIAL")
time.sleep(1)
else:
ad.log.error("Unable to bring up EmergencyDialer")
return False
ad.log.info("Make a phone call to %s", callee_number)
dial_phone_number(ad, callee_number)
ad.send_keycode("CALL")
def get_current_voice_rat(log, ad):
"""Return current Voice RAT
Args:
ad: Android device object.
"""
return get_current_voice_rat_for_subscription(
log, ad, get_outgoing_voice_sub_id(ad))
def get_current_voice_rat_for_subscription(log, ad, sub_id):
"""Return current Voice RAT for subscription id.
Args:
ad: Android device object.
sub_id: subscription id.
"""
return get_network_rat_for_subscription(log, ad, sub_id,
NETWORK_SERVICE_VOICE)
def hangup_call_by_adb(ad):
"""Make emergency call by EmergencyDialer.
Args:
ad: Caller android device object.
callee_number: Callee phone number.
"""
ad.log.info("End call by adb")
ad.send_keycode("ENDCALL")
def hangup_call(log, ad, is_emergency=False):
"""Hang up ongoing active call.
Args:
log: log object.
ad: android device object.
Returns:
True: if all calls are cleared
False: for errors
"""
# short circuit in case no calls are active
if not ad.droid.telecomIsInCall():
ad.log.warning("No active call exists.")
return True
ad.ed.clear_events(EventCallStateChanged)
ad.droid.telephonyStartTrackingCallState()
ad.log.info("Hangup call.")
if is_emergency:
for call in ad.droid.telecomCallGetCallIds():
ad.droid.telecomCallDisconnect(call)
else:
ad.droid.telecomEndCall()
try:
ad.ed.wait_for_event(
EventCallStateChanged,
is_event_match,
timeout=MAX_WAIT_TIME_CALL_IDLE_EVENT,
field=CallStateContainer.CALL_STATE,
value=TELEPHONY_STATE_IDLE)
except Empty:
ad.log.warning("Call state IDLE event is not received after hang up.")
finally:
ad.droid.telephonyStopTrackingCallStateChange()
if not wait_for_state(ad.droid.telecomIsInCall, False, 15, 1):
ad.log.error("Telecom is in call, hangup call failed.")
return False
return True
def initiate_emergency_dialer_call_by_adb(
log,
ad,
callee_number,
timeout=MAX_WAIT_TIME_CALL_INITIATION,
checking_interval=5):
"""Make emergency call by EmergencyDialer.
Args:
ad: Caller android device object.
callee_number: Callee phone number.
emergency : specify the call is emergency.
Optional. Default value is False.
Returns:
result: if phone call is placed successfully.
"""
try:
# Make a Call
ad.wakeup_screen()
ad.send_keycode("MENU")
ad.log.info("Call %s", callee_number)
ad.adb.shell("am start -a com.android.phone.EmergencyDialer.DIAL")
ad.adb.shell(
"am start -a android.intent.action.CALL_EMERGENCY -d tel:%s" %
callee_number)
if not timeout: return True
ad.log.info("Check call state")
# Verify Call State
elapsed_time = 0
while elapsed_time < timeout:
time.sleep(checking_interval)
elapsed_time += checking_interval
if check_call_state_connected_by_adb(ad):
ad.log.info("Call to %s is connected", callee_number)
return True
if check_call_state_idle_by_adb(ad):
ad.log.info("Call to %s failed", callee_number)
return False
ad.log.info("Make call to %s failed", callee_number)
return False
except Exception as e:
ad.log.error("initiate emergency call failed with error %s", e)
def initiate_call(log,
ad,
callee_number,
emergency=False,
incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
video=False):
"""Make phone call from caller to callee.
Args:
ad_caller: Caller android device object.
callee_number: Callee phone number.
emergency : specify the call is emergency.
Optional. Default value is False.
incall_ui_display: show the dialer UI foreground or backgroud
video: whether to initiate as video call
Returns:
result: if phone call is placed successfully.
"""
ad.ed.clear_events(EventCallStateChanged)
sub_id = get_outgoing_voice_sub_id(ad)
begin_time = get_device_epoch_time(ad)
ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
try:
# Make a Call
ad.log.info("Make a phone call to %s", callee_number)
if emergency:
ad.droid.telecomCallEmergencyNumber(callee_number)
else:
ad.droid.telecomCallNumber(callee_number, video)
# Verify OFFHOOK state
if not wait_for_call_offhook_for_subscription(
log, ad, sub_id, event_tracking_started=True):
ad.log.info("sub_id %s not in call offhook state", sub_id)
last_call_drop_reason(ad, begin_time=begin_time)
return False
else:
return True
finally:
if hasattr(ad, "sdm_log") and getattr(ad, "sdm_log"):
ad.adb.shell("i2cset -fy 3 64 6 1 b", ignore_status=True)
ad.adb.shell("i2cset -fy 3 65 6 1 b", ignore_status=True)
ad.droid.telephonyStopTrackingCallStateChangeForSubscription(sub_id)
if incall_ui_display == INCALL_UI_DISPLAY_FOREGROUND:
ad.droid.telecomShowInCallScreen()
elif incall_ui_display == INCALL_UI_DISPLAY_BACKGROUND:
ad.droid.showHomeScreen()
def last_call_drop_reason(ad, begin_time=None):
reasons = ad.search_logcat(
"qcril_qmi_voice_map_qmi_to_ril_last_call_failure_cause", begin_time)
reason_string = ""
if reasons:
log_msg = "Logcat call drop reasons:"
for reason in reasons:
log_msg = "%s\n\t%s" % (log_msg, reason["log_message"])
if "ril reason str" in reason["log_message"]:
reason_string = reason["log_message"].split(":")[-1].strip()
ad.log.info(log_msg)
reasons = ad.search_logcat("ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION",
begin_time)
if reasons:
ad.log.warning("ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION is seen")
ad.log.info("last call dumpsys: %s",
sorted(dumpsys_last_call_info(ad).items()))
return reason_string
def call_reject(log, ad_caller, ad_callee, reject=True):
"""Caller call Callee, then reject on callee.
"""
subid_caller = ad_caller.droid.subscriptionGetDefaultVoiceSubId()
subid_callee = ad_callee.incoming_voice_sub_id
ad_caller.log.info("Sub-ID Caller %s, Sub-ID Callee %s", subid_caller,
subid_callee)
return call_reject_for_subscription(log, ad_caller, ad_callee,
subid_caller, subid_callee, reject)
def call_reject_for_subscription(log,
ad_caller,
ad_callee,
subid_caller,
subid_callee,
reject=True):
"""
"""
caller_number = ad_caller.telephony['subscription'][subid_caller][
'phone_num']
callee_number = ad_callee.telephony['subscription'][subid_callee][
'phone_num']
ad_caller.log.info("Call from %s to %s", caller_number, callee_number)
if not initiate_call(log, ad_caller, callee_number):
ad_caller.log.error("Initiate call failed")
return False
if not wait_and_reject_call_for_subscription(
log, ad_callee, subid_callee, caller_number, WAIT_TIME_REJECT_CALL,
reject):
ad_callee.log.error("Reject call fail.")
return False
# Check if incoming call is cleared on callee or not.
if ad_callee.droid.telephonyGetCallStateForSubscription(
subid_callee) == TELEPHONY_STATE_RINGING:
ad_callee.log.error("Incoming call is not cleared")
return False
# Hangup on caller
hangup_call(log, ad_caller)
return True
def call_reject_leave_message(log,
ad_caller,
ad_callee,
verify_caller_func=None,
wait_time_in_call=WAIT_TIME_LEAVE_VOICE_MAIL):
"""On default voice subscription, Call from caller to callee,
reject on callee, caller leave a voice mail.
1. Caller call Callee.
2. Callee reject incoming call.
3. Caller leave a voice mail.
4. Verify callee received the voice mail notification.
Args:
ad_caller: caller android device object.
ad_callee: callee android device object.
verify_caller_func: function to verify caller is in correct state while in-call.
This is optional, default is None.
wait_time_in_call: time to wait when leaving a voice mail.
This is optional, default is WAIT_TIME_LEAVE_VOICE_MAIL
Returns:
True: if voice message is received on callee successfully.
False: for errors
"""
subid_caller = get_outgoing_voice_sub_id(ad_caller)
subid_callee = get_incoming_voice_sub_id(ad_callee)
return call_reject_leave_message_for_subscription(
log, ad_caller, ad_callee, subid_caller, subid_callee,
verify_caller_func, wait_time_in_call)
def check_reject_needed_for_voice_mail(log, ad_callee):
"""Check if the carrier requires reject call to receive voice mail or just keep ringing
Requested in b//155935290
Four Japan carriers do not need to reject
SBM, KDDI, Ntt Docomo, Rakuten
Args:
log: log object
ad_callee: android device object
Returns:
True if callee's carrier is not one of the four Japan carriers
False if callee's carrier is one of the four Japan carriers
"""
operators_no_reject = [CARRIER_NTT_DOCOMO,
CARRIER_KDDI,
CARRIER_RAKUTEN,
CARRIER_SBM]
operator_name = get_operator_name(log, ad_callee)
return operator_name not in operators_no_reject
def _is_on_message_waiting_event_true(event):
"""Private function to return if the received EventMessageWaitingIndicatorChanged
event MessageWaitingIndicatorContainer.IS_MESSAGE_WAITING field is True.
"""
return event['data'][MessageWaitingIndicatorContainer.IS_MESSAGE_WAITING]
def call_reject_leave_message_for_subscription(
log,
ad_caller,
ad_callee,
subid_caller,
subid_callee,
verify_caller_func=None,
wait_time_in_call=WAIT_TIME_LEAVE_VOICE_MAIL):
"""On specific voice subscription, Call from caller to callee,
reject on callee, caller leave a voice mail.
1. Caller call Callee.
2. Callee reject incoming call.
3. Caller leave a voice mail.
4. Verify callee received the voice mail notification.
Args:
ad_caller: caller android device object.
ad_callee: callee android device object.
subid_caller: caller's subscription id.
subid_callee: callee's subscription id.
verify_caller_func: function to verify caller is in correct state while in-call.
This is optional, default is None.
wait_time_in_call: time to wait when leaving a voice mail.
This is optional, default is WAIT_TIME_LEAVE_VOICE_MAIL
Returns:
True: if voice message is received on callee successfully.
False: for errors
"""
# Currently this test utility only works for TMO and ATT and SPT.
# It does not work for VZW (see b/21559800)
# "with VVM TelephonyManager APIs won't work for vm"
caller_number = ad_caller.telephony['subscription'][subid_caller][
'phone_num']
callee_number = ad_callee.telephony['subscription'][subid_callee][
'phone_num']
ad_caller.log.info("Call from %s to %s", caller_number, callee_number)
try:
voice_mail_count_before = ad_callee.droid.telephonyGetVoiceMailCountForSubscription(
subid_callee)
ad_callee.log.info("voice mail count is %s", voice_mail_count_before)
# -1 means there are unread voice mail, but the count is unknown
# 0 means either this API not working (VZW) or no unread voice mail.
if voice_mail_count_before != 0:
log.warning("--Pending new Voice Mail, please clear on phone.--")
if not initiate_call(log, ad_caller, callee_number):
ad_caller.log.error("Initiate call failed.")
return False
if check_reject_needed_for_voice_mail(log, ad_callee):
carrier_specific_delay_reject = 30
else:
carrier_specific_delay_reject = 2
carrier_reject_call = not check_reject_needed_for_voice_mail(log, ad_callee)
if not wait_and_reject_call_for_subscription(
log, ad_callee, subid_callee, incoming_number=caller_number, delay_reject=carrier_specific_delay_reject,
reject=carrier_reject_call):
ad_callee.log.error("Reject call fail.")
return False
ad_callee.droid.telephonyStartTrackingVoiceMailStateChangeForSubscription(
subid_callee)
# ensure that all internal states are updated in telecom
time.sleep(WAIT_TIME_ANDROID_STATE_SETTLING)
ad_callee.ed.clear_events(EventCallStateChanged)
if verify_caller_func and not verify_caller_func(log, ad_caller):
ad_caller.log.error("Caller not in correct state!")
return False
# TODO: b/26293512 Need to play some sound to leave message.
# Otherwise carrier voice mail server may drop this voice mail.
time.sleep(wait_time_in_call)
if not verify_caller_func:
caller_state_result = ad_caller.droid.telecomIsInCall()
else:
caller_state_result = verify_caller_func(log, ad_caller)
if not caller_state_result:
ad_caller.log.error("Caller not in correct state after %s seconds",
wait_time_in_call)
if not hangup_call(log, ad_caller):
ad_caller.log.error("Error in Hanging-Up Call")
return False
ad_callee.log.info("Wait for voice mail indicator on callee.")
try:
event = ad_callee.ed.wait_for_event(
EventMessageWaitingIndicatorChanged,
_is_on_message_waiting_event_true)
ad_callee.log.info("Got event %s", event)
except Empty:
ad_callee.log.warning("No expected event %s",
EventMessageWaitingIndicatorChanged)
return False
voice_mail_count_after = ad_callee.droid.telephonyGetVoiceMailCountForSubscription(
subid_callee)
ad_callee.log.info(
"telephonyGetVoiceMailCount output - before: %s, after: %s",
voice_mail_count_before, voice_mail_count_after)
# voice_mail_count_after should:
# either equals to (voice_mail_count_before + 1) [For ATT and SPT]
# or equals to -1 [For TMO]
# -1 means there are unread voice mail, but the count is unknown
if not check_voice_mail_count(log, ad_callee, voice_mail_count_before,
voice_mail_count_after):
log.error("before and after voice mail count is not incorrect.")
return False
finally:
ad_callee.droid.telephonyStopTrackingVoiceMailStateChangeForSubscription(
subid_callee)
return True
def call_voicemail_erase_all_pending_voicemail(log, ad):
"""Script for phone to erase all pending voice mail.
This script only works for TMO and ATT and SPT currently.
This script only works if phone have already set up voice mail options,
and phone should disable password protection for voice mail.
1. If phone don't have pending voice message, return True.
2. Dial voice mail number.
For TMO, the number is '123'
For ATT, the number is phone's number
For SPT, the number is phone's number
3. Wait for voice mail connection setup.
4. Wait for voice mail play pending voice message.
5. Send DTMF to delete one message.
The digit is '7'.
6. Repeat steps 4 and 5 until voice mail server drop this call.
(No pending message)
6. Check telephonyGetVoiceMailCount result. it should be 0.
Args:
log: log object
ad: android device object
Returns:
False if error happens. True is succeed.
"""
log.info("Erase all pending voice mail.")
count = ad.droid.telephonyGetVoiceMailCount()
if count == 0:
ad.log.info("No Pending voice mail.")
return True
if count == -1:
ad.log.info("There is pending voice mail, but the count is unknown")
count = MAX_SAVED_VOICE_MAIL
else:
ad.log.info("There are %s voicemails", count)
voice_mail_number = get_voice_mail_number(log, ad)
delete_digit = get_voice_mail_delete_digit(get_operator_name(log, ad))
if not initiate_call(log, ad, voice_mail_number):
log.error("Initiate call to voice mail failed.")
return False
time.sleep(WAIT_TIME_VOICE_MAIL_SERVER_RESPONSE)
callId = ad.droid.telecomCallGetCallIds()[0]
time.sleep(WAIT_TIME_VOICE_MAIL_SERVER_RESPONSE)
while (is_phone_in_call(log, ad) and (count > 0)):
ad.log.info("Press %s to delete voice mail.", delete_digit)
ad.droid.telecomCallPlayDtmfTone(callId, delete_digit)
ad.droid.telecomCallStopDtmfTone(callId)
time.sleep(WAIT_TIME_VOICE_MAIL_SERVER_RESPONSE)
count -= 1
if is_phone_in_call(log, ad):
hangup_call(log, ad)
# wait for telephonyGetVoiceMailCount to update correct result
remaining_time = MAX_WAIT_TIME_VOICE_MAIL_COUNT
while ((remaining_time > 0)
and (ad.droid.telephonyGetVoiceMailCount() != 0)):
time.sleep(1)
remaining_time -= 1
current_voice_mail_count = ad.droid.telephonyGetVoiceMailCount()
ad.log.info("telephonyGetVoiceMailCount: %s", current_voice_mail_count)
return (current_voice_mail_count == 0)
def call_setup_teardown(log,
ad_caller,
ad_callee,
ad_hangup=None,
verify_caller_func=None,
verify_callee_func=None,
wait_time_in_call=WAIT_TIME_IN_CALL,
incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
dialing_number_length=None,
video_state=None,
slot_id_callee=None,
voice_type_init=None,
call_stats_check=False,
result_info=result_dict):
""" Call process, including make a phone call from caller,
accept from callee, and hang up. The call is on default voice subscription
In call process, call from <droid_caller> to <droid_callee>,
accept the call, (optional)then hang up from <droid_hangup>.
Args:
ad_caller: Caller Android Device Object.
ad_callee: Callee Android Device Object.
ad_hangup: Android Device Object end the phone call.
Optional. Default value is None, and phone call will continue.
verify_call_mode_caller: func_ptr to verify caller in correct mode
Optional. Default is None
verify_call_mode_caller: func_ptr to verify caller in correct mode
Optional. Default is None
incall_ui_display: after answer the call, bring in-call UI to foreground or
background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
else, do nothing.
dialing_number_length: the number of digits used for dialing
slot_id_callee : the slot if of the callee to call to
Returns:
True if call process without any error.
False if error happened.
"""
subid_caller = get_outgoing_voice_sub_id(ad_caller)
if slot_id_callee is None:
subid_callee = get_incoming_voice_sub_id(ad_callee)
else:
subid_callee = get_subid_from_slot_index(log, ad_callee, slot_id_callee)
return call_setup_teardown_for_subscription(
log, ad_caller, ad_callee, subid_caller, subid_callee, ad_hangup,
verify_caller_func, verify_callee_func, wait_time_in_call,
incall_ui_display, dialing_number_length, video_state,
voice_type_init, call_stats_check, result_info)
def call_setup_teardown_for_subscription(
log,
ad_caller,
ad_callee,
subid_caller,
subid_callee,
ad_hangup=None,
verify_caller_func=None,
verify_callee_func=None,
wait_time_in_call=WAIT_TIME_IN_CALL,
incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
dialing_number_length=None,
video_state=None,
voice_type_init=None,
call_stats_check=False,
result_info=result_dict):
""" Call process, including make a phone call from caller,
accept from callee, and hang up. The call is on specified subscription
In call process, call from <droid_caller> to <droid_callee>,
accept the call, (optional)then hang up from <droid_hangup>.
Args:
ad_caller: Caller Android Device Object.
ad_callee: Callee Android Device Object.
subid_caller: Caller subscription ID
subid_callee: Callee subscription ID
ad_hangup: Android Device Object end the phone call.
Optional. Default value is None, and phone call will continue.
verify_call_mode_caller: func_ptr to verify caller in correct mode
Optional. Default is None
verify_call_mode_caller: func_ptr to verify caller in correct mode
Optional. Default is None
incall_ui_display: after answer the call, bring in-call UI to foreground or
background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
else, do nothing.
Returns:
TelResultWrapper which will evaluate as False if error.
"""
CHECK_INTERVAL = 5
begin_time = get_current_epoch_time()
if not verify_caller_func:
verify_caller_func = is_phone_in_call
if not verify_callee_func:
verify_callee_func = is_phone_in_call
caller_number = ad_caller.telephony['subscription'][subid_caller][
'phone_num']
callee_number = ad_callee.telephony['subscription'][subid_callee][
'phone_num']
callee_number = truncate_phone_number(
log,
caller_number,
callee_number,
dialing_number_length)
tel_result_wrapper = TelResultWrapper(CallResult('SUCCESS'))
msg = "Call from %s to %s" % (caller_number, callee_number)
if video_state:
msg = "Video %s" % msg
video = True
else:
video = False
if ad_hangup:
msg = "%s for duration of %s seconds" % (msg, wait_time_in_call)
ad_caller.log.info(msg)
for ad in (ad_caller, ad_callee):
call_ids = ad.droid.telecomCallGetCallIds()
setattr(ad, "call_ids", call_ids)
if call_ids:
ad.log.info("Pre-exist CallId %s before making call", call_ids)
if not initiate_call(
log,
ad_caller,
callee_number,
incall_ui_display=incall_ui_display,
video=video):
ad_caller.log.error("Initiate call failed.")
tel_result_wrapper.result_value = CallResult('INITIATE_FAILED')
return tel_result_wrapper
else:
ad_caller.log.info("Caller initate call successfully")
if not wait_and_answer_call_for_subscription(
log,
ad_callee,
subid_callee,
incoming_number=caller_number,
caller=ad_caller,
incall_ui_display=incall_ui_display,
video_state=video_state):
ad_callee.log.error("Answer call fail.")
tel_result_wrapper.result_value = CallResult(
'NO_RING_EVENT_OR_ANSWER_FAILED')
return tel_result_wrapper
else:
ad_callee.log.info("Callee answered the call successfully")
for ad, call_func in zip([ad_caller, ad_callee],
[verify_caller_func, verify_callee_func]):
call_ids = ad.droid.telecomCallGetCallIds()
new_call_ids = set(call_ids) - set(ad.call_ids)
if not new_call_ids:
ad.log.error(
"No new call ids are found after call establishment")
ad.log.error("telecomCallGetCallIds returns %s",
ad.droid.telecomCallGetCallIds())
tel_result_wrapper.result_value = CallResult('NO_CALL_ID_FOUND')
for new_call_id in new_call_ids:
if not wait_for_in_call_active(ad, call_id=new_call_id):
tel_result_wrapper.result_value = CallResult(
'CALL_STATE_NOT_ACTIVE_DURING_ESTABLISHMENT')
else:
ad.log.info("callProperties = %s",
ad.droid.telecomCallGetProperties(new_call_id))
if not ad.droid.telecomCallGetAudioState():
ad.log.error("Audio is not in call state")
tel_result_wrapper.result_value = CallResult(
'AUDIO_STATE_NOT_INCALL_DURING_ESTABLISHMENT')
if call_func(log, ad):
ad.log.info("Call is in %s state", call_func.__name__)
else:
ad.log.error("Call is not in %s state, voice in RAT %s",
call_func.__name__,
ad.droid.telephonyGetCurrentVoiceNetworkType())
tel_result_wrapper.result_value = CallResult(
'CALL_DROP_OR_WRONG_STATE_DURING_ESTABLISHMENT')
if not tel_result_wrapper:
return tel_result_wrapper
if call_stats_check:
voice_type_in_call = check_voice_network_type([ad_caller, ad_callee], voice_init=False)
phone_a_call_type = check_call_status(ad_caller,
voice_type_init[0],
voice_type_in_call[0])
result_info["Call Stats"] = phone_a_call_type
ad_caller.log.debug("Voice Call Type: %s", phone_a_call_type)
phone_b_call_type = check_call_status(ad_callee,
voice_type_init[1],
voice_type_in_call[1])
result_info["Call Stats"] = phone_b_call_type
ad_callee.log.debug("Voice Call Type: %s", phone_b_call_type)
return wait_for_call_end(
log,
ad_caller,
ad_callee,
ad_hangup,
verify_caller_func,
verify_callee_func,
begin_time,
check_interval=CHECK_INTERVAL,
tel_result_wrapper=TelResultWrapper(CallResult('SUCCESS')),
wait_time_in_call=wait_time_in_call)
def two_phone_call_leave_voice_mail(
log,
caller,
caller_idle_func,
caller_in_call_check_func,
callee,
callee_idle_func,
wait_time_in_call=WAIT_TIME_LEAVE_VOICE_MAIL):
"""Call from caller to callee, reject on callee, caller leave a voice mail.
1. Caller call Callee.
2. Callee reject incoming call.
3. Caller leave a voice mail.
4. Verify callee received the voice mail notification.
Args:
caller: caller android device object.
caller_idle_func: function to check caller's idle state.
caller_in_call_check_func: function to check caller's in-call state.
callee: callee android device object.
callee_idle_func: function to check callee's idle state.
wait_time_in_call: time to wait when leaving a voice mail.
This is optional, default is WAIT_TIME_LEAVE_VOICE_MAIL
Returns:
True: if voice message is received on callee successfully.
False: for errors
"""
ads = [caller, callee]
# Make sure phones are idle.
ensure_phones_idle(log, ads)
if caller_idle_func and not caller_idle_func(log, caller):
caller.log.error("Caller Failed to Reselect")
return False
if callee_idle_func and not callee_idle_func(log, callee):
callee.log.error("Callee Failed to Reselect")
return False
# TODO: b/26337871 Need to use proper API to check phone registered.
time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
# Make call and leave a message.
if not call_reject_leave_message(
log, caller, callee, caller_in_call_check_func, wait_time_in_call):
log.error("make a call and leave a message failed.")
return False
return True
def two_phone_call_short_seq(log,
phone_a,
phone_a_idle_func,
phone_a_in_call_check_func,
phone_b,
phone_b_idle_func,
phone_b_in_call_check_func,
call_sequence_func=None,
wait_time_in_call=WAIT_TIME_IN_CALL,
call_params=None):
"""Call process short sequence.
1. Ensure phone idle and in idle_func check return True.
2. Call from PhoneA to PhoneB, accept on PhoneB.
3. Check phone state, hangup on PhoneA.
4. Ensure phone idle and in idle_func check return True.
5. Call from PhoneA to PhoneB, accept on PhoneB.
6. Check phone state, hangup on PhoneB.
Args:
phone_a: PhoneA's android device object.
phone_a_idle_func: function to check PhoneA's idle state.
phone_a_in_call_check_func: function to check PhoneA's in-call state.
phone_b: PhoneB's android device object.
phone_b_idle_func: function to check PhoneB's idle state.
phone_b_in_call_check_func: function to check PhoneB's in-call state.
call_sequence_func: default parameter, not implemented.
wait_time_in_call: time to wait in call.
This is optional, default is WAIT_TIME_IN_CALL
call_params: list of call parameters for both MO/MT devices, including:
- MO device object
- MT device object
- Device object hanging up the call
- Function to check the in-call state of MO device
- Function to check the in-call state of MT device
and the format should be:
[(Args for the 1st call), (Args for the 2nd call), ......]
The format of args for each call should be:
(mo_device_obj, mt_device_obj, device_obj_hanging_up,
mo_in_call_check_func, mt_in_call_check_func)
Example of a call, which will not be hung up:
call_params = [
(ads[0], ads[1], None, mo_in_call_check_func,
mt_in_call_check_func)
]
Returns:
TelResultWrapper which will evaluate as False if error.
"""
ads = [phone_a, phone_b]
if not call_params:
call_params = [
(ads[0], ads[1], ads[0], phone_a_in_call_check_func,
phone_b_in_call_check_func),
(ads[0], ads[1], ads[1], phone_a_in_call_check_func,
phone_b_in_call_check_func),
]
tel_result = TelResultWrapper(CallResult('SUCCESS'))
for param in call_params:
# Make sure phones are idle.
ensure_phones_idle(log, ads)
if phone_a_idle_func and not phone_a_idle_func(log, phone_a):
phone_a.log.error("Phone A Failed to Reselect")
return TelResultWrapper(CallResult('CALL_SETUP_FAILURE'))
if phone_b_idle_func and not phone_b_idle_func(log, phone_b):
phone_b.log.error("Phone B Failed to Reselect")
return TelResultWrapper(CallResult('CALL_SETUP_FAILURE'))
# TODO: b/26337871 Need to use proper API to check phone registered.
time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
# Make call.
log.info("---> Call test: %s to %s <---", param[0].serial,
param[1].serial)
tel_result = call_setup_teardown(
log, *param, wait_time_in_call=wait_time_in_call)
if not tel_result:
log.error("Call Iteration Failed")
break
return tel_result
def two_phone_call_msim_short_seq(log,
phone_a,
phone_a_idle_func,
phone_a_in_call_check_func,
phone_b,
phone_b_idle_func,
phone_b_in_call_check_func,
call_sequence_func=None,
wait_time_in_call=WAIT_TIME_IN_CALL):
"""Call process short sequence.
1. Ensure phone idle and in idle_func check return True.
2. Call from PhoneA to PhoneB, accept on PhoneB.
3. Check phone state, hangup on PhoneA.
4. Ensure phone idle and in idle_func check return True.
5. Call from PhoneA to PhoneB, accept on PhoneB.
6. Check phone state, hangup on PhoneB.
Args:
phone_a: PhoneA's android device object.
phone_a_idle_func: function to check PhoneA's idle state.
phone_a_in_call_check_func: function to check PhoneA's in-call state.
phone_b: PhoneB's android device object.
phone_b_idle_func: function to check PhoneB's idle state.
phone_b_in_call_check_func: function to check PhoneB's in-call state.
call_sequence_func: default parameter, not implemented.
wait_time_in_call: time to wait in call.
This is optional, default is WAIT_TIME_IN_CALL
Returns:
True: if call sequence succeed.
False: for errors
"""
ads = [phone_a, phone_b]
call_params = [
(ads[0], ads[1], ads[0], phone_a_in_call_check_func,
phone_b_in_call_check_func),
(ads[0], ads[1], ads[1], phone_a_in_call_check_func,
phone_b_in_call_check_func),
]
for param in call_params:
# Make sure phones are idle.
ensure_phones_idle(log, ads)
if phone_a_idle_func and not phone_a_idle_func(log, phone_a):
phone_a.log.error("Phone A Failed to Reselect")
return False
if phone_b_idle_func and not phone_b_idle_func(log, phone_b):
phone_b.log.error("Phone B Failed to Reselect")
return False
# TODO: b/26337871 Need to use proper API to check phone registered.
time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
# Make call.
log.info("--> Call test: %s to %s <--", phone_a.serial, phone_b.serial)
slots = 2
for slot in range(slots):
set_subid_for_outgoing_call(
ads[0], get_subid_from_slot_index(log,ads[0],slot))
set_subid_for_outgoing_call(
ads[1], get_subid_from_slot_index(log,ads[1],slot))
time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
if not call_setup_teardown(log, *param,slot_id_callee = slot,
wait_time_in_call=wait_time_in_call):
log.error("Call Iteration Failed")
return False
if not call_setup_teardown(log, *param,slot_id_callee = 1-slot,
wait_time_in_call=wait_time_in_call):
log.error("Call Iteration Failed")
return False
return True
def two_phone_call_long_seq(log,
phone_a,
phone_a_idle_func,
phone_a_in_call_check_func,
phone_b,
phone_b_idle_func,
phone_b_in_call_check_func,
call_sequence_func=None,
wait_time_in_call=WAIT_TIME_IN_CALL):
"""Call process long sequence.
1. Ensure phone idle and in idle_func check return True.
2. Call from PhoneA to PhoneB, accept on PhoneB.
3. Check phone state, hangup on PhoneA.
4. Ensure phone idle and in idle_func check return True.
5. Call from PhoneA to PhoneB, accept on PhoneB.
6. Check phone state, hangup on PhoneB.
7. Ensure phone idle and in idle_func check return True.
8. Call from PhoneB to PhoneA, accept on PhoneA.
9. Check phone state, hangup on PhoneA.
10. Ensure phone idle and in idle_func check return True.
11. Call from PhoneB to PhoneA, accept on PhoneA.
12. Check phone state, hangup on PhoneB.
Args:
phone_a: PhoneA's android device object.
phone_a_idle_func: function to check PhoneA's idle state.
phone_a_in_call_check_func: function to check PhoneA's in-call state.
phone_b: PhoneB's android device object.
phone_b_idle_func: function to check PhoneB's idle state.
phone_b_in_call_check_func: function to check PhoneB's in-call state.
call_sequence_func: default parameter, not implemented.
wait_time_in_call: time to wait in call.
This is optional, default is WAIT_TIME_IN_CALL
Returns:
TelResultWrapper which will evaluate as False if error.
"""
ads = [phone_a, phone_b]
call_params = [
(ads[0], ads[1], ads[0], phone_a_in_call_check_func,
phone_b_in_call_check_func),
(ads[0], ads[1], ads[1], phone_a_in_call_check_func,
phone_b_in_call_check_func),
(ads[1], ads[0], ads[0], phone_b_in_call_check_func,
phone_a_in_call_check_func),
(ads[1], ads[0], ads[1], phone_b_in_call_check_func,
phone_a_in_call_check_func),
]
tel_result = TelResultWrapper(CallResult('SUCCESS'))
for param in call_params:
# Make sure phones are idle.
ensure_phones_idle(log, ads)
if phone_a_idle_func and not phone_a_idle_func(log, phone_a):
phone_a.log.error("Phone A Failed to Reselect")
return TelResultWrapper(CallResult('CALL_SETUP_FAILURE'))
if phone_b_idle_func and not phone_b_idle_func(log, phone_b):
phone_b.log.error("Phone B Failed to Reselect")
return TelResultWrapper(CallResult('CALL_SETUP_FAILURE'))
# TODO: b/26337871 Need to use proper API to check phone registered.
time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
# Make call.
log.info("---> Call test: %s to %s <---", param[0].serial,
param[1].serial)
tel_result = call_setup_teardown(
log, *param, wait_time_in_call=wait_time_in_call)
if not tel_result:
log.error("Call Iteration Failed")
break
return tel_result
def two_phone_call_msim_for_slot(log,
phone_a,
phone_a_slot,
phone_a_idle_func,
phone_a_in_call_check_func,
phone_b,
phone_b_slot,
phone_b_idle_func,
phone_b_in_call_check_func,
call_sequence_func=None,
wait_time_in_call=WAIT_TIME_IN_CALL,
retry=2):
"""Call process between 2 phones with specific slot.
1. Ensure phone idle and in idle_func check return True.
2. Call from PhoneA to PhoneB, accept on PhoneB.
3. Check phone state, hangup on PhoneA.
4. Ensure phone idle and in idle_func check return True.
5. Call from PhoneA to PhoneB, accept on PhoneB.
6. Check phone state, hangup on PhoneB.
Args:
phone_a: PhoneA's android device object.
phone_a_slot: 0 or 1 (pSIM or eSIM)
phone_a_idle_func: function to check PhoneA's idle state.
phone_a_in_call_check_func: function to check PhoneA's in-call state.
phone_b: PhoneB's android device object.
phone_b_slot: 0 or 1 (pSIM or eSIM)
phone_b_idle_func: function to check PhoneB's idle state.
phone_b_in_call_check_func: function to check PhoneB's in-call state.
call_sequence_func: default parameter, not implemented.
wait_time_in_call: time to wait in call.
This is optional, default is WAIT_TIME_IN_CALL
retry: times of retry if call_setup_teardown failed.
Returns:
True: if call sequence succeed.
False: for errors
"""
ads = [phone_a, phone_b]
call_params = [
(ads[0], ads[1], ads[0], phone_a_in_call_check_func,
phone_b_in_call_check_func),
(ads[0], ads[1], ads[1], phone_a_in_call_check_func,
phone_b_in_call_check_func),
]
tel_result = TelResultWrapper(CallResult('SUCCESS'))
for param in call_params:
# Make sure phones are idle.
ensure_phones_idle(log, ads)
if phone_a_idle_func and not phone_a_idle_func(log, phone_a):
phone_a.log.error("Phone A Failed to Reselect")
return TelResultWrapper(CallResult('CALL_SETUP_FAILURE'))
if phone_b_idle_func and not phone_b_idle_func(log, phone_b):
phone_b.log.error("Phone B Failed to Reselect")
return TelResultWrapper(CallResult('CALL_SETUP_FAILURE'))
# TODO: b/26337871 Need to use proper API to check phone registered.
time.sleep(WAIT_TIME_BETWEEN_REG_AND_CALL)
# Make call.
log.info("--> Call test: %s slot %s to %s slot %s <--", phone_a.serial,
phone_a_slot, phone_b.serial, phone_b_slot)
mo_default_voice_subid = get_subid_from_slot_index(log,ads[0],
phone_a_slot)
if mo_default_voice_subid == INVALID_SUB_ID:
log.warning("Sub ID of MO (%s) slot %s is invalid.", phone_a.serial,
phone_a_slot)
return TelResultWrapper(CallResult('CALL_SETUP_FAILURE'))
set_subid_for_outgoing_call(
ads[0], mo_default_voice_subid)
mt_default_voice_subid = get_subid_from_slot_index(log,ads[1],
phone_b_slot)
if mt_default_voice_subid == INVALID_SUB_ID:
log.warning("Sub ID of MT (%s) slot %s is invalid.", phone_b.serial,
phone_b_slot)
return TelResultWrapper(CallResult('CALL_SETUP_FAILURE'))
tel_result = call_setup_teardown(
log,
*param,
slot_id_callee=phone_b_slot,
wait_time_in_call=wait_time_in_call)
while not tel_result:
if retry <= 0:
log.error("Call Iteration failed.")
break
else:
log.info("RERUN call_setup_teardown.")
tel_result = call_setup_teardown(
log,
*param,
slot_id_callee=phone_b_slot,
wait_time_in_call=wait_time_in_call)
retry = retry - 1
return tel_result
def is_phone_in_call(log, ad):
"""Return True if phone in call.
Args:
log: log object.
ad: android device.
"""
try:
return ad.droid.telecomIsInCall()
except:
return "mCallState=2" in ad.adb.shell(
"dumpsys telephony.registry | grep mCallState")
def is_phone_in_call_active(ad, call_id=None):
"""Return True if phone in active call.
Args:
log: log object.
ad: android device.
call_id: the call id
"""
if ad.droid.telecomIsInCall():
if not call_id:
call_id = ad.droid.telecomCallGetCallIds()[0]
call_state = ad.droid.telecomCallGetCallState(call_id)
ad.log.info("%s state is %s", call_id, call_state)
return call_state == "ACTIVE"
else:
ad.log.info("Not in telecomIsInCall")
return False
def is_phone_in_call_volte(log, ad):
"""Return if phone is in VoLTE call.
Args:
ad: Android device object.
"""
return is_phone_in_call_volte_for_subscription(
log, ad, get_outgoing_voice_sub_id(ad))
def is_phone_in_call_volte_for_subscription(log, ad, sub_id):
"""Return if phone is in VoLTE call for subscription id.
Args:
ad: Android device object.
sub_id: subscription id.
"""
if not ad.droid.telecomIsInCall():
ad.log.error("Not in call.")
return False
nw_type = get_network_rat_for_subscription(log, ad, sub_id,
NETWORK_SERVICE_VOICE)
if nw_type != RAT_LTE:
ad.log.error("Voice rat on: %s. Expected: LTE", nw_type)
return False
return True
def is_phone_in_call_csfb(log, ad):
"""Return if phone is in CSFB call.
Args:
ad: Android device object.
"""
return is_phone_in_call_csfb_for_subscription(
log, ad, get_outgoing_voice_sub_id(ad))
def is_phone_in_call_csfb_for_subscription(log, ad, sub_id):
"""Return if phone is in CSFB call for subscription id.
Args:
ad: Android device object.
sub_id: subscription id.
"""
if not ad.droid.telecomIsInCall():
ad.log.error("Not in call.")
return False
nw_type = get_network_rat_for_subscription(log, ad, sub_id,
NETWORK_SERVICE_VOICE)
if nw_type == RAT_LTE:
ad.log.error("Voice rat on: %s. Expected: not LTE", nw_type)
return False
return True
def is_phone_in_call_3g(log, ad):
"""Return if phone is in 3G call.
Args:
ad: Android device object.
"""
return is_phone_in_call_3g_for_subscription(log, ad,
get_outgoing_voice_sub_id(ad))
def is_phone_in_call_3g_for_subscription(log, ad, sub_id):
"""Return if phone is in 3G call for subscription id.
Args:
ad: Android device object.
sub_id: subscription id.
"""
if not ad.droid.telecomIsInCall():
ad.log.error("Not in call.")
return False
nw_gen = get_network_gen_for_subscription(log, ad, sub_id,
NETWORK_SERVICE_VOICE)
if nw_gen != GEN_3G:
ad.log.error("Voice rat on: %s. Expected: 3g", nw_gen)
return False
return True
def is_phone_in_call_2g(log, ad):
"""Return if phone is in 2G call.
Args:
ad: Android device object.
"""
return is_phone_in_call_2g_for_subscription(log, ad,
get_outgoing_voice_sub_id(ad))
def is_phone_in_call_2g_for_subscription(log, ad, sub_id):
"""Return if phone is in 2G call for subscription id.
Args:
ad: Android device object.
sub_id: subscription id.
"""
if not ad.droid.telecomIsInCall():
ad.log.error("Not in call.")
return False
nw_gen = get_network_gen_for_subscription(log, ad, sub_id,
NETWORK_SERVICE_VOICE)
if nw_gen != GEN_2G:
ad.log.error("Voice rat on: %s. Expected: 2g", nw_gen)
return False
return True
def is_phone_in_call_1x(log, ad):
"""Return if phone is in 1x call.
Args:
ad: Android device object.
"""
return is_phone_in_call_1x_for_subscription(log, ad,
get_outgoing_voice_sub_id(ad))
def is_phone_in_call_1x_for_subscription(log, ad, sub_id):
"""Return if phone is in 1x call for subscription id.
Args:
ad: Android device object.
sub_id: subscription id.
"""
if not ad.droid.telecomIsInCall():
ad.log.error("Not in call.")
return False
nw_type = get_network_rat_for_subscription(log, ad, sub_id,
NETWORK_SERVICE_VOICE)
if nw_type != RAT_1XRTT:
ad.log.error("Voice rat on: %s. Expected: 1xrtt", nw_type)
return False
return True
def is_phone_in_call_wcdma(log, ad):
"""Return if phone is in WCDMA call.
Args:
ad: Android device object.
"""
return is_phone_in_call_wcdma_for_subscription(
log, ad, get_outgoing_voice_sub_id(ad))
def is_phone_in_call_wcdma_for_subscription(log, ad, sub_id):
"""Return if phone is in WCDMA call for subscription id.
Args:
ad: Android device object.
sub_id: subscription id.
"""
# Currently checking 'umts'.
# Changes may needed in the future.
if not ad.droid.telecomIsInCall():
ad.log.error("Not in call.")
return False
nw_type = get_network_rat_for_subscription(log, ad, sub_id,
NETWORK_SERVICE_VOICE)
if nw_type != RAT_UMTS:
ad.log.error("%s voice rat on: %s. Expected: umts", nw_type)
return False
return True
def is_phone_in_call_iwlan(log, ad, call_id=None):
"""Return if phone is in WiFi call.
Args:
ad: Android device object.
"""
if not ad.droid.telecomIsInCall():
ad.log.error("Not in call.")
return False
if not ad.droid.telephonyIsImsRegistered():
ad.log.info("IMS is not registered.")
return False
if not ad.droid.telephonyIsWifiCallingAvailable():
ad.log.info("IsWifiCallingAvailable is False")
return False
if not call_id:
call_ids = ad.droid.telecomCallGetCallIds()
if call_ids:
call_id = call_ids[-1]
if not call_id:
ad.log.error("Failed to get call id")
return False
else:
call_prop = ad.droid.telecomCallGetProperties(call_id)
if "WIFI" not in call_prop:
ad.log.info("callProperties = %s, expecting WIFI", call_prop)
return False
nw_type = get_network_rat(log, ad, NETWORK_SERVICE_DATA)
if nw_type != RAT_IWLAN:
ad.log.warning("Data rat on: %s. Expected: iwlan", nw_type)
return True
def is_phone_in_call_not_iwlan(log, ad):
"""Return if phone is in WiFi call for subscription id.
Args:
ad: Android device object.
sub_id: subscription id.
"""
if not ad.droid.telecomIsInCall():
ad.log.error("Not in call.")
return False
nw_type = get_network_rat(log, ad, NETWORK_SERVICE_DATA)
if nw_type == RAT_IWLAN:
ad.log.error("Data rat on: %s. Expected: not iwlan", nw_type)
return False
if is_wfc_enabled(log, ad):
ad.log.error("WiFi Calling feature bit is True.")
return False
return True
def swap_calls(log,
ads,
call_hold_id,
call_active_id,
num_swaps=1,
check_call_status=True):
"""PhoneA in call with B and C. Swap active/holding call on PhoneA.
Swap call and check status on PhoneA.
(This step may have multiple times according to 'num_swaps'.)
Check if all 3 phones are 'in-call'.
Args:
ads: list of ad object, at least three need to pass in.
Swap operation will happen on ads[0].
ads[1] and ads[2] are call participants.
call_hold_id: id for the holding call in ads[0].
call_hold_id should be 'STATE_HOLDING' when calling this function.
call_active_id: id for the active call in ads[0].
call_active_id should be 'STATE_ACTIVE' when calling this function.
num_swaps: how many swap/check operations will be done before return.
check_call_status: This is optional. Default value is True.
If this value is True, then call status (active/hold) will be
be checked after each swap operation.
Returns:
If no error happened, return True, otherwise, return False.
"""
if check_call_status:
# Check status before swap.
if ads[0].droid.telecomCallGetCallState(
call_active_id) != CALL_STATE_ACTIVE:
ads[0].log.error(
"Call_id:%s, state:%s, expected: STATE_ACTIVE", call_active_id,
ads[0].droid.telecomCallGetCallState(call_active_id))
return False
if ads[0].droid.telecomCallGetCallState(
call_hold_id) != CALL_STATE_HOLDING:
ads[0].log.error(
"Call_id:%s, state:%s, expected: STATE_HOLDING", call_hold_id,
ads[0].droid.telecomCallGetCallState(call_hold_id))
return False
i = 1
while (i <= num_swaps):
ads[0].log.info("swap_test %s: swap and check call status.", i)
ads[0].droid.telecomCallHold(call_active_id)
time.sleep(WAIT_TIME_IN_CALL)
# Swap object reference
call_active_id, call_hold_id = call_hold_id, call_active_id
if check_call_status:
# Check status
if ads[0].droid.telecomCallGetCallState(
call_active_id) != CALL_STATE_ACTIVE:
ads[0].log.error(
"Call_id:%s, state:%s, expected: STATE_ACTIVE",
call_active_id,
ads[0].droid.telecomCallGetCallState(call_active_id))
return False
if ads[0].droid.telecomCallGetCallState(
call_hold_id) != CALL_STATE_HOLDING:
ads[0].log.error(
"Call_id:%s, state:%s, expected: STATE_HOLDING",
call_hold_id,
ads[0].droid.telecomCallGetCallState(call_hold_id))
return False
# TODO: b/26296375 add voice check.
i += 1
#In the end, check all three phones are 'in-call'.
if not verify_incall_state(log, [ads[0], ads[1], ads[2]], True):
return False
return True
def get_audio_route(log, ad):
"""Gets the audio route for the active call
Args:
log: logger object
ad: android_device object
Returns:
Audio route string ["BLUETOOTH", "EARPIECE", "SPEAKER", "WIRED_HEADSET"
"WIRED_OR_EARPIECE"]
"""
audio_state = ad.droid.telecomCallGetAudioState()
return audio_state["AudioRoute"]
def set_audio_route(log, ad, route):
"""Sets the audio route for the active call
Args:
log: logger object
ad: android_device object
route: string ["BLUETOOTH", "EARPIECE", "SPEAKER", "WIRED_HEADSET"
"WIRED_OR_EARPIECE"]
Returns:
If no error happened, return True, otherwise, return False.
"""
ad.droid.telecomCallSetAudioRoute(route)
return True
def is_property_in_call_properties(log, ad, call_id, expected_property):
"""Return if the call_id has the expected property
Args:
log: logger object
ad: android_device object
call_id: call id.
expected_property: expected property.
Returns:
True if call_id has expected_property. False if not.
"""
properties = ad.droid.telecomCallGetProperties(call_id)
return (expected_property in properties)
def is_call_hd(log, ad, call_id):
"""Return if the call_id is HD call.
Args:
log: logger object
ad: android_device object
call_id: call id.
Returns:
True if call_id is HD call. False if not.
"""
return is_property_in_call_properties(log, ad, call_id,
CALL_PROPERTY_HIGH_DEF_AUDIO)
def get_cep_conference_call_id(ad):
"""Get CEP conference call id if there is an ongoing CEP conference call.
Args:
ad: android device object.
Returns:
call id for CEP conference call if there is an ongoing CEP conference call.
None otherwise.
"""
for call in ad.droid.telecomCallGetCallIds():
if len(ad.droid.telecomCallGetCallChildren(call)) != 0:
return call
return None
def is_phone_in_call_on_rat(log, ad, rat='volte', only_return_fn=None):
if rat.lower() == 'volte' or rat.lower() == '5g_volte':
if only_return_fn:
return is_phone_in_call_volte
else:
return is_phone_in_call_volte(log, ad)
elif rat.lower() == 'csfb' or rat.lower() == '5g_csfb':
if only_return_fn:
return is_phone_in_call_csfb
else:
return is_phone_in_call_csfb(log, ad)
elif rat.lower() == '3g':
if only_return_fn:
return is_phone_in_call_3g
else:
return is_phone_in_call_3g(log, ad)
elif rat.lower() == '2g':
if only_return_fn:
return is_phone_in_call_2g
else:
return is_phone_in_call_2g(log, ad)
elif rat.lower() == 'wfc' or rat.lower() == '5g_wfc':
if only_return_fn:
return is_phone_in_call_iwlan
else:
return is_phone_in_call_iwlan(log, ad)
else:
return None
def hold_unhold_test(log, ads):
""" Test hold/unhold functionality.
PhoneA is in call with PhoneB. The call on PhoneA is active.
Get call list on PhoneA.
Hold call_id on PhoneA.
Check call_id state.
Unhold call_id on PhoneA.
Check call_id state.
Args:
log: log object
ads: List of android objects.
This list should contain 2 android objects.
ads[0] is the ad to do hold/unhold operation.
Returns:
List of test result and call states.
The first element of the list is always the test result.
True if pass; False if fail.
The rest of the list contains call states.
"""
call_list = ads[0].droid.telecomCallGetCallIds()
log.info("Calls in PhoneA %s", call_list)
if num_active_calls(ads[0].log, ads[0]) != 1:
log.error("No voice call or too many voice calls in PhoneA!")
call_state_list = [ads[0].droid.telecomCallGetCallState(call_id) for call_id in call_list]
return [False] + call_state_list
call_id = call_list[0]
call_state = ads[0].droid.telecomCallGetCallState(call_id)
if call_state != CALL_STATE_ACTIVE:
log.error("Call_id:%s, state:%s, expected: STATE_ACTIVE",
call_id,
ads[0].droid.telecomCallGetCallState(call_id))
return [False, call_state]
# TODO: b/26296375 add voice check.
log.info("Hold call_id %s on PhoneA", call_id)
ads[0].droid.telecomCallHold(call_id)
time.sleep(WAIT_TIME_IN_CALL)
call_state = ads[0].droid.telecomCallGetCallState(call_id)
if call_state != CALL_STATE_HOLDING:
ads[0].log.error("Call_id:%s, state:%s, expected: STATE_HOLDING",
call_id,
ads[0].droid.telecomCallGetCallState(call_id))
return [False, call_state]
# TODO: b/26296375 add voice check.
log.info("Unhold call_id %s on PhoneA", call_id)
ads[0].droid.telecomCallUnhold(call_id)
time.sleep(WAIT_TIME_IN_CALL)
call_state = ads[0].droid.telecomCallGetCallState(call_id)
if call_state != CALL_STATE_ACTIVE:
log.error("Call_id:%s, state:%s, expected: STATE_ACTIVE",
call_id,
call_state)
return [False, call_state]
# TODO: b/26296375 add voice check.
return [True, call_state]
def phone_setup_call_hold_unhold_test(log,
ads,
call_direction=DIRECTION_MOBILE_ORIGINATED,
caller_func=None,
callee_func=None):
"""Test hold and unhold in voice call.
1. Clear call list.
2. Set up MO/MT call.
3. Test hold and unhold in call.
4. hangup call.
Args:
log: log object
ads: list of android objects, this list should have two ad.
call_direction: MO(DIRECTION_MOBILE_ORIGINATED) or MT(DIRECTION_MOBILE_TERMINATED) call.
caller_func: function to verify caller is in correct state while in-call.
callee_func: function to verify callee is in correct state while in-call.
Returns:
True if pass; False if fail.
"""
ads[0].droid.telecomCallClearCallList()
if num_active_calls(log, ads[0]) != 0:
ads[0].log.error("call list is not empty")
return False
log.info("begin hold/unhold test")
ad_caller = ads[0]
ad_callee = ads[1]
if call_direction != DIRECTION_MOBILE_ORIGINATED:
ad_caller = ads[1]
ad_callee = ads[0]
if not call_setup_teardown(
log,
ad_caller,
ad_callee,
ad_hangup=None,
verify_caller_func=caller_func,
verify_callee_func=callee_func):
return False
if not hold_unhold_test(ads[0].log, ads)[0]:
log.error("hold/unhold test fail.")
# hangup call in case voice call is still active.
hangup_call(log, ads[0])
return False
if not hangup_call(log, ads[0]):
log.error("call hangup failed")
return False
return True
def _test_call_long_duration(log, ads, dut_incall_check_func, total_duration):
log.info("Long Duration Call Test. Total duration = %s",
total_duration)
return call_setup_teardown(
log,
ads[0],
ads[1],
ads[0],
verify_caller_func=dut_incall_check_func,
wait_time_in_call=total_duration)
def _wait_for_ringing_event(log, ad, wait_time):
"""Wait for ringing event.
Args:
log: log object.
ad: android device object.
wait_time: max time to wait for ringing event.
Returns:
event_ringing if received ringing event.
otherwise return None.
"""
event_ringing = None
try:
event_ringing = ad.ed.wait_for_event(
EventCallStateChanged,
is_event_match,
timeout=wait_time,
field=CallStateContainer.CALL_STATE,
value=TELEPHONY_STATE_RINGING)
ad.log.info("Receive ringing event")
except Empty:
ad.log.info("No Ringing Event")
finally:
return event_ringing
def wait_for_telecom_ringing(log, ad, max_time=MAX_WAIT_TIME_TELECOM_RINGING):
"""Wait for android to be in telecom ringing state.
Args:
log: log object.
ad: android device.
max_time: maximal wait time. This is optional.
Default Value is MAX_WAIT_TIME_TELECOM_RINGING.
Returns:
If phone become in telecom ringing state within max_time, return True.
Return False if timeout.
"""
return _wait_for_droid_in_state(
log, ad, max_time, lambda log, ad: ad.droid.telecomIsRinging())
def wait_for_ringing_call(log, ad, incoming_number=None):
"""Wait for an incoming call on default voice subscription and
accepts the call.
Args:
log: log object.
ad: android device object.
incoming_number: Expected incoming number.
Optional. Default is None
Returns:
True: if incoming call is received and answered successfully.
False: for errors
"""
return wait_for_ringing_call_for_subscription(
log, ad, get_incoming_voice_sub_id(ad), incoming_number)
def wait_for_ringing_call_for_subscription(
log,
ad,
sub_id,
incoming_number=None,
caller=None,
event_tracking_started=False,
timeout=MAX_WAIT_TIME_CALLEE_RINGING,
interval=WAIT_TIME_BETWEEN_STATE_CHECK):
"""Wait for an incoming call on specified subscription.
Args:
log: log object.
ad: android device object.
sub_id: subscription ID
incoming_number: Expected incoming number. Default is None
event_tracking_started: True if event tracking already state outside
timeout: time to wait for ring
interval: checking interval
Returns:
True: if incoming call is received and answered successfully.
False: for errors
"""
if not event_tracking_started:
ad.ed.clear_events(EventCallStateChanged)
ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
ring_event_received = False
end_time = time.time() + timeout
try:
while time.time() < end_time:
if not ring_event_received:
event_ringing = _wait_for_ringing_event(log, ad, interval)
if event_ringing:
if incoming_number and not check_phone_number_match(
event_ringing['data']
[CallStateContainer.INCOMING_NUMBER], incoming_number):
ad.log.error(
"Incoming Number not match. Expected number:%s, actual number:%s",
incoming_number, event_ringing['data'][
CallStateContainer.INCOMING_NUMBER])
return False
ring_event_received = True
telephony_state = ad.droid.telephonyGetCallStateForSubscription(
sub_id)
telecom_state = ad.droid.telecomGetCallState()
if telephony_state == TELEPHONY_STATE_RINGING and (
telecom_state == TELEPHONY_STATE_RINGING):
ad.log.info("callee is in telephony and telecom RINGING state")
if caller:
if caller.droid.telecomIsInCall():
caller.log.info("Caller telecom is in call state")
return True
else:
caller.log.info("Caller telecom is NOT in call state")
else:
return True
else:
ad.log.info(
"telephony in %s, telecom in %s, expecting RINGING state",
telephony_state, telecom_state)
time.sleep(interval)
finally:
if not event_tracking_started:
ad.droid.telephonyStopTrackingCallStateChangeForSubscription(
sub_id)
def wait_for_call_offhook_for_subscription(
log,
ad,
sub_id,
event_tracking_started=False,
timeout=MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT,
interval=WAIT_TIME_BETWEEN_STATE_CHECK):
"""Wait for an incoming call on specified subscription.
Args:
log: log object.
ad: android device object.
sub_id: subscription ID
timeout: time to wait for ring
interval: checking interval
Returns:
True: if incoming call is received and answered successfully.
False: for errors
"""
if not event_tracking_started:
ad.ed.clear_events(EventCallStateChanged)
ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
offhook_event_received = False
end_time = time.time() + timeout
try:
while time.time() < end_time:
if not offhook_event_received:
if wait_for_call_offhook_event(log, ad, sub_id, True,
interval):
offhook_event_received = True
telephony_state = ad.droid.telephonyGetCallStateForSubscription(
sub_id)
telecom_state = ad.droid.telecomGetCallState()
if telephony_state == TELEPHONY_STATE_OFFHOOK and (
telecom_state == TELEPHONY_STATE_OFFHOOK):
ad.log.info("telephony and telecom are in OFFHOOK state")
return True
else:
ad.log.info(
"telephony in %s, telecom in %s, expecting OFFHOOK state",
telephony_state, telecom_state)
if offhook_event_received:
time.sleep(interval)
finally:
if not event_tracking_started:
ad.droid.telephonyStopTrackingCallStateChangeForSubscription(
sub_id)
def wait_for_call_offhook_event(
log,
ad,
sub_id,
event_tracking_started=False,
timeout=MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT):
"""Wait for an incoming call on specified subscription.
Args:
log: log object.
ad: android device object.
event_tracking_started: True if event tracking already state outside
timeout: time to wait for event
Returns:
True: if call offhook event is received.
False: if call offhook event is not received.
"""
if not event_tracking_started:
ad.ed.clear_events(EventCallStateChanged)
ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
try:
ad.ed.wait_for_event(
EventCallStateChanged,
is_event_match,
timeout=timeout,
field=CallStateContainer.CALL_STATE,
value=TELEPHONY_STATE_OFFHOOK)
ad.log.info("Got event %s", TELEPHONY_STATE_OFFHOOK)
except Empty:
ad.log.info("No event for call state change to OFFHOOK")
return False
finally:
if not event_tracking_started:
ad.droid.telephonyStopTrackingCallStateChangeForSubscription(
sub_id)
return True
def wait_and_answer_call_for_subscription(
log,
ad,
sub_id,
incoming_number=None,
incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
timeout=MAX_WAIT_TIME_CALLEE_RINGING,
caller=None,
video_state=None):
"""Wait for an incoming call on specified subscription and
accepts the call.
Args:
log: log object.
ad: android device object.
sub_id: subscription ID
incoming_number: Expected incoming number.
Optional. Default is None
incall_ui_display: after answer the call, bring in-call UI to foreground or
background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
else, do nothing.
Returns:
True: if incoming call is received and answered successfully.
False: for errors
"""
ad.ed.clear_events(EventCallStateChanged)
ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
try:
if not wait_for_ringing_call_for_subscription(
log,
ad,
sub_id,
incoming_number=incoming_number,
caller=caller,
event_tracking_started=True,
timeout=timeout):
ad.log.info("Incoming call ringing check failed.")
return False
ad.log.info("Accept the ring call")
ad.droid.telecomAcceptRingingCall(video_state)
if wait_for_call_offhook_for_subscription(
log, ad, sub_id, event_tracking_started=True):
return True
else:
ad.log.error("Could not answer the call.")
return False
except Exception as e:
log.error(e)
return False
finally:
ad.droid.telephonyStopTrackingCallStateChangeForSubscription(sub_id)
if incall_ui_display == INCALL_UI_DISPLAY_FOREGROUND:
ad.droid.telecomShowInCallScreen()
elif incall_ui_display == INCALL_UI_DISPLAY_BACKGROUND:
ad.droid.showHomeScreen()
def wait_and_reject_call(log,
ad,
incoming_number=None,
delay_reject=WAIT_TIME_REJECT_CALL,
reject=True):
"""Wait for an incoming call on default voice subscription and
reject the call.
Args:
log: log object.
ad: android device object.
incoming_number: Expected incoming number.
Optional. Default is None
delay_reject: time to wait before rejecting the call
Optional. Default is WAIT_TIME_REJECT_CALL
Returns:
True: if incoming call is received and reject successfully.
False: for errors
"""
return wait_and_reject_call_for_subscription(log, ad,
get_incoming_voice_sub_id(ad),
incoming_number, delay_reject,
reject)
def wait_and_reject_call_for_subscription(log,
ad,
sub_id,
incoming_number=None,
delay_reject=WAIT_TIME_REJECT_CALL,
reject=True):
"""Wait for an incoming call on specific subscription and
reject the call.
Args:
log: log object.
ad: android device object.
sub_id: subscription ID
incoming_number: Expected incoming number.
Optional. Default is None
delay_reject: time to wait before rejecting the call
Optional. Default is WAIT_TIME_REJECT_CALL
Returns:
True: if incoming call is received and reject successfully.
False: for errors
"""
if not wait_for_ringing_call_for_subscription(log, ad, sub_id,
incoming_number):
ad.log.error(
"Could not reject a call: incoming call in ringing check failed.")
return False
ad.ed.clear_events(EventCallStateChanged)
ad.droid.telephonyStartTrackingCallStateForSubscription(sub_id)
if reject is True:
# Delay between ringing and reject.
time.sleep(delay_reject)
is_find = False
# Loop the call list and find the matched one to disconnect.
for call in ad.droid.telecomCallGetCallIds():
if check_phone_number_match(
get_number_from_tel_uri(get_call_uri(ad, call)),
incoming_number):
ad.droid.telecomCallDisconnect(call)
ad.log.info("Callee reject the call")
is_find = True
if is_find is False:
ad.log.error("Callee did not find matching call to reject.")
return False
else:
# don't reject on callee. Just ignore the incoming call.
ad.log.info("Callee received incoming call. Ignore it.")
try:
ad.ed.wait_for_event(
EventCallStateChanged,
is_event_match_for_list,
timeout=MAX_WAIT_TIME_CALL_IDLE_EVENT,
field=CallStateContainer.CALL_STATE,
value_list=[TELEPHONY_STATE_IDLE, TELEPHONY_STATE_OFFHOOK])
except Empty:
ad.log.error("No onCallStateChangedIdle event received.")
return False
finally:
ad.droid.telephonyStopTrackingCallStateChangeForSubscription(sub_id)
return True
def wait_and_answer_call(log,
ad,
incoming_number=None,
incall_ui_display=INCALL_UI_DISPLAY_FOREGROUND,
caller=None,
video_state=None):
"""Wait for an incoming call on default voice subscription and
accepts the call.
Args:
ad: android device object.
incoming_number: Expected incoming number.
Optional. Default is None
incall_ui_display: after answer the call, bring in-call UI to foreground or
background. Optional, default value is INCALL_UI_DISPLAY_FOREGROUND.
if = INCALL_UI_DISPLAY_FOREGROUND, bring in-call UI to foreground.
if = INCALL_UI_DISPLAY_BACKGROUND, bring in-call UI to background.
else, do nothing.
Returns:
True: if incoming call is received and answered successfully.
False: for errors
"""
return wait_and_answer_call_for_subscription(
log,
ad,
get_incoming_voice_sub_id(ad),
incoming_number,
incall_ui_display=incall_ui_display,
caller=caller,
video_state=video_state)
def wait_for_in_call_active(ad,
timeout=MAX_WAIT_TIME_ACCEPT_CALL_TO_OFFHOOK_EVENT,
interval=WAIT_TIME_BETWEEN_STATE_CHECK,
call_id=None):
"""Wait for call reach active state.
Args:
log: log object.
ad: android device.
call_id: the call id
"""
if not call_id:
call_id = ad.droid.telecomCallGetCallIds()[0]
args = [ad, call_id]
if not wait_for_state(is_phone_in_call_active, True, timeout, interval,
*args):
ad.log.error("Call did not reach ACTIVE state")
return False
else:
return True
def wait_for_droid_in_call(log, ad, max_time):
"""Wait for android to be in call state.
Args:
log: log object.
ad: android device.
max_time: maximal wait time.
Returns:
If phone become in call state within max_time, return True.
Return False if timeout.
"""
return _wait_for_droid_in_state(log, ad, max_time, is_phone_in_call)
def wait_for_call_id_clearing(ad,
previous_ids,
timeout=MAX_WAIT_TIME_CALL_DROP):
while timeout > 0:
new_call_ids = ad.droid.telecomCallGetCallIds()
if len(new_call_ids) <= len(previous_ids):
return True
time.sleep(5)
timeout = timeout - 5
ad.log.error("Call id clearing failed. Before: %s; After: %s",
previous_ids, new_call_ids)
return False
def wait_for_call_end(
log,
ad_caller,
ad_callee,
ad_hangup,
verify_caller_func,
verify_callee_func,
call_begin_time,
check_interval=5,
tel_result_wrapper=TelResultWrapper(CallResult('SUCCESS')),
wait_time_in_call=WAIT_TIME_IN_CALL):
elapsed_time = 0
while (elapsed_time < wait_time_in_call):
check_interval = min(check_interval, wait_time_in_call - elapsed_time)
time.sleep(check_interval)
elapsed_time += check_interval
time_message = "at <%s>/<%s> second." % (elapsed_time, wait_time_in_call)
for ad, call_func in [(ad_caller, verify_caller_func),
(ad_callee, verify_callee_func)]:
if not call_func(log, ad):
ad.log.error(
"NOT in correct %s state at %s, voice in RAT %s",
call_func.__name__,
time_message,
ad.droid.telephonyGetCurrentVoiceNetworkType())
tel_result_wrapper.result_value = CallResult(
'CALL_DROP_OR_WRONG_STATE_AFTER_CONNECTED')
else:
ad.log.info("In correct %s state at %s",
call_func.__name__, time_message)
if not ad.droid.telecomCallGetAudioState():
ad.log.error("Audio is not in call state at %s", time_message)
tel_result_wrapper.result_value = CallResult(
'AUDIO_STATE_NOT_INCALL_AFTER_CONNECTED')
if not tel_result_wrapper:
break
if not tel_result_wrapper:
for ad in (ad_caller, ad_callee):
last_call_drop_reason(ad, call_begin_time)
try:
if ad.droid.telecomIsInCall():
ad.log.info("In call. End now.")
ad.droid.telecomEndCall()
except Exception as e:
log.error(str(e))
else:
if ad_hangup:
if not hangup_call(log, ad_hangup):
ad_hangup.log.info("Failed to hang up the call")
tel_result_wrapper.result_value = CallResult('CALL_HANGUP_FAIL')
if ad_hangup or not tel_result_wrapper:
for ad in (ad_caller, ad_callee):
if not wait_for_call_id_clearing(ad, getattr(ad, "caller_ids", [])):
tel_result_wrapper.result_value = CallResult(
'CALL_ID_CLEANUP_FAIL')
return tel_result_wrapper
def check_call(log, dut, dut_client):
result = True
if not call_setup_teardown(log, dut_client, dut,
dut):
if not call_setup_teardown(log, dut_client,
dut, dut):
dut.log.error("MT call failed")
result = False
if not call_setup_teardown(log, dut, dut_client,
dut):
dut.log.error("MO call failed")
result = False
return result
def check_call_in_wfc(log, dut, dut_client):
result = True
if not call_setup_teardown(log, dut_client, dut,
dut, None, is_phone_in_call_iwlan):
if not call_setup_teardown(log, dut_client,
dut, dut, None,
is_phone_in_call_iwlan):
dut.log.error("MT WFC call failed")
result = False
if not call_setup_teardown(log, dut, dut_client,
dut, is_phone_in_call_iwlan):
dut.log.error("MO WFC call failed")
result = False
return result
def check_call_in_volte(log, dut, dut_client):
result = True
if not call_setup_teardown(log, dut_client, dut,
dut, None, is_phone_in_call_volte):
if not call_setup_teardown(log, dut_client,
dut, dut, None,
is_phone_in_call_volte):
dut.log.error("MT VoLTE call failed")
result = False
if not call_setup_teardown(log, dut, dut_client,
dut, is_phone_in_call_volte):
dut.log.error("MO VoLTE call failed")
result = False
return result
def change_ims_setting(log,
ad,
dut_client,
wifi_network_ssid,
wifi_network_pass,
subid,
dut_capabilities,
airplane_mode,
wifi_enabled,
volte_enabled,
wfc_enabled,
nw_gen=RAT_LTE,
wfc_mode=None):
result = True
ad.log.info(
"Setting APM %s, WIFI %s, VoLTE %s, WFC %s, WFC mode %s",
airplane_mode, wifi_enabled, volte_enabled, wfc_enabled, wfc_mode)
toggle_airplane_mode_by_adb(log, ad, airplane_mode)
if wifi_enabled:
if not ensure_wifi_connected(log, ad,
wifi_network_ssid,
wifi_network_pass,
apm=airplane_mode):
ad.log.error("Fail to connected to WiFi")
result = False
else:
if not wifi_toggle_state(log, ad, False):
ad.log.error("Failed to turn off WiFi.")
result = False
toggle_volte(log, ad, volte_enabled)
toggle_wfc(log, ad, wfc_enabled)
if wfc_mode:
set_wfc_mode(log, ad, wfc_mode)
wfc_mode = ad.droid.imsGetWfcMode()
if wifi_enabled or not airplane_mode:
if not ensure_phone_subscription(log, ad):
ad.log.error("Failed to find valid subscription")
result = False
if airplane_mode:
if (CAPABILITY_WFC in dut_capabilities) and (wifi_enabled
and wfc_enabled):
if not wait_for_wfc_enabled(log, ad):
result = False
elif not check_call_in_wfc(log, ad, dut_client):
result = False
else:
if not wait_for_state(
ad.droid.telephonyGetCurrentVoiceNetworkType,
RAT_UNKNOWN):
ad.log.error(
"Voice RAT is %s not UNKNOWN",
ad.droid.telephonyGetCurrentVoiceNetworkType())
result = False
else:
ad.log.info("Voice RAT is in UNKKNOWN")
else:
if (wifi_enabled and wfc_enabled) and (
wfc_mode == WFC_MODE_WIFI_PREFERRED) and (
CAPABILITY_WFC in dut_capabilities):
if not wait_for_wfc_enabled(log, ad):
result = False
if not wait_for_state(
ad.droid.telephonyGetCurrentVoiceNetworkType,
RAT_UNKNOWN):
ad.log.error(
"Voice RAT is %s, not UNKNOWN",
ad.droid.telephonyGetCurrentVoiceNetworkType())
if not check_call_in_wfc(log, ad, dut_client):
result = False
else:
if not wait_for_wfc_disabled(log, ad):
ad.log.error("WFC is not disabled")
result = False
if volte_enabled and CAPABILITY_VOLTE in dut_capabilities:
if not wait_for_volte_enabled(log, ad):
result = False
if not check_call_in_volte(log, ad, dut_client):
result = False
else:
if not wait_for_not_network_rat(
log,
ad,
nw_gen,
voice_or_data=NETWORK_SERVICE_VOICE):
ad.log.error(
"Voice RAT is %s",
ad.droid.telephonyGetCurrentVoiceNetworkType(
))
result = False
if not wait_for_voice_attach(log, ad):
result = False
if not check_call(log, ad, dut_client):
result = False
user_config_profile = get_user_config_profile(ad)
ad.log.info("user_config_profile: %s ",
sorted(user_config_profile.items()))
return result
def verify_default_ims_setting(log,
ad,
dut_client,
carrier_configs,
default_wfc_enabled,
default_volte,
wfc_mode=None):
result = True
airplane_mode = ad.droid.connectivityCheckAirplaneMode()
default_wfc_mode = carrier_configs.get(
CarrierConfigs.DEFAULT_WFC_IMS_MODE_INT, wfc_mode)
if default_wfc_enabled:
wait_for_wfc_enabled(log, ad)
else:
wait_for_wfc_disabled(log, ad)
if airplane_mode:
wait_for_network_rat(
log,
ad,
RAT_UNKNOWN,
voice_or_data=NETWORK_SERVICE_VOICE)
else:
if default_volte:
wait_for_volte_enabled(log, ad)
else:
wait_for_not_network_rat(
log,
ad,
RAT_UNKNOWN,
voice_or_data=NETWORK_SERVICE_VOICE)
if not ensure_phone_subscription(log, ad):
ad.log.error("Failed to find valid subscription")
result = False
user_config_profile = get_user_config_profile(ad)
ad.log.info("user_config_profile = %s ",
sorted(user_config_profile.items()))
if user_config_profile["VoLTE Enabled"] != default_volte:
ad.log.error("VoLTE mode is not %s", default_volte)
result = False
else:
ad.log.info("VoLTE mode is %s as expected",
default_volte)
if user_config_profile["WFC Enabled"] != default_wfc_enabled:
ad.log.error("WFC enabled is not %s", default_wfc_enabled)
if user_config_profile["WFC Enabled"]:
if user_config_profile["WFC Mode"] != default_wfc_mode:
ad.log.error(
"WFC mode is not %s after IMS factory reset",
default_wfc_mode)
result = False
else:
ad.log.info("WFC mode is %s as expected",
default_wfc_mode)
if default_wfc_enabled and \
default_wfc_mode == WFC_MODE_WIFI_PREFERRED:
if not check_call_in_wfc(log, ad, dut_client):
result = False
elif not airplane_mode:
if default_volte:
if not check_call_in_volte(log, ad, dut_client):
result = False
else:
if not check_call(log, ad, dut_client):
result = False
if result == False:
user_config_profile = get_user_config_profile(ad)
ad.log.info("user_config_profile = %s ",
sorted(user_config_profile.items()))
return result
def truncate_phone_number(
log,
caller_number,
callee_number,
dialing_number_length,
skip_inter_area_call=False):
"""This function truncates the phone number of the caller/callee to test
7/10/11/12 digit dialing for North American numbering plan, and distinguish
if this is an inter-area call by comparing the area code.
Args:
log: logger object
caller_number: phone number of the caller
callee_number: phone number of the callee
dialing_number_length: the length of phone number (usually 7/10/11/12)
skip_inter_area_call: True to raise a TestSkip exception to skip dialing
the inter-area call. Otherwise False.
Returns:
The truncated phone number of the callee
"""
if not dialing_number_length:
return callee_number
trunc_position = 0 - int(dialing_number_length)
try:
caller_area_code = caller_number[:trunc_position]
callee_area_code = callee_number[:trunc_position]
callee_dial_number = callee_number[trunc_position:]
if caller_area_code != callee_area_code:
skip_inter_area_call = True
except:
skip_inter_area_call = True
if skip_inter_area_call:
msg = "Cannot make call from %s to %s by %s digits since inter-area \
call is not allowed" % (
caller_number, callee_number, dialing_number_length)
log.info(msg)
raise signals.TestSkip(msg)
else:
callee_number = callee_dial_number
return callee_number