blob: 3d7e9358adc8ca30d28e7e16ff4870ddaca45ad4 [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright 2022 - 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.
from future import standard_library
standard_library.install_aliases()
import json
import logging
import re
import os
import urllib.parse
import time
import acts.controllers.iperf_server as ipf
import struct
from acts import signals
from queue import Empty
from acts.asserts import abort_all
from acts.controllers.adb_lib.error import AdbCommandError, AdbError
from acts.controllers.android_device import list_adb_devices
from acts.controllers.android_device import list_fastboot_devices
from acts.libs.proc.job import TimeoutError
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 AOSP_PREFIX
from acts_contrib.test_utils.tel.tel_defines import CARD_POWER_DOWN
from acts_contrib.test_utils.tel.tel_defines import CARD_POWER_UP
from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_CONFERENCE
from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VOLTE
from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VOLTE_PROVISIONING
from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VOLTE_OVERRIDE_WFC_PROVISIONING
from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_HIDE_ENHANCED_4G_LTE_BOOL
from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_VT
from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC
from acts_contrib.test_utils.tel.tel_defines import CAPABILITY_WFC_MODE_CHANGE
from acts_contrib.test_utils.tel.tel_defines import CARRIER_UNKNOWN
from acts_contrib.test_utils.tel.tel_defines import COUNTRY_CODE_LIST
from acts_contrib.test_utils.tel.tel_defines import DIALER_PACKAGE_NAME
from acts_contrib.test_utils.tel.tel_defines import DATA_ROAMING_ENABLE
from acts_contrib.test_utils.tel.tel_defines import DATA_ROAMING_DISABLE
from acts_contrib.test_utils.tel.tel_defines import GEN_4G
from acts_contrib.test_utils.tel.tel_defines import GEN_UNKNOWN
from acts_contrib.test_utils.tel.tel_defines import INVALID_SIM_SLOT_INDEX
from acts_contrib.test_utils.tel.tel_defines import INVALID_SUB_ID
from acts_contrib.test_utils.tel.tel_defines import MAX_SCREEN_ON_TIME
from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_AIRPLANEMODE_EVENT
from acts_contrib.test_utils.tel.tel_defines import MESSAGE_PACKAGE_NAME
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 PHONE_NUMBER_STRING_FORMAT_7_DIGIT
from acts_contrib.test_utils.tel.tel_defines import PHONE_NUMBER_STRING_FORMAT_10_DIGIT
from acts_contrib.test_utils.tel.tel_defines import PHONE_NUMBER_STRING_FORMAT_11_DIGIT
from acts_contrib.test_utils.tel.tel_defines import PHONE_NUMBER_STRING_FORMAT_12_DIGIT
from acts_contrib.test_utils.tel.tel_defines import RAT_UNKNOWN
from acts_contrib.test_utils.tel.tel_defines import SERVICE_STATE_EMERGENCY_ONLY
from acts_contrib.test_utils.tel.tel_defines import SERVICE_STATE_IN_SERVICE
from acts_contrib.test_utils.tel.tel_defines import SERVICE_STATE_MAPPING
from acts_contrib.test_utils.tel.tel_defines import SERVICE_STATE_OUT_OF_SERVICE
from acts_contrib.test_utils.tel.tel_defines import SERVICE_STATE_POWER_OFF
from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_ABSENT
from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_LOADED
from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_NOT_READY
from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_PIN_REQUIRED
from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_READY
from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_UNKNOWN
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_STATE_CHECK
from acts_contrib.test_utils.tel.tel_defines import MAX_WAIT_TIME_FOR_STATE_CHANGE
from acts_contrib.test_utils.tel.tel_defines import WAIT_TIME_SYNC_DATE_TIME_FROM_NETWORK
from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_CELLULAR_PREFERRED
from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_ONLY
from acts_contrib.test_utils.tel.tel_defines import WFC_MODE_WIFI_PREFERRED
from acts_contrib.test_utils.tel.tel_defines import EventActiveDataSubIdChanged
from acts_contrib.test_utils.tel.tel_defines import EventDisplayInfoChanged
from acts_contrib.test_utils.tel.tel_defines import EventServiceStateChanged
from acts_contrib.test_utils.tel.tel_defines import NetworkCallbackContainer
from acts_contrib.test_utils.tel.tel_defines import ServiceStateContainer
from acts_contrib.test_utils.tel.tel_defines import DisplayInfoContainer
from acts_contrib.test_utils.tel.tel_defines import OverrideNetworkContainer
from acts_contrib.test_utils.tel.tel_logging_utils import disable_qxdm_logger
from acts_contrib.test_utils.tel.tel_logging_utils import get_screen_shot_log
from acts_contrib.test_utils.tel.tel_logging_utils import log_screen_shot
from acts_contrib.test_utils.tel.tel_logging_utils import start_qxdm_logger
from acts_contrib.test_utils.tel.tel_lookup_tables import connection_type_from_type_string
from acts_contrib.test_utils.tel.tel_lookup_tables import is_valid_rat
from acts_contrib.test_utils.tel.tel_lookup_tables import get_allowable_network_preference
from acts_contrib.test_utils.tel.tel_lookup_tables import get_voice_mail_count_check_function
from acts_contrib.test_utils.tel.tel_lookup_tables import get_voice_mail_check_number
from acts_contrib.test_utils.tel.tel_lookup_tables import network_preference_for_generation
from acts_contrib.test_utils.tel.tel_lookup_tables import operator_name_from_network_name
from acts_contrib.test_utils.tel.tel_lookup_tables import operator_name_from_plmn_id
from acts_contrib.test_utils.tel.tel_lookup_tables import rat_family_from_rat
from acts_contrib.test_utils.tel.tel_lookup_tables import rat_generation_from_rat
from acts_contrib.test_utils.tel.tel_subscription_utils import get_slot_index_from_subid
from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_by_adb
from acts_contrib.test_utils.tel.tel_subscription_utils import get_subid_from_slot_index
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 get_incoming_voice_sub_id
from acts_contrib.test_utils.tel.tel_subscription_utils import set_incoming_voice_sub_id
from acts.utils import adb_shell_ping
from acts.utils import load_config
from acts.logger import epoch_to_log_line_timestamp
from acts.utils import get_current_epoch_time
from acts.utils import exe_cmd
log = logging
STORY_LINE = "+19523521350"
CallResult = TelephonyVoiceTestResult.CallResult.Value
class TelResultWrapper(object):
"""Test results wrapper for Telephony test utils.
In order to enable metrics reporting without refactoring
all of the test utils this class is used to keep the
current return boolean scheme in tact.
"""
def __init__(self, result_value):
self._result_value = result_value
@property
def result_value(self):
return self._result_value
@result_value.setter
def result_value(self, result_value):
self._result_value = result_value
def __bool__(self):
return self._result_value == CallResult('SUCCESS')
def abort_all_tests(log, msg):
log.error("Aborting all ongoing tests due to: %s.", msg)
abort_all(msg)
def get_phone_number_by_adb(ad):
return phone_number_formatter(
ad.adb.shell("service call iphonesubinfo 13"))
def get_iccid_by_adb(ad):
return ad.adb.shell("service call iphonesubinfo 11")
def get_operator_by_adb(ad):
operator = ad.adb.getprop("gsm.sim.operator.alpha")
if "," in operator:
operator = operator.strip()[0]
return operator
def get_plmn_by_adb(ad):
plmn_id = ad.adb.getprop("gsm.sim.operator.numeric")
if "," in plmn_id:
plmn_id = plmn_id.strip()[0]
return plmn_id
def get_sub_id_by_adb(ad):
return ad.adb.shell("service call iphonesubinfo 5")
def setup_droid_properties_by_adb(log, ad, sim_filename=None):
sim_data = None
if sim_filename:
try:
sim_data = load_config(sim_filename)
except Exception:
log.warning("Failed to load %s!", sim_filename)
sub_id = get_sub_id_by_adb(ad)
iccid = get_iccid_by_adb(ad)
ad.log.info("iccid = %s", iccid)
if sim_data.get(iccid) and sim_data[iccid].get("phone_num"):
phone_number = phone_number_formatter(sim_data[iccid]["phone_num"])
else:
phone_number = get_phone_number_by_adb(ad)
if not phone_number and hasattr(ad, phone_number):
phone_number = ad.phone_number
if not phone_number:
ad.log.error("Failed to find valid phone number for %s", iccid)
abort_all_tests(ad.log, "Failed to find valid phone number for %s")
sub_record = {
'phone_num': phone_number,
'iccid': get_iccid_by_adb(ad),
'sim_operator_name': get_operator_by_adb(ad),
'operator': operator_name_from_plmn_id(get_plmn_by_adb(ad))
}
device_props = {'subscription': {sub_id: sub_record}}
ad.log.info("subId %s SIM record: %s", sub_id, sub_record)
setattr(ad, 'telephony', device_props)
def setup_droid_properties(log, ad, sim_filename=None):
if ad.skip_sl4a:
return setup_droid_properties_by_adb(
log, ad, sim_filename=sim_filename)
refresh_droid_config(log, ad)
sim_data = {}
if sim_filename:
try:
sim_data = load_config(sim_filename)
except Exception:
log.warning("Failed to load %s!", sim_filename)
if not ad.telephony["subscription"]:
abort_all_tests(ad.log, "No valid subscription")
ad.log.debug("Subscription DB %s", ad.telephony["subscription"])
result = True
active_sub_id = get_outgoing_voice_sub_id(ad)
for sub_id, sub_info in ad.telephony["subscription"].items():
ad.log.debug("Loop for Subid %s", sub_id)
sub_info["operator"] = get_operator_name(log, ad, sub_id)
iccid = sub_info["iccid"]
if not iccid:
ad.log.warning("Unable to find ICC-ID for subscriber %s", sub_id)
continue
if sub_info.get("phone_num"):
if iccid in sim_data and sim_data[iccid].get("phone_num"):
if not check_phone_number_match(sim_data[iccid]["phone_num"],
sub_info["phone_num"]):
ad.log.warning(
"phone_num %s in sim card data file for iccid %s"
" do not match phone_num %s from subscription",
sim_data[iccid]["phone_num"], iccid,
sub_info["phone_num"])
sub_info["phone_num"] = sim_data[iccid]["phone_num"]
else:
if iccid in sim_data and sim_data[iccid].get("phone_num"):
sub_info["phone_num"] = sim_data[iccid]["phone_num"]
elif sub_id == active_sub_id:
phone_number = get_phone_number_by_secret_code(
ad, sub_info["sim_operator_name"])
if phone_number:
sub_info["phone_num"] = phone_number
elif getattr(ad, "phone_num", None):
sub_info["phone_num"] = ad.phone_number
if (not sub_info.get("phone_num")) and sub_id == active_sub_id:
ad.log.info("sub_id %s sub_info = %s", sub_id, sub_info)
ad.log.error(
"Unable to retrieve phone number for sub %s with iccid"
" %s from device or testbed config or sim card file %s",
sub_id, iccid, sim_filename)
result = False
if not hasattr(
ad, 'roaming'
) and sub_info["sim_plmn"] != sub_info["network_plmn"] and sub_info["sim_operator_name"].strip(
) not in sub_info["network_operator_name"].strip():
ad.log.info("roaming is not enabled, enable it")
setattr(ad, 'roaming', True)
ad.log.info("SubId %s info: %s", sub_id, sorted(sub_info.items()))
get_phone_capability(ad)
data_roaming = getattr(ad, 'roaming', False)
if get_cell_data_roaming_state_by_adb(ad) != data_roaming:
set_cell_data_roaming_state_by_adb(ad, data_roaming)
# Setup VoWiFi MDN for Verizon. b/33187374
if not result:
abort_all_tests(ad.log, "Failed to find valid phone number")
ad.log.debug("telephony = %s", ad.telephony)
def refresh_droid_config(log, ad):
""" Update Android Device telephony records for each sub_id.
Args:
log: log object
ad: android device object
Returns:
None
"""
if not getattr(ad, 'telephony', {}):
setattr(ad, 'telephony', {"subscription": {}})
droid = ad.droid
sub_info_list = droid.subscriptionGetAllSubInfoList()
ad.log.info("SubInfoList is %s", sub_info_list)
if not sub_info_list: return
active_sub_id = get_outgoing_voice_sub_id(ad)
for sub_info in sub_info_list:
sub_id = sub_info["subscriptionId"]
sim_slot = sub_info["simSlotIndex"]
if sub_info.get("carrierId"):
carrier_id = sub_info["carrierId"]
else:
carrier_id = -1
if sub_info.get("isOpportunistic"):
isopportunistic = sub_info["isOpportunistic"]
else:
isopportunistic = -1
if sim_slot != INVALID_SIM_SLOT_INDEX:
if sub_id not in ad.telephony["subscription"]:
ad.telephony["subscription"][sub_id] = {}
sub_record = ad.telephony["subscription"][sub_id]
if sub_info.get("iccId"):
sub_record["iccid"] = sub_info["iccId"]
else:
sub_record[
"iccid"] = droid.telephonyGetSimSerialNumberForSubscription(
sub_id)
sub_record["sim_slot"] = sim_slot
if sub_info.get("mcc"):
sub_record["mcc"] = sub_info["mcc"]
if sub_info.get("mnc"):
sub_record["mnc"] = sub_info["mnc"]
if sub_info.get("displayName"):
sub_record["display_name"] = sub_info["displayName"]
try:
sub_record[
"phone_type"] = droid.telephonyGetPhoneTypeForSubscription(
sub_id)
except:
if not sub_record.get("phone_type"):
sub_record["phone_type"] = droid.telephonyGetPhoneType()
sub_record[
"sim_plmn"] = droid.telephonyGetSimOperatorForSubscription(
sub_id)
sub_record[
"sim_operator_name"] = droid.telephonyGetSimOperatorNameForSubscription(
sub_id)
sub_record[
"network_plmn"] = droid.telephonyGetNetworkOperatorForSubscription(
sub_id)
sub_record[
"network_operator_name"] = droid.telephonyGetNetworkOperatorNameForSubscription(
sub_id)
sub_record[
"sim_country"] = droid.telephonyGetSimCountryIsoForSubscription(
sub_id)
if active_sub_id == sub_id:
try:
sub_record[
"carrier_id"] = ad.droid.telephonyGetSimCarrierId()
sub_record[
"carrier_id_name"] = ad.droid.telephonyGetSimCarrierIdName(
)
except:
ad.log.info("Carrier ID is not supported")
if carrier_id == 2340:
ad.log.info("SubId %s info: %s", sub_id, sorted(
sub_record.items()))
continue
if carrier_id == 1989 and isopportunistic == "true":
ad.log.info("SubId %s info: %s", sub_id, sorted(
sub_record.items()))
continue
if not sub_info.get("number"):
sub_info[
"number"] = droid.telephonyGetLine1NumberForSubscription(
sub_id)
if sub_info.get("number"):
if sub_record.get("phone_num"):
# Use the phone number provided in sim info file by default
# as the sub_info["number"] may not be formatted in a
# dialable number
if not check_phone_number_match(sub_info["number"],
sub_record["phone_num"]):
ad.log.info(
"Subscriber phone number changed from %s to %s",
sub_record["phone_num"], sub_info["number"])
sub_record["phone_num"] = sub_info["number"]
else:
sub_record["phone_num"] = phone_number_formatter(
sub_info["number"])
ad.log.info("SubId %s info: %s", sub_id, sorted(
sub_record.items()))
def get_phone_number_by_secret_code(ad, operator):
if "T-Mobile" in operator:
ad.droid.telecomDialNumber("#686#")
ad.send_keycode("ENTER")
for _ in range(12):
output = ad.search_logcat("mobile number")
if output:
result = re.findall(r"mobile number is (\S+)",
output[-1]["log_message"])
ad.send_keycode("BACK")
return result[0]
else:
time.sleep(5)
return ""
def get_user_config_profile(ad):
return {
"Airplane Mode":
ad.droid.connectivityCheckAirplaneMode(),
"IMS Registered":
ad.droid.telephonyIsImsRegistered(),
"Preferred Network Type":
ad.droid.telephonyGetPreferredNetworkTypes(),
"VoLTE Platform Enabled":
ad.droid.imsIsEnhanced4gLteModeSettingEnabledByPlatform(),
"VoLTE Enabled":
ad.droid.imsIsEnhanced4gLteModeSettingEnabledByUser(),
"VoLTE Available":
ad.droid.telephonyIsVolteAvailable(),
"VT Available":
ad.droid.telephonyIsVideoCallingAvailable(),
"VT Enabled":
ad.droid.imsIsVtEnabledByUser(),
"VT Platform Enabled":
ad.droid.imsIsVtEnabledByPlatform(),
"WiFi State":
ad.droid.wifiCheckState(),
"WFC Available":
ad.droid.telephonyIsWifiCallingAvailable(),
"WFC Enabled":
ad.droid.imsIsWfcEnabledByUser(),
"WFC Platform Enabled":
ad.droid.imsIsWfcEnabledByPlatform(),
"WFC Mode":
ad.droid.imsGetWfcMode()
}
def get_num_active_sims(log, ad):
""" Get the number of active SIM cards by counting slots
Args:
ad: android_device object.
Returns:
result: The number of loaded (physical) SIM cards
"""
# using a dictionary as a cheap way to prevent double counting
# in the situation where multiple subscriptions are on the same SIM.
# yes, this is a corner corner case.
valid_sims = {}
subInfo = ad.droid.subscriptionGetAllSubInfoList()
for info in subInfo:
ssidx = info['simSlotIndex']
if ssidx == INVALID_SIM_SLOT_INDEX:
continue
valid_sims[ssidx] = True
return len(valid_sims.keys())
def toggle_airplane_mode_by_adb(log, ad, new_state=None):
""" Toggle the state of airplane mode.
Args:
log: log handler.
ad: android_device object.
new_state: Airplane mode state to set to.
If None, opposite of the current state.
strict_checking: Whether to turn on strict checking that checks all features.
Returns:
result: True if operation succeed. False if error happens.
"""
cur_state = bool(int(ad.adb.shell("settings get global airplane_mode_on")))
if new_state == cur_state:
ad.log.info("Airplane mode already in %s", new_state)
return True
elif new_state is None:
new_state = not cur_state
ad.log.info("Change airplane mode from %s to %s", cur_state, new_state)
try:
ad.adb.shell("settings put global airplane_mode_on %s" % int(new_state))
ad.adb.shell("am broadcast -a android.intent.action.AIRPLANE_MODE")
except Exception as e:
ad.log.error(e)
return False
changed_state = bool(int(ad.adb.shell("settings get global airplane_mode_on")))
return changed_state == new_state
def toggle_airplane_mode(log, ad, new_state=None, strict_checking=True):
""" Toggle the state of airplane mode.
Args:
log: log handler.
ad: android_device object.
new_state: Airplane mode state to set to.
If None, opposite of the current state.
strict_checking: Whether to turn on strict checking that checks all features.
Returns:
result: True if operation succeed. False if error happens.
"""
if ad.skip_sl4a:
return toggle_airplane_mode_by_adb(log, ad, new_state)
else:
return toggle_airplane_mode_msim(
log, ad, new_state, strict_checking=strict_checking)
def get_telephony_signal_strength(ad):
#{'evdoEcio': -1, 'asuLevel': 28, 'lteSignalStrength': 14, 'gsmLevel': 0,
# 'cdmaAsuLevel': 99, 'evdoDbm': -120, 'gsmDbm': -1, 'cdmaEcio': -160,
# 'level': 2, 'lteLevel': 2, 'cdmaDbm': -120, 'dbm': -112, 'cdmaLevel': 0,
# 'lteAsuLevel': 28, 'gsmAsuLevel': 99, 'gsmBitErrorRate': 0,
# 'lteDbm': -112, 'gsmSignalStrength': 99}
try:
signal_strength = ad.droid.telephonyGetSignalStrength()
if not signal_strength:
signal_strength = {}
except Exception as e:
ad.log.error(e)
signal_strength = {}
return signal_strength
def get_lte_rsrp(ad):
try:
if ad.adb.getprop("ro.build.version.release")[0] in ("9", "P"):
out = ad.adb.shell(
"dumpsys telephony.registry | grep -i signalstrength")
if out:
lte_rsrp = out.split()[9]
if lte_rsrp:
ad.log.info("lte_rsrp: %s ", lte_rsrp)
return lte_rsrp
else:
out = ad.adb.shell(
"dumpsys telephony.registry |grep -i primary=CellSignalStrengthLte")
if out:
lte_cell_info = out.split('mLte=')[1]
lte_rsrp = re.match(r'.*rsrp=(\S+).*', lte_cell_info).group(1)
if lte_rsrp:
ad.log.info("lte_rsrp: %s ", lte_rsrp)
return lte_rsrp
except Exception as e:
ad.log.error(e)
return None
def break_internet_except_sl4a_port(ad, sl4a_port):
ad.log.info("Breaking internet using iptables rules")
ad.adb.shell("iptables -I INPUT 1 -p tcp --dport %s -j ACCEPT" % sl4a_port,
ignore_status=True)
ad.adb.shell("iptables -I INPUT 2 -p tcp --sport %s -j ACCEPT" % sl4a_port,
ignore_status=True)
ad.adb.shell("iptables -I INPUT 3 -j DROP", ignore_status=True)
ad.adb.shell("ip6tables -I INPUT -j DROP", ignore_status=True)
return True
def resume_internet_with_sl4a_port(ad, sl4a_port):
ad.log.info("Bring internet back using iptables rules")
ad.adb.shell("iptables -D INPUT -p tcp --dport %s -j ACCEPT" % sl4a_port,
ignore_status=True)
ad.adb.shell("iptables -D INPUT -p tcp --sport %s -j ACCEPT" % sl4a_port,
ignore_status=True)
ad.adb.shell("iptables -D INPUT -j DROP", ignore_status=True)
ad.adb.shell("ip6tables -D INPUT -j DROP", ignore_status=True)
return True
def test_data_browsing_success_using_sl4a(log, ad):
result = True
web_page_list = ['https://www.google.com', 'https://www.yahoo.com',
'https://www.amazon.com', 'https://www.nike.com',
'https://www.facebook.com']
for website in web_page_list:
if not verify_http_connection(log, ad, website, retry=0):
ad.log.error("Failed to browse %s successfully!", website)
result = False
return result
def test_data_browsing_failure_using_sl4a(log, ad):
result = True
web_page_list = ['https://www.youtube.com', 'https://www.cnn.com',
'https://www.att.com', 'https://www.nbc.com',
'https://www.verizonwireless.com']
for website in web_page_list:
if not verify_http_connection(log, ad, website, retry=0,
expected_state=False):
ad.log.error("Browsing to %s worked!", website)
result = False
return result
def is_expected_event(event_to_check, events_list):
""" check whether event is present in the event list
Args:
event_to_check: event to be checked.
events_list: list of events
Returns:
result: True if event present in the list. False if not.
"""
for event in events_list:
if event in event_to_check['name']:
return True
return False
def is_sim_ready(log, ad, sim_slot_id=None):
""" check whether SIM is ready.
Args:
ad: android_device object.
sim_slot_id: check the SIM status for sim_slot_id
This is optional. If this is None, check default SIM.
Returns:
result: True if all SIMs are ready. False if not.
"""
if sim_slot_id is None:
status = ad.droid.telephonyGetSimState()
else:
status = ad.droid.telephonyGetSimStateForSlotId(sim_slot_id)
if status != SIM_STATE_READY:
ad.log.info("Sim state is %s, not ready", status)
return False
return True
def is_sim_ready_by_adb(log, ad):
state = ad.adb.getprop("gsm.sim.state")
ad.log.info("gsm.sim.state = %s", state)
return state == SIM_STATE_READY or state == SIM_STATE_LOADED
def wait_for_sim_ready_by_adb(log, ad, wait_time=90):
return _wait_for_droid_in_state(log, ad, wait_time, is_sim_ready_by_adb)
def is_sims_ready_by_adb(log, ad):
states = list(ad.adb.getprop("gsm.sim.state").split(","))
ad.log.info("gsm.sim.state = %s", states)
for state in states:
if state != SIM_STATE_READY and state != SIM_STATE_LOADED:
return False
return True
def wait_for_sims_ready_by_adb(log, ad, wait_time=90):
return _wait_for_droid_in_state(log, ad, wait_time, is_sims_ready_by_adb)
def get_service_state_by_adb(log, ad):
output = ad.adb.shell("dumpsys telephony.registry | grep mServiceState")
if "mVoiceRegState" in output:
result = re.findall(r"mVoiceRegState=(\S+)\((\S+)\)", output)
if result:
if getattr(ad, 'dsds', False):
default_slot = getattr(ad, 'default_slot', 0)
ad.log.info("mVoiceRegState is %s %s", result[default_slot][0],
result[default_slot][1])
return result[default_slot][1]
else:
ad.log.info("mVoiceRegState is %s %s", result[0][0],
result[0][1])
return result[0][1]
else:
result = re.search(r"mServiceState=(\S+)", output)
if result:
ad.log.info("mServiceState=%s %s", result.group(1),
SERVICE_STATE_MAPPING[result.group(1)])
return SERVICE_STATE_MAPPING[result.group(1)]
def _is_expecting_event(event_recv_list):
""" check for more event is expected in event list
Args:
event_recv_list: list of events
Returns:
result: True if more events are expected. False if not.
"""
for state in event_recv_list:
if state is False:
return True
return False
def _set_event_list(event_recv_list, sub_id_list, sub_id, value):
""" set received event in expected event list
Args:
event_recv_list: list of received events
sub_id_list: subscription ID list
sub_id: subscription id of current event
value: True or False
Returns:
None.
"""
for i in range(len(sub_id_list)):
if sub_id_list[i] == sub_id:
event_recv_list[i] = value
def _wait_for_bluetooth_in_state(log, ad, state, max_wait):
# FIXME: These event names should be defined in a common location
_BLUETOOTH_STATE_ON_EVENT = 'BluetoothStateChangedOn'
_BLUETOOTH_STATE_OFF_EVENT = 'BluetoothStateChangedOff'
ad.ed.clear_events(_BLUETOOTH_STATE_ON_EVENT)
ad.ed.clear_events(_BLUETOOTH_STATE_OFF_EVENT)
ad.droid.bluetoothStartListeningForAdapterStateChange()
try:
bt_state = ad.droid.bluetoothCheckState()
if bt_state == state:
return True
if max_wait <= 0:
ad.log.error("Time out: bluetooth state still %s, expecting %s",
bt_state, state)
return False
event = {
False: _BLUETOOTH_STATE_OFF_EVENT,
True: _BLUETOOTH_STATE_ON_EVENT
}[state]
event = ad.ed.pop_event(event, max_wait)
ad.log.info("Got event %s", event['name'])
return True
except Empty:
ad.log.error("Time out: bluetooth state still in %s, expecting %s",
bt_state, state)
return False
finally:
ad.droid.bluetoothStopListeningForAdapterStateChange()
# TODO: replace this with an event-based function
def _wait_for_wifi_in_state(log, ad, state, max_wait):
return _wait_for_droid_in_state(log, ad, max_wait,
lambda log, ad, state: \
(True if ad.droid.wifiCheckState() == state else False),
state)
def toggle_airplane_mode_msim(log, ad, new_state=None, strict_checking=True):
""" Toggle the state of airplane mode.
Args:
log: log handler.
ad: android_device object.
new_state: Airplane mode state to set to.
If None, opposite of the current state.
strict_checking: Whether to turn on strict checking that checks all features.
Returns:
result: True if operation succeed. False if error happens.
"""
cur_state = ad.droid.connectivityCheckAirplaneMode()
if cur_state == new_state:
ad.log.info("Airplane mode already in %s", new_state)
return True
elif new_state is None:
new_state = not cur_state
ad.log.info("Toggle APM mode, from current tate %s to %s", cur_state,
new_state)
sub_id_list = []
active_sub_info = ad.droid.subscriptionGetAllSubInfoList()
if active_sub_info:
for info in active_sub_info:
sub_id_list.append(info['subscriptionId'])
ad.ed.clear_all_events()
time.sleep(0.1)
service_state_list = []
if new_state:
service_state_list.append(SERVICE_STATE_POWER_OFF)
ad.log.info("Turn on airplane mode")
else:
# If either one of these 3 events show up, it should be OK.
# Normal SIM, phone in service
service_state_list.append(SERVICE_STATE_IN_SERVICE)
# NO SIM, or Dead SIM, or no Roaming coverage.
service_state_list.append(SERVICE_STATE_OUT_OF_SERVICE)
service_state_list.append(SERVICE_STATE_EMERGENCY_ONLY)
ad.log.info("Turn off airplane mode")
for sub_id in sub_id_list:
ad.droid.telephonyStartTrackingServiceStateChangeForSubscription(
sub_id)
timeout_time = time.time() + MAX_WAIT_TIME_AIRPLANEMODE_EVENT
ad.droid.connectivityToggleAirplaneMode(new_state)
try:
try:
event = ad.ed.wait_for_event(
EventServiceStateChanged,
is_event_match_for_list,
timeout=MAX_WAIT_TIME_AIRPLANEMODE_EVENT,
field=ServiceStateContainer.SERVICE_STATE,
value_list=service_state_list)
ad.log.info("Got event %s", event)
except Empty:
ad.log.warning("Did not get expected service state change to %s",
service_state_list)
finally:
for sub_id in sub_id_list:
ad.droid.telephonyStopTrackingServiceStateChangeForSubscription(
sub_id)
except Exception as e:
ad.log.error(e)
# APM on (new_state=True) will turn off bluetooth but may not turn it on
try:
if new_state and not _wait_for_bluetooth_in_state(
log, ad, False, timeout_time - time.time()):
ad.log.error(
"Failed waiting for bluetooth during airplane mode toggle")
if strict_checking: return False
except Exception as e:
ad.log.error("Failed to check bluetooth state due to %s", e)
if strict_checking:
raise
# APM on (new_state=True) will turn off wifi but may not turn it on
if new_state and not _wait_for_wifi_in_state(log, ad, False,
timeout_time - time.time()):
ad.log.error("Failed waiting for wifi during airplane mode toggle on")
if strict_checking: return False
if ad.droid.connectivityCheckAirplaneMode() != new_state:
ad.log.error("Set airplane mode to %s failed", new_state)
return False
return True
def wait_for_cbrs_data_active_sub_change_event(
ad,
event_tracking_started=False,
timeout=120):
"""Wait for an data change event on specified subscription.
Args:
ad: android device object.
event_tracking_started: True if event tracking already state outside
timeout: time to wait for event
Returns:
True: if data change event is received.
False: if data change event is not received.
"""
if not event_tracking_started:
ad.ed.clear_events(EventActiveDataSubIdChanged)
ad.droid.telephonyStartTrackingActiveDataChange()
try:
ad.ed.wait_for_event(
EventActiveDataSubIdChanged,
is_event_match,
timeout=timeout)
ad.log.info("Got event activedatasubidchanged")
except Empty:
ad.log.info("No event for data subid change")
return False
finally:
if not event_tracking_started:
ad.droid.telephonyStopTrackingActiveDataChange()
return True
def is_current_data_on_cbrs(ad, cbrs_subid):
"""Verifies if current data sub is on CBRS
Args:
ad: android device object.
cbrs_subid: sub_id against which we need to check
Returns:
True: if data is on cbrs
False: if data is not on cbrs
"""
if cbrs_subid is None:
return False
current_data = ad.droid.subscriptionGetActiveDataSubscriptionId()
ad.log.info("Current Data subid %s cbrs_subid %s", current_data, cbrs_subid)
if current_data == cbrs_subid:
return True
else:
return False
def get_current_override_network_type(ad, timeout=30):
"""Returns current override network type
Args:
ad: android device object.
timeout: max time to wait for event
Returns:
value: current override type
-1: if no event received
"""
override_value_list = [OverrideNetworkContainer.OVERRIDE_NETWORK_TYPE_NR_NSA,
OverrideNetworkContainer.OVERRIDE_NETWORK_TYPE_NONE,
OverrideNetworkContainer.OVERRIDE_NETWORK_TYPE_NR_MMWAVE,
OverrideNetworkContainer.OVERRIDE_NETWORK_TYPE_LTE_CA,
OverrideNetworkContainer.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO]
ad.ed.clear_events(EventDisplayInfoChanged)
ad.droid.telephonyStartTrackingDisplayInfoChange()
try:
event = ad.ed.wait_for_event(
EventDisplayInfoChanged,
is_event_match_for_list,
timeout=timeout,
field=DisplayInfoContainer.OVERRIDE,
value_list=override_value_list)
override_type = event['data']['override']
ad.log.info("Current Override Type is %s", override_type)
return override_type
except Empty:
ad.log.info("No event for display info change")
return -1
finally:
ad.droid.telephonyStopTrackingDisplayInfoChange()
return -1
def _phone_number_remove_prefix(number):
"""Remove the country code and other prefix from the input phone number.
Currently only handle phone number with the following formats:
(US phone number format)
+1abcxxxyyyy
1abcxxxyyyy
abcxxxyyyy
abc xxx yyyy
abc.xxx.yyyy
abc-xxx-yyyy
(EEUK phone number format)
+44abcxxxyyyy
0abcxxxyyyy
Args:
number: input phone number
Returns:
Phone number without country code or prefix
"""
if number is None:
return None, None
for country_code in COUNTRY_CODE_LIST:
if number.startswith(country_code):
return number[len(country_code):], country_code
if number[0] == "1" or number[0] == "0":
return number[1:], None
return number, None
def check_phone_number_match(number1, number2):
"""Check whether two input phone numbers match or not.
Compare the two input phone numbers.
If they match, return True; otherwise, return False.
Currently only handle phone number with the following formats:
(US phone number format)
+1abcxxxyyyy
1abcxxxyyyy
abcxxxyyyy
abc xxx yyyy
abc.xxx.yyyy
abc-xxx-yyyy
(EEUK phone number format)
+44abcxxxyyyy
0abcxxxyyyy
There are some scenarios we can not verify, one example is:
number1 = +15555555555, number2 = 5555555555
(number2 have no country code)
Args:
number1: 1st phone number to be compared.
number2: 2nd phone number to be compared.
Returns:
True if two phone numbers match. Otherwise False.
"""
number1 = phone_number_formatter(number1)
number2 = phone_number_formatter(number2)
# Handle extra country code attachment when matching phone number
if number1[-7:] in number2 or number2[-7:] in number1:
return True
else:
logging.info("phone number1 %s and number2 %s does not match" %
(number1, number2))
return False
def get_call_state_by_adb(ad):
slot_index_of_default_voice_subid = get_slot_index_from_subid(ad,
get_incoming_voice_sub_id(ad))
output = ad.adb.shell("dumpsys telephony.registry | grep mCallState")
if "mCallState" in output:
call_state_list = re.findall("mCallState=(\d)", output)
if call_state_list:
return call_state_list[slot_index_of_default_voice_subid]
def check_call_state_connected_by_adb(ad):
return "2" in get_call_state_by_adb(ad)
def check_call_state_idle_by_adb(ad):
return "0" in get_call_state_by_adb(ad)
def check_call_state_ring_by_adb(ad):
return "1" in get_call_state_by_adb(ad)
def get_incoming_call_number_by_adb(ad):
output = ad.adb.shell(
"dumpsys telephony.registry | grep mCallIncomingNumber")
return re.search(r"mCallIncomingNumber=(.*)", output).group(1)
def dumpsys_all_call_info(ad):
""" Get call information by dumpsys telecom. """
output = ad.adb.shell("dumpsys telecom")
calls = re.findall("Call TC@\d+: {(.*?)}", output, re.DOTALL)
calls_info = []
for call in calls:
call_info = {}
for attr in ("startTime", "endTime", "direction", "isInterrupted",
"callTechnologies", "callTerminationsReason",
"connectionService", "isVideoCall", "callProperties"):
match = re.search(r"%s: (.*)" % attr, call)
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)
call_info["inCallServices"] = re.findall(r"name: (.*)", call)
calls_info.append(call_info)
ad.log.debug("calls_info = %s", calls_info)
return calls_info
def dumpsys_carrier_config(ad):
output = ad.adb.shell("dumpsys carrier_config").split("\n")
output_phone_id_0 = []
output_phone_id_1 = []
current_output = []
for line in output:
if "Phone Id = 0" in line:
current_output = output_phone_id_0
elif "Phone Id = 1" in line:
current_output = output_phone_id_1
current_output.append(line.strip())
configs = {}
if ad.adb.getprop("ro.build.version.release")[0] in ("9", "P"):
phone_count = 1
if "," in ad.adb.getprop("gsm.network.type"):
phone_count = 2
else:
phone_count = ad.droid.telephonyGetPhoneCount()
slot_0_subid = get_subid_from_slot_index(ad.log, ad, 0)
if slot_0_subid != INVALID_SUB_ID:
configs[slot_0_subid] = {}
if phone_count == 2:
slot_1_subid = get_subid_from_slot_index(ad.log, ad, 1)
if slot_1_subid != INVALID_SUB_ID:
configs[slot_1_subid] = {}
attrs = [attr for attr in dir(CarrierConfigs) if not attr.startswith("__")]
for attr in attrs:
attr_string = getattr(CarrierConfigs, attr)
values = re.findall(
r"%s = (\S+)" % attr_string, "\n".join(output_phone_id_0))
if slot_0_subid != INVALID_SUB_ID:
if values:
value = values[-1]
if value == "true":
configs[slot_0_subid][attr_string] = True
elif value == "false":
configs[slot_0_subid][attr_string] = False
elif attr_string == CarrierConfigs.DEFAULT_WFC_IMS_MODE_INT:
if value == "0":
configs[slot_0_subid][attr_string] = WFC_MODE_WIFI_ONLY
elif value == "1":
configs[slot_0_subid][attr_string] = \
WFC_MODE_CELLULAR_PREFERRED
elif value == "2":
configs[slot_0_subid][attr_string] = \
WFC_MODE_WIFI_PREFERRED
else:
try:
configs[slot_0_subid][attr_string] = int(value)
except Exception:
configs[slot_0_subid][attr_string] = value
else:
configs[slot_0_subid][attr_string] = None
if phone_count == 2:
if slot_1_subid != INVALID_SUB_ID:
values = re.findall(
r"%s = (\S+)" % attr_string, "\n".join(output_phone_id_1))
if values:
value = values[-1]
if value == "true":
configs[slot_1_subid][attr_string] = True
elif value == "false":
configs[slot_1_subid][attr_string] = False
elif attr_string == CarrierConfigs.DEFAULT_WFC_IMS_MODE_INT:
if value == "0":
configs[slot_1_subid][attr_string] = \
WFC_MODE_WIFI_ONLY
elif value == "1":
configs[slot_1_subid][attr_string] = \
WFC_MODE_CELLULAR_PREFERRED
elif value == "2":
configs[slot_1_subid][attr_string] = \
WFC_MODE_WIFI_PREFERRED
else:
try:
configs[slot_1_subid][attr_string] = int(value)
except Exception:
configs[slot_1_subid][attr_string] = value
else:
configs[slot_1_subid][attr_string] = None
return configs
def get_phone_capability(ad):
carrier_configs = dumpsys_carrier_config(ad)
for sub_id in carrier_configs:
capabilities = []
if carrier_configs[sub_id][CarrierConfigs.VOLTE_AVAILABLE_BOOL]:
capabilities.append(CAPABILITY_VOLTE)
if carrier_configs[sub_id][CarrierConfigs.WFC_IMS_AVAILABLE_BOOL]:
capabilities.append(CAPABILITY_WFC)
if carrier_configs[sub_id][CarrierConfigs.EDITABLE_WFC_MODE_BOOL]:
capabilities.append(CAPABILITY_WFC_MODE_CHANGE)
if carrier_configs[sub_id][CarrierConfigs.SUPPORT_CONFERENCE_CALL_BOOL]:
capabilities.append(CAPABILITY_CONFERENCE)
if carrier_configs[sub_id][CarrierConfigs.VT_AVAILABLE_BOOL]:
capabilities.append(CAPABILITY_VT)
if carrier_configs[sub_id][CarrierConfigs.VOLTE_PROVISIONED_BOOL]:
capabilities.append(CAPABILITY_VOLTE_PROVISIONING)
if carrier_configs[sub_id][CarrierConfigs.VOLTE_OVERRIDE_WFC_BOOL]:
capabilities.append(CAPABILITY_VOLTE_OVERRIDE_WFC_PROVISIONING)
if carrier_configs[sub_id][CarrierConfigs.HIDE_ENHANCED_4G_LTE_BOOL]:
capabilities.append(CAPABILITY_HIDE_ENHANCED_4G_LTE_BOOL)
ad.log.info("Capabilities of sub ID %s: %s", sub_id, capabilities)
if not getattr(ad, 'telephony', {}):
ad.telephony["subscription"] = {}
ad.telephony["subscription"][sub_id] = {}
setattr(
ad.telephony["subscription"][sub_id],
'capabilities', capabilities)
else:
ad.telephony["subscription"][sub_id]["capabilities"] = capabilities
if CAPABILITY_WFC not in capabilities:
wfc_modes = []
else:
if carrier_configs[sub_id].get(
CarrierConfigs.EDITABLE_WFC_MODE_BOOL, False):
wfc_modes = [
WFC_MODE_CELLULAR_PREFERRED,
WFC_MODE_WIFI_PREFERRED]
else:
wfc_modes = [
carrier_configs[sub_id].get(
CarrierConfigs.DEFAULT_WFC_IMS_MODE_INT,
WFC_MODE_CELLULAR_PREFERRED)
]
if carrier_configs[sub_id].get(
CarrierConfigs.WFC_SUPPORTS_WIFI_ONLY_BOOL,
False) and WFC_MODE_WIFI_ONLY not in wfc_modes:
wfc_modes.append(WFC_MODE_WIFI_ONLY)
ad.telephony["subscription"][sub_id]["wfc_modes"] = wfc_modes
if wfc_modes:
ad.log.info("Supported WFC modes for sub ID %s: %s", sub_id,
wfc_modes)
def get_capability_for_subscription(ad, capability, subid):
if capability in ad.telephony["subscription"][subid].get(
"capabilities", []):
ad.log.info('Capability "%s" is available for sub ID %s.',
capability, subid)
return True
else:
ad.log.info('Capability "%s" is NOT available for sub ID %s.',
capability, subid)
return False
def phone_number_formatter(input_string, formatter=None):
"""Get expected format of input phone number string.
Args:
input_string: (string) input phone number.
The input could be 10/11/12 digital, with or without " "/"-"/"."
formatter: (int) expected format, this could be 7/10/11/12
if formatter is 7: output string would be 7 digital number.
if formatter is 10: output string would be 10 digital (standard) number.
if formatter is 11: output string would be "1" + 10 digital number.
if formatter is 12: output string would be "+1" + 10 digital number.
Returns:
If no error happen, return phone number in expected format.
Else, return None.
"""
if not input_string:
return ""
# make sure input_string is 10 digital
# Remove white spaces, dashes, dots
input_string = input_string.replace(" ", "").replace("-", "").replace(
".", "")
# Remove a country code with '+' sign and add 0 for Japan/Korea Carriers.
if (len(input_string) == 13
and (input_string[0:3] == "+81" or input_string[0:3] == "+82")):
input_string = "0" + input_string[3:]
return input_string
if not formatter:
return input_string
# Remove leading 0 for the phone with area code started with 0
input_string = input_string.lstrip("0")
# Remove "1" or "+1"from front
if (len(input_string) == PHONE_NUMBER_STRING_FORMAT_11_DIGIT
and input_string[0] == "1"):
input_string = input_string[1:]
elif (len(input_string) == PHONE_NUMBER_STRING_FORMAT_12_DIGIT
and input_string[0:2] == "+1"):
input_string = input_string[2:]
elif (len(input_string) == PHONE_NUMBER_STRING_FORMAT_7_DIGIT
and formatter == PHONE_NUMBER_STRING_FORMAT_7_DIGIT):
return input_string
elif len(input_string) != PHONE_NUMBER_STRING_FORMAT_10_DIGIT:
return None
# change input_string according to format
if formatter == PHONE_NUMBER_STRING_FORMAT_12_DIGIT:
input_string = "+1" + input_string
elif formatter == PHONE_NUMBER_STRING_FORMAT_11_DIGIT:
input_string = "1" + input_string
elif formatter == PHONE_NUMBER_STRING_FORMAT_10_DIGIT:
input_string = input_string
elif formatter == PHONE_NUMBER_STRING_FORMAT_7_DIGIT:
input_string = input_string[3:]
else:
return None
return input_string
def get_internet_connection_type(log, ad):
"""Get current active connection type name.
Args:
log: Log object.
ad: Android Device Object.
Returns:
current active connection type name.
"""
if not ad.droid.connectivityNetworkIsConnected():
return 'none'
return connection_type_from_type_string(
ad.droid.connectivityNetworkGetActiveConnectionTypeName())
def verify_http_connection(log,
ad,
url="https://www.google.com",
retry=5,
retry_interval=15,
expected_state=True):
"""Make ping request and return status.
Args:
log: log object
ad: Android Device Object.
url: Optional. The ping request will be made to this URL.
Default Value is "http://www.google.com/".
"""
if not getattr(ad, "data_droid", None):
ad.data_droid, ad.data_ed = ad.get_droid()
ad.data_ed.start()
else:
try:
if not ad.data_droid.is_live:
ad.data_droid, ad.data_ed = ad.get_droid()
ad.data_ed.start()
except Exception:
ad.log.info("Start new sl4a session for file download")
ad.data_droid, ad.data_ed = ad.get_droid()
ad.data_ed.start()
for i in range(0, retry + 1):
try:
http_response = ad.data_droid.httpPing(url)
except Exception as e:
ad.log.info("httpPing with %s", e)
http_response = None
if (expected_state and http_response) or (not expected_state
and not http_response):
ad.log.info("Http ping response for %s meet expected %s", url,
expected_state)
return True
if i < retry:
time.sleep(retry_interval)
ad.log.error("Http ping to %s is %s after %s second, expecting %s", url,
http_response, i * retry_interval, expected_state)
return False
def _generate_file_directory_and_file_name(url, out_path):
file_name = url.split("/")[-1]
if not out_path:
file_directory = "/sdcard/Download/"
elif not out_path.endswith("/"):
file_directory, file_name = os.path.split(out_path)
else:
file_directory = out_path
return file_directory, file_name
def _check_file_existence(ad, file_path, expected_file_size=None):
"""Check file existance by file_path. If expected_file_size
is provided, then also check if the file meet the file size requirement.
"""
out = None
try:
out = ad.adb.shell('stat -c "%%s" %s' % file_path)
except AdbError:
pass
# Handle some old version adb returns error message "No such" into std_out
if out and "No such" not in out:
if expected_file_size:
file_size = int(out)
if file_size >= expected_file_size:
ad.log.info("File %s of size %s exists", file_path, file_size)
return True
else:
ad.log.info("File %s is of size %s, does not meet expected %s",
file_path, file_size, expected_file_size)
return False
else:
ad.log.info("File %s exists", file_path)
return True
else:
ad.log.info("File %s does not exist.", file_path)
return False
def verify_internet_connection_by_ping(log,
ad,
retries=1,
expected_state=True,
timeout=60):
"""Verify internet connection by ping test.
Args:
log: log object
ad: Android Device Object.
"""
begin_time = get_current_epoch_time()
ip_addr = "54.230.144.105"
for dest in ("www.google.com", "www.amazon.com", ip_addr):
for i in range(retries):
ad.log.info("Ping %s - attempt %d", dest, i + 1)
result = adb_shell_ping(
ad, count=5, timeout=timeout, loss_tolerance=40, dest_ip=dest)
if result == expected_state:
ad.log.info(
"Internet connection by pinging to %s is %s as expected",
dest, expected_state)
if dest == ip_addr:
ad.log.warning("Suspect dns failure")
ad.log.info("DNS config: %s",
ad.adb.shell("getprop | grep dns").replace(
"\n", " "))
return False
return True
else:
ad.log.warning(
"Internet connection test by pinging %s is %s, expecting %s",
dest, result, expected_state)
if get_current_epoch_time() - begin_time < timeout * 1000:
time.sleep(5)
ad.log.error("Ping test doesn't meet expected %s", expected_state)
return False
def verify_internet_connection(log, ad, retries=3, expected_state=True):
"""Verify internet connection by ping test and http connection.
Args:
log: log object
ad: Android Device Object.
"""
if ad.droid.connectivityNetworkIsConnected() != expected_state:
ad.log.info("NetworkIsConnected = %s, expecting %s",
not expected_state, expected_state)
if verify_internet_connection_by_ping(
log, ad, retries=retries, expected_state=expected_state):
return True
for url in ("https://www.google.com", "https://www.amazon.com"):
if verify_http_connection(
log, ad, url=url, retry=retries,
expected_state=expected_state):
return True
ad.log.info("DNS config: %s", " ".join(
ad.adb.shell("getprop | grep dns").split()))
ad.log.info("Interface info:\n%s", ad.adb.shell("ifconfig"))
ad.log.info("NetworkAgentInfo: %s",
ad.adb.shell("dumpsys connectivity | grep NetworkAgentInfo"))
return False
def iperf_test_with_options(log,
ad,
iperf_server,
iperf_option,
timeout=180,
rate_dict=None,
blocking=True,
log_file_path=None):
"""iperf adb run helper.
Args:
log: log object
ad: Android Device Object.
iperf_server: The iperf host url".
iperf_option: The options to pass to iperf client
timeout: timeout for file download to complete.
rate_dict: dictionary that can be passed in to save data
blocking: run iperf in blocking mode if True
log_file_path: location to save logs
Returns:
True if iperf runs without throwing an exception
"""
try:
if log_file_path:
ad.adb.shell("rm %s" % log_file_path, ignore_status=True)
ad.log.info("Running adb iperf test with server %s", iperf_server)
ad.log.info("iperf options are %s", iperf_option)
if not blocking:
ad.run_iperf_client_nb(
iperf_server,
iperf_option,
timeout=timeout + 60,
log_file_path=log_file_path)
return True
result, data = ad.run_iperf_client(
iperf_server, iperf_option, timeout=timeout + 120)
ad.log.info("iperf test result with server %s is %s", iperf_server,
result)
if result:
iperf_str = ''.join(data)
iperf_result = ipf.IPerfResult(iperf_str, 'None')
if "-u" in iperf_option:
udp_rate = iperf_result.avg_rate
if udp_rate is None:
ad.log.warning(
"UDP rate is none, IPerf server returned error: %s",
iperf_result.error)
ad.log.info("iperf3 UDP DL speed is %.6s Mbps", (udp_rate/1000000))
else:
tx_rate = iperf_result.avg_send_rate
rx_rate = iperf_result.avg_receive_rate
if (tx_rate or rx_rate) is None:
ad.log.warning(
"A TCP rate is none, iperf server returned error: %s",
iperf_result.error)
ad.log.info(
"iperf3 TCP - UL speed is %.6s Mbps, DL speed is %.6s Mbps",
(tx_rate/1000000), (rx_rate/1000000))
if rate_dict is not None:
rate_dict["Uplink"] = tx_rate
rate_dict["Downlink"] = rx_rate
return result
except AdbError as e:
ad.log.warning("Fail to run iperf test with exception %s", e)
raise
def iperf_udp_test_by_adb(log,
ad,
iperf_server,
port_num=None,
reverse=False,
timeout=180,
limit_rate=None,
pacing_timer=None,
omit=10,
ipv6=False,
rate_dict=None,
blocking=True,
log_file_path=None,
retry=5):
"""Iperf test by adb using UDP.
Args:
log: log object
ad: Android Device Object.
iperf_Server: The iperf host url".
port_num: TCP/UDP server port
reverse: whether to test download instead of upload
timeout: timeout for file download to complete.
limit_rate: iperf bandwidth option. None by default
omit: the omit option provided in iperf command.
ipv6: whether to run the test as ipv6
rate_dict: dictionary that can be passed in to save data
blocking: run iperf in blocking mode if True
log_file_path: location to save logs
retry: times of retry when the server is unavailable
"""
iperf_option = "-u -i 1 -t %s -O %s -J" % (timeout, omit)
if limit_rate:
iperf_option += " -b %s" % limit_rate
if pacing_timer:
iperf_option += " --pacing-timer %s" % pacing_timer
if ipv6:
iperf_option += " -6"
if reverse:
iperf_option += " -R"
for _ in range(retry):
if port_num:
iperf_option_final = iperf_option + " -p %s" % port_num
port_num += 1
else:
iperf_option_final = iperf_option
try:
return iperf_test_with_options(log,
ad,
iperf_server,
iperf_option_final,
timeout,
rate_dict,
blocking,
log_file_path)
except (AdbCommandError, TimeoutError) as error:
continue
except AdbError:
return False
def iperf_test_by_adb(log,
ad,
iperf_server,
port_num=None,
reverse=False,
timeout=180,
limit_rate=None,
omit=10,
ipv6=False,
rate_dict=None,
blocking=True,
log_file_path=None,
retry=5):
"""Iperf test by adb using TCP.
Args:
log: log object
ad: Android Device Object.
iperf_server: The iperf host url".
port_num: TCP/UDP server port
reverse: whether to test download instead of upload
timeout: timeout for file download to complete.
limit_rate: iperf bandwidth option. None by default
omit: the omit option provided in iperf command.
ipv6: whether to run the test as ipv6
rate_dict: dictionary that can be passed in to save data
blocking: run iperf in blocking mode if True
log_file_path: location to save logs
retry: times of retry when the server is unavailable
"""
iperf_option = "-t %s -O %s -J" % (timeout, omit)
if limit_rate:
iperf_option += " -b %s" % limit_rate
if ipv6:
iperf_option += " -6"
if reverse:
iperf_option += " -R"
for _ in range(retry):
if port_num:
iperf_option_final = iperf_option + " -p %s" % port_num
port_num += 1
else:
iperf_option_final = iperf_option
try:
return iperf_test_with_options(log,
ad,
iperf_server,
iperf_option_final,
timeout,
rate_dict=rate_dict,
blocking=blocking,
log_file_path=log_file_path)
except (AdbCommandError, TimeoutError) as error:
continue
except AdbError:
return False
def trigger_modem_crash(ad, timeout=120):
cmd = "echo restart > /sys/kernel/debug/msm_subsys/modem"
ad.log.info("Triggering Modem Crash from kernel using adb command %s", cmd)
ad.adb.shell(cmd)
time.sleep(timeout)
return True
def trigger_modem_crash_by_modem(ad, timeout=120):
begin_time = get_device_epoch_time(ad)
ad.adb.shell(
"setprop persist.vendor.sys.modem.diag.mdlog false",
ignore_status=True)
# Legacy pixels use persist.sys.modem.diag.mdlog.
ad.adb.shell(
"setprop persist.sys.modem.diag.mdlog false", ignore_status=True)
disable_qxdm_logger(ad)
cmd = ('am instrument -w -e request "4b 25 03 00" '
'"com.google.mdstest/com.google.mdstest.instrument.'
'ModemCommandInstrumentation"')
ad.log.info("Crash modem by %s", cmd)
ad.adb.shell(cmd, ignore_status=True)
time.sleep(timeout) # sleep time for sl4a stability
reasons = ad.search_logcat("modem subsystem failure reason", begin_time)
if reasons:
ad.log.info("Modem crash is triggered successfully")
ad.log.info(reasons[-1]["log_message"])
return True
else:
ad.log.warning("There is no modem subsystem failure reason logcat")
return False
def phone_switch_to_msim_mode(ad, retries=3, timeout=60):
result = False
if not ad.is_apk_installed("com.google.mdstest"):
raise signals.TestAbortClass("mdstest is not installed")
mode = ad.droid.telephonyGetPhoneCount()
if mode == 2:
ad.log.info("Device already in MSIM mode")
return True
for i in range(retries):
ad.adb.shell(
"setprop persist.vendor.sys.modem.diag.mdlog false", ignore_status=True)
ad.adb.shell(
"setprop persist.sys.modem.diag.mdlog false", ignore_status=True)
disable_qxdm_logger(ad)
cmd = ('am instrument -w -e request "WriteEFS" -e item '
'"/google/pixel_multisim_config" -e data "02 00 00 00" '
'"com.google.mdstest/com.google.mdstest.instrument.'
'ModemConfigInstrumentation"')
ad.log.info("Switch to MSIM mode by using %s", cmd)
ad.adb.shell(cmd, ignore_status=True)
time.sleep(timeout)
ad.adb.shell("setprop persist.radio.multisim.config dsds")
reboot_device(ad)
# Verify if device is really in msim mode
mode = ad.droid.telephonyGetPhoneCount()
if mode == 2:
ad.log.info("Device correctly switched to MSIM mode")
result = True
if "Sprint" in ad.adb.getprop("gsm.sim.operator.alpha"):
cmd = ('am instrument -w -e request "WriteEFS" -e item '
'"/google/pixel_dsds_imei_mapping_slot_record" -e data "03"'
' "com.google.mdstest/com.google.mdstest.instrument.'
'ModemConfigInstrumentation"')
ad.log.info("Switch Sprint to IMEI1 slot using %s", cmd)
ad.adb.shell(cmd, ignore_status=True)
time.sleep(timeout)
reboot_device(ad)
break
else:
ad.log.warning("Attempt %d - failed to switch to MSIM", (i + 1))
return result
def phone_switch_to_ssim_mode(ad, retries=3, timeout=30):
result = False
if not ad.is_apk_installed("com.google.mdstest"):
raise signals.TestAbortClass("mdstest is not installed")
mode = ad.droid.telephonyGetPhoneCount()
if mode == 1:
ad.log.info("Device already in SSIM mode")
return True
for i in range(retries):
ad.adb.shell(
"setprop persist.vendor.sys.modem.diag.mdlog false", ignore_status=True)
ad.adb.shell(
"setprop persist.sys.modem.diag.mdlog false", ignore_status=True)
disable_qxdm_logger(ad)
cmds = ('am instrument -w -e request "WriteEFS" -e item '
'"/google/pixel_multisim_config" -e data "01 00 00 00" '
'"com.google.mdstest/com.google.mdstest.instrument.'
'ModemConfigInstrumentation"',
'am instrument -w -e request "WriteEFS" -e item "/nv/item_files'
'/modem/uim/uimdrv/uim_extended_slot_mapping_config" -e data '
'"00 01 02 01" "com.google.mdstest/com.google.mdstest.'
'instrument.ModemConfigInstrumentation"')
for cmd in cmds:
ad.log.info("Switch to SSIM mode by using %s", cmd)
ad.adb.shell(cmd, ignore_status=True)
time.sleep(timeout)
ad.adb.shell("setprop persist.radio.multisim.config ssss")
reboot_device(ad)
# Verify if device is really in ssim mode
mode = ad.droid.telephonyGetPhoneCount()
if mode == 1:
ad.log.info("Device correctly switched to SSIM mode")
result = True
break
else:
ad.log.warning("Attempt %d - failed to switch to SSIM", (i + 1))
return result
def lock_lte_band_by_mds(ad, band):
disable_qxdm_logger(ad)
ad.log.info("Write band %s locking to efs file", band)
if band == "4":
item_string = (
"4B 13 26 00 08 00 00 00 40 00 08 00 0B 00 08 00 00 00 00 00 00 00 "
"2F 6E 76 2F 69 74 65 6D 5F 66 69 6C 65 73 2F 6D 6F 64 65 6D 2F 6D "
"6D 6F 64 65 2F 6C 74 65 5F 62 61 6E 64 70 72 65 66 00")
elif band == "13":
item_string = (
"4B 13 26 00 08 00 00 00 40 00 08 00 0A 00 00 10 00 00 00 00 00 00 "
"2F 6E 76 2F 69 74 65 6D 5F 66 69 6C 65 73 2F 6D 6F 64 65 6D 2F 6D "
"6D 6F 64 65 2F 6C 74 65 5F 62 61 6E 64 70 72 65 66 00")
else:
ad.log.error("Band %s is not supported", band)
return False
cmd = ('am instrument -w -e request "%s" com.google.mdstest/com.google.'
'mdstest.instrument.ModemCommandInstrumentation')
for _ in range(3):
if "SUCCESS" in ad.adb.shell(cmd % item_string, ignore_status=True):
break
else:
ad.log.error("Fail to write band by %s" % (cmd % item_string))
return False
# EFS Sync
item_string = "4B 13 30 00 2A 00 2F 00"
for _ in range(3):
if "SUCCESS" in ad.adb.shell(cmd % item_string, ignore_status=True):
break
else:
ad.log.error("Fail to sync efs by %s" % (cmd % item_string))
return False
time.sleep(5)
reboot_device(ad)
def get_cell_data_roaming_state_by_adb(ad):
"""Get Cell Data Roaming state. True for enabled, False for disabled"""
state_mapping = {"1": True, "0": False}
return state_mapping[ad.adb.shell("settings get global data_roaming")]
def set_cell_data_roaming_state_by_adb(ad, state):
"""Set Cell Data Roaming state."""
state_mapping = {True: "1", False: "0"}
ad.log.info("Set data roaming to %s", state)
ad.adb.shell("settings put global data_roaming %s" % state_mapping[state])
def toggle_cell_data_roaming(ad, state):
"""Enable cell data roaming for default data subscription.
Wait for the data roaming status to be DATA_STATE_CONNECTED
or DATA_STATE_DISCONNECTED.
Args:
log: Log object.
ad: Android Device Object.
state: True or False for enable or disable cell data roaming.
Returns:
True if success.
False if failed.
"""
state_int = {True: DATA_ROAMING_ENABLE, False: DATA_ROAMING_DISABLE}[state]
action_str = {True: "Enable", False: "Disable"}[state]
if ad.droid.connectivityCheckDataRoamingMode() == state:
ad.log.info("Data roaming is already in state %s", state)
return True
if not ad.droid.connectivitySetDataRoaming(state_int):
ad.error.info("Fail to config data roaming into state %s", state)
return False
if ad.droid.connectivityCheckDataRoamingMode() == state:
ad.log.info("Data roaming is configured into state %s", state)
return True
else:
ad.log.error("Data roaming is not configured into state %s", state)
return False
def verify_incall_state(log, ads, expected_status):
"""Verify phones in incall state or not.
Verify if all phones in the array <ads> are in <expected_status>.
Args:
log: Log object.
ads: Array of Android Device Object. All droid in this array will be tested.
expected_status: If True, verify all Phones in incall state.
If False, verify all Phones not in incall state.
"""
result = True
for ad in ads:
if ad.droid.telecomIsInCall() is not expected_status:
ad.log.error("InCall status:%s, expected:%s",
ad.droid.telecomIsInCall(), expected_status)
result = False
return result
def verify_active_call_number(log, ad, expected_number):
"""Verify the number of current active call.
Verify if the number of current active call in <ad> is
equal to <expected_number>.
Args:
ad: Android Device Object.
expected_number: Expected active call number.
"""
calls = ad.droid.telecomCallGetCallIds()
if calls is None:
actual_number = 0
else:
actual_number = len(calls)
if actual_number != expected_number:
ad.log.error("Active Call number is %s, expecting", actual_number,
expected_number)
return False
return True
def num_active_calls(log, ad):
"""Get the count of current active calls.
Args:
log: Log object.
ad: Android Device Object.
Returns:
Count of current active calls.
"""
calls = ad.droid.telecomCallGetCallIds()
return len(calls) if calls else 0
def get_carrier_provisioning_for_subscription(ad, feature_flag,
tech, sub_id=None):
""" Gets Provisioning Values for Subscription Id
Args:
ad: Android device object.
sub_id: Subscription Id
feature_flag: voice, video, ut, sms
tech: wlan, wwan
"""
try:
if sub_id is None:
sub_id = ad.droid.subscriptionGetDefaultVoiceSubId()
result = ad.droid.imsMmTelIsSupported(sub_id, feature_flag, tech)
ad.log.info("SubId %s - imsMmTelIsSupported for %s on %s - %s",
sub_id, feature_flag, tech, result)
return result
except Exception as e:
ad.log.error(e)
return False
def _wait_for_droid_in_state(log, ad, max_time, state_check_func, *args,
**kwargs):
while max_time >= 0:
if state_check_func(log, ad, *args, **kwargs):
return True
time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
max_time -= WAIT_TIME_BETWEEN_STATE_CHECK
return False
def _wait_for_droid_in_state_for_subscription(
log, ad, sub_id, max_time, state_check_func, *args, **kwargs):
while max_time >= 0:
if state_check_func(log, ad, sub_id, *args, **kwargs):
return True
time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
max_time -= WAIT_TIME_BETWEEN_STATE_CHECK
return False
def _wait_for_droids_in_state(log, ads, max_time, state_check_func, *args,
**kwargs):
while max_time > 0:
success = True
for ad in ads:
if not state_check_func(log, ad, *args, **kwargs):
success = False
break
if success:
return True
time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
max_time -= WAIT_TIME_BETWEEN_STATE_CHECK
return False
def _is_attached(log, ad, voice_or_data):
return _is_attached_for_subscription(
log, ad, ad.droid.subscriptionGetDefaultSubId(), voice_or_data)
def _is_attached_for_subscription(log, ad, sub_id, voice_or_data):
rat = get_network_rat_for_subscription(log, ad, sub_id, voice_or_data)
ad.log.info("Sub_id %s network RAT is %s for %s", sub_id, rat,
voice_or_data)
return rat != RAT_UNKNOWN
def is_voice_attached(log, ad):
return _is_attached_for_subscription(
log, ad, ad.droid.subscriptionGetDefaultSubId(), NETWORK_SERVICE_VOICE)
def wait_for_data_attach(log, ad, max_time):
"""Wait for android device to attach on data.
Args:
log: log object.
ad: android device.
max_time: maximal wait time.
Returns:
Return True if device attach data within max_time.
Return False if timeout.
"""
return _wait_for_droid_in_state(log, ad, max_time, _is_attached,
NETWORK_SERVICE_DATA)
def wait_for_data_attach_for_subscription(log, ad, sub_id, max_time):
"""Wait for android device to attach on data in subscription id.
Args:
log: log object.
ad: android device.
sub_id: subscription id.
max_time: maximal wait time.
Returns:
Return True if device attach data within max_time.
Return False if timeout.
"""
return _wait_for_droid_in_state_for_subscription(
log, ad, sub_id, max_time, _is_attached_for_subscription,
NETWORK_SERVICE_DATA)
def get_phone_number(log, ad):
"""Get phone number for default subscription
Args:
log: log object.
ad: Android device object.
Returns:
Phone number.
"""
return get_phone_number_for_subscription(log, ad,
get_outgoing_voice_sub_id(ad))
def get_phone_number_for_subscription(log, ad, subid):
"""Get phone number for subscription
Args:
log: log object.
ad: Android device object.
subid: subscription id.
Returns:
Phone number.
"""
number = None
try:
number = ad.telephony['subscription'][subid]['phone_num']
except KeyError:
number = ad.droid.telephonyGetLine1NumberForSubscription(subid)
return number
def set_phone_number(log, ad, phone_num):
"""Set phone number for default subscription
Args:
log: log object.
ad: Android device object.
phone_num: phone number string.
Returns:
True if success.
"""
return set_phone_number_for_subscription(log, ad,
get_outgoing_voice_sub_id(ad),
phone_num)
def set_phone_number_for_subscription(log, ad, subid, phone_num):
"""Set phone number for subscription
Args:
log: log object.
ad: Android device object.
subid: subscription id.
phone_num: phone number string.
Returns:
True if success.
"""
try:
ad.telephony['subscription'][subid]['phone_num'] = phone_num
except Exception:
return False
return True
def get_operator_name(log, ad, subId=None):
"""Get operator name (e.g. vzw, tmo) of droid.
Args:
ad: Android device object.
sub_id: subscription ID
Optional, default is None
Returns:
Operator name.
"""
try:
if subId is not None:
result = operator_name_from_plmn_id(
ad.droid.telephonyGetNetworkOperatorForSubscription(subId))
else:
result = operator_name_from_plmn_id(
ad.droid.telephonyGetNetworkOperator())
except KeyError:
try:
if subId is not None:
result = ad.droid.telephonyGetNetworkOperatorNameForSubscription(
subId)
else:
result = ad.droid.telephonyGetNetworkOperatorName()
result = operator_name_from_network_name(result)
except Exception:
result = CARRIER_UNKNOWN
ad.log.info("Operator Name is %s", result)
return result
def get_model_name(ad):
"""Get android device model name
Args:
ad: Android device object
Returns:
model name string
"""
# TODO: Create translate table.
model = ad.model
if (model.startswith(AOSP_PREFIX)):
model = model[len(AOSP_PREFIX):]
return model
def is_droid_in_rat_family(log, ad, rat_family, voice_or_data=None):
return is_droid_in_rat_family_for_subscription(
log, ad, ad.droid.subscriptionGetDefaultSubId(), rat_family,
voice_or_data)
def is_droid_in_rat_family_for_subscription(log,
ad,
sub_id,
rat_family,
voice_or_data=None):
return is_droid_in_rat_family_list_for_subscription(
log, ad, sub_id, [rat_family], voice_or_data)
def is_droid_in_rat_familiy_list(log, ad, rat_family_list, voice_or_data=None):
return is_droid_in_rat_family_list_for_subscription(
log, ad, ad.droid.subscriptionGetDefaultSubId(), rat_family_list,
voice_or_data)
def is_droid_in_rat_family_list_for_subscription(log,
ad,
sub_id,
rat_family_list,
voice_or_data=None):
service_list = [NETWORK_SERVICE_DATA, NETWORK_SERVICE_VOICE]
if voice_or_data:
service_list = [voice_or_data]
for service in service_list:
nw_rat = get_network_rat_for_subscription(log, ad, sub_id, service)
if nw_rat == RAT_UNKNOWN or not is_valid_rat(nw_rat):
continue
if rat_family_from_rat(nw_rat) in rat_family_list:
return True
return False
def is_droid_in_network_generation(log, ad, nw_gen, voice_or_data):
"""Checks if a droid in expected network generation ("2g", "3g" or "4g").
Args:
log: log object.
ad: android device.
nw_gen: expected generation "4g", "3g", "2g".
voice_or_data: check voice network generation or data network generation
This parameter is optional. If voice_or_data is None, then if
either voice or data in expected generation, function will return True.
Returns:
True if droid in expected network generation. Otherwise False.
"""
return is_droid_in_network_generation_for_subscription(
log, ad, ad.droid.subscriptionGetDefaultSubId(), nw_gen, voice_or_data)
def is_droid_in_network_generation_for_subscription(log, ad, sub_id, nw_gen,
voice_or_data):
"""Checks if a droid in expected network generation ("2g", "3g" or "4g").
Args:
log: log object.
ad: android device.
nw_gen: expected generation "4g", "3g", "2g".
voice_or_data: check voice network generation or data network generation
This parameter is optional. If voice_or_data is None, then if
either voice or data in expected generation, function will return True.
Returns:
True if droid in expected network generation. Otherwise False.
"""
service_list = [NETWORK_SERVICE_DATA, NETWORK_SERVICE_VOICE]
if voice_or_data:
service_list = [voice_or_data]
for service in service_list:
nw_rat = get_network_rat_for_subscription(log, ad, sub_id, service)
ad.log.info("%s network rat is %s", service, nw_rat)
if nw_rat == RAT_UNKNOWN or not is_valid_rat(nw_rat):
continue
if rat_generation_from_rat(nw_rat) == nw_gen:
ad.log.info("%s network rat %s is expected %s", service, nw_rat,
nw_gen)
return True
else:
ad.log.info("%s network rat %s is %s, does not meet expected %s",
service, nw_rat, rat_generation_from_rat(nw_rat),
nw_gen)
return False
return False
def get_network_rat(log, ad, voice_or_data):
"""Get current network type (Voice network type, or data network type)
for default subscription id
Args:
ad: Android Device Object
voice_or_data: Input parameter indicating to get voice network type or
data network type.
Returns:
Current voice/data network type.
"""
return get_network_rat_for_subscription(
log, ad, ad.droid.subscriptionGetDefaultSubId(), voice_or_data)
def get_network_rat_for_subscription(log, ad, sub_id, voice_or_data):
"""Get current network type (Voice network type, or data network type)
for specified subscription id
Args:
ad: Android Device Object
sub_id: subscription ID
voice_or_data: Input parameter indicating to get voice network type or
data network type.
Returns:
Current voice/data network type.
"""
if voice_or_data == NETWORK_SERVICE_VOICE:
ret_val = ad.droid.telephonyGetCurrentVoiceNetworkTypeForSubscription(
sub_id)
elif voice_or_data == NETWORK_SERVICE_DATA:
ret_val = ad.droid.telephonyGetCurrentDataNetworkTypeForSubscription(
sub_id)
else:
ret_val = ad.droid.telephonyGetNetworkTypeForSubscription(sub_id)
if ret_val is None:
log.error("get_network_rat(): Unexpected null return value")
return RAT_UNKNOWN
else:
return ret_val
def get_network_gen(log, ad, voice_or_data):
"""Get current network generation string (Voice network type, or data network type)
Args:
ad: Android Device Object
voice_or_data: Input parameter indicating to get voice network generation
or data network generation.
Returns:
Current voice/data network generation.
"""
return get_network_gen_for_subscription(
log, ad, ad.droid.subscriptionGetDefaultSubId(), voice_or_data)
def get_network_gen_for_subscription(log, ad, sub_id, voice_or_data):
"""Get current network generation string (Voice network type, or data network type)
Args:
ad: Android Device Object
voice_or_data: Input parameter indicating to get voice network generation
or data network generation.
Returns:
Current voice/data network generation.
"""
try:
return rat_generation_from_rat(
get_network_rat_for_subscription(log, ad, sub_id, voice_or_data))
except KeyError as e:
ad.log.error("KeyError %s", e)
return GEN_UNKNOWN
def check_voice_mail_count(log, ad, voice_mail_count_before,
voice_mail_count_after):
"""function to check if voice mail count is correct after leaving a new voice message.
"""
return get_voice_mail_count_check_function(get_operator_name(log, ad))(
voice_mail_count_before, voice_mail_count_after)
def get_voice_mail_number(log, ad):
"""function to get the voice mail number
"""
voice_mail_number = get_voice_mail_check_number(get_operator_name(log, ad))
if voice_mail_number is None:
return get_phone_number(log, ad)
return voice_mail_number
def reset_preferred_network_type_to_allowable_range(log, ad):
"""If preferred network type is not in allowable range, reset to GEN_4G
preferred network type.
Args:
log: log object
ad: android device object
Returns:
None
"""
for sub_id, sub_info in ad.telephony["subscription"].items():
current_preference = \
ad.droid.telephonyGetPreferredNetworkTypesForSubscription(sub_id)
ad.log.debug("sub_id network preference is %s", current_preference)
try:
if current_preference not in get_allowable_network_preference(
sub_info["operator"], sub_info["phone_type"]):
network_preference = network_preference_for_generation(
GEN_4G, sub_info["operator"], sub_info["phone_type"])
ad.droid.telephonySetPreferredNetworkTypesForSubscription(
network_preference, sub_id)
except KeyError:
pass
def set_phone_screen_on(log, ad, screen_on_time=MAX_SCREEN_ON_TIME):
"""Set phone screen on time.
Args:
log: Log object.
ad: Android device object.
screen_on_time: screen on time.
This is optional, default value is MAX_SCREEN_ON_TIME.
Returns:
True if set successfully.
"""
ad.droid.setScreenTimeout(screen_on_time)
return screen_on_time == ad.droid.getScreenTimeout()
def set_phone_silent_mode(log, ad, silent_mode=True):
"""Set phone silent mode.
Args:
log: Log object.
ad: Android device object.
silent_mode: set phone silent or not.
This is optional, default value is True (silent mode on).
Returns:
True if set successfully.
"""
ad.droid.toggleRingerSilentMode(silent_mode)
ad.droid.setMediaVolume(0)
ad.droid.setVoiceCallVolume(0)
ad.droid.setAlarmVolume(0)
ad.adb.ensure_root()
ad.adb.shell("setprop ro.audio.silent 1", ignore_status=True)
ad.adb.shell("cmd notification set_dnd on", ignore_status=True)
return silent_mode == ad.droid.checkRingerSilentMode()
def set_preferred_network_mode_pref(log,
ad,
sub_id,
network_preference,
timeout=WAIT_TIME_ANDROID_STATE_SETTLING):
"""Set Preferred Network Mode for Sub_id
Args:
log: Log object.
ad: Android device object.
sub_id: Subscription ID.
network_preference: Network Mode Type
"""
begin_time = get_device_epoch_time(ad)
if ad.droid.telephonyGetPreferredNetworkTypesForSubscription(
sub_id) == network_preference:
ad.log.info("Current ModePref for Sub %s is in %s", sub_id,
network_preference)
return True
ad.log.info("Setting ModePref to %s for Sub %s", network_preference,
sub_id)
while timeout >= 0:
if ad.droid.telephonySetPreferredNetworkTypesForSubscription(
network_preference, sub_id):
return True
time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
timeout = timeout - WAIT_TIME_BETWEEN_STATE_CHECK
error_msg = "Failed to set sub_id %s PreferredNetworkType to %s" % (
sub_id, network_preference)
search_results = ad.search_logcat(
"REQUEST_SET_PREFERRED_NETWORK_TYPE error", begin_time=begin_time)
if search_results:
log_message = search_results[-1]["log_message"]
if "DEVICE_IN_USE" in log_message:
error_msg = "%s due to DEVICE_IN_USE" % error_msg
else:
error_msg = "%s due to %s" % (error_msg, log_message)
ad.log.error(error_msg)
return False
def set_call_state_listen_level(log, ad, value, sub_id):
"""Set call state listen level for subscription id.
Args:
log: Log object.
ad: Android device object.
value: True or False
sub_id :Subscription ID.
Returns:
True or False
"""
if sub_id == INVALID_SUB_ID:
log.error("Invalid Subscription ID")
return False
ad.droid.telephonyAdjustPreciseCallStateListenLevelForSubscription(
"Foreground", value, sub_id)
ad.droid.telephonyAdjustPreciseCallStateListenLevelForSubscription(
"Ringing", value, sub_id)
ad.droid.telephonyAdjustPreciseCallStateListenLevelForSubscription(
"Background", value, sub_id)
return True
def is_event_match(event, field, value):
"""Return if <field> in "event" match <value> or not.
Args:
event: event to test. This event need to have <field>.
field: field to match.
value: value to match.
Returns:
True if <field> in "event" match <value>.
False otherwise.
"""
return is_event_match_for_list(event, field, [value])
def is_event_match_for_list(event, field, value_list):
"""Return if <field> in "event" match any one of the value
in "value_list" or not.
Args:
event: event to test. This event need to have <field>.
field: field to match.
value_list: a list of value to match.
Returns:
True if <field> in "event" match one of the value in "value_list".
False otherwise.
"""
try:
value_in_event = event['data'][field]
except KeyError:
return False
for value in value_list:
if value_in_event == value:
return True
return False
def is_network_call_back_event_match(event, network_callback_id,
network_callback_event):
try:
return (
(network_callback_id == event['data'][NetworkCallbackContainer.ID])
and (network_callback_event == event['data']
[NetworkCallbackContainer.NETWORK_CALLBACK_EVENT]))
except KeyError:
return False
def is_build_id(log, ad, build_id):
"""Return if ad's build id is the same as input parameter build_id.
Args:
log: log object.
ad: android device object.
build_id: android build id.
Returns:
True if ad's build id is the same as input parameter build_id.
False otherwise.
"""
actual_bid = ad.droid.getBuildID()
ad.log.info("BUILD DISPLAY: %s", ad.droid.getBuildDisplay())
#In case we want to log more stuff/more granularity...
#log.info("{} BUILD ID:{} ".format(ad.serial, ad.droid.getBuildID()))
#log.info("{} BUILD FINGERPRINT: {} "
# .format(ad.serial), ad.droid.getBuildFingerprint())
#log.info("{} BUILD TYPE: {} "
# .format(ad.serial), ad.droid.getBuildType())
#log.info("{} BUILD NUMBER: {} "
# .format(ad.serial), ad.droid.getBuildNumber())
if actual_bid.upper() != build_id.upper():
ad.log.error("%s: Incorrect Build ID", ad.model)
return False
return True
def is_uri_equivalent(uri1, uri2):
"""Check whether two input uris match or not.
Compare Uris.
If Uris are tel URI, it will only take the digit part
and compare as phone number.
Else, it will just do string compare.
Args:
uri1: 1st uri to be compared.
uri2: 2nd uri to be compared.
Returns:
True if two uris match. Otherwise False.
"""
#If either is None/empty we return false
if not uri1 or not uri2:
return False
try:
if uri1.startswith('tel:') and uri2.startswith('tel:'):
uri1_number = get_number_from_tel_uri(uri1)
uri2_number = get_number_from_tel_uri(uri2)
return check_phone_number_match(uri1_number, uri2_number)
else:
return uri1 == uri2
except AttributeError as e:
return False
def get_call_uri(ad, call_id):
"""Get call's uri field.
Get Uri for call_id in ad.
Args:
ad: android device object.
call_id: the call id to get Uri from.
Returns:
call's Uri if call is active and have uri field. None otherwise.
"""
try:
call_detail = ad.droid.telecomCallGetDetails(call_id)
return call_detail["Handle"]["Uri"]
except:
return None
def get_number_from_tel_uri(uri):
"""Get Uri number from tel uri
Args:
uri: input uri
Returns:
If input uri is tel uri, return the number part.
else return None.
"""
if uri.startswith('tel:'):
uri_number = ''.join(
i for i in urllib.parse.unquote(uri) if i.isdigit())
return uri_number
else:
return None
def install_carriersettings_apk(ad, carriersettingsapk, skip_setup_wizard=True):
""" Carrier Setting Installation Steps
Pull sl4a apk from device. Terminate all sl4a sessions,
Reboot the device to bootloader, wipe the device by fastboot.
Reboot the device. wait for device to complete booting
"""
status = True
if carriersettingsapk is None:
ad.log.warning("CarrierSettingsApk is not provided, aborting")
return False
ad.log.info("Push carriersettings apk to the Android device.")
android_apk_path = "/product/priv-app/CarrierSettings/CarrierSettings.apk"
ad.adb.push("%s %s" % (carriersettingsapk, android_apk_path))
ad.stop_services()
attempts = 3
for i in range(1, attempts + 1):
try:
if ad.serial in list_adb_devices():
ad.log.info("Reboot to bootloader")
ad.adb.reboot("bootloader", ignore_status=True)
time.sleep(30)
if ad.serial in list_fastboot_devices():
ad.log.info("Reboot in fastboot")
ad.fastboot.reboot()
ad.wait_for_boot_completion()
ad.root_adb()
if ad.is_sl4a_installed():
break
time.sleep(10)
break
except Exception as e:
ad.log.warning(e)
if i == attempts:
abort_all_tests(log, str(e))
time.sleep(5)
try:
ad.start_adb_logcat()
except:
ad.log.error("Failed to start adb logcat!")
if skip_setup_wizard:
ad.exit_setup_wizard()
return status
def bring_up_sl4a(ad, attemps=3):
for i in range(attemps):
try:
droid, ed = ad.get_droid()
ed.start()
ad.log.info("Brought up new sl4a session")
break
except Exception as e:
if i < attemps - 1:
ad.log.info(e)
time.sleep(10)
else:
ad.log.error(e)
raise
def reboot_device(ad, recover_sim_state=True):
sim_state = is_sim_ready(ad.log, ad)
ad.reboot()
if ad.qxdm_log:
start_qxdm_logger(ad)
ad.unlock_screen()
if recover_sim_state:
if not unlock_sim(ad):
ad.log.error("Unable to unlock SIM")
return False
if sim_state and not _wait_for_droid_in_state(
log, ad, MAX_WAIT_TIME_FOR_STATE_CHANGE, is_sim_ready):
ad.log.error("Sim state didn't reach pre-reboot ready state")
return False
return True
def unlocking_device(ad, device_password=None):
"""First unlock device attempt, required after reboot"""
ad.unlock_screen(device_password)
time.sleep(2)
ad.adb.wait_for_device(timeout=180)
if not ad.is_waiting_for_unlock_pin():
return True
else:
ad.unlock_screen(device_password)
time.sleep(2)
ad.adb.wait_for_device(timeout=180)
if ad.wait_for_window_ready():
return True
ad.log.error("Unable to unlock to user window")
return False
def refresh_sl4a_session(ad):
try:
ad.droid.logI("Checking SL4A connection")
ad.log.debug("Existing sl4a session is active")
return True
except Exception as e:
ad.log.warning("Existing sl4a session is NOT active: %s", e)
try:
ad.terminate_all_sessions()
except Exception as e:
ad.log.info("terminate_all_sessions with error %s", e)
ad.ensure_screen_on()
ad.log.info("Open new sl4a connection")
bring_up_sl4a(ad)
def get_sim_state(ad):
try:
state = ad.droid.telephonyGetSimState()
except Exception as e:
ad.log.error(e)
state = ad.adb.getprop("gsm.sim.state")
return state
def is_sim_locked(ad):
return get_sim_state(ad) == SIM_STATE_PIN_REQUIRED
def is_sim_lock_enabled(ad):
# TODO: add sl4a fascade to check if sim is locked
return getattr(ad, "is_sim_locked", False)
def unlock_sim(ad):
#The puk and pin can be provided in testbed config file.
#"AndroidDevice": [{"serial": "84B5T15A29018214",
# "adb_logcat_param": "-b all",
# "puk": "12345678",
# "puk_pin": "1234"}]
if not is_sim_locked(ad):
return True
else:
ad.is_sim_locked = True
puk_pin = getattr(ad, "puk_pin", "1111")
try:
if not hasattr(ad, 'puk'):
ad.log.info("Enter SIM pin code")
ad.droid.telephonySupplyPin(puk_pin)
else:
ad.log.info("Enter PUK code and pin")
ad.droid.telephonySupplyPuk(ad.puk, puk_pin)
except:
# if sl4a is not available, use adb command
ad.unlock_screen(puk_pin)
if is_sim_locked(ad):
ad.unlock_screen(puk_pin)
time.sleep(30)
return not is_sim_locked(ad)
def send_dialer_secret_code(ad, secret_code):
"""Send dialer secret code.
ad: android device controller
secret_code: the secret code to be sent to dialer. the string between
code prefix *#*# and code postfix #*#*. *#*#<xxx>#*#*
"""
action = 'android.provider.Telephony.SECRET_CODE'
uri = 'android_secret_code://%s' % secret_code
intent = ad.droid.makeIntent(
action,
uri,
None, # type
None, # extras
None, # categories,
None, # packagename,
None, # classname,
0x01000000) # flags
ad.log.info('Issuing dialer secret dialer code: %s', secret_code)
ad.droid.sendBroadcastIntent(intent)
def enable_radio_log_on(ad):
if ad.adb.getprop("persist.vendor.radio.adb_log_on") != "1":
ad.log.info("Enable radio adb_log_on and reboot")
adb_disable_verity(ad)
ad.adb.shell("setprop persist.vendor.radio.adb_log_on 1")
reboot_device(ad)
def adb_disable_verity(ad):
if ad.adb.getprop("ro.boot.veritymode") == "enforcing":
ad.adb.disable_verity()
reboot_device(ad)
ad.adb.remount()
def recover_build_id(ad):
build_fingerprint = ad.adb.getprop(
"ro.vendor.build.fingerprint") or ad.adb.getprop(
"ro.build.fingerprint")
if not build_fingerprint:
return
build_id = build_fingerprint.split("/")[3]
if ad.adb.getprop("ro.build.id") != build_id:
build_id_override(ad, build_id)
def enable_privacy_usage_diagnostics(ad):
try:
ad.ensure_screen_on()
ad.send_keycode('HOME')
# open the UI page on which we need to enable the setting
cmd = ('am start -n com.google.android.gms/com.google.android.gms.'
'usagereporting.settings.UsageReportingActivity')
ad.adb.shell(cmd)
# perform the toggle
ad.send_keycode('TAB')
ad.send_keycode('ENTER')
except Exception:
ad.log.info("Unable to toggle Usage and Diagnostics")
def build_id_override(ad, new_build_id=None, postfix=None):
build_fingerprint = ad.adb.getprop(
"ro.build.fingerprint") or ad.adb.getprop(
"ro.vendor.build.fingerprint")
if build_fingerprint:
build_id = build_fingerprint.split("/")[3]
else:
build_id = None
existing_build_id = ad.adb.getprop("ro.build.id")
if postfix is not None and postfix in build_id:
ad.log.info("Build id already contains %s", postfix)
return
if not new_build_id:
if postfix and build_id:
new_build_id = "%s.%s" % (build_id, postfix)
if not new_build_id or existing_build_id == new_build_id:
return
ad.log.info("Override build id %s with %s", existing_build_id,
new_build_id)
enable_privacy_usage_diagnostics(ad)
adb_disable_verity(ad)
ad.adb.remount()
if "backup.prop" not in ad.adb.shell("ls /sdcard/"):
ad.adb.shell("cp /system/build.prop /sdcard/backup.prop")
ad.adb.shell("cat /system/build.prop | grep -v ro.build.id > /sdcard/test.prop")
ad.adb.shell("echo ro.build.id=%s >> /sdcard/test.prop" % new_build_id)
ad.adb.shell("cp /sdcard/test.prop /system/build.prop")
reboot_device(ad)
ad.log.info("ro.build.id = %s", ad.adb.getprop("ro.build.id"))
def enable_connectivity_metrics(ad):
cmds = [
"pm enable com.android.connectivity.metrics",
"am startservice -a com.google.android.gms.usagereporting.OPTIN_UR",
"am broadcast -a com.google.gservices.intent.action.GSERVICES_OVERRIDE"
" -e usagestats:connectivity_metrics:enable_data_collection 1",
"am broadcast -a com.google.gservices.intent.action.GSERVICES_OVERRIDE"
" -e usagestats:connectivity_metrics:telephony_snapshot_period_millis 180000"
# By default it turn on all modules
#"am broadcast -a com.google.gservices.intent.action.GSERVICES_OVERRIDE"
#" -e usagestats:connectivity_metrics:data_collection_bitmap 62"
]
for cmd in cmds:
ad.adb.shell(cmd, ignore_status=True)
def force_connectivity_metrics_upload(ad):
cmd = "cmd jobscheduler run --force com.android.connectivity.metrics %s"
for job_id in [2, 3, 5, 4, 1, 6]:
ad.adb.shell(cmd % job_id, ignore_status=True)
def system_file_push(ad, src_file_path, dst_file_path):
"""Push system file on a device.
Push system file need to change some system setting and remount.
"""
cmd = "%s %s" % (src_file_path, dst_file_path)
out = ad.adb.push(cmd, timeout=300, ignore_status=True)
skip_sl4a = True if "sl4a.apk" in src_file_path else False
if "Read-only file system" in out:
ad.log.info("Change read-only file system")
adb_disable_verity(ad)
out = ad.adb.push(cmd, timeout=300, ignore_status=True)
if "Read-only file system" in out:
ad.reboot(skip_sl4a)
out = ad.adb.push(cmd, timeout=300, ignore_status=True)
if "error" in out:
ad.log.error("%s failed with %s", cmd, out)
return False
else:
ad.log.info("push %s succeed")
if skip_sl4a: ad.reboot(skip_sl4a)
return True
else:
return True
elif "error" in out:
return False
else:
return True
def set_preferred_apn_by_adb(ad, pref_apn_name):
"""Select Pref APN
Set Preferred APN on UI using content query/insert
It needs apn name as arg, and it will match with plmn id
"""
try:
plmn_id = get_plmn_by_adb(ad)
out = ad.adb.shell("content query --uri content://telephony/carriers "
"--where \"apn='%s' and numeric='%s'\"" %
(pref_apn_name, plmn_id))
if "No result found" in out:
ad.log.warning("Cannot find APN %s on device", pref_apn_name)
return False
else:
apn_id = re.search(r'_id=(\d+)', out).group(1)
ad.log.info("APN ID is %s", apn_id)
ad.adb.shell("content insert --uri content:"
"//telephony/carriers/preferapn --bind apn_id:i:%s" %
(apn_id))
out = ad.adb.shell("content query --uri "
"content://telephony/carriers/preferapn")
if "No result found" in out:
ad.log.error("Failed to set prefer APN %s", pref_apn_name)
return False
elif apn_id == re.search(r'_id=(\d+)', out).group(1):
ad.log.info("Preferred APN set to %s", pref_apn_name)
return True
except Exception as e:
ad.log.error("Exception while setting pref apn %s", e)
return True
def check_apm_mode_on_by_serial(ad, serial_id):
try:
apm_check_cmd = "|".join(("adb -s %s shell dumpsys wifi" % serial_id,
"grep -i airplanemodeon", "cut -f2 -d ' '"))
output = exe_cmd(apm_check_cmd)
if output.decode("utf-8").split("\n")[0] == "true":
return True
else:
return False
except Exception as e:
ad.log.warning("Exception during check apm mode on %s", e)
return True
def set_apm_mode_on_by_serial(ad, serial_id):
try:
cmd1 = "adb -s %s shell settings put global airplane_mode_on 1" % serial_id
cmd2 = "adb -s %s shell am broadcast -a android.intent.action.AIRPLANE_MODE" % serial_id
exe_cmd(cmd1)
exe_cmd(cmd2)
except Exception as e:
ad.log.warning("Exception during set apm mode on %s", e)
return True
def print_radio_info(ad, extra_msg=""):
for prop in ("gsm.version.baseband", "persist.radio.ver_info",
"persist.radio.cnv.ver_info"):
output = ad.adb.getprop(prop)
ad.log.info("%s%s = %s", extra_msg, prop, output)
def wait_for_state(state_check_func,
state,
max_wait_time=MAX_WAIT_TIME_FOR_STATE_CHANGE,
checking_interval=WAIT_TIME_BETWEEN_STATE_CHECK,
*args,
**kwargs):
while max_wait_time >= 0:
if state_check_func(*args, **kwargs) == state:
return True
time.sleep(checking_interval)
max_wait_time -= checking_interval
return False
def power_off_sim_by_adb(ad, sim_slot_id,
timeout=MAX_WAIT_TIME_FOR_STATE_CHANGE):
"""Disable pSIM/eSIM SUB by adb command.
Args:
ad: android device object.
sim_slot_id: slot 0 or slot 1.
timeout: wait time for state change.
Returns:
True if success, False otherwise.
"""
release_version = int(ad.adb.getprop("ro.build.version.release"))
if sim_slot_id == 0 and release_version < 12:
ad.log.error(
"The disable pSIM SUB command only support for Android S or higher "
"version, abort test.")
raise signals.TestSkip(
"The disable pSIM SUB command only support for Android S or higher "
"version, abort test.")
try:
if sim_slot_id:
ad.adb.shell("am broadcast -a android.telephony.euicc.action."
"TEST_PROFILE -n com.google.android.euicc/com.android.euicc."
"receiver.ProfileTestReceiver --es 'operation' 'switch' --ei "
"'subscriptionId' -1")
else:
sub_id = get_subid_by_adb(ad, sim_slot_id)
# The command only support for Android S. (b/159605922)
ad.adb.shell(
"cmd phone disable-physical-subscription %d" % sub_id)
except Exception as e:
ad.log.error(e)
return False
while timeout > 0:
if get_subid_by_adb(ad, sim_slot_id) == INVALID_SUB_ID:
return True
timeout = timeout - WAIT_TIME_BETWEEN_STATE_CHECK
time.sleep(WAIT_TIME_BETWEEN_STATE_CHECK)
sim_state = ad.adb.getprop("gsm.sim.state").split(",")
ad.log.warning("Fail to power off SIM slot %d, sim_state=%s",
sim_slot_id, sim_state[sim_slot_id])
return False
def power_on_sim_by_adb(ad, sim_slot_id,
timeout=MAX_WAIT_TIME_FOR_STATE_CHANGE):
"""Enable pSIM/eSIM SUB by adb command.
Args:
ad: android device object.
sim_slot_id: slot 0 or slot 1.
timeout: wait time for state change.
Returns:
True if success, False otherwise.
"""
release_version = int(ad.adb.getprop("ro.build.version.release"))
if sim_slot_id == 0 and release_version < 12:
ad.log.error(
"The enable pSIM SUB command only support for Android S or higher "
"version, abort test.")
raise signals.TestSkip(
"The enable pSIM SUB command only support for Android S or higher "
"version, abort test.")
try:
output = ad.adb.shell(
"dumpsys isub | grep addSubInfoRecord | grep slotIndex=%d" %
sim_slot_id)
pattern = re.compile(r"subId=(\d+)")
sub_id = pattern.findall(output)
sub_id = int(sub_id[-1]) if sub_id else INVALID_SUB_ID
if sim_slot_id:
ad.adb.shell("am broadcast -a android.telephony.euicc.action."
"TEST_PROFILE -n com.google.android.euicc/com.android.euicc."
"receiver.ProfileTestReceiver --es 'operation' 'switch' --ei "
"'subscriptionId' %d" % sub_id)
else:
# The command only support for Android S or higher. (b/159605922)
ad.adb.shell(
"cmd phone enable-physical-subscription %d" % sub_id)
except Exception as e:
ad.log.error(e)