blob: 686f793add2209e0da77bbda30f21ac3f480800e [file] [log] [blame]
/*
* Copyright (C) 2009 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 com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.Manifest;
import android.Manifest.permission;
import android.app.UiAutomation;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserManager;
import android.provider.Settings;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation.RadioPowerState;
import android.telephony.AvailableNetworkInfo;
import android.telephony.CallAttributes;
import android.telephony.CallQuality;
import android.telephony.CarrierConfigManager;
import android.telephony.CellLocation;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneStateListener;
import android.telephony.PreciseCallState;
import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.UiccCardInfo;
import android.telephony.UiccSlotInfo;
import android.telephony.cts.locationaccessingapp.CtsLocationAccessService;
import android.telephony.cts.locationaccessingapp.ICtsLocationAccessControl;
import android.telephony.data.ApnSetting;
import android.telephony.emergency.EmergencyNumber;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import androidx.test.InstrumentationRegistry;
import com.android.compatibility.common.util.ShellIdentityUtils;
import com.android.compatibility.common.util.TestThread;
import com.android.internal.telephony.uicc.IccUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Build, install and run the tests by running the commands below:
* make cts -j64
* cts-tradefed run cts -m CtsTelephonyTestCases --test android.telephony.cts.TelephonyManagerTest
*/
public class TelephonyManagerTest {
private TelephonyManager mTelephonyManager;
private SubscriptionManager mSubscriptionManager;
private PackageManager mPackageManager;
private boolean mOnCellLocationChangedCalled = false;
private boolean mServiceStateChangedCalled = false;
private boolean mRadioRebootTriggered = false;
private boolean mHasRadioPowerOff = false;
private ServiceState mServiceState;
private final Object mLock = new Object();
private CarrierConfigManager mCarrierConfigManager;
private String mSelfPackageName;
private String mSelfCertHash;
private static final int TOLERANCE = 1000;
private PhoneStateListener mListener;
private static ConnectivityManager mCm;
private static final String TAG = "TelephonyManagerTest";
private static final List<Integer> ROAMING_TYPES = Arrays.asList(
ServiceState.ROAMING_TYPE_DOMESTIC,
ServiceState.ROAMING_TYPE_INTERNATIONAL,
ServiceState.ROAMING_TYPE_NOT_ROAMING,
ServiceState.ROAMING_TYPE_UNKNOWN);
private static final List<Integer> NETWORK_TYPES = Arrays.asList(
TelephonyManager.NETWORK_TYPE_UNKNOWN,
TelephonyManager.NETWORK_TYPE_GPRS,
TelephonyManager.NETWORK_TYPE_EDGE,
TelephonyManager.NETWORK_TYPE_UMTS,
TelephonyManager.NETWORK_TYPE_CDMA,
TelephonyManager.NETWORK_TYPE_EVDO_0,
TelephonyManager.NETWORK_TYPE_EVDO_A,
TelephonyManager.NETWORK_TYPE_1xRTT,
TelephonyManager.NETWORK_TYPE_HSDPA,
TelephonyManager.NETWORK_TYPE_HSUPA,
TelephonyManager.NETWORK_TYPE_HSPA,
TelephonyManager.NETWORK_TYPE_IDEN,
TelephonyManager.NETWORK_TYPE_EVDO_B,
TelephonyManager.NETWORK_TYPE_LTE,
TelephonyManager.NETWORK_TYPE_EHRPD,
TelephonyManager.NETWORK_TYPE_HSPAP,
TelephonyManager.NETWORK_TYPE_GSM,
TelephonyManager.NETWORK_TYPE_TD_SCDMA,
TelephonyManager.NETWORK_TYPE_IWLAN,
TelephonyManager.NETWORK_TYPE_LTE_CA,
TelephonyManager.NETWORK_TYPE_NR);
private static final int EMERGENCY_NUMBER_SOURCE_RIL_ECCLIST = 0;
private static final Set<Integer> EMERGENCY_NUMBER_SOURCE_SET;
private static final String PLMN_A = "123456";
private static final String PLMN_B = "78901";
private static final List<String> FPLMN_TEST = Arrays.asList(PLMN_A, PLMN_B);
private static final int MAX_FPLMN_NUM = 100;
private static final int MIN_FPLMN_NUM = 3;
private static final String TEST_FORWARD_NUMBER = "54321";
private static final String TESTING_PLMN = "12345";
private static final int RADIO_HAL_VERSION_1_3 = makeRadioVersion(1, 3);
static {
EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
EMERGENCY_NUMBER_SOURCE_SET.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
EMERGENCY_NUMBER_SOURCE_SET.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM);
EMERGENCY_NUMBER_SOURCE_SET.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE);
EMERGENCY_NUMBER_SOURCE_SET.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG);
EMERGENCY_NUMBER_SOURCE_SET.add(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT);
}
private static final Set<Integer> EMERGENCY_SERVICE_CATEGORY_SET;
static {
EMERGENCY_SERVICE_CATEGORY_SET = new HashSet<Integer>();
EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE);
EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE);
EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE);
EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD);
EMERGENCY_SERVICE_CATEGORY_SET.add(
EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE);
EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC);
EMERGENCY_SERVICE_CATEGORY_SET.add(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC);
}
private static final String LOCATION_ACCESS_APP_CURRENT_PACKAGE =
CtsLocationAccessService.class.getPackage().getName();
private static final String LOCATION_ACCESS_APP_SDK28_PACKAGE =
CtsLocationAccessService.class.getPackage().getName() + ".sdk28";
private int mTestSub;
private TelephonyManagerTest.CarrierConfigReceiver mReceiver;
private int mRadioVersion;
private static class CarrierConfigReceiver extends BroadcastReceiver {
private CountDownLatch mLatch = new CountDownLatch(1);
private final int mSubId;
CarrierConfigReceiver(int subId) {
mSubId = subId;
}
@Override
public void onReceive(Context context, Intent intent) {
if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(intent.getAction())) {
int subId = intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
if (mSubId == subId) {
mLatch.countDown();
}
}
}
void clearQueue() {
mLatch = new CountDownLatch(1);
}
void waitForCarrierConfigChanged() throws Exception {
mLatch.await(5000, TimeUnit.MILLISECONDS);
}
}
@Before
public void setUp() throws Exception {
mCm = getContext().getSystemService(ConnectivityManager.class);
mSubscriptionManager = getContext().getSystemService(SubscriptionManager.class);
mPackageManager = getContext().getPackageManager();
mCarrierConfigManager = getContext().getSystemService(CarrierConfigManager.class);
mSelfPackageName = getContext().getPackageName();
mSelfCertHash = getCertHash(mSelfPackageName);
mTestSub = SubscriptionManager.getDefaultSubscriptionId();
mTelephonyManager = getContext().getSystemService(TelephonyManager.class)
.createForSubscriptionId(mTestSub);
mReceiver = new CarrierConfigReceiver(mTestSub);
Pair<Integer, Integer> radioVersion = mTelephonyManager.getRadioHalVersion();
mRadioVersion = makeRadioVersion(radioVersion.first, radioVersion.second);
IntentFilter filter = new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
// ACTION_CARRIER_CONFIG_CHANGED is sticky, so we will get a callback right away.
getContext().registerReceiver(mReceiver, filter);
}
@After
public void tearDown() throws Exception {
if (mListener != null) {
// unregister the listener
mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE);
}
if (mReceiver != null) {
getContext().unregisterReceiver(mReceiver);
mReceiver = null;
}
}
private String getCertHash(String pkgName) throws Exception {
try {
PackageInfo pInfo = mPackageManager.getPackageInfo(pkgName,
PackageManager.GET_SIGNATURES
| PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
MessageDigest md = MessageDigest.getInstance("SHA-1");
return IccUtils.bytesToHexString(md.digest(pInfo.signatures[0].toByteArray()));
} catch (PackageManager.NameNotFoundException ex) {
Log.e(TAG, pkgName + " not found", ex);
throw ex;
} catch (NoSuchAlgorithmException ex) {
Log.e(TAG, "Algorithm SHA1 is not found.");
throw ex;
}
}
/** Checks whether the cellular stack should be running on this device. */
private boolean hasCellular() {
return mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
&& mTelephonyManager.getPhoneCount() > 0;
}
@Test
public void testHasCarrierPrivilegesViaCarrierConfigs() throws Exception {
if (!hasCellular()) return;
PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mTestSub);
try {
assertNotNull("CarrierConfigManager#getConfigForSubId() returned null",
carrierConfig);
assertFalse("CarrierConfigManager#getConfigForSubId() returned empty bundle",
carrierConfig.isEmpty());
// purge the certs in carrierConfigs first
carrierConfig.putStringArray(
CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY, new String[]{});
overrideCarrierConfig(carrierConfig);
// verify we don't have privilege through carrierConfigs or Uicc
assertFalse(mTelephonyManager.hasCarrierPrivileges());
carrierConfig.putStringArray(
CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY,
new String[]{mSelfCertHash});
// verify we now have privilege after adding certificate to carrierConfigs
overrideCarrierConfig(carrierConfig);
assertTrue(mTelephonyManager.hasCarrierPrivileges());
} finally {
// purge the newly added certificate
carrierConfig.putStringArray(
CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY, new String[]{});
// carrierConfig.remove(CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY);
overrideCarrierConfig(carrierConfig);
// verify we no longer have privilege after removing certificate
assertFalse(mTelephonyManager.hasCarrierPrivileges());
}
}
private void overrideCarrierConfig(PersistableBundle bundle) throws Exception {
mReceiver.clearQueue();
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mCarrierConfigManager,
(cm) -> cm.overrideConfig(mTestSub, bundle));
mReceiver.waitForCarrierConfigChanged();
}
public static void grantLocationPermissions() {
UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
String packageName = getContext().getPackageName();
uiAutomation.grantRuntimePermission(packageName, permission.ACCESS_COARSE_LOCATION);
uiAutomation.grantRuntimePermission(packageName, permission.ACCESS_FINE_LOCATION);
uiAutomation.grantRuntimePermission(packageName, permission.ACCESS_BACKGROUND_LOCATION);
}
@Test
public void testDevicePolicyApn() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
Log.d(TAG, "skipping test on device without FEATURE_TELEPHONY present");
return;
}
// These methods aren't accessible to anything except system and phone by design, so we just
// look for security exceptions here.
try {
List<ApnSetting> apns = mTelephonyManager.getDevicePolicyOverrideApns(getContext());
fail("SecurityException expected");
} catch (SecurityException e) {
// expected
}
try {
ApnSetting.Builder builder = new ApnSetting.Builder();
ApnSetting setting = builder
.setEntryName("asdf")
.setApnName("asdf")
.setApnTypeBitmask(ApnSetting.TYPE_DEFAULT)
.build();
int id = mTelephonyManager.addDevicePolicyOverrideApn(getContext(), setting);
fail("SecurityException expected");
} catch (SecurityException e) {
// expected
}
try {
ApnSetting.Builder builder = new ApnSetting.Builder();
ApnSetting setting = builder
.setEntryName("asdf")
.setApnName("asdf")
.setApnTypeBitmask(ApnSetting.TYPE_DEFAULT)
.build();
boolean success = mTelephonyManager.modifyDevicePolicyOverrideApn(
getContext(), 0, setting);
fail("SecurityException expected");
} catch (SecurityException e) {
// expected
}
}
@Test
public void testListen() throws Throwable {
if (!InstrumentationRegistry.getContext().getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
Log.d(TAG, "Skipping test that requires PackageManager.FEATURE_TELEPHONY");
return;
}
if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
// TODO: temp workaround, need to adjust test to for CDMA
return;
}
grantLocationPermissions();
TestThread t = new TestThread(new Runnable() {
public void run() {
Looper.prepare();
mListener = new PhoneStateListener() {
@Override
public void onCellLocationChanged(CellLocation location) {
if(!mOnCellLocationChangedCalled) {
synchronized (mLock) {
mOnCellLocationChangedCalled = true;
mLock.notify();
}
}
}
};
synchronized (mLock) {
mLock.notify(); // mListener is ready
}
Looper.loop();
}
});
synchronized (mLock) {
t.start();
mLock.wait(TOLERANCE); // wait for mListener
}
// Test register
synchronized (mLock) {
// .listen generates an onCellLocationChanged event
mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_CELL_LOCATION);
mLock.wait(TOLERANCE);
assertTrue("Test register, mOnCellLocationChangedCalled should be true.",
mOnCellLocationChangedCalled);
}
synchronized (mLock) {
mOnCellLocationChangedCalled = false;
CellLocation.requestLocationUpdate();
mLock.wait(TOLERANCE);
assertTrue("Test register, mOnCellLocationChangedCalled should be true.",
mOnCellLocationChangedCalled);
}
// unregister the listener
mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE);
Thread.sleep(TOLERANCE);
// Test unregister
synchronized (mLock) {
mOnCellLocationChangedCalled = false;
// unregister again, to make sure doing so does not call the listener
mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE);
CellLocation.requestLocationUpdate();
mLock.wait(TOLERANCE);
assertFalse("Test unregister, mOnCellLocationChangedCalled should be false.",
mOnCellLocationChangedCalled);
}
}
/**
* The getter methods here are all related to the information about the telephony.
* These getters are related to concrete location, phone, service provider company, so
* it's no need to get details of these information, just make sure they are in right
* condition(>0 or not null).
*/
@Test
public void testTelephonyManager() {
assertTrue(mTelephonyManager.getNetworkType() >= TelephonyManager.NETWORK_TYPE_UNKNOWN);
assertTrue(mTelephonyManager.getPhoneType() >= TelephonyManager.PHONE_TYPE_NONE);
assertTrue(mTelephonyManager.getSimState() >= TelephonyManager.SIM_STATE_UNKNOWN);
assertTrue(mTelephonyManager.getDataActivity() >= TelephonyManager.DATA_ACTIVITY_NONE);
assertTrue(mTelephonyManager.getDataState() >= TelephonyManager.DATA_DISCONNECTED);
assertTrue(mTelephonyManager.getCallState() >= TelephonyManager.CALL_STATE_IDLE);
for (int i = 0; i < mTelephonyManager.getPhoneCount(); ++i) {
assertTrue(mTelephonyManager.getSimState(i) >= TelephonyManager.SIM_STATE_UNKNOWN);
}
// Make sure devices without MMS service won't fail on this
if (InstrumentationRegistry.getContext().getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
&& (mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE)) {
assertFalse(mTelephonyManager.getMmsUserAgent().isEmpty());
assertFalse(mTelephonyManager.getMmsUAProfUrl().isEmpty());
}
// The following methods may return any value depending on the state of the device. Simply
// call them to make sure they do not throw any exceptions.
mTelephonyManager.getVoiceMailNumber();
mTelephonyManager.getSimOperatorName();
mTelephonyManager.getNetworkCountryIso();
mTelephonyManager.getCellLocation();
mTelephonyManager.getSimCarrierId();
mTelephonyManager.getSimCarrierIdName();
mTelephonyManager.getSimSpecificCarrierId();
mTelephonyManager.getSimSpecificCarrierIdName();
mTelephonyManager.getCarrierIdFromSimMccMnc();
mTelephonyManager.isDataRoamingEnabled();
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getSimSerialNumber());
mTelephonyManager.getSimOperator();
mTelephonyManager.getSignalStrength();
mTelephonyManager.getNetworkOperatorName();
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getSubscriberId());
mTelephonyManager.getLine1Number();
mTelephonyManager.getNetworkOperator();
mTelephonyManager.getSimCountryIso();
mTelephonyManager.getVoiceMailAlphaTag();
mTelephonyManager.isNetworkRoaming();
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getDeviceId());
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getDeviceId(mTelephonyManager.getSlotIndex()));
mTelephonyManager.getDeviceSoftwareVersion();
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getDeviceSoftwareVersion(mTelephonyManager.getSlotIndex()));
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getImei());
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getImei(mTelephonyManager.getSlotIndex()));
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.isManualNetworkSelectionAllowed());
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getManualNetworkSelectionPlmn());
mTelephonyManager.getPhoneCount();
mTelephonyManager.getDataEnabled();
mTelephonyManager.getNetworkSpecifier();
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, (tm) -> tm.getNai());
TelecomManager telecomManager = getContext().getSystemService(TelecomManager.class);
PhoneAccountHandle defaultAccount = telecomManager
.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL);
mTelephonyManager.getVoicemailRingtoneUri(defaultAccount);
mTelephonyManager.isVoicemailVibrationEnabled(defaultAccount);
mTelephonyManager.getSubscriptionId(defaultAccount);
mTelephonyManager.getCarrierConfig();
mTelephonyManager.isVoiceCapable();
mTelephonyManager.isSmsCapable();
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.isDataConnectionAllowed());
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.isAnyRadioPoweredOn());
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.resetIms(tm.getSlotIndex()));
// Verify TelephonyManager.getCarrierPrivilegeStatus
List<Integer> validCarrierPrivilegeStatus = new ArrayList<>();
validCarrierPrivilegeStatus.add(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
validCarrierPrivilegeStatus.add(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
validCarrierPrivilegeStatus.add(
TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED);
validCarrierPrivilegeStatus.add(
TelephonyManager.CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES);
int carrierPrivilegeStatusResult = ShellIdentityUtils.invokeMethodWithShellPermissions(
mTelephonyManager, (tm) -> tm.getCarrierPrivilegeStatus(Process.myUid()));
assertTrue(validCarrierPrivilegeStatus.contains(carrierPrivilegeStatusResult));
// Verify TelephonyManager.getCarrierPrivilegedPackagesForAllActiveSubscriptions
List<String> resultForGetCarrierPrivilegedApis =
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getCarrierPrivilegedPackagesForAllActiveSubscriptions());
assertNotNull(resultForGetCarrierPrivilegedApis);
for (String result : resultForGetCarrierPrivilegedApis) {
assertFalse(TextUtils.isEmpty(result));
}
mTelephonyManager.getDefaultRespondViaMessageApplication();
mTelephonyManager.getAndUpdateDefaultRespondViaMessageApplication();
}
/**
* Due to the corresponding API is hidden in R and will be public in S, this test
* is commented and will be un-commented in Android S.
*
@Test
public void testGetCallForwarding() {
List<Integer> callForwardingReasons = new ArrayList<>();
callForwardingReasons.add(CallForwardingInfo.REASON_UNCONDITIONAL);
callForwardingReasons.add(CallForwardingInfo.REASON_BUSY);
callForwardingReasons.add(CallForwardingInfo.REASON_NO_REPLY);
callForwardingReasons.add(CallForwardingInfo.REASON_NOT_REACHABLE);
callForwardingReasons.add(CallForwardingInfo.REASON_ALL);
callForwardingReasons.add(CallForwardingInfo.REASON_ALL_CONDITIONAL);
Set<Integer> callForwardingStatus = new HashSet<Integer>();
callForwardingStatus.add(CallForwardingInfo.STATUS_INACTIVE);
callForwardingStatus.add(CallForwardingInfo.STATUS_ACTIVE);
callForwardingStatus.add(CallForwardingInfo.STATUS_FDN_CHECK_FAILURE);
callForwardingStatus.add(CallForwardingInfo.STATUS_UNKNOWN_ERROR);
callForwardingStatus.add(CallForwardingInfo.STATUS_NOT_SUPPORTED);
for (int callForwardingReasonToGet : callForwardingReasons) {
Log.d(TAG, "[testGetCallForwarding] callForwardingReasonToGet: "
+ callForwardingReasonToGet);
CallForwardingInfo callForwardingInfo =
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getCallForwarding(callForwardingReasonToGet));
assertNotNull(callForwardingInfo);
assertTrue(callForwardingStatus.contains(callForwardingInfo.getStatus()));
assertTrue(callForwardingReasons.contains(callForwardingInfo.getReason()));
callForwardingInfo.getNumber();
assertTrue(callForwardingInfo.getTimeoutSeconds() >= 0);
}
}
*/
/**
* Due to the corresponding API is hidden in R and will be public in S, this test
* is commented and will be un-commented in Android S.
*
@Test
public void testSetCallForwarding() {
List<Integer> callForwardingReasons = new ArrayList<>();
callForwardingReasons.add(CallForwardingInfo.REASON_UNCONDITIONAL);
callForwardingReasons.add(CallForwardingInfo.REASON_BUSY);
callForwardingReasons.add(CallForwardingInfo.REASON_NO_REPLY);
callForwardingReasons.add(CallForwardingInfo.REASON_NOT_REACHABLE);
callForwardingReasons.add(CallForwardingInfo.REASON_ALL);
callForwardingReasons.add(CallForwardingInfo.REASON_ALL_CONDITIONAL);
// Enable Call Forwarding
for (int callForwardingReasonToEnable : callForwardingReasons) {
final CallForwardingInfo callForwardingInfoToEnable = new CallForwardingInfo(
CallForwardingInfo.STATUS_ACTIVE,
callForwardingReasonToEnable,
TEST_FORWARD_NUMBER,
// time seconds
1);
Log.d(TAG, "[testSetCallForwarding] Enable Call Forwarding. Status: "
+ CallForwardingInfo.STATUS_ACTIVE + " Reason: "
+ callForwardingReasonToEnable + " Number: " + TEST_FORWARD_NUMBER
+ " Time Seconds: 1");
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.setCallForwarding(callForwardingInfoToEnable));
}
// Disable Call Forwarding
for (int callForwardingReasonToDisable : callForwardingReasons) {
final CallForwardingInfo callForwardingInfoToDisable = new CallForwardingInfo(
CallForwardingInfo.STATUS_INACTIVE,
callForwardingReasonToDisable,
TEST_FORWARD_NUMBER,
// time seconds
1);
Log.d(TAG, "[testSetCallForwarding] Disable Call Forwarding. Status: "
+ CallForwardingInfo.STATUS_INACTIVE + " Reason: "
+ callForwardingReasonToDisable + " Number: " + TEST_FORWARD_NUMBER
+ " Time Seconds: 1");
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.setCallForwarding(callForwardingInfoToDisable));
}
}
*/
/**
* Due to the corresponding API is hidden in R and will be public in S, this test
* is commented and will be un-commented in Android S.
*
@Test
public void testGetCallWaitingStatus() {
Set<Integer> callWaitingStatus = new HashSet<Integer>();
callWaitingStatus.add(TelephonyManager.CALL_WAITING_STATUS_ACTIVE);
callWaitingStatus.add(TelephonyManager.CALL_WAITING_STATUS_INACTIVE);
callWaitingStatus.add(TelephonyManager.CALL_WAITING_STATUS_UNKNOWN_ERROR);
callWaitingStatus.add(TelephonyManager.CALL_WAITING_STATUS_NOT_SUPPORTED);
int status = ShellIdentityUtils.invokeMethodWithShellPermissions(
mTelephonyManager, (tm) -> tm.getCallWaitingStatus());
assertTrue(callWaitingStatus.contains(status));
}
*/
/**
* Due to the corresponding API is hidden in R and will be public in S, this test
* is commented and will be un-commented in Android S.
*
@Test
public void testSetCallWaitingStatus() {
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.setCallWaitingStatus(true));
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.setCallWaitingStatus(false));
}
*/
@Test
public void testCellLocationFinePermission() {
withRevokedPermission(LOCATION_ACCESS_APP_CURRENT_PACKAGE, () -> {
try {
Bundle cellLocationBundle = (Bundle) performLocationAccessCommand(
CtsLocationAccessService.COMMAND_GET_CELL_LOCATION);
CellLocation cellLocation = cellLocationBundle == null ? null :
CellLocation.newFromBundle(cellLocationBundle);
assertTrue(cellLocation == null || cellLocation.isEmpty());
} catch (SecurityException e) {
// expected
}
try {
List cis = (List) performLocationAccessCommand(
CtsLocationAccessService.COMMAND_GET_CELL_INFO);
assertTrue(cis == null || cis.isEmpty());
} catch (SecurityException e) {
// expected
}
}, Manifest.permission.ACCESS_FINE_LOCATION);
}
@Test
public void testServiceStateLocationSanitization() {
withRevokedPermission(LOCATION_ACCESS_APP_CURRENT_PACKAGE, () -> {
ServiceState ss = (ServiceState) performLocationAccessCommand(
CtsLocationAccessService.COMMAND_GET_SERVICE_STATE);
assertServiceStateSanitization(ss, true);
withRevokedPermission(LOCATION_ACCESS_APP_CURRENT_PACKAGE, () -> {
ServiceState ss1 = (ServiceState) performLocationAccessCommand(
CtsLocationAccessService.COMMAND_GET_SERVICE_STATE);
assertServiceStateSanitization(ss1, false);
},
Manifest.permission.ACCESS_COARSE_LOCATION);
},
Manifest.permission.ACCESS_FINE_LOCATION);
}
@Test
public void testServiceStateListeningWithoutPermissions() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) return;
withRevokedPermission(LOCATION_ACCESS_APP_CURRENT_PACKAGE, () -> {
ServiceState ss = (ServiceState) performLocationAccessCommand(
CtsLocationAccessService.COMMAND_GET_SERVICE_STATE_FROM_LISTENER);
assertServiceStateSanitization(ss, true);
withRevokedPermission(LOCATION_ACCESS_APP_CURRENT_PACKAGE, () -> {
ServiceState ss1 = (ServiceState) performLocationAccessCommand(
CtsLocationAccessService
.COMMAND_GET_SERVICE_STATE_FROM_LISTENER);
assertServiceStateSanitization(ss1, false);
},
Manifest.permission.ACCESS_COARSE_LOCATION);
},
Manifest.permission.ACCESS_FINE_LOCATION);
}
@Test
public void testRegistryPermissionsForCellLocation() {
withRevokedPermission(LOCATION_ACCESS_APP_CURRENT_PACKAGE, () -> {
CellLocation cellLocation = (CellLocation) performLocationAccessCommand(
CtsLocationAccessService.COMMAND_LISTEN_CELL_LOCATION);
assertNull(cellLocation);
},
Manifest.permission.ACCESS_FINE_LOCATION);
}
@Test
public void testRegistryPermissionsForCellInfo() {
withRevokedPermission(LOCATION_ACCESS_APP_CURRENT_PACKAGE, () -> {
CellLocation cellLocation = (CellLocation) performLocationAccessCommand(
CtsLocationAccessService.COMMAND_LISTEN_CELL_INFO);
assertNull(cellLocation);
},
Manifest.permission.ACCESS_FINE_LOCATION);
}
@Test
public void testSdk28CellLocation() {
// Verify that a target-sdk 28 app can access cell location with ACCESS_COARSE_LOCATION, but
// not with no location permissions at all.
withRevokedPermission(LOCATION_ACCESS_APP_SDK28_PACKAGE, () -> {
try {
performLocationAccessCommandSdk28(
CtsLocationAccessService.COMMAND_GET_CELL_LOCATION);
} catch (SecurityException e) {
fail("SDK28 should have access to cell location with coarse permission");
}
withRevokedPermission(LOCATION_ACCESS_APP_SDK28_PACKAGE, () -> {
try {
Bundle cellLocationBundle = (Bundle) performLocationAccessCommandSdk28(
CtsLocationAccessService.COMMAND_GET_CELL_LOCATION);
CellLocation cellLocation = cellLocationBundle == null ? null :
CellLocation.newFromBundle(cellLocationBundle);
assertTrue(cellLocation == null || cellLocation.isEmpty());
} catch (SecurityException e) {
// expected
}
}, Manifest.permission.ACCESS_COARSE_LOCATION);
}, Manifest.permission.ACCESS_FINE_LOCATION);
}
private ICtsLocationAccessControl getLocationAccessAppControl() {
Intent bindIntent = new Intent(CtsLocationAccessService.CONTROL_ACTION);
bindIntent.setComponent(new ComponentName(
LOCATION_ACCESS_APP_CURRENT_PACKAGE,
CtsLocationAccessService.class.getName()));
return bindLocationAccessControl(bindIntent);
}
private ICtsLocationAccessControl getLocationAccessAppControlSdk28() {
Intent bindIntent = new Intent(CtsLocationAccessService.CONTROL_ACTION);
bindIntent.setComponent(new ComponentName(
LOCATION_ACCESS_APP_SDK28_PACKAGE,
CtsLocationAccessService.class.getName()));
return bindLocationAccessControl(bindIntent);
}
private ICtsLocationAccessControl bindLocationAccessControl(Intent bindIntent) {
LinkedBlockingQueue<ICtsLocationAccessControl> pipe =
new LinkedBlockingQueue<>();
getContext().bindService(bindIntent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
pipe.offer(ICtsLocationAccessControl.Stub.asInterface(service));
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, Context.BIND_AUTO_CREATE);
try {
return pipe.poll(TOLERANCE, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
fail("interrupted");
}
fail("Unable to connect to location access test app");
return null;
}
private Object performLocationAccessCommand(String command) {
ICtsLocationAccessControl control = getLocationAccessAppControl();
try {
List ret = control.performCommand(command);
if (!ret.isEmpty()) return ret.get(0);
} catch (RemoteException e) {
fail("Remote exception");
}
return null;
}
private Object performLocationAccessCommandSdk28(String command) {
ICtsLocationAccessControl control = getLocationAccessAppControlSdk28();
try {
List ret = control.performCommand(command);
if (!ret.isEmpty()) return ret.get(0);
} catch (RemoteException e) {
fail("Remote exception");
}
return null;
}
private void withRevokedPermission(String packageName, Runnable r, String permission) {
InstrumentationRegistry.getInstrumentation()
.getUiAutomation().revokeRuntimePermission(packageName, permission);
try {
r.run();
} finally {
InstrumentationRegistry.getInstrumentation()
.getUiAutomation().grantRuntimePermission(packageName, permission);
}
}
private void assertServiceStateSanitization(ServiceState state, boolean sanitizedForFineOnly) {
if (state == null) return;
if (state.getNetworkRegistrationInfoList() != null) {
for (NetworkRegistrationInfo nrs : state.getNetworkRegistrationInfoList()) {
assertNull(nrs.getCellIdentity());
}
}
if (sanitizedForFineOnly) return;
assertTrue(TextUtils.isEmpty(state.getOperatorAlphaLong()));
assertTrue(TextUtils.isEmpty(state.getOperatorAlphaShort()));
assertTrue(TextUtils.isEmpty(state.getOperatorNumeric()));
}
@Test
public void testGetRadioHalVersion() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
Log.d(TAG,"skipping test on device without FEATURE_TELEPHONY present");
return;
}
Pair<Integer, Integer> version = mTelephonyManager.getRadioHalVersion();
// The version must be valid, and the versions start with 1.0
assertFalse("Invalid Radio HAL Version: " + version,
version.first < 1 || version.second < 0);
}
@Test
public void testCreateForPhoneAccountHandle() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
Log.d(TAG, "Skipping test that requires FEATURE_TELEPHONY");
return;
}
TelecomManager telecomManager = getContext().getSystemService(TelecomManager.class);
PhoneAccountHandle handle =
telecomManager.getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL);
TelephonyManager telephonyManager = mTelephonyManager.createForPhoneAccountHandle(handle);
String globalSubscriberId = ShellIdentityUtils.invokeMethodWithShellPermissions(
mTelephonyManager, (tm) -> tm.getSubscriberId());
String localSubscriberId = ShellIdentityUtils.invokeMethodWithShellPermissions(
telephonyManager, (tm) -> tm.getSubscriberId());
assertEquals(globalSubscriberId, localSubscriberId);
}
@Test
public void testCreateForPhoneAccountHandle_InvalidHandle(){
PhoneAccountHandle handle =
new PhoneAccountHandle(new ComponentName("com.example.foo", "bar"), "baz");
assertNull(mTelephonyManager.createForPhoneAccountHandle(handle));
}
/**
* Tests that the phone count returned is valid.
*/
@Test
public void testGetPhoneCount() {
int phoneCount = mTelephonyManager.getPhoneCount();
int phoneType = mTelephonyManager.getPhoneType();
switch (phoneType) {
case TelephonyManager.PHONE_TYPE_GSM:
case TelephonyManager.PHONE_TYPE_CDMA:
assertTrue("Phone count should be > 0", phoneCount > 0);
break;
case TelephonyManager.PHONE_TYPE_NONE:
assertTrue("Phone count should be 0", phoneCount == 0 || phoneCount == 1);
break;
default:
throw new IllegalArgumentException("Did you add a new phone type? " + phoneType);
}
}
/**
* Tests that the device properly reports either a valid IMEI, MEID/ESN, or a valid MAC address
* if only a WiFi device. At least one of them must be valid.
*/
@Test
public void testGetDeviceId() {
String deviceId = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getDeviceId());
verifyDeviceId(deviceId);
}
/**
* Tests the max number of active SIMs method
*/
@Test
public void testGetMaxNumberOfSimultaneouslyActiveSims() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
Log.d(TAG, "Skipping test that requires FEATURE_TELEPHONY");
return;
}
int maxNum = mTelephonyManager.getMaxNumberOfSimultaneouslyActiveSims();
assertTrue(maxNum >= 1);
}
/**
* Tests that the device properly reports either a valid IMEI, MEID/ESN, or a valid MAC address
* if only a WiFi device. At least one of them must be valid.
*/
@Test
public void testGetDeviceIdForSlot() {
String deviceId = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getDeviceId(mTelephonyManager.getSlotIndex()));
verifyDeviceId(deviceId);
// Also verify that no exception is thrown for any slot index (including invalid ones)
for (int i = -1; i <= mTelephonyManager.getPhoneCount(); i++) {
// The compiler error 'local variables referenced from a lambda expression must be final
// or effectively final' is reported when using i, so assign it to a final variable.
final int currI = i;
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getDeviceId(currI));
}
}
private void verifyDeviceId(String deviceId) {
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
// Either IMEI or MEID need to be valid.
try {
assertImei(deviceId);
} catch (AssertionError e) {
assertMeidEsn(deviceId);
}
} else if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
assertSerialNumber();
assertMacAddress(getWifiMacAddress());
} else if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) {
assertSerialNumber();
assertMacAddress(getBluetoothMacAddress());
} else if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET)) {
assertTrue(mCm.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET) != null);
}
}
private static void assertImei(String id) {
assertFalse("Imei should not be empty or null", TextUtils.isEmpty(id));
// IMEI may include the check digit
String imeiPattern = "[0-9]{14,15}";
String invalidPattern = "[0]{14,15}";
assertTrue("IMEI " + id + " does not match pattern " + imeiPattern,
Pattern.matches(imeiPattern, id));
assertFalse("IMEI " + id + " must not be a zero sequence" + invalidPattern,
Pattern.matches(invalidPattern, id));
if (id.length() == 15) {
// if the ID is 15 digits, the 15th must be a check digit.
assertImeiCheckDigit(id);
}
}
private static void assertImeiCheckDigit(String deviceId) {
int expectedCheckDigit = getLuhnCheckDigit(deviceId.substring(0, 14));
int actualCheckDigit = Character.digit(deviceId.charAt(14), 10);
assertEquals("Incorrect check digit for " + deviceId, expectedCheckDigit, actualCheckDigit);
}
/**
* Use decimal value (0-9) to index into array to get sum of its digits
* needed by Lunh check.
*
* Example: DOUBLE_DIGIT_SUM[6] = 3 because 6 * 2 = 12 => 1 + 2 = 3
*/
private static final int[] DOUBLE_DIGIT_SUM = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9};
/**
* Calculate the check digit by starting from the right, doubling every
* each digit, summing all the digits including the doubled ones, and
* finding a number to make the sum divisible by 10.
*
* @param deviceId not including the check digit
* @return the check digit
*/
private static int getLuhnCheckDigit(String deviceId) {
int sum = 0;
int dontDoubleModulus = deviceId.length() % 2;
for (int i = deviceId.length() - 1; i >= 0; --i) {
int digit = Character.digit(deviceId.charAt(i), 10);
if (i % 2 == dontDoubleModulus) {
sum += digit;
} else {
sum += DOUBLE_DIGIT_SUM[digit];
}
}
sum %= 10;
return sum == 0 ? 0 : 10 - sum;
}
private static void assertMeidEsn(String id) {
// CDMA device IDs may either be a 14-hex-digit MEID or an
// 8-hex-digit ESN. If it's an ESN, it may not be a
// pseudo-ESN.
assertFalse("Meid ESN should not be empty or null", TextUtils.isEmpty(id));
if (id.length() == 14) {
assertMeidFormat(id);
} else if (id.length() == 8) {
assertHexadecimalEsnFormat(id);
} else {
fail("device id on CDMA must be 14-digit hex MEID or 8-digit hex ESN.");
}
}
private static void assertHexadecimalEsnFormat(String deviceId) {
String esnPattern = "[0-9a-fA-F]{8}";
String invalidPattern = "[0]{8}";
assertTrue("ESN hex device id " + deviceId + " does not match pattern " + esnPattern,
Pattern.matches(esnPattern, deviceId));
assertFalse("ESN hex device id " + deviceId + " must not be a pseudo-ESN",
"80".equals(deviceId.substring(0, 2)));
assertFalse("ESN hex device id " + deviceId + "must not be a zero sequence",
Pattern.matches(invalidPattern, deviceId));
}
private static void assertMeidFormat(String deviceId) {
// MEID must NOT include the check digit.
String meidPattern = "[0-9a-fA-F]{14}";
String invalidPattern = "[0]{14}";
assertTrue("MEID device id " + deviceId + " does not match pattern "
+ meidPattern, Pattern.matches(meidPattern, deviceId));
assertFalse("MEID device id " + deviceId + "must not be a zero sequence",
Pattern.matches(invalidPattern, deviceId));
}
private void assertSerialNumber() {
String serial = ShellIdentityUtils.invokeStaticMethodWithShellPermissions(
Build::getSerial);
assertNotNull("Non-telephony devices must have a Build.getSerial() number.",
serial);
assertTrue("Hardware id must be no longer than 20 characters.",
serial.length() <= 20);
assertTrue("Hardware id must be alphanumeric.",
Pattern.matches("[0-9A-Za-z]+", serial));
}
private void assertMacAddress(String macAddress) {
String macPattern = "([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}";
assertTrue("MAC Address " + macAddress + " does not match pattern " + macPattern,
Pattern.matches(macPattern, macAddress));
}
/** @return mac address which requires the WiFi system to be enabled */
private String getWifiMacAddress() {
WifiManager wifiManager = getContext().getSystemService(WifiManager.class);
boolean enabled = wifiManager.isWifiEnabled();
try {
if (!enabled) {
wifiManager.setWifiEnabled(true);
}
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
return wifiInfo.getMacAddress();
} finally {
if (!enabled) {
wifiManager.setWifiEnabled(false);
}
}
}
private String getBluetoothMacAddress() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter == null) {
return "";
}
return adapter.getAddress();
}
private static final String ISO_COUNTRY_CODE_PATTERN = "[a-z]{2}";
@Test
public void testGetNetworkCountryIso() {
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
String countryCode = mTelephonyManager.getNetworkCountryIso();
assertTrue("Country code '" + countryCode + "' did not match "
+ ISO_COUNTRY_CODE_PATTERN,
Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode));
for (int i = 0; i < mTelephonyManager.getPhoneCount(); i++) {
countryCode = mTelephonyManager.getNetworkCountryIso(i);
assertTrue("Country code '" + countryCode + "' did not match "
+ ISO_COUNTRY_CODE_PATTERN + " for slot " + i,
Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode));
}
} else {
// Non-telephony may still have the property defined if it has a SIM.
}
}
@Test
public void testSetSystemSelectionChannels() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
Log.d(TAG, "skipping test on device without FEATURE_TELEPHONY present");
return;
}
LinkedBlockingQueue<Boolean> queue = new LinkedBlockingQueue<>(1);
final UiAutomation uiAutomation =
InstrumentationRegistry.getInstrumentation().getUiAutomation();
try {
uiAutomation.adoptShellPermissionIdentity();
// This is a oneway binder call, meaning we may return before the permission check
// happens. Hold shell permissions until we get a response.
mTelephonyManager.setSystemSelectionChannels(Collections.emptyList(),
getContext().getMainExecutor(), queue::offer);
Boolean result = queue.poll(1000, TimeUnit.MILLISECONDS);
// Ensure we get a result
assertNotNull(result);
// Only verify the result for supported devices on IRadio 1.3+
if (mRadioVersion >= RADIO_HAL_VERSION_1_3) {
assertTrue(result);
}
} catch (InterruptedException e) {
fail("interrupted");
} finally {
uiAutomation.dropShellPermissionIdentity();
}
// Try calling the API that doesn't provide feedback. We have no way of knowing if it
// succeeds, so just make sure nothing crashes.
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
tp -> tp.setSystemSelectionChannels(Collections.emptyList()));
}
@Test
public void testGetSimCountryIso() {
String countryCode = mTelephonyManager.getSimCountryIso();
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
assertTrue("Country code '" + countryCode + "' did not match "
+ ISO_COUNTRY_CODE_PATTERN,
Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode));
} else {
// Non-telephony may still have the property defined if it has a SIM.
}
}
@Test
public void testResetSettings() throws Exception {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
Log.d(TAG, "skipping test on device without FEATURE_TELEPHONY present");
return;
}
UserManager userManager = getContext().getSystemService(UserManager.class);
boolean canChangeMobileNetworkSettings = userManager != null
&& !userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
assertTrue("Primary user must be able to configure mobile networks to pass this test",
canChangeMobileNetworkSettings);
//First check permissions are correct
try {
mTelephonyManager.resetSettings();
fail("TelephonyManager#resetSettings requires the"
+ " android.Manifest.permission.NETWORK_SETTINGS permission");
} catch (SecurityException e) {
//expected
}
// and then do a reset to move data to default.
try {
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
TelephonyManager::resetSettings, "android.permission.NETWORK_SETTINGS");
} catch (SecurityException e) {
fail("TelephonyManager#resetSettings requires the"
+ " android.Manifest.permission.NETWORK_SETTINGS permission");
}
LinkedBlockingQueue<Boolean> queue = new LinkedBlockingQueue<>(2);
final ContentObserver mobileDataChangeObserver = new ContentObserver(
new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange) {
queue.offer(isDataEnabled());
}
};
getContext().getContentResolver().registerContentObserver(
getObservableDataEnabledUri(mTestSub), /* notifyForDescendants= */ false,
mobileDataChangeObserver);
boolean defaultDataSetting = isDataEnabled();
// set data to not the default!
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
tm -> tm.setDataEnabled(!defaultDataSetting));
Boolean dataChangedResult = queue.poll(TOLERANCE, TimeUnit.MILLISECONDS);
assertNotNull("Data setting was not changed", dataChangedResult);
assertEquals("Data enable change didn't work", !defaultDataSetting,
dataChangedResult);
// and then do a reset to move data to default again.
try {
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
TelephonyManager::resetSettings, "android.permission.NETWORK_SETTINGS");
} catch (SecurityException e) {
fail("TelephonyManager#resetSettings requires the"
+ " android.Manifest.permission.NETWORK_SETTINGS permission");
}
dataChangedResult = queue.poll(TOLERANCE, TimeUnit.MILLISECONDS);
assertNotNull("Data setting was not changed", dataChangedResult);
assertEquals("resetSettings did not reset default data", defaultDataSetting,
dataChangedResult);
}
@Test
public void testGetServiceState() throws InterruptedException {
if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
return;
}
TestThread t = new TestThread(new Runnable() {
public void run() {
Looper.prepare();
mListener = new PhoneStateListener() {
@Override
public void onServiceStateChanged(ServiceState serviceState) {
synchronized (mLock) {
mServiceState = serviceState;
mLock.notify();
}
}
};
mTelephonyManager.listen(mListener, PhoneStateListener.LISTEN_SERVICE_STATE);
Looper.loop();
}
});
synchronized (mLock) {
t.start();
mLock.wait(TOLERANCE);
}
assertEquals(mServiceState, mTelephonyManager.getServiceState());
}
@Test
public void testGetSimLocale() throws InterruptedException {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
Log.d(TAG,"skipping test that requires Telephony");
return;
}
if (SubscriptionManager.getDefaultSubscriptionId()
== SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
fail("Expected SIM inserted");
}
Locale locale = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getSimLocale());
Log.d(TAG, "testGetSimLocale: " + locale);
assertNotNull(locale);
}
/**
* Tests that a GSM device properly reports either the correct TAC (type allocation code) or
* null.
* The TAC should match the first 8 digits of the IMEI.
*/
@Test
public void testGetTac() {
String tac = mTelephonyManager.getTypeAllocationCode();
String imei = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getImei());
if (tac == null || imei == null) {
return;
}
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
assertEquals(imei.substring(0, 8), tac);
}
}
}
/**
* Tests that a CDMA device properly reports either the correct MC (manufacturer code) or null.
* The MC should match the first 8 digits of the MEID.
*/
@Test
public void testGetMc() {
String mc = mTelephonyManager.getManufacturerCode();
String meid = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getMeid());
if (mc == null || meid == null) {
return;
}
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
assertEquals(meid.substring(0, 8), mc);
}
}
}
/**
* Tests that the device properly reports either a valid IMEI or null.
*/
@Test
public void testGetImei() {
String imei = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getImei());
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
assertImei(imei);
}
}
}
/**
* Tests that the device properly reports either a valid IMEI or null.
*/
@Test
public void testGetImeiForSlot() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
for (int i = 0; i < mTelephonyManager.getPhoneCount(); i++) {
// The compiler error 'local variables referenced from a lambda expression must be final
// or effectively final' is reported when using i, so assign it to a final variable.
final int currI = i;
String imei = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getImei(currI));
if (!TextUtils.isEmpty(imei)) {
assertImei(imei);
}
}
// Also verify that no exception is thrown for any slot index (including invalid ones)
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getImei(-1));
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getImei(mTelephonyManager.getPhoneCount()));
}
/**
* Verifies that {@link TelephonyManager#getRadioPowerState()} does not throw any exception
* and returns radio on.
*/
@Test
public void testGetRadioPowerState() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
// Also verify that no exception is thrown.
assertThat(mTelephonyManager.getRadioPowerState()).isEqualTo(
TelephonyManager.RADIO_POWER_ON);
}
/**
* Verifies that {@link TelephonyManager#setCarrierDataEnabled(boolean)} does not throw any
* exception. TODO enhance later if we have an API to get data enabled state.
*/
@Test
public void testSetCarrierDataEnabled() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
// Also verify that no exception is thrown.
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.setCarrierDataEnabled(false));
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.setCarrierDataEnabled(true));
}
/**
* Verifies that {@link TelephonyManager#rebootRadio()} does not throw any exception
* and final radio state is radio power on.
*/
@Test
public void testRebootRadio() throws Throwable {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
assertEquals(mTelephonyManager.getServiceState().getState(), ServiceState.STATE_IN_SERVICE);
TestThread t = new TestThread(new Runnable() {
public void run() {
Looper.prepare();
mListener = new PhoneStateListener() {
@Override
public void onRadioPowerStateChanged(
@RadioPowerState int state) {
synchronized (mLock) {
if (state == TelephonyManager.RADIO_POWER_ON && mHasRadioPowerOff) {
mRadioRebootTriggered = true;
mLock.notify();
} else if (state == TelephonyManager.RADIO_POWER_OFF) {
// reboot must go to power off
mHasRadioPowerOff = true;
}
}
}
};
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.listen(mListener,
PhoneStateListener.LISTEN_RADIO_POWER_STATE_CHANGED));
Looper.loop();
}
});
assertThat(mTelephonyManager.getRadioPowerState()).isEqualTo(
TelephonyManager.RADIO_POWER_ON);
assertThat(mRadioRebootTriggered).isFalse();
assertThat(mHasRadioPowerOff).isFalse();
boolean success = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.rebootRadio());
//skip this test if not supported or unsuccessful (success=false)
if(!success) {
return;
}
t.start();
synchronized (mLock) {
// reboot takes longer time
if (!mRadioRebootTriggered) {
mLock.wait(10000);
}
}
assertThat(mTelephonyManager.getRadioPowerState()).isEqualTo(
TelephonyManager.RADIO_POWER_ON);
assertThat(mRadioRebootTriggered).isTrue();
// note, other telephony states might not resumes properly at this point. e.g, service state
// might still in the transition from OOS to In service. Thus we need to wait for in
// service state before running next tests.
t = new TestThread(new Runnable() {
public void run() {
Looper.prepare();
mListener = new PhoneStateListener() {
@Override
public void onServiceStateChanged(ServiceState serviceState) {
synchronized (mLock) {
if (serviceState.getState() == ServiceState.STATE_IN_SERVICE) {
mServiceStateChangedCalled = true;
mLock.notify();
}
}
}
};
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.listen(mListener, PhoneStateListener.LISTEN_SERVICE_STATE));
Looper.loop();
}
});
synchronized (mLock) {
t.start();
if (!mServiceStateChangedCalled) {
mLock.wait(60000);
}
}
assertThat(mTelephonyManager.getServiceState().getState()).isEqualTo(
ServiceState.STATE_IN_SERVICE);
}
/**
* Verifies that {@link TelephonyManager#getAidForAppType(int)} does not throw any exception
* for all supported subscription app type.
*/
@Test
public void testGetAidForAppType() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getAidForAppType(TelephonyManager.APPTYPE_SIM));
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getAidForAppType(TelephonyManager.APPTYPE_CSIM));
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getAidForAppType(TelephonyManager.APPTYPE_RUIM));
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getAidForAppType(TelephonyManager.APPTYPE_ISIM));
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getAidForAppType(TelephonyManager.APPTYPE_USIM));
}
/**
* Verifies that {@link TelephonyManager#getIsimDomain()} does not throw any exception
*/
@Test
public void testGetIsimDomain() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getIsimDomain());
}
/**
* Verifies that {@link TelephonyManager#getIsimImpu()} does not throw any exception when called
* and has the correct permissions.
*/
@Ignore("API moved back to @hide for Android R.")
@Test
public void testGetIsimImpu() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
TelephonyManager::getIsimImpu);
// Try without the correct permissions and ensure it fails.
try {
mTelephonyManager.getIsimImpu();
fail();
} catch (SecurityException e) {
// expected
}
}
/**
* Basic test to ensure {@link NetworkRegistrationInfo#getRegisteredPlmn()} provides valid
* information.
*/
@Test
public void testNetworkRegistrationInfoRegisteredPlmn() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
// get NetworkRegistration object
ServiceState ss = mTelephonyManager.getServiceState();
assertNotNull(ss);
boolean hasRegistered = false;
for (NetworkRegistrationInfo nwReg : ss.getNetworkRegistrationInfoList()) {
if (nwReg.isRegistered()
&& nwReg.getTransportType() == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
hasRegistered = true;
String plmnId = nwReg.getRegisteredPlmn();
// CDMA doesn't have PLMN IDs. Rather than put CID|NID here, instead it will be
// empty. It's a case that's becoming less important over time, but for now a
// device that's only registered on CDMA needs to pass this test.
if (nwReg.getCellIdentity() instanceof android.telephony.CellIdentityCdma) {
assertTrue(TextUtils.isEmpty(plmnId));
} else {
assertFalse(TextUtils.isEmpty(plmnId));
assertTrue("PlmnId() out of range [00000 - 999999], PLMN ID=" + plmnId,
plmnId.matches("^[0-9]{5,6}$"));
}
}
}
assertTrue(hasRegistered);
}
/**
* Basic test to ensure {@link NetworkRegistrationInfo#isRoaming()} does not throw any
* exception.
*/
@Test
public void testNetworkRegistrationInfoIsRoaming() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
// get NetworkRegistration object
NetworkRegistrationInfo nwReg = mTelephonyManager.getServiceState()
.getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_CS,
AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
assertThat(nwReg).isNotNull();
nwReg.isRoaming();
}
/**
* Basic test to ensure {@link NetworkRegistrationInfo#getRoamingType()} ()} does not throw any
* exception and returns valid result
* @see ServiceState.RoamingType
*/
@Test
public void testNetworkRegistrationInfoGetRoamingType() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
// get NetworkRegistration object for voice
NetworkRegistrationInfo nwReg = mTelephonyManager.getServiceState()
.getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_CS,
AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
assertNotNull(nwReg);
assertThat(nwReg.getRoamingType()).isIn(ROAMING_TYPES);
// getNetworkRegistration object for data
// get NetworkRegistration object for voice
nwReg = mTelephonyManager.getServiceState()
.getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
assertThat(nwReg).isNotNull();
assertThat(nwReg.getRoamingType()).isIn(ROAMING_TYPES);
}
/**
* Basic test to ensure {@link NetworkRegistrationInfo#getAccessNetworkTechnology()} not
* throw any exception and returns valid result
* @see android.telephony.Annotation.NetworkType
*/
@Test
public void testNetworkRegistationStateGetAccessNetworkTechnology() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
// get NetworkRegistration object for voice
NetworkRegistrationInfo nwReg = mTelephonyManager.getServiceState()
.getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_CS,
AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
assertThat(nwReg).isNotNull();
assertThat(nwReg.getAccessNetworkTechnology()).isIn(NETWORK_TYPES);
// get NetworkRegistation object for data
nwReg = mTelephonyManager.getServiceState()
.getNetworkRegistrationInfo(NetworkRegistrationInfo.DOMAIN_PS,
AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
assertThat(nwReg).isNotNull();
assertThat(nwReg.getAccessNetworkTechnology()).isIn(NETWORK_TYPES);
}
/**
* Tests that the device properly reports either a valid MEID or null.
*/
@Test
public void testGetMeid() {
String meid = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getMeid());
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
assertMeidEsn(meid);
}
}
}
/**
* Tests that the device properly reports either a valid MEID or null.
*/
@Test
public void testGetMeidForSlot() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
SubscriptionManager sm = getContext().getSystemService(SubscriptionManager.class);
List<SubscriptionInfo> subInfos = sm.getActiveSubscriptionInfoList();
if (subInfos != null) {
for (SubscriptionInfo subInfo : subInfos) {
int slotIndex = subInfo.getSimSlotIndex();
int subId = subInfo.getSubscriptionId();
TelephonyManager tm = mTelephonyManager.createForSubscriptionId(subId);
if (tm.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
String meid = ShellIdentityUtils.invokeMethodWithShellPermissions(
mTelephonyManager,
(telephonyManager) -> telephonyManager.getMeid(slotIndex));
if (!TextUtils.isEmpty(meid)) {
assertMeidEsn(meid);
}
}
}
}
// Also verify that no exception is thrown for any slot index (including invalid ones)
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getMeid(-1));
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getMeid(mTelephonyManager.getPhoneCount()));
}
/**
* Tests sendDialerSpecialCode API.
* Expects a security exception since the caller does not have carrier privileges or is not the
* current default dialer app.
*/
@Test
public void testSendDialerSpecialCode() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
Log.d(TAG, "Skipping test that requires FEATURE_TELEPHONY");
return;
}
try {
mTelephonyManager.sendDialerSpecialCode("4636");
fail("Expected SecurityException. App does not have carrier privileges or is not the "
+ "default dialer app");
} catch (SecurityException expected) {
}
}
/**
* Tests that the device properly reports the contents of EF_FPLMN or null
*/
@Test
public void testGetForbiddenPlmns() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
String[] plmns = mTelephonyManager.getForbiddenPlmns();
int phoneType = mTelephonyManager.getPhoneType();
switch (phoneType) {
case TelephonyManager.PHONE_TYPE_GSM:
assertNotNull("Forbidden PLMNs must be valid or an empty list!", plmns);
case TelephonyManager.PHONE_TYPE_CDMA:
case TelephonyManager.PHONE_TYPE_NONE:
if (plmns == null) {
return;
}
}
for(String plmn : plmns) {
assertTrue(
"Invalid Length for PLMN-ID, must be 5 or 6! plmn=" + plmn,
plmn.length() >= 5 && plmn.length() <= 6);
assertTrue(
"PLMNs must be strings of digits 0-9! plmn=" + plmn,
android.text.TextUtils.isDigitsOnly(plmn));
}
}
/**
* Tests that the device properly sets and pads the contents of EF_FPLMN
*/
@Test
public void testSetForbiddenPlmns() {
if (!supportSetFplmn()) {
return;
}
String[] originalFplmns = mTelephonyManager.getForbiddenPlmns();
try {
int numFplmnsSet = mTelephonyManager.setForbiddenPlmns(FPLMN_TEST);
String[] writtenFplmns = mTelephonyManager.getForbiddenPlmns();
assertEquals("Wrong return value for setFplmns with less than required fplmns: "
+ numFplmnsSet, FPLMN_TEST.size(), numFplmnsSet);
assertEquals("Wrong Fplmns content written", FPLMN_TEST, Arrays.asList(writtenFplmns));
} finally {
// Restore
mTelephonyManager.setForbiddenPlmns(Arrays.asList(originalFplmns));
}
}
/**
* Tests that the device properly truncates the contents of EF_FPLMN when provided size
* is too big.
*/
@Test
public void testSetForbiddenPlmnsTruncate() {
if (!supportSetFplmn()) {
return;
}
String[] originalFplmns = mTelephonyManager.getForbiddenPlmns();
try {
List<String> targetFplmns = new ArrayList<>();
for (int i = 0; i < MIN_FPLMN_NUM; i++) {
targetFplmns.add(PLMN_A);
}
for (int i = MIN_FPLMN_NUM; i < MAX_FPLMN_NUM; i++) {
targetFplmns.add(PLMN_B);
}
int numFplmnsSet = mTelephonyManager.setForbiddenPlmns(targetFplmns);
String[] writtenFplmns = mTelephonyManager.getForbiddenPlmns();
assertTrue("Wrong return value for setFplmns with overflowing fplmns: " + numFplmnsSet,
numFplmnsSet < MAX_FPLMN_NUM);
assertEquals("Number of Fplmns set does not equal number of Fplmns available",
numFplmnsSet, writtenFplmns.length);
assertEquals("Wrong Fplmns content written", targetFplmns.subList(0, numFplmnsSet),
Arrays.asList(writtenFplmns));
} finally {
// Restore
mTelephonyManager.setForbiddenPlmns(Arrays.asList(originalFplmns));
}
}
/**
* Tests that the device properly deletes the contents of EF_FPLMN
*/
@Test
public void testSetForbiddenPlmnsDelete() {
if (!supportSetFplmn()) {
return;
}
String[] originalFplmns = mTelephonyManager.getForbiddenPlmns();
try {
// Support test for empty SIM
List<String> targetDummyFplmns = new ArrayList<>();
for (int i = 0; i < MIN_FPLMN_NUM; i++) {
targetDummyFplmns.add(PLMN_A);
}
mTelephonyManager.setForbiddenPlmns(targetDummyFplmns);
String[] writtenDummyFplmns = mTelephonyManager.getForbiddenPlmns();
assertEquals(targetDummyFplmns, Arrays.asList(writtenDummyFplmns));
List<String> targetFplmns = new ArrayList<>();
int numFplmnsSet = mTelephonyManager.setForbiddenPlmns(targetFplmns);
String[] writtenFplmns = mTelephonyManager.getForbiddenPlmns();
assertEquals("Wrong return value for setFplmns with empty list", 0, numFplmnsSet);
assertEquals("Wrong number of Fplmns written", 0, writtenFplmns.length);
// TODO wait for 10 minutes or so for the FPLMNS list to grow back
} finally {
// Restore
mTelephonyManager.setForbiddenPlmns(Arrays.asList(originalFplmns));
}
}
/**
* Tests that setForbiddenPlmns properly handles null input
*/
@Test
public void testSetForbiddenPlmnsVoid() {
if (!supportSetFplmn()) {
return;
}
String[] originalFplmns = mTelephonyManager.getForbiddenPlmns();
try {
mTelephonyManager.setForbiddenPlmns(null);
fail("Expected IllegalArgumentException. Null input is not allowed");
} catch (IllegalArgumentException expected) {
} finally {
// Restore
mTelephonyManager.setForbiddenPlmns(Arrays.asList(originalFplmns));
}
}
/**
* Tests that the device properly reports the contents of ManualNetworkSelectionPlmn
* The setting is not persisted selection
*/
@Test
public void testGetManualNetworkSelectionPlmnNonPersisted() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
try {
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.setNetworkSelectionModeManual(
TESTING_PLMN/* operatorNumeric */, false /* persistSelection */));
String plmn = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getManualNetworkSelectionPlmn());
assertEquals(TESTING_PLMN, plmn);
} finally {
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.setNetworkSelectionModeAutomatic());
}
}
/**
* Tests that the device properly reports the contents of ManualNetworkSelectionPlmn
* The setting is persisted selection
*/
@Test
public void testGetManualNetworkSelectionPlmnPersisted() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
try {
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.setNetworkSelectionModeManual(
TESTING_PLMN/* operatorNumeric */, true /* persistSelection */));
String plmn = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getManualNetworkSelectionPlmn());
assertEquals(TESTING_PLMN, plmn);
} finally {
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.setNetworkSelectionModeAutomatic());
}
}
/**
* Verify that TelephonyManager.getCardIdForDefaultEuicc returns a positive value or either
* UNINITIALIZED_CARD_ID or UNSUPPORTED_CARD_ID.
*/
@Test
public void testGetCardIdForDefaultEuicc() {
int cardId = mTelephonyManager.getCardIdForDefaultEuicc();
assertTrue("Card ID for default EUICC is not a valid value",
cardId == TelephonyManager.UNSUPPORTED_CARD_ID
|| cardId == TelephonyManager.UNINITIALIZED_CARD_ID
|| cardId >= 0);
}
/**
* Tests that a SecurityException is thrown when trying to access UiccCardsInfo.
*/
@Test
public void testGetUiccCardsInfoException() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
Log.d(TAG, "skipping test on device without FEATURE_TELEPHONY present");
return;
}
try {
// Requires READ_PRIVILEGED_PHONE_STATE or carrier privileges
List<UiccCardInfo> infos = mTelephonyManager.getUiccCardsInfo();
fail("Expected SecurityException. App does not have carrier privileges");
} catch (SecurityException e) {
}
}
/**
* Tests that UiccCardsInfo methods don't crash.
*/
@Test
public void testGetUiccCardsInfo() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
Log.d(TAG, "Skipping test that requires FEATURE_TELEPHONY");
return;
}
// Requires READ_PRIVILEGED_PHONE_STATE or carrier privileges
List<UiccCardInfo> infos =
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getUiccCardsInfo());
// test that these methods don't crash
if (infos.size() > 0) {
UiccCardInfo info = infos.get(0);
info.getIccId();
info.getEid();
info.isRemovable();
info.isEuicc();
info.getCardId();
info.getSlotIndex();
}
}
private static Context getContext() {
return InstrumentationRegistry.getContext();
}
/**
* Tests that the device properly reports the contents of NetworkSelectionMode
*/
@Test
public void testGetNetworkSelectionMode() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
try {
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.setNetworkSelectionModeAutomatic());
} catch (Exception e) {
}
int networkMode = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getNetworkSelectionMode());
assertEquals(TelephonyManager.NETWORK_SELECTION_MODE_AUTO, networkMode);
}
/**
* Tests that the device properly sets the network selection mode to automatic.
* Expects a security exception since the caller does not have carrier privileges.
*/
@Test
public void testSetNetworkSelectionModeAutomatic() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
Log.d(TAG, "Skipping test that requires FEATURE_TELEPHONY");
return;
}
try {
mTelephonyManager.setNetworkSelectionModeAutomatic();
fail("Expected SecurityException. App does not have carrier privileges.");
} catch (SecurityException expected) {
}
}
/**
* Tests that the device properly asks the radio to connect to the input network and change
* selection mode to manual.
* Expects a security exception since the caller does not have carrier privileges.
*/
@Test
public void testSetNetworkSelectionModeManual() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
Log.d(TAG, "Skipping test that requires FEATURE_TELEPHONY");
return;
}
try {
mTelephonyManager.setNetworkSelectionModeManual(
"" /* operatorNumeric */, false /* persistSelection */);
fail("Expected SecurityException. App does not have carrier privileges.");
} catch (SecurityException expected) {
}
}
/**
* Tests that the device properly check whether selection mode was manual.
*/
@Test
public void testIsManualNetworkSelectionAllowed() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
Log.d(TAG, "Skipping test that requires FEATURE_TELEPHONY");
return;
}
assertTrue(ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.isManualNetworkSelectionAllowed()));
}
/**
* Construct a CallAttributes object and test getters.
*/
@Test
public void testCallAttributes() {
CallQuality cq = new CallQuality();
PreciseCallState pcs = new PreciseCallState();
CallAttributes ca = new CallAttributes(pcs, TelephonyManager.NETWORK_TYPE_UNKNOWN, cq);
assertEquals(pcs, ca.getPreciseCallState());
assertEquals(TelephonyManager.NETWORK_TYPE_UNKNOWN, ca.getNetworkType());
assertEquals(cq, ca.getCallQuality());
}
/**
* Checks that a zeroed-out default CallQuality object can be created
*/
@Test
public void testCallQuality() {
CallQuality cq = new CallQuality();
assertEquals(0, cq.getDownlinkCallQualityLevel());
assertEquals(0, cq.getUplinkCallQualityLevel());
assertEquals(0, cq.getCallDuration());
assertEquals(0, cq.getNumRtpPacketsTransmitted());
assertEquals(0, cq.getNumRtpPacketsReceived());
assertEquals(0, cq.getNumRtpPacketsTransmittedLost());
assertEquals(0, cq.getNumRtpPacketsNotReceived());
assertEquals(0, cq.getAverageRelativeJitter());
assertEquals(0, cq.getMaxRelativeJitter());
assertEquals(0, cq.getAverageRoundTripTime());
assertEquals(0, cq.getCodecType());
}
// Reference: packages/services/Telephony/ecc/input/eccdata.txt
private static final Map<String, String> EMERGENCY_NUMBERS_FOR_COUNTRIES =
new HashMap<String, String>() {{
put("au", "000");
put("ca", "911");
put("de", "112");
put("gb", "999");
put("in", "112");
put("jp", "110");
put("sg", "999");
put("tw", "110");
put("us", "911");
}};
/**
* Tests TelephonyManager.getEmergencyNumberList.
*
* Also enforce country-specific emergency number in CTS.
*/
@Test
public void testGetEmergencyNumberList() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
Map<Integer, List<EmergencyNumber>> emergencyNumberList =
mTelephonyManager.getEmergencyNumberList();
assertFalse(emergencyNumberList == null);
checkEmergencyNumberFormat(emergencyNumberList);
int defaultSubId = mSubscriptionManager.getDefaultSubscriptionId();
for (Map.Entry<String, String> entry : EMERGENCY_NUMBERS_FOR_COUNTRIES.entrySet()) {
if (mTelephonyManager.getNetworkCountryIso().equals(entry.getKey())) {
assertTrue(checkIfEmergencyNumberListHasSpecificAddress(
emergencyNumberList.get(defaultSubId), entry.getValue()));
}
}
}
/**
* Tests TelephonyManager.isEmergencyNumber.
*
* Also enforce country-specific emergency number in CTS.
*/
@Test
public void testIsEmergencyNumber() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
for (Map.Entry<String, String> entry : EMERGENCY_NUMBERS_FOR_COUNTRIES.entrySet()) {
if (mTelephonyManager.getNetworkCountryIso().equals(entry.getKey())) {
assertTrue(mTelephonyManager.isEmergencyNumber(entry.getValue()));
}
}
}
/**
* Tests TelephonyManager.isPotentialEmergencyNumber.
*/
@Test
public void testIsPotentialEmergencyNumber() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
String countryIso = mTelephonyManager.getNetworkCountryIso();
String potentialEmergencyAddress = "91112345";
// According to com.android.i18n.phonenumbers.ShortNumberInfo, in
// these countries, if extra digits are added to an emergency number,
// it no longer connects to the emergency service.
if (countryIso.equals("br") || countryIso.equals("cl") || countryIso.equals("ni")) {
assertFalse(ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.isPotentialEmergencyNumber(potentialEmergencyAddress)));
} else {
assertTrue(ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.isPotentialEmergencyNumber(potentialEmergencyAddress)));
}
}
/**
* Tests {@link TelephonyManager#getSupportedRadioAccessFamily()}
*/
@Test
public void testGetRadioAccessFamily() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
long raf = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getSupportedRadioAccessFamily());
assertThat(raf).isNotEqualTo(TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN);
}
private static void assertSetOpportunisticSubSuccess(int value) {
assertThat(value).isEqualTo(TelephonyManager.SET_OPPORTUNISTIC_SUB_SUCCESS);
}
private static void assertSetOpportunisticNoOpportunisticSub(int value) {
assertThat(value).isEqualTo(
TelephonyManager.SET_OPPORTUNISTIC_SUB_NO_OPPORTUNISTIC_SUB_AVAILABLE);
}
/**
* Tests {@link TelephonyManager#setPreferredOpportunisticDataSubscription} and
* {@link TelephonyManager#getPreferredOpportunisticDataSubscription}
*/
@Test
public void testPreferredOpportunisticDataSubscription() {
int randomSubId = 1;
int activeSubscriptionInfoCount = ShellIdentityUtils.invokeMethodWithShellPermissions(
mSubscriptionManager, (tm) -> tm.getActiveSubscriptionInfoCount());
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
if (mTelephonyManager.getPhoneCount() == 1) {
return;
}
if (mTelephonyManager.getPhoneCount() == 2 && activeSubscriptionInfoCount != 2) {
fail("This test requires two SIM cards.");
}
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.setPreferredOpportunisticDataSubscription(
SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false,
null, null));
// wait for the data change to take effect
waitForMs(500);
int subId =
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getPreferredOpportunisticDataSubscription());
assertThat(subId).isEqualTo(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
List<SubscriptionInfo> subscriptionInfoList =
ShellIdentityUtils.invokeMethodWithShellPermissions(mSubscriptionManager,
(tm) -> tm.getOpportunisticSubscriptions());
Consumer<Integer> callbackSuccess = TelephonyManagerTest::assertSetOpportunisticSubSuccess;
Consumer<Integer> callbackNoOpSub =
TelephonyManagerTest::assertSetOpportunisticNoOpportunisticSub;
if (subscriptionInfoList == null || subscriptionInfoList.size() == 0) {
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.setPreferredOpportunisticDataSubscription(randomSubId, false,
AsyncTask.SERIAL_EXECUTOR, callbackNoOpSub));
// wait for the data change to take effect
waitForMs(500);
subId = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getPreferredOpportunisticDataSubscription());
assertThat(subId).isEqualTo(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
} else {
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.setPreferredOpportunisticDataSubscription(
subscriptionInfoList.get(0).getSubscriptionId(), false,
AsyncTask.SERIAL_EXECUTOR, callbackSuccess));
// wait for the data change to take effect
waitForMs(500);
subId = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getPreferredOpportunisticDataSubscription());
assertThat(subId).isEqualTo(subscriptionInfoList.get(0).getSubscriptionId());
}
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.setPreferredOpportunisticDataSubscription(
SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, false,
AsyncTask.SERIAL_EXECUTOR, callbackSuccess));
// wait for the data change to take effect
waitForMs(500);
subId = ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getPreferredOpportunisticDataSubscription());
assertThat(subId).isEqualTo(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
}
private static void assertUpdateAvailableNetworkSuccess(int value) {
assertThat(value).isEqualTo(TelephonyManager.UPDATE_AVAILABLE_NETWORKS_SUCCESS);
}
private static void assertUpdateAvailableNetworkInvalidArguments(int value) {
assertThat(value).isEqualTo(TelephonyManager.UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
}
private static void assertUpdateAvailableNetworkNoOpportunisticSub(int value) {
assertThat(value).isEqualTo(
TelephonyManager.UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE);
}
private static boolean checkIfEmergencyNumberListHasSpecificAddress(
List<EmergencyNumber> emergencyNumberList, String address) {
for (EmergencyNumber emergencyNumber : emergencyNumberList) {
if (address.equals(emergencyNumber.getNumber())) {
return true;
}
}
return false;
}
private static void checkEmergencyNumberFormat(
Map<Integer, List<EmergencyNumber>> emergencyNumberLists) {
for (List<EmergencyNumber> emergencyNumberList : emergencyNumberLists.values()) {
for (EmergencyNumber emergencyNumber : emergencyNumberList) {
// Validate Emergency number address
assertTrue(validateEmergencyNumberAddress(emergencyNumber.getNumber()));
// Validate Emergency number country Iso
assertTrue(validateEmergencyNumberCountryIso(emergencyNumber.getCountryIso()));
// Validate Emergency number mnc
assertTrue(validateEmergencyNumberMnc(emergencyNumber.getMnc()));
// Validate Emergency service category list
assertTrue(validateEmergencyServiceCategoryList(
emergencyNumber.getEmergencyServiceCategories()));
// Validate Emergency number source list
assertTrue(validateEmergencyNumberSourceList(
emergencyNumber.getEmergencyNumberSources()));
// Validate Emergency URN list
// (just verify it is not null, because the support of this field is optional)
assertTrue(emergencyNumber.getEmergencyUrns() != null);
// Validat Emergency call routing
assertTrue(validateEmergencyCallRouting(
emergencyNumber.getEmergencyCallRouting()));
// Valid the emergency number should be at least in a valid source.
assertTrue(validateEmergencyNumberFromAnySource(emergencyNumber));
// Valid the emergency number should be at least in a valid category.
assertTrue(validateEmergencyNumberInAnyCategory(emergencyNumber));
}
// Validate compareTo
assertTrue(validateEmergencyNumberCompareTo(emergencyNumberList));
}
}
/**
* Tests {@link TelephonyManager#updateAvailableNetworks}
*/
@Test
public void testUpdateAvailableNetworks() {
int randomSubId = 1;
int activeSubscriptionInfoCount = ShellIdentityUtils.invokeMethodWithShellPermissions(
mSubscriptionManager, (tm) -> tm.getActiveSubscriptionInfoCount());
boolean isOpportunisticNetworkEnabled = ShellIdentityUtils.invokeMethodWithShellPermissions(
mTelephonyManager, (tm) -> tm.isOpportunisticNetworkEnabled());
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
if (!isOpportunisticNetworkEnabled) {
return;
}
if (mTelephonyManager.getPhoneCount() == 1) {
return;
}
if (mTelephonyManager.getPhoneCount() == 2 && activeSubscriptionInfoCount != 2) {
fail("This test requires two SIM cards.");
}
List<SubscriptionInfo> subscriptionInfoList =
ShellIdentityUtils.invokeMethodWithShellPermissions(mSubscriptionManager,
(tm) -> tm.getOpportunisticSubscriptions());
List<String> mccMncs = new ArrayList<String>();
List<Integer> bands = new ArrayList<Integer>();
List<AvailableNetworkInfo> availableNetworkInfos = new ArrayList<AvailableNetworkInfo>();
Consumer<Integer> callbackSuccess =
TelephonyManagerTest::assertUpdateAvailableNetworkSuccess;
Consumer<Integer> callbackFailure =
TelephonyManagerTest::assertUpdateAvailableNetworkInvalidArguments;
Consumer<Integer> callbackNoOpSub =
TelephonyManagerTest::assertUpdateAvailableNetworkNoOpportunisticSub;
if (subscriptionInfoList == null || subscriptionInfoList.size() == 0
|| !mSubscriptionManager.isActiveSubscriptionId(
subscriptionInfoList.get(0).getSubscriptionId())) {
AvailableNetworkInfo availableNetworkInfo = new AvailableNetworkInfo(randomSubId,
AvailableNetworkInfo.PRIORITY_HIGH, mccMncs, bands);
availableNetworkInfos.add(availableNetworkInfo);
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.updateAvailableNetworks(availableNetworkInfos,
AsyncTask.SERIAL_EXECUTOR, callbackNoOpSub));
// wait for the data change to take effect
waitForMs(500);
// clear all the operations at the end of test.
availableNetworkInfos.clear();
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.updateAvailableNetworks(availableNetworkInfos,
AsyncTask.SERIAL_EXECUTOR, callbackFailure));
} else {
AvailableNetworkInfo availableNetworkInfo = new AvailableNetworkInfo(
subscriptionInfoList.get(0).getSubscriptionId(),
AvailableNetworkInfo.PRIORITY_HIGH, mccMncs, bands);
availableNetworkInfos.add(availableNetworkInfo);
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.updateAvailableNetworks(availableNetworkInfos,
AsyncTask.SERIAL_EXECUTOR, callbackSuccess));
// wait for the data change to take effect
waitForMs(500);
// clear all the operations at the end of test.
availableNetworkInfos.clear();
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.updateAvailableNetworks(availableNetworkInfos,
AsyncTask.SERIAL_EXECUTOR, callbackSuccess));
}
}
@Test
public void testSwitchMultiSimConfig() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
boolean rebootRequired = ShellIdentityUtils.invokeMethodWithShellPermissions(
mTelephonyManager, (tm) -> tm.doesSwitchMultiSimConfigTriggerReboot());
// It's hard to test if reboot is needed.
if (!rebootRequired) {
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.switchMultiSimConfig(1));
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.switchMultiSimConfig(2));
} else {
// This should result in no-op.
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.switchMultiSimConfig(mTelephonyManager.getPhoneCount()));
}
}
@Test
public void testIccOpenLogicalChannelBySlot() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
// just verify no crash
try {
ShellIdentityUtils.invokeMethodWithShellPermissions(
mTelephonyManager, (tm) -> tm.iccOpenLogicalChannelBySlot(0, null, 0));
} catch (IllegalArgumentException e) {
// IllegalArgumentException is okay, just not SecurityException
}
}
@Test
public void testIccCloseLogicalChannelBySlot() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
// just verify no crash
try {
ShellIdentityUtils.invokeMethodWithShellPermissions(
mTelephonyManager, (tm) -> tm.iccCloseLogicalChannelBySlot(0, 0));
} catch (IllegalArgumentException e) {
// IllegalArgumentException is okay, just not SecurityException
}
}
@Test
public void testIccTransmitApduLogicalChannelBySlot() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
int slotIndex = getValidSlotIndex();
String result = ShellIdentityUtils.invokeMethodWithShellPermissions(
mTelephonyManager, (tm) -> tm.iccTransmitApduLogicalChannelBySlot(
slotIndex,
0 /* channel */,
0 /* cla */,
0 /* instruction */,
0 /* p1 */,
0 /* p2 */,
0 /* p3 */,
null /* data */));
assertTrue(TextUtils.isEmpty(result));
}
@Test
public void testIccTransmitApduBasicChannelBySlot() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
// just verify no crash
int slotIndex = getValidSlotIndex();
try {
ShellIdentityUtils.invokeMethodWithShellPermissions(
mTelephonyManager, (tm) -> tm.iccTransmitApduBasicChannelBySlot(
slotIndex,
0 /* cla */,
0 /* instruction */,
0 /* p1 */,
0 /* p2 */,
0 /* p3 */,
null /* data */));
} catch (IllegalArgumentException e ) {
// IllegalArgumentException is okay, just not SecurityException
}
}
@Test
public void testIsDataEnabledForApn() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
// verify SecurityException
try {
mTelephonyManager.isDataEnabledForApn(ApnSetting.TYPE_MMS);
fail("testIsDataEnabledForApn: Expected SecurityException on isDataEnabledForApn");
} catch (SecurityException se) {
// expected
}
// test with permission
try {
ShellIdentityUtils.invokeMethodWithShellPermissions(
mTelephonyManager, (tm) -> tm.isDataEnabledForApn(ApnSetting.TYPE_MMS));
} catch (SecurityException se) {
fail("testIsDataEnabledForApn: SecurityException not expected");
}
}
@Test
public void testGetCarrierInfoForImsiEncryption() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
// test without permission: verify SecurityException
try {
mTelephonyManager.getCarrierInfoForImsiEncryption(TelephonyManager.KEY_TYPE_EPDG);
fail("testGetCarrierInfoForImsiEncryption: "
+ "SecurityException expected on getCarrierInfoForImsiEncryption");
} catch (SecurityException se) {
// expected
}
try {
mTelephonyManager.getCarrierInfoForImsiEncryption(TelephonyManager.KEY_TYPE_WLAN);
fail("testGetCarrierInfoForImsiEncryption: "
+ "SecurityException expected on getCarrierInfoForImsiEncryption");
} catch (SecurityException se) {
// expected
}
// test with permission
try {
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getCarrierInfoForImsiEncryption(TelephonyManager.KEY_TYPE_EPDG));
} catch (SecurityException se) {
fail("testGetCarrierInfoForImsiEncryption: SecurityException not expected");
} catch (IllegalArgumentException iae) {
// IllegalArgumentException is okay, just not SecurityException
}
try {
ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
(tm) -> tm.getCarrierInfoForImsiEncryption(TelephonyManager.KEY_TYPE_WLAN));
} catch (SecurityException se) {
fail("testGetCarrierInfoForImsiEncryption: SecurityException not expected");
} catch (IllegalArgumentException iae) {
// IllegalArgumentException is okay, just not SecurityException
}
}
@Test
public void testResetCarrierKeysForImsiEncryption() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
// test without permission: verify SecurityException
try {
mTelephonyManager.resetCarrierKeysForImsiEncryption();
fail("testResetCarrierKeysForImsiEncryption: SecurityException expected");
} catch (SecurityException se) {
// expected
}
// test with permission
try {
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
mTelephonyManager,
(tm) -> tm.resetCarrierKeysForImsiEncryption());
} catch (SecurityException se) {
fail("testResetCarrierKeysForImsiEncryption: SecurityException not expected");
}
}
@Test
public void testIsInEmergencySmsMode() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
// test without permission: verify SecurityException
try {
mTelephonyManager.isInEmergencySmsMode();
fail("testIsInEmergencySmsMode: SecurityException expected");
} catch (SecurityException se) {
// expected
}
// test with permission
try {
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
mTelephonyManager,
(tm) -> tm.isInEmergencySmsMode());
} catch (SecurityException se) {
fail("testIsInEmergencySmsMode: SecurityException not expected");
}
}
@Test
public void testGetSubscriptionId() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
TelephonyManager tm = mTelephonyManager.createForSubscriptionId(1);
int subId = tm.getSubscriptionId();
assertEquals(1, subId);
}
@Test
public void testSetAllowedNetworkTypes() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
// test without permission: verify SecurityException
long allowedNetworkTypes = TelephonyManager.NETWORK_TYPE_BITMASK_NR;
try {
mTelephonyManager.setAllowedNetworkTypes(allowedNetworkTypes);
fail("testSetPolicyDataEnabled: SecurityException expected");
} catch (SecurityException se) {
// expected
}
// test with permission
try {
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
mTelephonyManager,
(tm) -> tm.setAllowedNetworkTypes(allowedNetworkTypes));
long deviceAllowedNetworkTypes = ShellIdentityUtils.invokeMethodWithShellPermissions(
mTelephonyManager, (tm) -> {
return tm.getAllowedNetworkTypes();
}
);
assertEquals(allowedNetworkTypes, deviceAllowedNetworkTypes);
} catch (SecurityException se) {
fail("testSetAllowedNetworkTypes: SecurityException not expected");
}
}
@Test
public void testDisAllowedNetworkTypes() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
long allowedNetworkTypes = -1 & (~TelephonyManager.NETWORK_TYPE_BITMASK_NR);
long networkTypeBitmask = TelephonyManager.NETWORK_TYPE_BITMASK_NR
| TelephonyManager.NETWORK_TYPE_BITMASK_LTE
| TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA;
try {
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
mTelephonyManager,
(tm) -> tm.setAllowedNetworkTypes(allowedNetworkTypes));
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
mTelephonyManager,
(tm) -> tm.setPreferredNetworkTypeBitmask(networkTypeBitmask));
long modemNetworkTypeBitmask = ShellIdentityUtils.invokeMethodWithShellPermissions(
mTelephonyManager, (tm) -> {
return tm.getPreferredNetworkTypeBitmask();
}
);
long radioAccessFamily = ShellIdentityUtils.invokeMethodWithShellPermissions(
mTelephonyManager, (tm) -> {
return tm.getSupportedRadioAccessFamily();
}
);
// RadioAccessFamily won't include all bits of RAFs group, so transfer to preferred
// network type instead of using bitmask directly
int modemPreferredNetworkType = RadioAccessFamily.getNetworkTypeFromRaf(
(int) modemNetworkTypeBitmask);
int preferredNetworkType = RadioAccessFamily.getNetworkTypeFromRaf(
(int) (networkTypeBitmask & allowedNetworkTypes & radioAccessFamily));
assertEquals(preferredNetworkType, modemPreferredNetworkType);
} catch (SecurityException se) {
fail("testDisAllowedNetworkTypes: SecurityException not expected");
}
}
@Test
public void testGetSupportedModemCount() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
int supportedModemCount = mTelephonyManager.getSupportedModemCount();
int activeModemCount = mTelephonyManager.getActiveModemCount();
assertTrue(activeModemCount >= 0);
assertTrue(supportedModemCount >= activeModemCount);
}
@Test
public void testIsModemEnabledForSlot() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return;
}
int activeModemCount = mTelephonyManager.getActiveModemCount();
for (int i = 0; i < activeModemCount; i++) {
// Call isModemEnabledForSlot for each slot and verify no crash.
mTelephonyManager.isModemEnabledForSlot(i);
}
}
private boolean isDataEnabled() {
return ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
TelephonyManager::isDataEnabled);
}
private Uri getObservableDataEnabledUri(int subId) {
Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA);
if (mTelephonyManager.getActiveModemCount() != 1) {
uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA + subId);
}
return uri;
}
/**
* Validate Emergency Number address that only contains the dialable character.
*
* @param address Emergency number address to validate
* @return {@code true} if the address is valid; {@code false} otherwise.
*/
private static boolean validateEmergencyNumberAddress(String address) {
if (address == null) {
return false;
}
for (char c : address.toCharArray()) {
if (!isDialable(c)) {
return false;
}
}
return true;
}
/**
* Validate Emergency Number country Iso
*
* @param countryIso Emergency number country iso to validate
* @return {@code true} if the country iso is valid; {@code false} otherwise.
*/
private static boolean validateEmergencyNumberCountryIso(String countryIso) {
if (countryIso == null) {
return false;
}
int length = countryIso.length();
return length >= 0 && length <= 2;
}
/**
* Validate Emergency Number MNC
*
* @param mnc Emergency number MNC to validate
* @return {@code true} if the MNC is valid; {@code false} otherwise.
*/
private static boolean validateEmergencyNumberMnc(String mnc) {
if (mnc == null) {
return false;
}
int length = mnc.length();
return length >= 0 && length <= 3;
}
/**
* Validate Emergency service category list
*
* @param categories Emergency service category list to validate
* @return {@code true} if the category list is valid; {@code false} otherwise.
*/
private static boolean validateEmergencyServiceCategoryList(List<Integer> categories) {
if (categories == null) {
return false;
}
if (categories.contains(EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED)) {
return categories.size() == 1;
}
for (int category : categories) {
if (!EMERGENCY_SERVICE_CATEGORY_SET.contains(category)) {
return false;
}
}
return true;
}
/**
* Validate Emergency number source list
*
* @param categories Emergency number source list to validate
* @return {@code true} if the source list is valid; {@code false} otherwise.
*/
private static boolean validateEmergencyNumberSourceList(List<Integer> sources) {
if (sources == null) {
return false;
}
for (int source : sources) {
if (!EMERGENCY_NUMBER_SOURCE_SET.contains(source)) {
return false;
}
}
return true;
}
/**
* Validate Emergency call routing.
*
* @param routing Emergency call routing to validate
* @return {@code true} if the emergency call routing is valid; {@code false} otherwise.
*/
private static boolean validateEmergencyCallRouting(int routing) {
return routing >= EmergencyNumber.EMERGENCY_CALL_ROUTING_UNKNOWN
&& routing <= (EmergencyNumber.EMERGENCY_CALL_ROUTING_EMERGENCY
| EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
}
/**
* Valid the emergency number should be at least from a valid source.
*
* @param emergencyNumber Emergency number to verify
* @return {@code true} if the emergency number is from any source; {@code false} otherwise.
*/
private static boolean validateEmergencyNumberFromAnySource(EmergencyNumber emergencyNumber) {
boolean isFromAnySource = false;
for (int possibleSourceValue = EMERGENCY_NUMBER_SOURCE_RIL_ECCLIST;
possibleSourceValue <= (EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING
| EmergencyNumber.EMERGENCY_NUMBER_SOURCE_SIM
| EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE
| EmergencyNumber.EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG
| EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DEFAULT);
possibleSourceValue++) {
if (emergencyNumber.isFromSources(possibleSourceValue)) {
isFromAnySource = true;
break;
}
}
return isFromAnySource;
}
/**
* Valid the emergency number should be at least in a valid category.
*
* @param emergencyNumber Emergency number to verify
* @return {@code true} if it is in any category; {@code false} otherwise.
*/
private static boolean validateEmergencyNumberInAnyCategory(EmergencyNumber emergencyNumber) {
boolean isInAnyCategory = false;
for (int possibleCategoryValue = EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
possibleCategoryValue <= (EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE
| EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AMBULANCE
| EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE
| EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD
| EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE
| EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_MIEC
| EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_AIEC);
possibleCategoryValue++) {
if (emergencyNumber.isInEmergencyServiceCategories(possibleCategoryValue)) {
isInAnyCategory = true;
break;
}
}
return isInAnyCategory;
}
private static boolean validateEmergencyNumberCompareTo(
List<EmergencyNumber> emergencyNumberList) {
if (emergencyNumberList == null) {
return false;
}
if (emergencyNumberList.size() > 0) {
EmergencyNumber emergencyNumber = emergencyNumberList.get(0);
if (emergencyNumber.compareTo(emergencyNumber) != 0) {
return false;
}
}
return true;
}
private static boolean isDialable(char c) {
return (c >= '0' && c <= '9') || c == '*' || c == '#' || c == '+' || c == 'N';
}
private int getValidSlotIndex() {
return ShellIdentityUtils.invokeMethodWithShellPermissions(
mTelephonyManager, (tm) -> {
List<UiccCardInfo> cardInfos = mTelephonyManager.getUiccCardsInfo();
Set<String> presentCards = Arrays.stream(mTelephonyManager.getUiccSlotsInfo())
.filter(UiccSlotInfo::getIsActive)
.map(UiccSlotInfo::getCardId)
.filter(Objects::nonNull)
// hack around getUiccSlotsInfo not stripping trailing F
.map(s -> s.endsWith("F") ? s.substring(0, s.length() - 1) : s)
.collect(Collectors.toSet());
int slotIndex = -1;
for (UiccCardInfo cardInfo : cardInfos) {
if (presentCards.contains(cardInfo.getIccId())
|| presentCards.contains(cardInfo.getEid())) {
slotIndex = cardInfo.getSlotIndex();
break;
}
}
if (slotIndex < 0) {
fail("Test must be run with SIM card inserted, presentCards = "
+ presentCards + "cardinfos = " + cardInfos);
}
return slotIndex;
});
}
public static void waitForMs(long ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
Log.d(TAG, "InterruptedException while waiting: " + e);
}
}
/**
* Verify that the phone is supporting the action of setForbiddenPlmn.
*
* @return whether to proceed the test
*/
private boolean supportSetFplmn() {
if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
return false;
}
return mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM;
}
private static int makeRadioVersion(int major, int minor) {
if (major < 0 || minor < 0) return 0;
return major * 100 + minor;
}
}