blob: 1fb175292dd4176adaacc9c6585a2f2487545ab5 [file] [log] [blame]
/*
* Copyright (C) 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.
*/
package android.telephony.cts;
import static android.telephony.PreciseDisconnectCause.NO_DISCONNECT_CAUSE_AVAILABLE;
import static android.telephony.PreciseDisconnectCause.TEMPORARY_FAILURE;
import static android.telephony.mockmodem.MockSimService.MOCK_SIM_PROFILE_ID_TWN_CHT;
import static android.telephony.mockmodem.MockSimService.MOCK_SIM_PROFILE_ID_TWN_FET;
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
import static com.android.internal.telephony.RILConstants.RIL_REQUEST_RADIO_POWER;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeNoException;
import static org.junit.Assume.assumeTrue;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.hardware.radio.network.Domain;
import android.hardware.radio.sim.Carrier;
import android.hardware.radio.sim.CarrierRestrictions;
import android.hardware.radio.voice.LastCallFailCause;
import android.hardware.radio.voice.UusInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemProperties;
import android.telecom.PhoneAccount;
import android.telecom.TelecomManager;
import android.telephony.AccessNetworkConstants;
import android.telephony.CarrierRestrictionRules;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.telephony.cts.util.TelephonyUtils;
import android.telephony.mockmodem.MockCallControlInfo;
import android.telephony.mockmodem.MockModemConfigInterface;
import android.telephony.mockmodem.MockModemManager;
import android.util.Log;
import androidx.annotation.RequiresApi;
import androidx.test.InstrumentationRegistry;
import com.android.compatibility.common.util.ShellIdentityUtils;
import com.android.internal.telephony.uicc.IccUtils;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier;
/** Test MockModemService interfaces. */
public class TelephonyManagerTestOnMockModem {
private static final String TAG = "TelephonyManagerTestOnMockModem";
private static final long WAIT_TIME_MS = 20000;
private static MockModemManager sMockModemManager;
private static TelephonyManager sTelephonyManager;
private static final String ALLOW_MOCK_MODEM_PROPERTY = "persist.radio.allow_mock_modem";
private static final String BOOT_ALLOW_MOCK_MODEM_PROPERTY = "ro.boot.radio.allow_mock_modem";
private static final boolean DEBUG = !"user".equals(Build.TYPE);
private static final String RESOURCE_PACKAGE_NAME = "android";
private static boolean sIsMultiSimDevice;
private static HandlerThread sServiceStateChangeCallbackHandlerThread;
private static Handler sServiceStateChangeCallbackHandler;
private static HandlerThread sCallDisconnectCauseCallbackHandlerThread;
private static Handler sCallDisconnectCauseCallbackHandler;
private static HandlerThread sCallStateChangeCallbackHandlerThread;
private static Handler sCallStateChangeCallbackHandler;
private static ServiceStateListener sServiceStateCallback;
private static CallDisconnectCauseListener sCallDisconnectCauseCallback;
private static CallStateListener sCallStateCallback;
private int mServiceState;
private int mExpectedRegState;
private int mPreciseCallDisconnectCause;
private int mCallState;
private final Object mServiceStateChangeLock = new Object();
private final Object mCallDisconnectCauseLock = new Object();
private final Object mCallStateChangeLock = new Object();
private final Executor mServiceStateChangeExecutor = Runnable::run;
private final Executor mCallDisconnectCauseExecutor = Runnable::run;
private final Executor mCallStateChangeExecutor = Runnable::run;
private boolean mResetCarrierStatusInfo;
private static String mShaId;
private static final int TIMEOUT_IN_SEC_FOR_MODEM_CB = 10;
@BeforeClass
public static void beforeAllTests() throws Exception {
Log.d(TAG, "TelephonyManagerTestOnMockModem#beforeAllTests()");
if (!hasTelephonyFeature()) {
return;
}
enforceMockModemDeveloperSetting();
sTelephonyManager =
(TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
sIsMultiSimDevice = isMultiSim(sTelephonyManager);
sMockModemManager = new MockModemManager();
assertNotNull(sMockModemManager);
assertTrue(sMockModemManager.connectMockModemService());
sServiceStateChangeCallbackHandlerThread =
new HandlerThread("TelephonyManagerServiceStateChangeCallbackTest");
sServiceStateChangeCallbackHandlerThread.start();
sServiceStateChangeCallbackHandler =
new Handler(sServiceStateChangeCallbackHandlerThread.getLooper());
sCallDisconnectCauseCallbackHandlerThread =
new HandlerThread("TelephonyManagerCallDisconnectCauseCallbackTest");
sCallDisconnectCauseCallbackHandlerThread.start();
sCallDisconnectCauseCallbackHandler =
new Handler(sCallDisconnectCauseCallbackHandlerThread.getLooper());
sCallStateChangeCallbackHandlerThread =
new HandlerThread("TelephonyManagerCallStateChangeCallbackTest");
sCallStateChangeCallbackHandlerThread.start();
sCallStateChangeCallbackHandler =
new Handler(sCallStateChangeCallbackHandlerThread.getLooper());
mShaId = getShaId(TelephonyUtils.CTS_APP_PACKAGE);
final PackageManager pm = getContext().getPackageManager();
if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
//Wait for Telephony FW and RIL initialization are done
TimeUnit.SECONDS.sleep(4);
}
}
@AfterClass
public static void afterAllTests() throws Exception {
Log.d(TAG, "TelephonyManagerTestOnMockModem#afterAllTests()");
if (!hasTelephonyFeature()) {
return;
}
if (sServiceStateChangeCallbackHandlerThread != null) {
sServiceStateChangeCallbackHandlerThread.quitSafely();
sServiceStateChangeCallbackHandlerThread = null;
}
if (sCallDisconnectCauseCallbackHandlerThread != null) {
sCallDisconnectCauseCallbackHandlerThread.quitSafely();
sCallDisconnectCauseCallbackHandlerThread = null;
}
if (sCallStateChangeCallbackHandlerThread != null) {
sCallStateChangeCallbackHandlerThread.quitSafely();
sCallStateChangeCallbackHandlerThread = null;
}
if (sServiceStateCallback != null) {
sTelephonyManager.unregisterTelephonyCallback(sServiceStateCallback);
sServiceStateCallback = null;
}
if (sCallDisconnectCauseCallback != null) {
sTelephonyManager.unregisterTelephonyCallback(sCallDisconnectCauseCallback);
sCallDisconnectCauseCallback = null;
}
if (sCallStateCallback != null) {
sTelephonyManager.unregisterTelephonyCallback(sCallStateCallback);
sCallStateCallback = null;
}
// Rebind all interfaces which is binding to MockModemService to default.
assertNotNull(sMockModemManager);
// Reset the modified error response of RIL_REQUEST_RADIO_POWER to the original behavior
// and -1 means to disable the modifed mechanism in mock modem
sMockModemManager.forceErrorResponse(0, RIL_REQUEST_RADIO_POWER, -1);
assertTrue(sMockModemManager.disconnectMockModemService());
sMockModemManager = null;
mShaId = null;
}
@Before
public void beforeTest() {
assumeTrue(hasTelephonyFeature());
try {
sTelephonyManager.getHalVersion(TelephonyManager.HAL_SERVICE_RADIO);
} catch (IllegalStateException e) {
assumeNoException("Skipping tests because Telephony service is null", e);
}
}
@After
public void afterTest() {
if (mResetCarrierStatusInfo) {
try {
TelephonyUtils.resetCarrierRestrictionStatusAllowList(
androidx.test.platform.app.InstrumentationRegistry.getInstrumentation());
} catch (Exception e) {
e.printStackTrace();
} finally {
mResetCarrierStatusInfo = false;
}
}
}
private static Context getContext() {
return InstrumentationRegistry.getInstrumentation().getContext();
}
private static String getShaId(String packageName) {
try {
final PackageManager packageManager = getContext().getPackageManager();
MessageDigest sha1MDigest = MessageDigest.getInstance("SHA1");
final PackageInfo packageInfo = packageManager.getPackageInfo(packageName,
PackageManager.GET_SIGNATURES);
for (Signature signature : packageInfo.signatures) {
final byte[] signatureSha1 = sha1MDigest.digest(signature.toByteArray());
return IccUtils.bytesToHexString(signatureSha1);
}
} catch (NoSuchAlgorithmException | PackageManager.NameNotFoundException ex) {
ex.printStackTrace();
}
return null;
}
private static boolean hasTelephonyFeature() {
final PackageManager pm = getContext().getPackageManager();
if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
Log.d(TAG, "Skipping test that requires FEATURE_TELEPHONY");
return false;
}
return true;
}
private static boolean isMultiSim(TelephonyManager tm) {
return tm != null && tm.getPhoneCount() > 1;
}
private static boolean isSimHotSwapCapable() {
boolean isSimHotSwapCapable = false;
int resourceId =
getContext()
.getResources()
.getIdentifier("config_hotswapCapable", "bool", RESOURCE_PACKAGE_NAME);
if (resourceId > 0) {
isSimHotSwapCapable = getContext().getResources().getBoolean(resourceId);
} else {
Log.d(TAG, "Fail to get the resource Id, using default.");
}
Log.d(TAG, "isSimHotSwapCapable = " + (isSimHotSwapCapable ? "true" : "false"));
return isSimHotSwapCapable;
}
private static void enforceMockModemDeveloperSetting() throws Exception {
boolean isAllowed = SystemProperties.getBoolean(ALLOW_MOCK_MODEM_PROPERTY, false);
boolean isAllowedForBoot =
SystemProperties.getBoolean(BOOT_ALLOW_MOCK_MODEM_PROPERTY, false);
// Check for developer settings for user build. Always allow for debug builds
if (!(isAllowed || isAllowedForBoot) && !DEBUG) {
throw new IllegalStateException(
"!! Enable Mock Modem before running this test !! "
+ "Developer options => Allow Mock Modem");
}
}
private int getActiveSubId(int phoneId) {
InstrumentationRegistry.getInstrumentation()
.getUiAutomation()
.adoptShellPermissionIdentity("android.permission.READ_PRIVILEGED_PHONE_STATE");
int[] allSubs =
getContext()
.getSystemService(SubscriptionManager.class)
.getActiveSubscriptionIdList();
int subsLength = allSubs.length;
Log.d(TAG, " Active Sub length is " + subsLength);
return (phoneId < subsLength) ? allSubs[phoneId] : -1;
}
private int getRegState(int domain, int subId) {
int reg;
InstrumentationRegistry.getInstrumentation()
.getUiAutomation()
.adoptShellPermissionIdentity("android.permission.READ_PHONE_STATE");
ServiceState ss = sTelephonyManager.createForSubscriptionId(subId).getServiceState();
assertNotNull(ss);
NetworkRegistrationInfo nri =
ss.getNetworkRegistrationInfo(domain, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
assertNotNull(nri);
reg = nri.getRegistrationState();
Log.d(TAG, "SS: " + nri.registrationStateToString(reg));
return reg;
}
private void waitForCondition(BooleanSupplier condition, Object lock, long maxWaitMillis)
throws Exception {
long now = System.currentTimeMillis();
long deadlineTime = now + maxWaitMillis;
while (!condition.getAsBoolean() && now < deadlineTime) {
lock.wait(deadlineTime - now);
now = System.currentTimeMillis();
}
}
@Test
public void testSimStateChange() throws Throwable {
Log.d(TAG, "TelephonyManagerTestOnMockModem#testSimStateChange");
assumeTrue(isSimHotSwapCapable());
int slotId = 0;
// Remove the SIM for initial state
sMockModemManager.removeSimCard(slotId);
int simCardState = sTelephonyManager.getSimCardState();
Log.d(TAG, "Current SIM card state: " + simCardState);
assertTrue(
Arrays.asList(TelephonyManager.SIM_STATE_UNKNOWN, TelephonyManager.SIM_STATE_ABSENT)
.contains(simCardState));
// Insert a SIM
assertTrue(sMockModemManager.insertSimCard(slotId, MOCK_SIM_PROFILE_ID_TWN_CHT));
simCardState = sTelephonyManager.getSimCardState();
assertEquals(TelephonyManager.SIM_STATE_PRESENT, simCardState);
// Check SIM state ready
simCardState = sTelephonyManager.getSimState();
assertEquals(TelephonyManager.SIM_STATE_READY, simCardState);
// Remove the SIM
assertTrue(sMockModemManager.removeSimCard(slotId));
simCardState = sTelephonyManager.getSimCardState();
assertEquals(TelephonyManager.SIM_STATE_ABSENT, simCardState);
}
@Test
public void testServiceStateChange() throws Throwable {
Log.d(TAG, "TelephonyManagerTestOnMockModem#testServiceStateChange");
assumeTrue(isSimHotSwapCapable());
int slotId = 0;
int subId;
// Insert a SIM
sMockModemManager.insertSimCard(slotId, MOCK_SIM_PROFILE_ID_TWN_CHT);
TimeUnit.SECONDS.sleep(2);
subId = getActiveSubId(slotId);
assertTrue(subId > 0);
// Register service state change callback
synchronized (mServiceStateChangeLock) {
mServiceState = ServiceState.STATE_OUT_OF_SERVICE;
mExpectedRegState = ServiceState.STATE_IN_SERVICE;
}
sServiceStateChangeCallbackHandler.post(
() -> {
sServiceStateCallback = new ServiceStateListener();
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
sTelephonyManager,
(tm) ->
tm.registerTelephonyCallback(
mServiceStateChangeExecutor, sServiceStateCallback));
});
// Enter Service
Log.d(TAG, "testServiceStateChange: Enter Service");
sMockModemManager.changeNetworkService(slotId, MOCK_SIM_PROFILE_ID_TWN_CHT, true);
// Expect: Home State
synchronized (mServiceStateChangeLock) {
if (mServiceState != ServiceState.STATE_IN_SERVICE) {
Log.d(TAG, "Wait for service state change to in service");
waitForCondition(
() -> (ServiceState.STATE_IN_SERVICE == mServiceState),
mServiceStateChangeLock,
WAIT_TIME_MS);
}
}
assertEquals(
getRegState(NetworkRegistrationInfo.DOMAIN_CS, subId),
NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
// Leave Service
synchronized (mServiceStateChangeLock) {
mExpectedRegState = ServiceState.STATE_OUT_OF_SERVICE;
}
Log.d(TAG, "testServiceStateChange: Leave Service");
sMockModemManager.changeNetworkService(slotId, MOCK_SIM_PROFILE_ID_TWN_CHT, false);
// Expect: Seaching State
synchronized (mServiceStateChangeLock) {
if (mServiceState != ServiceState.STATE_OUT_OF_SERVICE) {
Log.d(TAG, "Wait for service state change to out of service");
waitForCondition(
() -> (ServiceState.STATE_OUT_OF_SERVICE == mServiceState),
mServiceStateChangeLock,
WAIT_TIME_MS);
}
}
assertEquals(
getRegState(NetworkRegistrationInfo.DOMAIN_CS, subId),
NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING);
// Unregister service state change callback
sTelephonyManager.unregisterTelephonyCallback(sServiceStateCallback);
sServiceStateCallback = null;
// Remove the SIM
sMockModemManager.removeSimCard(slotId);
}
private class ServiceStateListener extends TelephonyCallback
implements TelephonyCallback.ServiceStateListener {
@Override
public void onServiceStateChanged(ServiceState serviceState) {
Log.d(TAG, "Callback: service state = " + serviceState.getVoiceRegState());
synchronized (mServiceStateChangeLock) {
mServiceState = serviceState.getVoiceRegState();
if (serviceState.getVoiceRegState() == mExpectedRegState) {
mServiceStateChangeLock.notify();
}
}
}
}
private class CallDisconnectCauseListener extends TelephonyCallback
implements TelephonyCallback.CallDisconnectCauseListener {
@Override
public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) {
synchronized (mCallDisconnectCauseLock) {
mPreciseCallDisconnectCause = preciseDisconnectCause;
Log.d(TAG, "Callback: call disconnect cause = " + mPreciseCallDisconnectCause);
mCallDisconnectCauseLock.notify();
}
}
}
private class CallStateListener extends TelephonyCallback
implements TelephonyCallback.CallStateListener {
@Override
public void onCallStateChanged(int state) {
synchronized (mCallStateChangeLock) {
mCallState = state;
mCallStateChangeLock.notify();
}
}
}
@Test
public void testVoiceCallState() throws Throwable {
Log.d(TAG, "TelephonyManagerTestOnMockModem#testVoiceCallState");
// Skip the test if it is a data-only device
final PackageManager pm = getContext().getPackageManager();
if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) {
Log.d(TAG, "Skipping test: Not test on data-only device");
return;
}
assumeTrue(isSimHotSwapCapable());
int slotId = 0;
int subId;
TelecomManager telecomManager =
(TelecomManager) getContext().getSystemService(Context.TELECOM_SERVICE);
// Insert a SIM
Log.d(TAG, "Start to insert a SIM");
sMockModemManager.insertSimCard(slotId, MOCK_SIM_PROFILE_ID_TWN_FET);
TimeUnit.SECONDS.sleep(1);
// Register service state change callback
synchronized (mServiceStateChangeLock) {
mServiceState = ServiceState.STATE_OUT_OF_SERVICE;
mExpectedRegState = ServiceState.STATE_IN_SERVICE;
}
sServiceStateChangeCallbackHandler.post(
() -> {
sServiceStateCallback = new ServiceStateListener();
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
sTelephonyManager,
(tm) ->
tm.registerTelephonyCallback(
mServiceStateChangeExecutor, sServiceStateCallback));
});
// In service
Log.d(TAG, "Start to register CS only network");
sMockModemManager.changeNetworkService(
slotId, MOCK_SIM_PROFILE_ID_TWN_FET, true, Domain.CS);
// Verify service state
synchronized (mServiceStateChangeLock) {
if (mServiceState != ServiceState.STATE_IN_SERVICE) {
Log.d(TAG, "Wait for service state change to in service");
waitForCondition(() -> (
ServiceState.STATE_IN_SERVICE == mServiceState),
mServiceStateChangeLock,
WAIT_TIME_MS);
}
}
subId = getActiveSubId(slotId);
assertTrue(subId > 0);
assertEquals(
getRegState(NetworkRegistrationInfo.DOMAIN_PS, subId),
NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING);
assertEquals(
getRegState(NetworkRegistrationInfo.DOMAIN_CS, subId),
NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
// Register call state change callback
mCallState = TelephonyManager.CALL_STATE_IDLE;
sCallStateChangeCallbackHandler.post(
() -> {
sCallStateCallback = new CallStateListener();
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
sTelephonyManager,
(tm) ->
tm.registerTelephonyCallback(
mCallStateChangeExecutor, sCallStateCallback));
});
//Disable Ims to make sure the call would go thorugh with a CS call
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
sTelephonyManager, (tm) -> tm.disableIms(slotId));
// Sleep 3s to make sure ims is disabled
TimeUnit.SECONDS.sleep(3);
// Dial a CS voice call
Log.d(TAG, "Start dialing call");
String phoneNumber = "+886987654321";
final Uri address = Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null);
// Place outgoing call
telecomManager.placeCall(address, null);
TimeUnit.SECONDS.sleep(1);
// Verify call state
synchronized (mCallStateChangeLock) {
if (mCallState == TelephonyManager.CALL_STATE_IDLE) {
Log.d(TAG, "Wait for call state change to offhook");
mCallStateChangeLock.wait(WAIT_TIME_MS);
}
assertEquals(TelephonyManager.CALL_STATE_OFFHOOK, mCallState);
}
// Verify the call is a CS call
assertTrue(sMockModemManager.getNumberOfOngoingCSCalls(slotId) > 0);
// Hang up the call
Log.d(TAG, "Hangup call");
telecomManager.endCall();
TimeUnit.SECONDS.sleep(1);
// Verify call state
synchronized (mCallStateChangeLock) {
if (mCallState == TelephonyManager.CALL_STATE_OFFHOOK) {
Log.d(TAG, "Wait for call state change to idle");
mCallStateChangeLock.wait(WAIT_TIME_MS);
}
assertEquals(TelephonyManager.CALL_STATE_IDLE, mCallState);
}
// Register call disconnect cause callback
mPreciseCallDisconnectCause = NO_DISCONNECT_CAUSE_AVAILABLE;
sCallDisconnectCauseCallbackHandler.post(
() -> {
sCallDisconnectCauseCallback = new CallDisconnectCauseListener();
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
sTelephonyManager,
(tm) ->
tm.registerTelephonyCallback(
mCallDisconnectCauseExecutor,
sCallDisconnectCauseCallback));
});
// Trigger an incoming call
Log.d(TAG, "Trigger an incoming call.");
UusInfo[] uusInfo = new UusInfo[0];
MockCallControlInfo callControlInfo = new MockCallControlInfo();
callControlInfo.setActiveDurationInMs(1000);
callControlInfo.setCallEndInfo(
LastCallFailCause.TEMPORARY_FAILURE, "cts-test-call-failure");
sMockModemManager.triggerIncomingVoiceCall(
slotId, phoneNumber, uusInfo, null, callControlInfo);
// Verify call state
synchronized (mCallStateChangeLock) {
if (mCallState == TelephonyManager.CALL_STATE_IDLE) {
Log.d(TAG, "Wait for call state change to ringing");
mCallStateChangeLock.wait(WAIT_TIME_MS);
}
assertEquals(TelephonyManager.CALL_STATE_RINGING, mCallState);
}
Log.d(TAG, "Answer the call");
telecomManager.acceptRingingCall();
// Verify call state
synchronized (mCallStateChangeLock) {
if (mCallState == TelephonyManager.CALL_STATE_RINGING) {
Log.d(TAG, "Wait for call state change to offhook");
mCallStateChangeLock.wait(WAIT_TIME_MS);
}
assertEquals(TelephonyManager.CALL_STATE_OFFHOOK, mCallState);
}
synchronized (mCallDisconnectCauseLock) {
if (mPreciseCallDisconnectCause == NO_DISCONNECT_CAUSE_AVAILABLE) {
Log.d(TAG, "Wait for disconnect cause to TEMPORARY_FAILURE");
mCallDisconnectCauseLock.wait(WAIT_TIME_MS);
}
assertEquals(TEMPORARY_FAILURE, mPreciseCallDisconnectCause);
}
// Unregister service state change callback
sTelephonyManager.unregisterTelephonyCallback(sServiceStateCallback);
sServiceStateCallback = null;
// Unregister call disconnect cause callback
sTelephonyManager.unregisterTelephonyCallback(sCallDisconnectCauseCallback);
sCallDisconnectCauseCallback = null;
// Unregister call state change callback
sTelephonyManager.unregisterTelephonyCallback(sCallStateCallback);
sCallStateCallback = null;
// Out of service
sMockModemManager.changeNetworkService(
slotId, MOCK_SIM_PROFILE_ID_TWN_FET, false, Domain.CS);
// Remove the SIM
sMockModemManager.removeSimCard(slotId);
}
@Test
public void testDsdsServiceStateChange() throws Throwable {
Log.d(TAG, "TelephonyManagerTestOnMockModem#testDsdsServiceStateChange");
assumeTrue("Skip test: Not test on single SIM device", sIsMultiSimDevice);
int slotId_0 = 0;
int slotId_1 = 1;
int subId_0;
int subId_1;
// Insert a SIM
sMockModemManager.insertSimCard(slotId_0, MOCK_SIM_PROFILE_ID_TWN_CHT);
sMockModemManager.insertSimCard(slotId_1, MOCK_SIM_PROFILE_ID_TWN_FET);
TimeUnit.SECONDS.sleep(2);
subId_0 = getActiveSubId(slotId_0);
subId_1 = getActiveSubId(slotId_1);
// Register service state change callback
synchronized (mServiceStateChangeLock) {
mServiceState = ServiceState.STATE_OUT_OF_SERVICE;
mExpectedRegState = ServiceState.STATE_IN_SERVICE;
}
sServiceStateChangeCallbackHandler.post(
() -> {
sServiceStateCallback = new ServiceStateListener();
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
sTelephonyManager,
(tm) ->
tm.registerTelephonyCallback(
mServiceStateChangeExecutor, sServiceStateCallback));
});
// Enter Service
Log.d(TAG, "testDsdsServiceStateChange: Enter Service");
sMockModemManager.changeNetworkService(slotId_0, MOCK_SIM_PROFILE_ID_TWN_CHT, true);
sMockModemManager.changeNetworkService(slotId_1, MOCK_SIM_PROFILE_ID_TWN_FET, true);
// Expect: Home State
synchronized (mServiceStateChangeLock) {
if (mServiceState != ServiceState.STATE_IN_SERVICE) {
Log.d(TAG, "Wait for service state change to in service");
waitForCondition(
() -> (ServiceState.STATE_IN_SERVICE == mServiceState),
mServiceStateChangeLock,
WAIT_TIME_MS);
}
}
if (subId_0 > 0) {
assertEquals(
getRegState(NetworkRegistrationInfo.DOMAIN_CS, subId_0),
NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
}
if (subId_1 > 0) {
TimeUnit.SECONDS.sleep(2);
assertEquals(
getRegState(NetworkRegistrationInfo.DOMAIN_CS, subId_1),
NetworkRegistrationInfo.REGISTRATION_STATE_HOME);
}
assertTrue(subId_0 > 0 || subId_1 > 0);
// Leave Service
synchronized (mServiceStateChangeLock) {
mExpectedRegState = ServiceState.STATE_OUT_OF_SERVICE;
}
Log.d(TAG, "testDsdsServiceStateChange: Leave Service");
sMockModemManager.changeNetworkService(slotId_0, MOCK_SIM_PROFILE_ID_TWN_CHT, false);
sMockModemManager.changeNetworkService(slotId_1, MOCK_SIM_PROFILE_ID_TWN_FET, false);
// Expect: Seaching State
synchronized (mServiceStateChangeLock) {
if (mServiceState != ServiceState.STATE_OUT_OF_SERVICE) {
Log.d(TAG, "Wait for service state change to out of service");
waitForCondition(
() -> (ServiceState.STATE_OUT_OF_SERVICE == mServiceState),
mServiceStateChangeLock,
WAIT_TIME_MS);
}
}
if (subId_0 > 0) {
assertEquals(
getRegState(NetworkRegistrationInfo.DOMAIN_CS, subId_0),
NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING);
}
if (subId_1 > 0) {
assertEquals(
getRegState(NetworkRegistrationInfo.DOMAIN_CS, subId_1),
NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING);
}
// Unregister service state change callback
sTelephonyManager.unregisterTelephonyCallback(sServiceStateCallback);
sServiceStateCallback = null;
// Remove the SIM
sMockModemManager.removeSimCard(slotId_0);
sMockModemManager.removeSimCard(slotId_1);
}
/**
* Verify the NotRestricted status of the device with READ_PHONE_STATE permission granted.
*/
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Test
public void getCarrierRestrictionStatus_ReadPhoneState_NotRestricted() throws Exception {
LinkedBlockingQueue<Integer> carrierRestrictionStatusResult = new LinkedBlockingQueue<>(1);
try {
sMockModemManager.updateCarrierRestrictionInfo(null,
CarrierRestrictions.CarrierRestrictionStatus.NOT_RESTRICTED);
TelephonyUtils.addCarrierRestrictionStatusAllowList(
androidx.test.platform.app.InstrumentationRegistry.getInstrumentation(),
TelephonyUtils.CTS_APP_PACKAGE, 10110, mShaId);
mResetCarrierStatusInfo = true;
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sTelephonyManager,
tm -> tm.getCarrierRestrictionStatus(getContext().getMainExecutor(),
carrierRestrictionStatusResult::offer),
Manifest.permission.READ_PHONE_STATE);
} catch (SecurityException ex) {
fail();
}
Integer value = carrierRestrictionStatusResult.poll(TIMEOUT_IN_SEC_FOR_MODEM_CB,
TimeUnit.SECONDS);
assertNotNull(value);
assertEquals(TelephonyManager.CARRIER_RESTRICTION_STATUS_NOT_RESTRICTED, value.intValue());
}
/**
* Verify the Restricted status of the device with READ_PHONE_STATE permission granted.
*/
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Test
public void getCarrierRestrictionStatus_ReadPhoneState_Restricted() throws Exception {
LinkedBlockingQueue<Integer> carrierRestrictionStatusResult = new LinkedBlockingQueue<>(1);
try {
sMockModemManager.updateCarrierRestrictionInfo(getCarrierList(false),
CarrierRestrictions.CarrierRestrictionStatus.RESTRICTED);
TelephonyUtils.addCarrierRestrictionStatusAllowList(
androidx.test.platform.app.InstrumentationRegistry.getInstrumentation(),
TelephonyUtils.CTS_APP_PACKAGE, 10110, mShaId);
mResetCarrierStatusInfo = true;
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sTelephonyManager,
tm -> tm.getCarrierRestrictionStatus(getContext().getMainExecutor(),
carrierRestrictionStatusResult::offer),
Manifest.permission.READ_PHONE_STATE);
} catch (SecurityException ex) {
fail();
}
Integer value = carrierRestrictionStatusResult.poll(TIMEOUT_IN_SEC_FOR_MODEM_CB,
TimeUnit.SECONDS);
assertNotNull(value);
assertEquals(TelephonyManager.CARRIER_RESTRICTION_STATUS_RESTRICTED, value.intValue());
}
/**
* Verify the Restricted To Caller status of the device with READ_PHONE_STATE permission
* granted.
*/
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Test
public void getCarrierRestrictionStatus_ReadPhoneState_RestrictedToCaller_MNO() throws Exception {
LinkedBlockingQueue<Integer> carrierRestrictionStatusResult = new LinkedBlockingQueue<>(1);
try {
sMockModemManager.updateCarrierRestrictionInfo(getCarrierList(false),
CarrierRestrictions.CarrierRestrictionStatus.RESTRICTED);
TelephonyUtils.addCarrierRestrictionStatusAllowList(
androidx.test.platform.app.InstrumentationRegistry.getInstrumentation(),
TelephonyUtils.CTS_APP_PACKAGE, 1839, mShaId);
mResetCarrierStatusInfo = true;
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sTelephonyManager,
tm -> tm.getCarrierRestrictionStatus(getContext().getMainExecutor(),
carrierRestrictionStatusResult::offer),
Manifest.permission.READ_PHONE_STATE);
} catch (SecurityException ex) {
fail();
}
Integer value = carrierRestrictionStatusResult.poll(TIMEOUT_IN_SEC_FOR_MODEM_CB,
TimeUnit.SECONDS);
assertNotNull(value);
assertEquals(TelephonyManager.CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER,
value.intValue());
}
/**
* Verify the Restricted status of the device with READ_PHONE_STATE permission granted.
* MVNO operator reference without GID
*/
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Test
public void getCarrierRestrictionStatus_ReadPhoneState_RestrictedToCaller_MNO1() throws Exception {
LinkedBlockingQueue<Integer> carrierRestrictionStatusResult = new LinkedBlockingQueue<>(1);
try {
sMockModemManager.updateCarrierRestrictionInfo(getCarrierList(false),
CarrierRestrictions.CarrierRestrictionStatus.RESTRICTED);
TelephonyUtils.addCarrierRestrictionStatusAllowList(
androidx.test.platform.app.InstrumentationRegistry.getInstrumentation(),
TelephonyUtils.CTS_APP_PACKAGE, 2032, mShaId);
mResetCarrierStatusInfo = true;
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sTelephonyManager,
tm -> tm.getCarrierRestrictionStatus(getContext().getMainExecutor(),
carrierRestrictionStatusResult::offer),
Manifest.permission.READ_PHONE_STATE);
} catch (SecurityException ex) {
fail();
}
Integer value = carrierRestrictionStatusResult.poll(TIMEOUT_IN_SEC_FOR_MODEM_CB,
TimeUnit.SECONDS);
assertNotNull(value);
assertEquals(TelephonyManager.CARRIER_RESTRICTION_STATUS_RESTRICTED,
value.intValue());
}
/**
* Verify the Restricted To Caller status of the device with READ_PHONE_STATE permission
* granted.
*/
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Test
public void getCarrierRestrictionStatus_ReadPhoneState_RestrictedToCaller_MVNO() throws Exception {
LinkedBlockingQueue<Integer> carrierRestrictionStatusResult = new LinkedBlockingQueue<>(1);
try {
sMockModemManager.updateCarrierRestrictionInfo(getCarrierList(true),
CarrierRestrictions.CarrierRestrictionStatus.RESTRICTED);
TelephonyUtils.addCarrierRestrictionStatusAllowList(
androidx.test.platform.app.InstrumentationRegistry.getInstrumentation(),
TelephonyUtils.CTS_APP_PACKAGE, 2032, mShaId);
mResetCarrierStatusInfo = true;
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sTelephonyManager,
tm -> tm.getCarrierRestrictionStatus(getContext().getMainExecutor(),
carrierRestrictionStatusResult::offer),
Manifest.permission.READ_PHONE_STATE);
} catch (SecurityException ex) {
fail();
}
Integer value = carrierRestrictionStatusResult.poll(TIMEOUT_IN_SEC_FOR_MODEM_CB,
TimeUnit.SECONDS);
assertNotNull(value);
assertEquals(TelephonyManager.CARRIER_RESTRICTION_STATUS_RESTRICTED_TO_CALLER,
value.intValue());
}
/**
* Verify the Unknown status of the device with READ_PHONE_STATE permission granted.
*/
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Test
public void getCarrierRestrictionStatus_ReadPhoneState_Unknown() throws Exception {
LinkedBlockingQueue<Integer> carrierRestrictionStatusResult = new LinkedBlockingQueue<>(1);
try {
sMockModemManager.updateCarrierRestrictionInfo(null,
CarrierRestrictions.CarrierRestrictionStatus.UNKNOWN);
TelephonyUtils.addCarrierRestrictionStatusAllowList(
androidx.test.platform.app.InstrumentationRegistry.getInstrumentation(),
TelephonyUtils.CTS_APP_PACKAGE, 10110, mShaId);
mResetCarrierStatusInfo = true;
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sTelephonyManager,
tm -> tm.getCarrierRestrictionStatus(getContext().getMainExecutor(),
carrierRestrictionStatusResult::offer),
Manifest.permission.READ_PHONE_STATE);
} catch (SecurityException ex) {
fail();
}
Integer value = carrierRestrictionStatusResult.poll(TIMEOUT_IN_SEC_FOR_MODEM_CB,
TimeUnit.SECONDS);
assertNotNull(value);
assertEquals(TelephonyManager.CARRIER_RESTRICTION_STATUS_UNKNOWN, value.intValue());
}
private Carrier[] getCarrierList(boolean isGidRequired) {
android.hardware.radio.sim.Carrier carrier
= new android.hardware.radio.sim.Carrier();
carrier.mcc = "310";
carrier.mnc = "599";
if (isGidRequired) {
carrier.matchType = Carrier.MATCH_TYPE_GID1;
carrier.matchData = "BA01450000000000";
}
Carrier[] carrierList = new Carrier[1];
carrierList[0] = carrier;
return carrierList;
}
/**
* Test for primaryImei will return the IMEI that is set through mockModem
*/
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@Test
public void testGetPrimaryImei() {
assumeTrue(sTelephonyManager.getActiveModemCount() > 0);
String primaryImei = ShellIdentityUtils.invokeMethodWithShellPermissions(sTelephonyManager,
(tm) -> tm.getPrimaryImei());
assertNotNull(primaryImei);
assertEquals(MockModemConfigInterface.DEFAULT_PHONE1_IMEI, primaryImei);
}
/**
* Verify the change of PrimaryImei with respect to sim slot.
*/
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
@Test
public void onImeiMappingChanged() {
// As first step verify the primary Imei against the default allocation
assumeTrue(sTelephonyManager.getActiveModemCount() > 1);
String primaryImei = ShellIdentityUtils.invokeMethodWithShellPermissions(sTelephonyManager,
(tm) -> tm.getPrimaryImei());
assertNotNull(primaryImei);
assertEquals(MockModemConfigInterface.DEFAULT_PHONE1_IMEI, primaryImei);
String slot0Imei = ShellIdentityUtils.invokeMethodWithShellPermissions(sTelephonyManager,
(tm) -> tm.getImei(0));
assertEquals(slot0Imei, primaryImei);
// Second step is change the PrimaryImei to slot2 and verify the same.
if (sMockModemManager.changeImeiMapping()) {
Log.d(TAG, "Verifying primary IMEI after change in IMEI mapping");
primaryImei = ShellIdentityUtils.invokeMethodWithShellPermissions(sTelephonyManager,
(tm) -> tm.getPrimaryImei());
assertNotNull(primaryImei);
assertEquals(MockModemConfigInterface.DEFAULT_PHONE1_IMEI, primaryImei);
String slot1Imei = ShellIdentityUtils.invokeMethodWithShellPermissions(
sTelephonyManager,
(tm) -> tm.getImei(1));
assertEquals(slot1Imei, primaryImei);
}
}
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
@Test
public void getAllowedCarriers_ReadPhoneState_Restricted() throws Exception {
sMockModemManager.updateCarrierRestrictionInfo(getCarrierList(false),
CarrierRestrictions.CarrierRestrictionStatus.RESTRICTED);
CarrierRestrictionRules rules =runWithShellPermissionIdentity(() -> {
return sTelephonyManager.getCarrierRestrictionRules();
}, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
assertEquals(TelephonyManager.CARRIER_RESTRICTION_STATUS_RESTRICTED,
rules.getCarrierRestrictionStatus());
}
}