blob: 5acd6d95553cc754c4114928c9740fffac6509bb [file]
#!/usr/bin/env python3
#
# Copyright 2021 - The Android Open Source Project
#
# 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 dataclasses
import re
import time
from typing import List
from acts import asserts
from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_PIN_REQUIRED
from acts_contrib.test_utils.tel.tel_defines import SIM_STATE_PUK_REQUIRED
from acts_contrib.test_utils.tel.tel_test_utils import is_sim_ready
from acts_contrib.test_utils.tel.tel_test_utils import power_off_sim
from acts_contrib.test_utils.tel.tel_test_utils import power_on_sim
AT_COMMAND_INSTRUMENTATION = 'com.google.mdstest/com.google.mdstest.instrument.ModemATCommandInstrumentation'
AT_COMMAND_FAILURE = 'INSTRUMENTATION_RESULT: result=FAILURE'
LAB_PSIM_ADM_PW = '3131313131313131'
LAB_ESIM_ADM_PW = '35363738FFFFFFFF'
LAB_SIM_DEFAULT_PIN1 = '1234'
LAB_SIM_DEFAULT_PUK1 = '12345678'
UI_ELEMENT_TEXT_SECURITY_SIM_CARD_LOCK = 'SIM card lock'
UI_ELEMENT_TEXT_LOCK_SIM_SET = 'Lock SIM card'
UI_ELEMENT_TEXT_OK = 'OK'
SHORT_MNC_LENGTH = 2
@dataclasses.dataclass
class SimInfo:
sub_id: int
slot_index: int
imsi: str
mcc_mnc: str
msin: str
display_name: str
def get_sim_info(ad) -> List[SimInfo]:
"""Get Lab SIM subscription information.
Args:
ad: Android device obj.
Returns:
List[SimInfo]: A list of sim information dataclass
"""
sim_info = []
sub_info_list = ad.droid.subscriptionGetActiveSubInfoList()
if not sub_info_list:
asserts.skip('No Valid SIM in device')
for sub_info in sub_info_list:
sub_id = sub_info['subscriptionId']
imsi = get_sim_imsi(ad, sub_id)
mcc_mnc = get_sim_mcc_mnc(ad, sub_id)
msin = get_sim_msin(imsi, mcc_mnc)
sim_info.append(
SimInfo(
sub_id=sub_id,
slot_index=sub_info['simSlotIndex'],
imsi=imsi,
mcc_mnc=mcc_mnc,
msin=msin,
display_name=sub_info['displayName']))
ad.log.info(sim_info)
return sim_info
def get_sim_msin(imsi, mcc_mnc):
"""Split IMSI to get msin value."""
msin = imsi.split(mcc_mnc)[1]
return msin
def get_sim_mcc_mnc(ad, sub_id):
"""Get SIM MCC+MNC value by sub id."""
return ad.droid.telephonyGetSimOperatorForSubscription(sub_id)
def get_sim_imsi(ad, sub_id):
"""Get SIM IMSI value by sub id."""
return ad.droid.telephonyGetSubscriberIdForSubscription(sub_id)
def unlock_sim_dsds(ad,
dsds=False,
pin=LAB_SIM_DEFAULT_PIN1,
puk=LAB_SIM_DEFAULT_PUK1) -> bool:
"""Unlock SIM pin1/puk1 on single or dual sim mode.
Args:
ad: Android device obj.
dsds: True is dual sim mode, use adb command to unlock.
pin: pin1 code, use LAB_DEFAULT_PIN1 for default value.
puk: puk1 code, use LAB_DEFAULT_PUK1 for default value.
Returns:
True if unlock sim success. False otherwise.
"""
ad.unlock_screen()
ad.log.info('[Dual_sim=%s] Unlock SIM', dsds)
if not dsds:
if is_sim_pin_locked(ad):
ad.log.info('Unlock SIM pin')
ad.droid.telephonySupplyPin(pin)
elif is_sim_puk_locked(ad):
ad.log.info('Unlock SIM puk')
ad.droid.telephonySupplyPuk(puk, pin)
time.sleep(1)
return is_sim_ready(ad.log, ad)
else:
# Checks both pSIM and eSIM states.
for slot_index in range(2):
if is_sim_pin_locked(ad, slot_index):
ad.log.info('[Slot index=%s] Unlock SIM PIN', slot_index)
if not unlock_pin_by_mds(ad, slot_index, pin):
ad.log.info('[Slot index=%s] AT+CPIN unlock error', slot_index)
elif is_sim_puk_locked(ad, slot_index):
ad.log.info('[Slot index=%s] Unlock SIM PUK', slot_index)
unlock_puk_by_adb(ad, pin, puk)
time.sleep(1)
if not is_sim_ready(ad.log, ad, slot_index):
return False
return True
def unlock_puk_by_mds(ad, slot_index, pin, puk) -> bool:
"""Runs AT command to disable SIM PUK1 locked.
Args:
ad: Android device obj.
slot_index: sim slot id.
pin: pin1 code.
puk: puk1 code.
Returns:
True if response 'OK'. False otherwise.
"""
set_at_command_channel(ad, slot_index)
command = r'AT+CPIN=\"' + puk + r'\",\"' + pin + r'\"'
cmd = (f'am instrument -w -e request "{command}" '
f'-e response wait {AT_COMMAND_INSTRUMENTATION}')
ad.log.info('Unlock sim pin by AT command')
output = ad.adb.shell(cmd)
if grep(AT_COMMAND_FAILURE, output):
asserts.skip('Failed to run MDS test command')
if grep('OK', output):
return True
else:
return False
def unlock_pin_by_mds(ad, slot_index, pin) -> bool:
"""Runs AT command to disable SIM PIN1 locked.
Args:
ad: Android device obj.
slot_index: sim slot id.
pin: pin1 code, use LAB_DEFAULT_PIN1 for default value.
Returns:
True if response 'OK'. False otherwise.
"""
set_at_command_channel(ad, slot_index)
command = r'AT+CPIN=\"' + pin + r'\"'
cmd = (f'am instrument -w -e request "{command}" '
f'-e response wait {AT_COMMAND_INSTRUMENTATION}')
ad.log.info('Unlock sim pin by AT command')
output = ad.adb.shell(cmd)
if grep(AT_COMMAND_FAILURE, output):
asserts.skip('Failed to run MDS test command')
if grep('OK', output):
return True
else:
return False
def unlock_puk_by_adb(ad, pin, puk) -> None:
"""Unlock puk1 by adb keycode.
Args:
ad: Android device obj.
pin: pin1 code.
puk: puk1 code.
"""
for key_code in puk:
ad.send_keycode(key_code)
time.sleep(1)
ad.send_keycode('ENTER')
time.sleep(1)
# PIN required 2 times
for _ in range(2):
for key_code in pin:
ad.send_keycode(key_code)
time.sleep(1)
ad.send_keycode('ENTER')
time.sleep(1)
def lock_puk_by_mds(ad, slot_index) -> bool:
"""Inputs wrong PIN1 code 3 times to make PUK1 locked.
Args:
ad: Android device obj.
slot_index: Sim slot id.
Returns:
True if SIM puk1 locked. False otherwise.
"""
ad.unlock_screen()
wrong_pin = '1111'
for count in range(3):
if not unlock_pin_by_mds(ad, slot_index, wrong_pin):
ad.log.info('Error input pin:%d', count+1)
time.sleep(1)
ad.reboot()
return is_sim_puk_locked(ad, slot_index)
def is_sim_puk_locked(ad, slot_index=None) -> bool:
"""Checks whether SIM puk1 is locked on single or dual sim mode.
Args:
ad: Android device obj.
slot_index: Check the SIM status for slot_index.
This is optional. If this is None, check default SIM.
Returns:
True if SIM puk1 locked. False otherwise.
"""
if slot_index is None:
status = ad.droid.telephonyGetSimState()
else:
status = ad.droid.telephonyGetSimStateForSlotId(slot_index)
if status != SIM_STATE_PUK_REQUIRED:
ad.log.info('Sim state is %s', status)
return False
return True
def is_sim_pin_locked(ad, slot_index=None) -> bool:
"""Checks whether SIM pin is locked on single or dual sim mode.
Args:
ad: Android device obj.
slot_index: Check the SIM status for slot_index. This is optional. If this
is None, check default SIM.
Returns:
True if SIM pin1 locked. False otherwise.
"""
if slot_index is None:
status = ad.droid.telephonyGetSimState()
else:
status = ad.droid.telephonyGetSimStateForSlotId(slot_index)
if status != SIM_STATE_PIN_REQUIRED:
ad.log.info('Sim state is %s', status)
return False
return True
def set_at_command_channel(ad, slot_index: int) -> bool:
"""Runs AT command to set AT command channel by MDS tool(pSIM=1,eSIM=2).
Args:
ad: Android device obj.
slot_index: Sim slot id.
Returns:
True if response 'OK'. False otherwise.
"""
channel = slot_index + 1
command = f'AT+CSUS={channel}'
cmd = (f'am instrument -w -e request "{command}" '
f'-e response wait {AT_COMMAND_INSTRUMENTATION}')
ad.log.info('Set AT command channel')
output = ad.adb.shell(cmd)
if grep(AT_COMMAND_FAILURE, output):
asserts.skip('Failed to run MDS test command')
if grep('OK', output):
return True
else:
return False
def sim_enable_pin_by_mds(ad, pin) -> bool:
"""Runs AT command to enable SIM PIN1 locked by MDS tool.
Args:
ad: Android device obj.
pin: PIN1 code.
Returns:
True if response 'OK'. False otherwise.
"""
command = r'AT+CLCK=\"SC\",1,\"' + pin + r'\"'
cmd = (f'am instrument -w -e request "{command}" '
f'-e response wait {AT_COMMAND_INSTRUMENTATION}')
ad.log.info('Enable sim pin by AT command')
output = ad.adb.shell(cmd)
if grep(AT_COMMAND_FAILURE, output):
asserts.skip('Failed to run MDS test command')
if grep('OK', output):
return True
else:
return False
def sim_disable_pin_by_mds(ad, pin) -> bool:
"""Runs AT command to disable SIM PIN1 locked by MDS tool.
Args:
ad: Android device obj.
pin: PIN1 code.
Returns:
True if response 'OK'. False otherwise.
"""
command = r'AT+CLCK=\"SC\",0,\"' + pin + r'\"'
cmd = (f'am instrument -w -e request "{command}" '
f'-e response wait {AT_COMMAND_INSTRUMENTATION}')
ad.log.info('Disable sim pin by AT command')
output = ad.adb.shell(cmd)
if grep(AT_COMMAND_FAILURE, output):
asserts.skip('Failed to run MDS test command')
if grep('OK', output):
return True
else:
return False
def set_sim_lock(ad, enable, slot_index, pin=LAB_SIM_DEFAULT_PIN1) -> bool:
"""Enable/disable SIM card lock.
Args:
ad: Android device obj.
enable: True is to enable sim lock. False is to disable.
slot_index: Sim slot id.
pin: Pin1 code.
Returns:
True if enable/disable SIM lock successfully.False otherwise.
"""
if enable:
ad.log.info('[Slot:%d]Enable SIM pin1 locked by mds', slot_index)
if not set_at_command_channel(ad, slot_index):
ad.log.info('[Slot:%d] set AT command on MDS tool not OK', slot_index)
if sim_enable_pin_by_mds(ad, pin):
ad.reboot()
return is_sim_pin_locked(ad, slot_index)
else:
ad.log.info('[Slot:%d]Disable SIM pin1 locked by mds', slot_index)
if not set_at_command_channel(ad, slot_index):
ad.log.info('[Slot:%d] set AT command on MDS tool not OK', slot_index)
if sim_disable_pin_by_mds(ad, pin):
ad.reboot()
return is_sim_ready(ad.log, ad, slot_index)
def activate_sim(ad,
slot_index=None,
dsds=False,
pin=LAB_SIM_DEFAULT_PIN1,
puk=LAB_SIM_DEFAULT_PUK1) -> bool:
"""Activate sim state with slot id. Check sim lock state after activating.
Args:
ad: Android_device obj.
slot_index: Sim slot id.
dsds: True is dual sim mode, False is single mode.
pin: pin1 code, use LAB_DEFAULT_PIN1 for default value.
puk: puk1 code, use LAB_DEFAULT_PUK1 for default value.
Returns:
True if activate SIM lock successfully.False otherwise.
"""
ad.log.info('Disable SIM slot')
if not power_off_sim(ad, slot_index):
return False
time.sleep(2)
ad.log.info('Enable SIM slot')
if not power_on_sim(ad, slot_index):
return False
unlock_sim_dsds(ad, dsds, pin, puk)
return True
def grep(regex, output):
"""Returns the line in an output stream that matches a given regex pattern."""
lines = output.strip().splitlines()
results = []
for line in lines:
if re.search(regex, line):
results.append(line.strip())
return results
def modify_sim_imsi(ad,
new_imsi,
sim_info,
sim_adm=LAB_PSIM_ADM_PW):
"""Uses ADB Content Provider Command to Read/Update EF (go/pmw-see-adb).
Args:
ad: Android_device obj.
new_imsi: New IMSI string to be set.
sim_info: SimInfo dataclass log.
sim_adm: SIM slot adm password.
"""
cmd = (f"content update --uri content://com.google.android.wsdsimeditor.EF/EFIMSI "
f"--bind data:s:'{new_imsi}' --bind format:s:'raw' "
f"--bind adm:s:'{sim_adm}' --where slot={sim_info.slot_index}")
ad.log.info('Update IMSI cmd = %s', cmd)
ad.adb.shell(cmd)
time.sleep(5)
modified_imsi = get_sim_imsi(ad, sim_info.sub_id)
asserts.assert_equal(new_imsi, modified_imsi)