blob: 137a43b0ca289ff2287605c0a192cea93a007541 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.telephony.cts;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.telephony.TelephonyManager.SET_OPPORTUNISTIC_SUB_SUCCESS;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
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 static org.junit.Assume.assumeTrue;
import android.annotation.Nullable;
import android.app.UiAutomation;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.Uri;
import android.os.Looper;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionPlan;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsException;
import android.telephony.ims.ImsManager;
import android.telephony.ims.ImsMmTelManager;
import android.telephony.ims.ImsRcsManager;
import android.telephony.ims.RcsUceAdapter;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
import com.android.compatibility.common.util.CarrierPrivilegeUtils;
import com.android.compatibility.common.util.PropertyUtil;
import com.android.compatibility.common.util.ShellIdentityUtils;
import com.android.compatibility.common.util.SystemUtil;
import com.android.compatibility.common.util.TestThread;
import com.android.internal.util.ArrayUtils;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.time.Period;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class SubscriptionManagerTest {
private static final String TAG = "SubscriptionManagerTest";
private static final String MODIFY_PHONE_STATE = "android.permission.MODIFY_PHONE_STATE";
private static final String READ_PRIVILEGED_PHONE_STATE =
"android.permission.READ_PRIVILEGED_PHONE_STATE";
private static final List<Uri> CONTACTS = new ArrayList<>();
static {
CONTACTS.add(Uri.fromParts("tel", "+16505551212", null));
CONTACTS.add(Uri.fromParts("tel", "+16505552323", null));
}
// time to wait when testing APIs which enable or disable subscriptions. The time waiting
// to enable is longer because enabling a subscription can take longer than disabling
private static final int SUBSCRIPTION_DISABLE_WAIT_MS = 5000;
private static final int SUBSCRIPTION_ENABLE_WAIT_MS = 50000;
// time to wait for subscription plans to expire
private static final int SUBSCRIPTION_PLAN_EXPIRY_MS = 50;
private static final int SUBSCRIPTION_PLAN_CLEAR_WAIT_MS = 5000;
private int mSubId;
private int mDefaultVoiceSubId;
private String mPackageName;
private SubscriptionManager mSm;
private SubscriptionManagerTest.CarrierConfigReceiver mReceiver;
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);
}
}
private void overrideCarrierConfig(PersistableBundle bundle, int subId) throws Exception {
mReceiver = new CarrierConfigReceiver(subId);
IntentFilter filter = new IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
// ACTION_CARRIER_CONFIG_CHANGED is sticky, so we will get a callback right away.
InstrumentationRegistry.getContext().registerReceiver(mReceiver, filter);
mReceiver.waitForCarrierConfigChanged();
mReceiver.clearQueue();
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(
InstrumentationRegistry.getContext().getSystemService(CarrierConfigManager.class),
(cm) -> cm.overrideConfig(subId, bundle));
mReceiver.waitForCarrierConfigChanged();
InstrumentationRegistry.getContext().unregisterReceiver(mReceiver);
mReceiver = null;
}
/**
* Callback used in testRegisterNetworkCallback that allows caller to block on
* {@code onAvailable}.
*/
private static class TestNetworkCallback extends ConnectivityManager.NetworkCallback {
private final CountDownLatch mAvailableLatch = new CountDownLatch(1);
public void waitForAvailable() throws InterruptedException {
assertTrue("Cellular network did not come up after 5 seconds",
mAvailableLatch.await(5, TimeUnit.SECONDS));
}
@Override
public void onAvailable(Network network) {
mAvailableLatch.countDown();
}
}
@BeforeClass
public static void setUpClass() throws Exception {
if (!isSupported()) return;
final TestNetworkCallback callback = new TestNetworkCallback();
final ConnectivityManager cm = InstrumentationRegistry.getContext()
.getSystemService(ConnectivityManager.class);
cm.registerNetworkCallback(new NetworkRequest.Builder()
.addTransportType(TRANSPORT_CELLULAR)
.addCapability(NET_CAPABILITY_INTERNET)
.build(), callback);
try {
// Wait to get callback for availability of internet
callback.waitForAvailable();
} catch (InterruptedException e) {
fail("NetworkCallback wait was interrupted.");
} finally {
cm.unregisterNetworkCallback(callback);
}
}
@AfterClass
public static void tearDownClass() throws Exception {
if (!isSupported()) return;
TelephonyUtils.flushTelephonyMetrics(InstrumentationRegistry.getInstrumentation());
}
@Before
public void setUp() throws Exception {
assumeTrue(isSupported());
mSm = InstrumentationRegistry.getContext().getSystemService(SubscriptionManager.class);
mSubId = SubscriptionManager.getDefaultDataSubscriptionId();
mDefaultVoiceSubId = SubscriptionManager.getDefaultVoiceSubscriptionId();
mPackageName = InstrumentationRegistry.getContext().getPackageName();
}
@After
public void tearDown() throws Exception {
if (mReceiver != null) {
InstrumentationRegistry.getContext().unregisterReceiver(mReceiver);
mReceiver = null;
}
}
/**
* Correctness check that both {@link PackageManager#FEATURE_TELEPHONY} and
* {@link NetworkCapabilities#TRANSPORT_CELLULAR} network must both be
* either defined or undefined; you can't cross the streams.
*/
@Test
public void testCorrectness() throws Exception {
final boolean hasCellular = findCellularNetwork() != null;
if (!hasCellular) {
fail("Device claims to support " + PackageManager.FEATURE_TELEPHONY
+ " but has no active cellular network, which is required for validation");
}
if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
fail("Device must have a valid default data subId for validation");
}
}
@Test
public void testGetActiveSubscriptionInfoCount() throws Exception {
assertTrue(mSm.getActiveSubscriptionInfoCount() <=
mSm.getActiveSubscriptionInfoCountMax());
}
@Test
public void testGetActiveSubscriptionInfoForIcc() throws Exception {
SubscriptionInfo info = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
(sm) -> sm.getActiveSubscriptionInfo(mSubId));
assertNotNull(ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
(sm) -> sm.getActiveSubscriptionInfoForIcc(info.getIccId())));
}
@Test
public void testIsActiveSubscriptionId() throws Exception {
assertTrue(mSm.isActiveSubscriptionId(mSubId));
}
@Test
public void testGetSubscriptionIds() throws Exception {
int slotId = SubscriptionManager.getSlotIndex(mSubId);
int[] subIds = mSm.getSubscriptionIds(slotId);
assertNotNull(subIds);
assertTrue(ArrayUtils.contains(subIds, mSubId));
}
@Test
public void testGetResourcesForSubId() {
Resources r = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
(sm) -> sm.getResourcesForSubId(InstrumentationRegistry.getContext(), mSubId));
// this is an old method which returns mcc/mnc as ints, so use the old SM.getMcc/Mnc methods
// because they also use ints
assertEquals(mSm.getActiveSubscriptionInfo(mSubId).getMcc(), r.getConfiguration().mcc);
assertEquals(mSm.getActiveSubscriptionInfo(mSubId).getMnc(), r.getConfiguration().mnc);
}
@Test
public void testIsUsableSubscriptionId() throws Exception {
assertTrue(SubscriptionManager.isUsableSubscriptionId(mSubId));
}
@Test
public void testActiveSubscriptions() throws Exception {
List<SubscriptionInfo> subList = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
(sm) -> sm.getActiveSubscriptionInfoList());
int[] idList = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
(sm) -> sm.getActiveSubscriptionIdList());
// Assert when there is no sim card present or detected
assertNotNull("Active subscriber required", subList);
assertNotNull("Active subscriber required", idList);
assertFalse("Active subscriber required", subList.isEmpty());
assertNotEquals("Active subscriber required", 0, idList.length);
for (int i = 0; i < subList.size(); i++) {
assertTrue(subList.get(i).getSubscriptionId() >= 0);
assertTrue(subList.get(i).getSimSlotIndex() >= 0);
assertTrue(ArrayUtils.contains(idList, subList.get(i).getSubscriptionId()));
if (i >= 1) {
assertTrue(subList.get(i - 1).getSimSlotIndex()
<= subList.get(i).getSimSlotIndex());
assertTrue(subList.get(i - 1).getSimSlotIndex() < subList.get(i).getSimSlotIndex()
|| subList.get(i - 1).getSubscriptionId()
< subList.get(i).getSubscriptionId());
}
}
}
@Test
public void testSubscriptionPlans() throws Exception {
// Make ourselves the owner
setSubPlanOwner(mSubId, mPackageName);
// Push empty list and we get empty back
mSm.setSubscriptionPlans(mSubId, Arrays.asList());
assertEquals(Arrays.asList(), mSm.getSubscriptionPlans(mSubId));
// Push simple plan and get it back
final SubscriptionPlan plan = buildValidSubscriptionPlan(System.currentTimeMillis());
mSm.setSubscriptionPlans(mSubId, Arrays.asList(plan));
assertEquals(Arrays.asList(plan), mSm.getSubscriptionPlans(mSubId));
// Push plan with expiration time and verify that it expired
mSm.setSubscriptionPlans(mSubId, Arrays.asList(plan), SUBSCRIPTION_PLAN_EXPIRY_MS);
Thread.sleep(SUBSCRIPTION_PLAN_EXPIRY_MS);
Thread.sleep(SUBSCRIPTION_PLAN_CLEAR_WAIT_MS);
assertTrue(mSm.getSubscriptionPlans(mSubId).isEmpty());
// Now revoke our access
setSubPlanOwner(mSubId, null);
try {
mSm.setSubscriptionPlans(mSubId, Arrays.asList());
fail();
} catch (SecurityException expected) {
}
try {
mSm.getSubscriptionPlans(mSubId);
fail();
} catch (SecurityException expected) {
}
}
@Test
public void testSubscriptionPlansOverrideCongested() throws Exception {
final ConnectivityManager cm = InstrumentationRegistry.getContext()
.getSystemService(ConnectivityManager.class);
final Network net = findCellularNetwork();
assertNotNull("Active cellular network required", net);
// Make ourselves the owner
setSubPlanOwner(mSubId, mPackageName);
// Missing plans means no overrides
mSm.setSubscriptionPlans(mSubId, Arrays.asList());
try {
mSm.setSubscriptionOverrideCongested(mSubId, true, 0);
fail();
} catch (SecurityException | IllegalStateException expected) {
}
// Defining plans means we get to override
mSm.setSubscriptionPlans(mSubId,
Arrays.asList(buildValidSubscriptionPlan(System.currentTimeMillis())));
// Cellular is uncongested by default
assertTrue(cm.getNetworkCapabilities(net).hasCapability(NET_CAPABILITY_NOT_CONGESTED));
// Override should make it go congested
{
final CountDownLatch latch = waitForNetworkCapabilities(net, caps -> {
return !caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED);
});
mSm.setSubscriptionOverrideCongested(
mSubId, true, TelephonyManager.getAllNetworkTypes(), 0);
assertTrue(latch.await(10, TimeUnit.SECONDS));
}
// Clearing override should make it go uncongested
{
final CountDownLatch latch = waitForNetworkCapabilities(net, caps -> {
return caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED);
});
mSm.setSubscriptionOverrideCongested(mSubId, false, 0);
assertTrue(latch.await(10, TimeUnit.SECONDS));
}
// Now revoke our access
setSubPlanOwner(mSubId, null);
try {
mSm.setSubscriptionOverrideCongested(
mSubId, true, TelephonyManager.getAllNetworkTypes(), 0);
fail();
} catch (SecurityException | IllegalStateException expected) {
}
}
@Test
public void testSubscriptionInfoRecord() {
if (!isAutomotive()) return;
UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
uiAutomation.adoptShellPermissionIdentity();
String uniqueId = "00:01:02:03:04:05";
String displayName = "device_name";
mSm.addSubscriptionInfoRecord(uniqueId, displayName, 0,
SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM);
assertNotNull(mSm.getActiveSubscriptionInfoForIcc(uniqueId));
mSm.removeSubscriptionInfoRecord(uniqueId,
SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM);
assertNull(mSm.getActiveSubscriptionInfoForIcc(uniqueId));
uiAutomation.dropShellPermissionIdentity();
// Testing permission fail
try {
mSm.addSubscriptionInfoRecord(uniqueId, displayName, 0,
SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM);
mSm.removeSubscriptionInfoRecord(uniqueId,
SubscriptionManager.SUBSCRIPTION_TYPE_REMOTE_SIM);
fail("SecurityException should be thrown without MODIFY_PHONE_STATE");
} catch (SecurityException expected) {
// expected
}
}
@Test
public void testSetDefaultVoiceSubId() {
int oldSubId = SubscriptionManager.getDefaultVoiceSubscriptionId();
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.adoptShellPermissionIdentity();
try {
mSm.setDefaultVoiceSubscriptionId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
assertEquals(SubscriptionManager.INVALID_SUBSCRIPTION_ID,
SubscriptionManager.getDefaultVoiceSubscriptionId());
mSm.setDefaultVoiceSubscriptionId(oldSubId);
assertEquals(oldSubId, SubscriptionManager.getDefaultVoiceSubscriptionId());
} finally {
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.dropShellPermissionIdentity();
}
}
@Test
public void testSubscriptionPlansOverrideUnmetered() throws Exception {
final ConnectivityManager cm = InstrumentationRegistry.getContext()
.getSystemService(ConnectivityManager.class);
final Network net = findCellularNetwork();
assertNotNull("Active cellular network required", net);
// TODO: Remove this check after b/176119724 is fixed.
if (!isUnmetered5GSupported()) return;
// Cellular is metered by default
assertFalse(cm.getNetworkCapabilities(net).hasCapability(
NET_CAPABILITY_TEMPORARILY_NOT_METERED));
// Override should make it go temporarily unmetered
{
final CountDownLatch latch = waitForNetworkCapabilities(net, caps -> {
return caps.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
});
mSm.setSubscriptionOverrideUnmetered(
mSubId, true, TelephonyManager.getAllNetworkTypes(), 0);
assertTrue(latch.await(10, TimeUnit.SECONDS));
}
// Clearing override should make it go metered
{
final CountDownLatch latch = waitForNetworkCapabilities(net, caps -> {
return !caps.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
});
mSm.setSubscriptionOverrideUnmetered(
mSubId, false, TelephonyManager.getAllNetworkTypes(), 0);
assertTrue(latch.await(10, TimeUnit.SECONDS));
}
}
@Test
public void testSubscriptionPlansUnmetered() throws Exception {
final ConnectivityManager cm = InstrumentationRegistry.getContext()
.getSystemService(ConnectivityManager.class);
final Network net = findCellularNetwork();
assertNotNull("Active cellular network required", net);
// TODO: Remove this check after b/176119724 is fixed.
if (!isUnmetered5GSupported()) return;
// Make ourselves the owner and define some plans
setSubPlanOwner(mSubId, mPackageName);
mSm.setSubscriptionPlans(mSubId,
Arrays.asList(buildValidSubscriptionPlan(System.currentTimeMillis())));
// Cellular is metered by default
assertFalse(cm.getNetworkCapabilities(net).hasCapability(
NET_CAPABILITY_TEMPORARILY_NOT_METERED));
SubscriptionPlan unmeteredPlan = SubscriptionPlan.Builder
.createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
Period.ofMonths(1))
.setTitle("CTS")
.setDataLimit(SubscriptionPlan.BYTES_UNLIMITED,
SubscriptionPlan.LIMIT_BEHAVIOR_THROTTLED)
.build();
// Unmetered plan should make it go unmetered
{
final CountDownLatch latch = waitForNetworkCapabilities(net, caps -> {
return caps.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
});
mSm.setSubscriptionPlans(mSubId, Arrays.asList(unmeteredPlan));
assertTrue(latch.await(10, TimeUnit.SECONDS));
}
// Metered plan should make it go metered
{
final CountDownLatch latch = waitForNetworkCapabilities(net, caps -> {
return !caps.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED);
});
mSm.setSubscriptionPlans(mSubId,
Arrays.asList(buildValidSubscriptionPlan(System.currentTimeMillis())));
assertTrue(latch.await(10, TimeUnit.SECONDS));
}
}
@Test
public void testSubscriptionPlansInvalid() throws Exception {
// Make ourselves the owner
setSubPlanOwner(mSubId, mPackageName);
// Empty plans can't override
assertOverrideFails();
// Nonrecurring plan in the past can't override
assertOverrideFails(SubscriptionPlan.Builder
.createNonrecurring(ZonedDateTime.now().minusDays(14),
ZonedDateTime.now().minusDays(7))
.setTitle("CTS")
.setDataLimit(1_000_000_000, SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
.build());
// Plan with undefined limit can't override
assertOverrideFails(SubscriptionPlan.Builder
.createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
Period.ofMonths(1))
.setTitle("CTS")
.build());
// We can override when there is an active plan somewhere
final SubscriptionPlan older = SubscriptionPlan.Builder
.createNonrecurring(ZonedDateTime.now().minusDays(14),
ZonedDateTime.now().minusDays(7))
.setTitle("CTS")
.setDataLimit(1_000_000_000, SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
.build();
final SubscriptionPlan newer = SubscriptionPlan.Builder
.createNonrecurring(ZonedDateTime.now().minusDays(7),
ZonedDateTime.now().plusDays(7))
.setTitle("CTS")
.setDataLimit(1_000_000_000, SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
.build();
assertOverrideSuccess(older, newer);
}
@Test
public void testSubscriptionPlansNetworkTypeValidation() throws Exception {
// Make ourselves the owner
setSubPlanOwner(mSubId, mPackageName);
// Error when adding 2 plans with the same network type
List<SubscriptionPlan> plans = new ArrayList<>();
plans.add(buildValidSubscriptionPlan(System.currentTimeMillis()));
plans.add(SubscriptionPlan.Builder
.createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
Period.ofMonths(1))
.setTitle("CTS")
.setNetworkTypes(new int[] {TelephonyManager.NETWORK_TYPE_LTE})
.build());
plans.add(SubscriptionPlan.Builder
.createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
Period.ofMonths(1))
.setTitle("CTS")
.setNetworkTypes(new int[] {TelephonyManager.NETWORK_TYPE_LTE})
.build());
try {
mSm.setSubscriptionPlans(mSubId, plans);
fail();
} catch (IllegalArgumentException expected) {
}
// Error when there is no general plan
plans.clear();
plans.add(SubscriptionPlan.Builder
.createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
Period.ofMonths(1))
.setTitle("CTS")
.setNetworkTypes(new int[] {TelephonyManager.NETWORK_TYPE_LTE})
.build());
try {
mSm.setSubscriptionPlans(mSubId, plans);
fail();
} catch (IllegalArgumentException expected) {
}
}
@Test
public void testSubscriptionPlanResetNetworkTypes() {
long time = System.currentTimeMillis();
SubscriptionPlan plan = SubscriptionPlan.Builder
.createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
Period.ofMonths(1))
.setTitle("CTS")
.setNetworkTypes(new int[] {TelephonyManager.NETWORK_TYPE_LTE})
.setDataLimit(1_000_000_000, SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
.setDataUsage(500_000_000, time)
.resetNetworkTypes()
.build();
assertEquals(plan, buildValidSubscriptionPlan(time));
}
@Test
public void testSubscriptionGrouping() throws Exception {
// Set subscription group with current sub Id. This should fail
// because we don't have MODIFY_PHONE_STATE or carrier privilege permission.
List<Integer> subGroup = new ArrayList();
subGroup.add(mSubId);
try {
mSm.createSubscriptionGroup(subGroup);
fail();
} catch (SecurityException expected) {
}
// Getting subscriptions in group should return null as setSubscriptionGroup
// should fail.
SubscriptionInfo info = mSm.getActiveSubscriptionInfo(mSubId);
assertNull(info.getGroupUuid());
// Remove from subscription group with current sub Id. This should fail
// because we don't have MODIFY_PHONE_STATE or carrier privilege permission.
try {
mSm.addSubscriptionsIntoGroup(subGroup, null);
fail();
} catch (NullPointerException expected) {
}
// Add into subscription group that doesn't exist. This should fail
// because we don't have MODIFY_PHONE_STATE or carrier privilege permission.
try {
ParcelUuid groupUuid = new ParcelUuid(UUID.randomUUID());
mSm.addSubscriptionsIntoGroup(subGroup, groupUuid);
fail();
} catch (SecurityException expected) {
}
// Remove from subscription group with current sub Id. This should fail
// because we don't have MODIFY_PHONE_STATE or carrier privilege permission.
try {
mSm.removeSubscriptionsFromGroup(subGroup, null);
fail();
} catch (NullPointerException expected) {
}
}
@Test
public void testSubscriptionGroupingWithPermission() throws Exception {
// Set subscription group with current sub Id.
List<Integer> subGroup = new ArrayList();
subGroup.add(mSubId);
ParcelUuid uuid = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
(sm) -> sm.createSubscriptionGroup(subGroup));
// Getting subscriptions in group.
List<SubscriptionInfo> infoList = mSm.getSubscriptionsInGroup(uuid);
assertNotNull(infoList);
assertEquals(1, infoList.size());
assertNull(infoList.get(0).getGroupUuid());
infoList = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
(sm) -> sm.getSubscriptionsInGroup(uuid));
assertNotNull(infoList);
assertEquals(1, infoList.size());
assertEquals(uuid, infoList.get(0).getGroupUuid());
List<SubscriptionInfo> availableInfoList;
try {
mSm.getAvailableSubscriptionInfoList();
fail("SecurityException should be thrown without READ_PRIVILEGED_PHONE_STATE");
} catch (SecurityException ex) {
// Ignore
}
availableInfoList = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
(sm) -> sm.getAvailableSubscriptionInfoList());
if (availableInfoList.size() > 1) {
List<Integer> availableSubGroup = availableInfoList.stream()
.map(info -> info.getSubscriptionId())
.filter(subId -> subId != mSubId)
.collect(Collectors.toList());
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
(sm) -> sm.addSubscriptionsIntoGroup(availableSubGroup, uuid));
infoList = mSm.getSubscriptionsInGroup(uuid);
assertNotNull(infoList);
assertEquals(availableInfoList.size(), infoList.size());
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
(sm) -> sm.removeSubscriptionsFromGroup(availableSubGroup, uuid));
}
// Remove from subscription group with current sub Id.
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
(sm) -> sm.removeSubscriptionsFromGroup(subGroup, uuid));
infoList = mSm.getSubscriptionsInGroup(uuid);
assertNotNull(infoList);
assertTrue(infoList.isEmpty());
}
@Test
public void testAddSubscriptionIntoNewGroupWithPermission() throws Exception {
// Set subscription group with current sub Id.
List<Integer> subGroup = new ArrayList();
subGroup.add(mSubId);
ParcelUuid uuid = new ParcelUuid(UUID.randomUUID());
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
(sm) -> sm.addSubscriptionsIntoGroup(subGroup, uuid));
// Getting subscriptions in group.
List<SubscriptionInfo> infoList = mSm.getSubscriptionsInGroup(uuid);
assertNotNull(infoList);
assertEquals(1, infoList.size());
assertNull(infoList.get(0).getGroupUuid());
infoList = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
(sm) -> sm.getSubscriptionsInGroup(uuid));
assertNotNull(infoList);
assertEquals(1, infoList.size());
assertEquals(uuid, infoList.get(0).getGroupUuid());
// Remove from subscription group with current sub Id.
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
(sm) -> sm.removeSubscriptionsFromGroup(subGroup, uuid));
infoList = mSm.getSubscriptionsInGroup(uuid);
assertNotNull(infoList);
assertTrue(infoList.isEmpty());
}
@Test
public void testSettingOpportunisticSubscription() throws Exception {
// Set subscription to be opportunistic. This should fail
// because we don't have MODIFY_PHONE_STATE or carrier privilege permission.
try {
mSm.setOpportunistic(true, mSubId);
fail();
} catch (SecurityException expected) {
}
// Shouldn't crash.
SubscriptionInfo info = mSm.getActiveSubscriptionInfo(mSubId);
info.isOpportunistic();
}
@Test
public void testMccMncString() {
SubscriptionInfo info = mSm.getActiveSubscriptionInfo(mSubId);
String mcc = info.getMccString();
String mnc = info.getMncString();
assertTrue(mcc == null || mcc.length() <= 3);
assertTrue(mnc == null || mnc.length() <= 3);
}
@Test
public void testSetUiccApplicationsEnabled() throws Exception {
boolean canDisable = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
(sm) -> sm.canDisablePhysicalSubscription());
if (canDisable) {
Object lock = new Object();
AtomicBoolean functionCallCompleted = new AtomicBoolean(false);
// enabled starts off as true
AtomicBoolean valueToWaitFor = new AtomicBoolean(false);
TestThread t = new TestThread(new Runnable() {
@Override
public void run() {
Looper.prepare();
SubscriptionManager.OnSubscriptionsChangedListener listener =
new SubscriptionManager.OnSubscriptionsChangedListener() {
@Override
public void onSubscriptionsChanged() {
if (valueToWaitFor.get() == mSm.getActiveSubscriptionInfo(
mSubId).areUiccApplicationsEnabled()) {
synchronized (lock) {
functionCallCompleted.set(true);
lock.notifyAll();
}
}
}
};
mSm.addOnSubscriptionsChangedListener(listener);
Looper.loop();
}
});
// Disable the UICC application and wait until we detect the subscription change to
// verify
t.start();
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
(sm) -> sm.setUiccApplicationsEnabled(mSubId, false));
synchronized (lock) {
if (!functionCallCompleted.get()) {
lock.wait(SUBSCRIPTION_DISABLE_WAIT_MS);
}
}
if (!functionCallCompleted.get()) {
fail("testSetUiccApplicationsEnabled was not able to disable the UICC app on time");
}
// Enable the UICC application and wait again
functionCallCompleted.set(false);
valueToWaitFor.set(true);
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
(sm) -> sm.setUiccApplicationsEnabled(mSubId, true));
synchronized (lock) {
if (!functionCallCompleted.get()) {
lock.wait(SUBSCRIPTION_ENABLE_WAIT_MS);
}
}
if (!functionCallCompleted.get()) {
fail("testSetUiccApplicationsEnabled was not able to enable to UICC app on time");
}
// Reset default data and voice subId as it may have been changed as part of the
// calls above
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
(sm) -> sm.setDefaultDataSubId(mSubId));
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
(sm) -> sm.setDefaultVoiceSubscriptionId(mDefaultVoiceSubId));
// Other tests also expect that cellular data must be available if telephony is
// supported. Wait for that before returning.
final CountDownLatch latch = waitForCellularNetwork();
latch.await(10, TimeUnit.SECONDS);
}
}
@Test
public void testSubscriptionInfoCarrierId() {
SubscriptionInfo info = mSm.getActiveSubscriptionInfo(mSubId);
int carrierId = info.getCarrierId();
assertTrue(carrierId >= TelephonyManager.UNKNOWN_CARRIER_ID);
}
@Test
public void testGetOpportunisticSubscriptions() throws Exception {
List<SubscriptionInfo> infoList = mSm.getOpportunisticSubscriptions();
for (SubscriptionInfo info : infoList) {
assertTrue(info.isOpportunistic());
}
}
@Test
public void testGetEnabledSubscriptionId() {
int slotId = SubscriptionManager.getSlotIndex(mSubId);
if (!SubscriptionManager.isValidSlotIndex(slotId)) {
fail("Invalid slot id " + slotId + " for subscription id " + mSubId);
}
int enabledSubId = executeWithShellPermissionAndDefault(-1, mSm,
(sm) -> sm.getEnabledSubscriptionId(slotId));
assertEquals(mSubId, enabledSubId);
}
@Test
public void testSetAndCheckSubscriptionEnabled() {
boolean enabled = executeWithShellPermissionAndDefault(false, mSm,
(sm) -> sm.isSubscriptionEnabled(mSubId));
AtomicBoolean waitForIsEnabledValue = new AtomicBoolean(!enabled);
// wait for the first call to take effect
Object lock = new Object();
AtomicBoolean setSubscriptionEnabledCallCompleted = new AtomicBoolean(false);
TestThread t = new TestThread(new Runnable() {
@Override
public void run() {
Looper.prepare();
SubscriptionManager.OnSubscriptionsChangedListener listener =
new SubscriptionManager.OnSubscriptionsChangedListener() {
@Override
public void onSubscriptionsChanged() {
boolean waitForValue = waitForIsEnabledValue.get();
if (executeWithShellPermissionAndDefault(!waitForValue, mSm,
(sm) -> sm.isSubscriptionEnabled(mSubId)) == waitForValue) {
synchronized (lock) {
setSubscriptionEnabledCallCompleted.set(true);
lock.notifyAll();
}
}
}
};
mSm.addOnSubscriptionsChangedListener(listener);
Looper.loop();
}
});
try {
t.start();
// Enable or disable subscription may require users UX confirmation or may not be
// supported. Call APIs to make sure there's no crash.
executeWithShellPermissionAndDefault(false, mSm,
(sm) -> sm.setSubscriptionEnabled(mSubId, !enabled));
synchronized (lock) {
if (!setSubscriptionEnabledCallCompleted.get()) {
lock.wait(SUBSCRIPTION_DISABLE_WAIT_MS);
}
}
if (!setSubscriptionEnabledCallCompleted.get()) {
// not treating this as test failure as it may be due to UX confirmation or may not
// be supported
Log.e(TAG, "setSubscriptionEnabled() did not complete");
executeWithShellPermissionAndDefault(false, mSm,
(sm) -> sm.setSubscriptionEnabled(mSubId, enabled));
return;
}
// switch back to the original value
waitForIsEnabledValue.set(enabled);
setSubscriptionEnabledCallCompleted.set(false);
executeWithShellPermissionAndDefault(false, mSm,
(sm) -> sm.setSubscriptionEnabled(mSubId, enabled));
// wait to make sure device is left in the same state after the test as it was before
// the test
synchronized (lock) {
if (!setSubscriptionEnabledCallCompleted.get()) {
lock.wait(SUBSCRIPTION_ENABLE_WAIT_MS);
}
}
if (!setSubscriptionEnabledCallCompleted.get()) {
// treat this as failure because it worked the first time
fail("setSubscriptionEnabled() did not work second time");
}
// Reset default subIds as they may have changed as part of the calls above
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
(sm) -> sm.setDefaultDataSubId(mSubId));
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
(sm) -> sm.setDefaultVoiceSubId(mDefaultVoiceSubId));
// Other tests also expect that cellular data must be available if telephony is
// supported. Wait for that before returning.
final CountDownLatch latch = waitForCellularNetwork();
latch.await(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
fail("InterruptedException");
}
}
@Test
public void testGetActiveDataSubscriptionId() {
int activeDataSubIdCurrent = executeWithShellPermissionAndDefault(
SubscriptionManager.INVALID_SUBSCRIPTION_ID, mSm,
(sm) -> sm.getActiveDataSubscriptionId());
if (activeDataSubIdCurrent != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
List<SubscriptionInfo> subscriptionInfos = mSm.getCompleteActiveSubscriptionInfoList();
boolean foundSub = subscriptionInfos.stream()
.anyMatch(x -> x.getSubscriptionId() == activeDataSubIdCurrent);
assertTrue(foundSub);
}
}
@Test
public void testSetPreferredDataSubscriptionId() {
int preferredSubId = executeWithShellPermissionAndDefault(-1, mSm,
(sm) -> sm.getPreferredDataSubscriptionId());
if (preferredSubId != SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
// Make sure to switch back to primary/default data sub first.
setPreferredDataSubId(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
}
List<SubscriptionInfo> subscriptionInfos = mSm.getCompleteActiveSubscriptionInfoList();
for (SubscriptionInfo subInfo : subscriptionInfos) {
// Only test on opportunistic subscriptions.
if (!subInfo.isOpportunistic()) continue;
setPreferredDataSubId(subInfo.getSubscriptionId());
}
// Switch data back to previous preferredSubId.
setPreferredDataSubId(preferredSubId);
}
@Test
public void testRestoreAllSimSpecificSettingsFromBackup() throws Exception {
int activeDataSubId = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
(sm) -> sm.getActiveDataSubscriptionId());
assertNotEquals(activeDataSubId, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
SubscriptionInfo activeSubInfo = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
(sm) -> sm.getActiveSubscriptionInfo(activeDataSubId));
String isoCountryCode = activeSubInfo.getCountryIso();
byte[] backupData = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
(sm) -> sm.getAllSimSpecificSettingsForBackup());
assertTrue(backupData.length > 0);
PersistableBundle bundle = new PersistableBundle();
bundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL, true);
bundle.putBoolean(CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL, false);
overrideCarrierConfig(bundle, activeDataSubId);
// Get the original ims values.
ImsManager imsManager = InstrumentationRegistry.getContext().getSystemService(
ImsManager.class);
ImsMmTelManager mMmTelManager = imsManager.getImsMmTelManager(activeDataSubId);
boolean isVolteVtEnabledOriginal = ShellIdentityUtils.invokeMethodWithShellPermissions(
mMmTelManager, (m) -> m.isAdvancedCallingSettingEnabled());
boolean isVtImsEnabledOriginal = ShellIdentityUtils.invokeMethodWithShellPermissions(
mMmTelManager, (m) -> m.isVtSettingEnabled());
boolean isVoWiFiSettingEnabledOriginal =
ShellIdentityUtils.invokeMethodWithShellPermissions(
mMmTelManager, (m) -> m.isVoWiFiSettingEnabled());
int voWifiModeOriginal = ShellIdentityUtils.invokeMethodWithShellPermissions(
mMmTelManager, (m) -> m.getVoWiFiModeSetting());
int voWiFiRoamingModeOriginal = ShellIdentityUtils.invokeMethodWithShellPermissions(
mMmTelManager, (m) -> m.getVoWiFiRoamingModeSetting());
// Get the original RcsUce values.
ImsRcsManager imsRcsManager = imsManager.getImsRcsManager(activeDataSubId);
RcsUceAdapter rcsUceAdapter = imsRcsManager.getUceAdapter();
boolean isImsRcsUceEnabledOriginal =
ShellIdentityUtils.invokeThrowableMethodWithShellPermissions(
rcsUceAdapter, (a) -> a.isUceSettingEnabled(), ImsException.class,
android.Manifest.permission.READ_PHONE_STATE);
//Change values in DB.
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.setAdvancedCallingSettingEnabled(!isVolteVtEnabledOriginal));
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.setVtSettingEnabled(!isVtImsEnabledOriginal));
ShellIdentityUtils.invokeThrowableMethodWithShellPermissionsNoReturn(
rcsUceAdapter, (a) -> a.setUceSettingEnabled(!isImsRcsUceEnabledOriginal),
ImsException.class);
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.setVoWiFiSettingEnabled(!isVoWiFiSettingEnabledOriginal));
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.setVoWiFiModeSetting((voWifiModeOriginal + 1) % 3));
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mMmTelManager,
(m) -> m.setVoWiFiRoamingModeSetting((voWiFiRoamingModeOriginal + 1) % 3));
// Restore back to original values.
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
(sm) -> sm.restoreAllSimSpecificSettingsFromBackup(backupData));
// Get ims values to verify with.
boolean isVolteVtEnabledAfterRestore = ShellIdentityUtils.invokeMethodWithShellPermissions(
mMmTelManager, (m) -> m.isAdvancedCallingSettingEnabled());
boolean isVtImsEnabledAfterRestore = ShellIdentityUtils.invokeMethodWithShellPermissions(
mMmTelManager, (m) -> m.isVtSettingEnabled());
boolean isVoWiFiSettingEnabledAfterRestore =
ShellIdentityUtils.invokeMethodWithShellPermissions(
mMmTelManager, (m) -> m.isVoWiFiSettingEnabled());
int voWifiModeAfterRestore = ShellIdentityUtils.invokeMethodWithShellPermissions(
mMmTelManager, (m) -> m.getVoWiFiModeSetting());
int voWiFiRoamingModeAfterRestore = ShellIdentityUtils.invokeMethodWithShellPermissions(
mMmTelManager, (m) -> m.getVoWiFiRoamingModeSetting());
// Get RcsUce values to verify with.
boolean isImsRcsUceEnabledAfterRestore =
ShellIdentityUtils.invokeThrowableMethodWithShellPermissions(
rcsUceAdapter, (a) -> a.isUceSettingEnabled(), ImsException.class,
android.Manifest.permission.READ_PHONE_STATE);
assertEquals(isVolteVtEnabledOriginal, isVolteVtEnabledAfterRestore);
if (isoCountryCode == null || isoCountryCode.equals("us") || isoCountryCode.equals("ca")) {
assertEquals(!isVoWiFiSettingEnabledOriginal, isVoWiFiSettingEnabledAfterRestore);
} else {
assertEquals(isVoWiFiSettingEnabledOriginal, isVoWiFiSettingEnabledAfterRestore);
}
assertEquals(voWifiModeOriginal, voWifiModeAfterRestore);
assertEquals(voWiFiRoamingModeOriginal, voWiFiRoamingModeAfterRestore);
assertEquals(isVtImsEnabledOriginal, isVtImsEnabledAfterRestore);
assertEquals(isImsRcsUceEnabledOriginal, isImsRcsUceEnabledAfterRestore);
// restore original carrier config.
overrideCarrierConfig(null, activeDataSubId);
try {
// Check api call will fail without proper permissions.
mSm.restoreAllSimSpecificSettingsFromBackup(backupData);
fail("SecurityException expected");
} catch (SecurityException e) {
// expected
}
}
@Test
public void testSetAndGetD2DStatusSharing() {
UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
uiAutomation.adoptShellPermissionIdentity(MODIFY_PHONE_STATE);
int originalD2DStatusSharing = mSm.getDeviceToDeviceStatusSharingPreference(mSubId);
mSm.setDeviceToDeviceStatusSharingPreference(mSubId,
SubscriptionManager.D2D_SHARING_ALL_CONTACTS);
assertEquals(SubscriptionManager.D2D_SHARING_ALL_CONTACTS,
mSm.getDeviceToDeviceStatusSharingPreference(mSubId));
mSm.setDeviceToDeviceStatusSharingPreference(mSubId, SubscriptionManager.D2D_SHARING_ALL);
assertEquals(SubscriptionManager.D2D_SHARING_ALL,
mSm.getDeviceToDeviceStatusSharingPreference(mSubId));
mSm.setDeviceToDeviceStatusSharingPreference(mSubId, originalD2DStatusSharing);
uiAutomation.dropShellPermissionIdentity();
}
@Test
public void testSetAndGetD2DSharingContacts() {
UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
uiAutomation.adoptShellPermissionIdentity(MODIFY_PHONE_STATE);
List<Uri> originalD2DSharingContacts = mSm.getDeviceToDeviceStatusSharingContacts(mSubId);
mSm.setDeviceToDeviceStatusSharingContacts(mSubId, CONTACTS);
assertEquals(CONTACTS, mSm.getDeviceToDeviceStatusSharingContacts(mSubId));
mSm.setDeviceToDeviceStatusSharingContacts(mSubId, originalD2DSharingContacts);
uiAutomation.dropShellPermissionIdentity();
}
@Test
public void tetsSetAndGetPhoneNumber() throws Exception {
// The phone number may be anything depends on the state of SIM and device.
// Simply call the getter and make sure no exception.
// Getters accessiable with READ_PRIVILEGED_PHONE_STATE
try {
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.adoptShellPermissionIdentity(READ_PRIVILEGED_PHONE_STATE);
mSm.getPhoneNumber(mSubId);
mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_UICC);
mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER);
mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_IMS);
} finally {
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.dropShellPermissionIdentity();
}
// Getters accessiable with READ_PHONE_NUMBERS
try {
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.adoptShellPermissionIdentity(android.Manifest.permission.READ_PHONE_NUMBERS);
mSm.getPhoneNumber(mSubId);
mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_UICC);
mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER);
mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_IMS);
} finally {
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.dropShellPermissionIdentity();
}
// Getters and the setter accessiable with carrier privilege
final String carrierNumber = "1234567890";
CarrierPrivilegeUtils.withCarrierPrivileges(
InstrumentationRegistry.getContext(),
mSubId,
() -> {
mSm.getPhoneNumber(mSubId);
mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_UICC);
mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_IMS);
mSm.setCarrierPhoneNumber(mSubId, carrierNumber);
assertEquals(
carrierNumber,
mSm.getPhoneNumber(
mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER));
});
// Otherwise, getter and setter will hit SecurityException
try {
mSm.getPhoneNumber(mSubId);
fail("Expect SecurityException from getPhoneNumber()");
} catch (SecurityException e) {
// expected
}
try {
mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_UICC);
fail("Expect SecurityException from getPhoneNumber()");
} catch (SecurityException e) {
// expected
}
try {
mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_IMS);
fail("Expect SecurityException from getPhoneNumber()");
} catch (SecurityException e) {
// expected
}
try {
mSm.getPhoneNumber(mSubId, SubscriptionManager.PHONE_NUMBER_SOURCE_CARRIER);
fail("Expect SecurityException from getPhoneNumber()");
} catch (SecurityException e) {
// expected
}
try {
mSm.setCarrierPhoneNumber(mSubId, "987");
fail("Expect SecurityException from setCarrierPhoneNumber()");
} catch (SecurityException e) {
// expected
}
}
private Set<Integer> getSupportedUsageSettings() throws Exception {
final Set<Integer> supportedUsageSettings = new HashSet();
final Context context = InstrumentationRegistry.getContext();
// Vendors can add supported usage settings by adding resources.
try {
int[] usageSettingsFromResource = context.getResources().getIntArray(
com.android.internal.R.array.config_supported_cellular_usage_settings);
for (int setting : usageSettingsFromResource) {
supportedUsageSettings.add(setting);
}
} catch (Resources.NotFoundException ignore) {
}
// For devices shipping with Radio HAL 2.0 and/or non-HAL devices launching with T,
// the usage settings are required to be supported if the rest of the telephony stack
// has support for that mode of operation.
if (PropertyUtil.isVendorApiLevelAtLeast(android.os.Build.VERSION_CODES.TIRAMISU)) {
final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_DATA)) {
supportedUsageSettings.add(SubscriptionManager.USAGE_SETTING_DATA_CENTRIC);
}
if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) {
supportedUsageSettings.add(SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC);
}
}
return supportedUsageSettings;
}
private int getUsageSetting() throws Exception {
SubscriptionInfo info = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
(sm) -> sm.getActiveSubscriptionInfo(mSubId));
return info.getUsageSetting();
}
private void checkUsageSetting(int inputSetting, boolean isSupported) throws Exception {
final int initialSetting = getUsageSetting();
PersistableBundle bundle = new PersistableBundle();
bundle.putInt(CarrierConfigManager.KEY_CELLULAR_USAGE_SETTING_INT, inputSetting);
overrideCarrierConfig(bundle, mSubId);
final int newSetting = getUsageSetting();
assertEquals(isSupported ? inputSetting : initialSetting, newSetting);
}
@Test
public void testCellularUsageSetting() throws Exception {
Set<Integer> supportedUsageSettings = getSupportedUsageSettings();
// If any setting works, default must be allowed.
if (supportedUsageSettings.size() > 0) {
supportedUsageSettings.add(SubscriptionManager.USAGE_SETTING_DEFAULT);
}
final int[] allUsageSettings = new int[]{
SubscriptionManager.USAGE_SETTING_UNKNOWN,
SubscriptionManager.USAGE_SETTING_DEFAULT,
SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC,
SubscriptionManager.USAGE_SETTING_DATA_CENTRIC,
3 /* undefined value */};
try {
for (int setting : allUsageSettings) {
checkUsageSetting(setting, supportedUsageSettings.contains(setting));
}
} finally {
overrideCarrierConfig(null, mSubId);
}
}
@Nullable
private PersistableBundle getBundleFromBackupData(byte[] data) {
try (ByteArrayInputStream bis = new ByteArrayInputStream(data)) {
return PersistableBundle.readFromStream(bis);
} catch (IOException e) {
return null;
}
}
private void setPreferredDataSubId(int subId) {
final LinkedBlockingQueue<Integer> resultQueue = new LinkedBlockingQueue<>(1);
Executor executor = (command)-> command.run();
Consumer<Integer> consumer = (res)-> {
if (res == null) {
resultQueue.offer(-1);
} else {
resultQueue.offer(res);
}
};
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mSm,
(sm) -> sm.setPreferredDataSubscriptionId(subId, false,
executor, consumer));
int res = -1;
try {
res = resultQueue.poll(2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
fail("Cannot get the modem result in time");
}
assertEquals(SET_OPPORTUNISTIC_SUB_SUCCESS, res);
int getValue = executeWithShellPermissionAndDefault(-1, mSm,
(sm) -> sm.getPreferredDataSubscriptionId());
assertEquals(subId, getValue);
}
private <T, U> T executeWithShellPermissionAndDefault(T defaultValue, U targetObject,
ShellIdentityUtils.ShellPermissionMethodHelper<T, U> helper) {
try {
return ShellIdentityUtils.invokeMethodWithShellPermissions(targetObject, helper);
} catch (Exception e) {
// do nothing, return default
}
return defaultValue;
}
private void assertOverrideSuccess(SubscriptionPlan... plans) {
mSm.setSubscriptionPlans(mSubId, Arrays.asList(plans));
mSm.setSubscriptionOverrideCongested(mSubId, false, 0);
}
private void assertOverrideFails(SubscriptionPlan... plans) {
mSm.setSubscriptionPlans(mSubId, Arrays.asList(plans));
try {
mSm.setSubscriptionOverrideCongested(mSubId, false, 0);
fail();
} catch (SecurityException | IllegalStateException expected) {
}
}
public static CountDownLatch waitForNetworkCapabilities(Network network,
Predicate<NetworkCapabilities> predicate) {
final CountDownLatch latch = new CountDownLatch(1);
final ConnectivityManager cm = InstrumentationRegistry.getContext()
.getSystemService(ConnectivityManager.class);
cm.registerNetworkCallback(new NetworkRequest.Builder().build(),
new NetworkCallback() {
@Override
public void onCapabilitiesChanged(Network net, NetworkCapabilities caps) {
if (net.equals(network) && predicate.test(caps)) {
latch.countDown();
cm.unregisterNetworkCallback(this);
}
}
});
return latch;
}
/**
* Corresponding to findCellularNetwork()
*/
private static CountDownLatch waitForCellularNetwork() {
final CountDownLatch latch = new CountDownLatch(1);
final ConnectivityManager cm = InstrumentationRegistry.getContext()
.getSystemService(ConnectivityManager.class);
cm.registerNetworkCallback(new NetworkRequest.Builder().build(),
new NetworkCallback() {
@Override
public void onCapabilitiesChanged(Network net, NetworkCapabilities caps) {
if (caps.hasTransport(TRANSPORT_CELLULAR)
&& caps.hasCapability(NET_CAPABILITY_INTERNET)
&& caps.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
latch.countDown();
cm.unregisterNetworkCallback(this);
}
}
});
return latch;
}
private static SubscriptionPlan buildValidSubscriptionPlan(long dataUsageTime) {
return SubscriptionPlan.Builder
.createRecurring(ZonedDateTime.parse("2007-03-14T00:00:00.000Z"),
Period.ofMonths(1))
.setTitle("CTS")
.setDataLimit(1_000_000_000, SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED)
.setDataUsage(500_000_000, dataUsageTime)
.build();
}
private static @Nullable Network findCellularNetwork() {
final ConnectivityManager cm = InstrumentationRegistry.getContext()
.getSystemService(ConnectivityManager.class);
for (Network net : cm.getAllNetworks()) {
final NetworkCapabilities caps = cm.getNetworkCapabilities(net);
if (caps != null && caps.hasTransport(TRANSPORT_CELLULAR)
&& caps.hasCapability(NET_CAPABILITY_INTERNET)
&& caps.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
return net;
}
}
return null;
}
private static boolean isSupported() {
return InstrumentationRegistry.getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION);
}
private static boolean isAutomotive() {
return InstrumentationRegistry.getContext().getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
}
private static boolean isDSDS() {
TelephonyManager tm = InstrumentationRegistry.getContext()
.getSystemService(TelephonyManager.class);
return tm != null && tm.getPhoneCount() > 1;
}
private static void setSubPlanOwner(int subId, String packageName) throws Exception {
SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
"cmd netpolicy set sub-plan-owner " + subId + " " + packageName);
}
private boolean isUnmetered5GSupported() {
final CarrierConfigManager ccm = InstrumentationRegistry.getContext()
.getSystemService(CarrierConfigManager.class);
PersistableBundle carrierConfig = ccm.getConfigForSubId(mSubId);
final TelephonyManager tm = InstrumentationRegistry.getContext()
.getSystemService(TelephonyManager.class);
int dataNetworkType = tm.getDataNetworkType(mSubId);
long supportedRats = ShellIdentityUtils.invokeMethodWithShellPermissions(tm,
TelephonyManager::getSupportedRadioAccessFamily);
boolean validCarrier = carrierConfig.getBoolean(
CarrierConfigManager.KEY_NETWORK_TEMP_NOT_METERED_SUPPORTED_BOOL);
boolean validCapabilities = (supportedRats & TelephonyManager.NETWORK_TYPE_BITMASK_NR) != 0;
// TODO: need to check for TelephonyDisplayInfo override for NR NSA
boolean validNetworkType = dataNetworkType == TelephonyManager.NETWORK_TYPE_NR;
return validCarrier && validNetworkType && validCapabilities;
}
}