Verify carrier privileges for VCN-providing packages
This change adds and verifies packages in the VcnManagmentService, ensuring that carrier privilege gain/loss correctly starts/tears down VCN instances. Bug: 163431877 Test: atest FrameworksNetTests Change-Id: I63203188c57fdde1cfc58aaf1108aa3e70eb4a50
This commit is contained in:
@@ -23,6 +23,6 @@ import android.os.ParcelUuid;
|
||||
* @hide
|
||||
*/
|
||||
interface IVcnManagementService {
|
||||
void setVcnConfig(in ParcelUuid subscriptionGroup, in VcnConfig config);
|
||||
void setVcnConfig(in ParcelUuid subscriptionGroup, in VcnConfig config, in String opPkgName);
|
||||
void clearVcnConfig(in ParcelUuid subscriptionGroup);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import static com.android.internal.annotations.VisibleForTesting.Visibility;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.PersistableBundle;
|
||||
@@ -45,11 +46,17 @@ import java.util.Set;
|
||||
public final class VcnConfig implements Parcelable {
|
||||
@NonNull private static final String TAG = VcnConfig.class.getSimpleName();
|
||||
|
||||
private static final String PACKAGE_NAME_KEY = "mPackageName";
|
||||
@NonNull private final String mPackageName;
|
||||
|
||||
private static final String GATEWAY_CONNECTION_CONFIGS_KEY = "mGatewayConnectionConfigs";
|
||||
@NonNull private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs;
|
||||
|
||||
private VcnConfig(@NonNull Set<VcnGatewayConnectionConfig> tunnelConfigs) {
|
||||
mGatewayConnectionConfigs = Collections.unmodifiableSet(tunnelConfigs);
|
||||
private VcnConfig(
|
||||
@NonNull String packageName,
|
||||
@NonNull Set<VcnGatewayConnectionConfig> gatewayConnectionConfigs) {
|
||||
mPackageName = packageName;
|
||||
mGatewayConnectionConfigs = Collections.unmodifiableSet(gatewayConnectionConfigs);
|
||||
|
||||
validate();
|
||||
}
|
||||
@@ -61,6 +68,8 @@ public final class VcnConfig implements Parcelable {
|
||||
*/
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
public VcnConfig(@NonNull PersistableBundle in) {
|
||||
mPackageName = in.getString(PACKAGE_NAME_KEY);
|
||||
|
||||
final PersistableBundle gatewayConnectionConfigsBundle =
|
||||
in.getPersistableBundle(GATEWAY_CONNECTION_CONFIGS_KEY);
|
||||
mGatewayConnectionConfigs =
|
||||
@@ -72,8 +81,19 @@ public final class VcnConfig implements Parcelable {
|
||||
}
|
||||
|
||||
private void validate() {
|
||||
Objects.requireNonNull(mPackageName, "packageName was null");
|
||||
Preconditions.checkCollectionNotEmpty(
|
||||
mGatewayConnectionConfigs, "gatewayConnectionConfigs");
|
||||
mGatewayConnectionConfigs, "gatewayConnectionConfigs was empty");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the package name of the provisioning app.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public String getProvisioningPackageName() {
|
||||
return mPackageName;
|
||||
}
|
||||
|
||||
/** Retrieves the set of configured tunnels. */
|
||||
@@ -91,6 +111,8 @@ public final class VcnConfig implements Parcelable {
|
||||
public PersistableBundle toPersistableBundle() {
|
||||
final PersistableBundle result = new PersistableBundle();
|
||||
|
||||
result.putString(PACKAGE_NAME_KEY, mPackageName);
|
||||
|
||||
final PersistableBundle gatewayConnectionConfigsBundle =
|
||||
PersistableBundleUtils.fromList(
|
||||
new ArrayList<>(mGatewayConnectionConfigs),
|
||||
@@ -102,7 +124,7 @@ public final class VcnConfig implements Parcelable {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mGatewayConnectionConfigs);
|
||||
return Objects.hash(mPackageName, mGatewayConnectionConfigs);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -112,7 +134,8 @@ public final class VcnConfig implements Parcelable {
|
||||
}
|
||||
|
||||
final VcnConfig rhs = (VcnConfig) other;
|
||||
return mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs);
|
||||
return mPackageName.equals(rhs.mPackageName)
|
||||
&& mGatewayConnectionConfigs.equals(rhs.mGatewayConnectionConfigs);
|
||||
}
|
||||
|
||||
// Parcelable methods
|
||||
@@ -143,9 +166,17 @@ public final class VcnConfig implements Parcelable {
|
||||
|
||||
/** This class is used to incrementally build {@link VcnConfig} objects. */
|
||||
public static class Builder {
|
||||
@NonNull private final String mPackageName;
|
||||
|
||||
@NonNull
|
||||
private final Set<VcnGatewayConnectionConfig> mGatewayConnectionConfigs = new ArraySet<>();
|
||||
|
||||
public Builder(@NonNull Context context) {
|
||||
Objects.requireNonNull(context, "context was null");
|
||||
|
||||
mPackageName = context.getOpPackageName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a configuration for an individual gateway connection.
|
||||
*
|
||||
@@ -168,7 +199,7 @@ public final class VcnConfig implements Parcelable {
|
||||
*/
|
||||
@NonNull
|
||||
public VcnConfig build() {
|
||||
return new VcnConfig(mGatewayConnectionConfigs);
|
||||
return new VcnConfig(mPackageName, mGatewayConnectionConfigs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ public final class VcnManager {
|
||||
requireNonNull(config, "config was null");
|
||||
|
||||
try {
|
||||
mService.setVcnConfig(subscriptionGroup, config);
|
||||
mService.setVcnConfig(subscriptionGroup, config, mContext.getOpPackageName());
|
||||
} catch (ServiceSpecificException e) {
|
||||
throw new IOException(e);
|
||||
} catch (RemoteException e) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubsc
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkProvider;
|
||||
@@ -122,6 +123,7 @@ public class VcnManagementService extends IVcnManagementService.Stub {
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
static final String VCN_CONFIG_FILE = "/data/system/vcn/configs.xml";
|
||||
|
||||
// TODO(b/176956496): Directly use CarrierServiceBindHelper.UNBIND_DELAY_MILLIS
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
static final long CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS = TimeUnit.SECONDS.toMillis(30);
|
||||
|
||||
@@ -346,9 +348,8 @@ public class VcnManagementService extends IVcnManagementService.Stub {
|
||||
|
||||
// Start any VCN instances as necessary
|
||||
for (Entry<ParcelUuid, VcnConfig> entry : mConfigs.entrySet()) {
|
||||
if (snapshot.getActiveSubscriptionGroups().contains(entry.getKey())) {
|
||||
// TODO: Add checks to ensure provisioning app is currently carrier
|
||||
// privileged on this subscription
|
||||
if (snapshot.packageHasPermissionsForSubscriptionGroup(
|
||||
entry.getKey(), entry.getValue().getProvisioningPackageName())) {
|
||||
if (!mVcns.containsKey(entry.getKey())) {
|
||||
startVcnLocked(entry.getKey(), entry.getValue());
|
||||
}
|
||||
@@ -361,7 +362,10 @@ public class VcnManagementService extends IVcnManagementService.Stub {
|
||||
// Schedule teardown of any VCN instances that have lost carrier privileges (after a
|
||||
// delay)
|
||||
for (Entry<ParcelUuid, Vcn> entry : mVcns.entrySet()) {
|
||||
if (!snapshot.getActiveSubscriptionGroups().contains(entry.getKey())) {
|
||||
final VcnConfig config = mConfigs.get(entry.getKey());
|
||||
if (config == null
|
||||
|| !snapshot.packageHasPermissionsForSubscriptionGroup(
|
||||
entry.getKey(), config.getProvisioningPackageName())) {
|
||||
final ParcelUuid uuidToTeardown = entry.getKey();
|
||||
final Vcn instanceToTeardown = entry.getValue();
|
||||
|
||||
@@ -408,11 +412,20 @@ public class VcnManagementService extends IVcnManagementService.Stub {
|
||||
* <p>Implements the IVcnManagementService Binder interface.
|
||||
*/
|
||||
@Override
|
||||
public void setVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) {
|
||||
public void setVcnConfig(
|
||||
@NonNull ParcelUuid subscriptionGroup,
|
||||
@NonNull VcnConfig config,
|
||||
@NonNull String opPkgName) {
|
||||
requireNonNull(subscriptionGroup, "subscriptionGroup was null");
|
||||
requireNonNull(config, "config was null");
|
||||
requireNonNull(opPkgName, "opPkgName was null");
|
||||
if (!config.getProvisioningPackageName().equals(opPkgName)) {
|
||||
throw new IllegalArgumentException("Mismatched caller and VcnConfig creator");
|
||||
}
|
||||
Slog.v(TAG, "VCN config updated for subGrp: " + subscriptionGroup);
|
||||
|
||||
mContext.getSystemService(AppOpsManager.class)
|
||||
.checkPackage(mDeps.getBinderCallingUid(), config.getProvisioningPackageName());
|
||||
enforceCallingUserAndCarrierPrivilege(subscriptionGroup);
|
||||
|
||||
Binder.withCleanCallingIdentity(() -> {
|
||||
|
||||
@@ -36,6 +36,8 @@ import android.telephony.CarrierConfigManager;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
|
||||
@@ -79,6 +81,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
|
||||
@NonNull private final TelephonySubscriptionTrackerCallback mCallback;
|
||||
@NonNull private final Dependencies mDeps;
|
||||
|
||||
@NonNull private final TelephonyManager mTelephonyManager;
|
||||
@NonNull private final SubscriptionManager mSubscriptionManager;
|
||||
@NonNull private final CarrierConfigManager mCarrierConfigManager;
|
||||
|
||||
@@ -106,6 +109,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
|
||||
mCallback = Objects.requireNonNull(callback, "Missing callback");
|
||||
mDeps = Objects.requireNonNull(deps, "Missing deps");
|
||||
|
||||
mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
|
||||
mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class);
|
||||
mCarrierConfigManager = mContext.getSystemService(CarrierConfigManager.class);
|
||||
|
||||
@@ -139,7 +143,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
|
||||
* so callbacks & broadcasts are all serialized on mHandler, avoiding the need for locking.
|
||||
*/
|
||||
public void handleSubscriptionsChanged() {
|
||||
final Set<ParcelUuid> activeSubGroups = new ArraySet<>();
|
||||
final Map<ParcelUuid, Set<String>> privilegedPackages = new HashMap<>();
|
||||
final Map<Integer, ParcelUuid> newSubIdToGroupMap = new HashMap<>();
|
||||
|
||||
final List<SubscriptionInfo> allSubs = mSubscriptionManager.getAllSubscriptionInfoList();
|
||||
@@ -166,12 +170,22 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
|
||||
// group.
|
||||
if (subInfo.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX
|
||||
&& mReadySubIdsBySlotId.values().contains(subInfo.getSubscriptionId())) {
|
||||
activeSubGroups.add(subInfo.getGroupUuid());
|
||||
// TODO (b/172619301): Cache based on callbacks from CarrierPrivilegesTracker
|
||||
|
||||
final TelephonyManager subIdSpecificTelephonyManager =
|
||||
mTelephonyManager.createForSubscriptionId(subInfo.getSubscriptionId());
|
||||
|
||||
final ParcelUuid subGroup = subInfo.getGroupUuid();
|
||||
final Set<String> pkgs =
|
||||
privilegedPackages.getOrDefault(subGroup, new ArraySet<>());
|
||||
pkgs.addAll(subIdSpecificTelephonyManager.getPackagesWithCarrierPrivileges());
|
||||
|
||||
privilegedPackages.put(subGroup, pkgs);
|
||||
}
|
||||
}
|
||||
|
||||
final TelephonySubscriptionSnapshot newSnapshot =
|
||||
new TelephonySubscriptionSnapshot(newSubIdToGroupMap, activeSubGroups);
|
||||
new TelephonySubscriptionSnapshot(newSubIdToGroupMap, privilegedPackages);
|
||||
|
||||
// If snapshot was meaningfully updated, fire the callback
|
||||
if (!newSnapshot.equals(mCurrentSnapshot)) {
|
||||
@@ -231,25 +245,40 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
|
||||
/** TelephonySubscriptionSnapshot is a class containing info about active subscriptions */
|
||||
public static class TelephonySubscriptionSnapshot {
|
||||
private final Map<Integer, ParcelUuid> mSubIdToGroupMap;
|
||||
private final Set<ParcelUuid> mActiveGroups;
|
||||
private final Map<ParcelUuid, Set<String>> mPrivilegedPackages;
|
||||
|
||||
public static final TelephonySubscriptionSnapshot EMPTY_SNAPSHOT =
|
||||
new TelephonySubscriptionSnapshot(Collections.emptyMap(), Collections.emptySet());
|
||||
new TelephonySubscriptionSnapshot(Collections.emptyMap(), Collections.emptyMap());
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
TelephonySubscriptionSnapshot(
|
||||
@NonNull Map<Integer, ParcelUuid> subIdToGroupMap,
|
||||
@NonNull Set<ParcelUuid> activeGroups) {
|
||||
mSubIdToGroupMap = Collections.unmodifiableMap(
|
||||
Objects.requireNonNull(subIdToGroupMap, "subIdToGroupMap was null"));
|
||||
mActiveGroups = Collections.unmodifiableSet(
|
||||
Objects.requireNonNull(activeGroups, "activeGroups was null"));
|
||||
@NonNull Map<ParcelUuid, Set<String>> privilegedPackages) {
|
||||
Objects.requireNonNull(subIdToGroupMap, "subIdToGroupMap was null");
|
||||
Objects.requireNonNull(privilegedPackages, "privilegedPackages was null");
|
||||
|
||||
mSubIdToGroupMap = Collections.unmodifiableMap(subIdToGroupMap);
|
||||
|
||||
final Map<ParcelUuid, Set<String>> unmodifiableInnerSets = new ArrayMap<>();
|
||||
for (Entry<ParcelUuid, Set<String>> entry : privilegedPackages.entrySet()) {
|
||||
unmodifiableInnerSets.put(
|
||||
entry.getKey(), Collections.unmodifiableSet(entry.getValue()));
|
||||
}
|
||||
mPrivilegedPackages = Collections.unmodifiableMap(unmodifiableInnerSets);
|
||||
}
|
||||
|
||||
/** Returns the active subscription groups */
|
||||
@NonNull
|
||||
public Set<ParcelUuid> getActiveSubscriptionGroups() {
|
||||
return mActiveGroups;
|
||||
return mPrivilegedPackages.keySet();
|
||||
}
|
||||
|
||||
/** Checks if the provided package is carrier privileged for the specified sub group. */
|
||||
public boolean packageHasPermissionsForSubscriptionGroup(
|
||||
@NonNull ParcelUuid subGrp, @NonNull String packageName) {
|
||||
final Set<String> privilegedPackages = mPrivilegedPackages.get(subGrp);
|
||||
|
||||
return privilegedPackages != null && privilegedPackages.contains(packageName);
|
||||
}
|
||||
|
||||
/** Returns the Subscription Group for a given subId. */
|
||||
@@ -276,7 +305,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mSubIdToGroupMap, mActiveGroups);
|
||||
return Objects.hash(mSubIdToGroupMap, mPrivilegedPackages);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -288,7 +317,15 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
|
||||
final TelephonySubscriptionSnapshot other = (TelephonySubscriptionSnapshot) obj;
|
||||
|
||||
return mSubIdToGroupMap.equals(other.mSubIdToGroupMap)
|
||||
&& mActiveGroups.equals(other.mActiveGroups);
|
||||
&& mPrivilegedPackages.equals(other.mPrivilegedPackages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TelephonySubscriptionSnapshot{ "
|
||||
+ "mSubIdToGroupMap=" + mSubIdToGroupMap
|
||||
+ ", mPrivilegedPackages=" + mPrivilegedPackages
|
||||
+ " }";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,12 +18,17 @@ package android.net.vcn;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.os.Parcel;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -33,12 +38,15 @@ import java.util.Set;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class VcnConfigTest {
|
||||
private static final String TEST_PACKAGE_NAME = VcnConfigTest.class.getPackage().getName();
|
||||
private static final Set<VcnGatewayConnectionConfig> GATEWAY_CONNECTION_CONFIGS =
|
||||
Collections.singleton(VcnGatewayConnectionConfigTest.buildTestConfig());
|
||||
|
||||
private final Context mContext = mock(Context.class);
|
||||
|
||||
// Public visibility for VcnManagementServiceTest
|
||||
public static VcnConfig buildTestConfig() {
|
||||
VcnConfig.Builder builder = new VcnConfig.Builder();
|
||||
public static VcnConfig buildTestConfig(@NonNull Context context) {
|
||||
VcnConfig.Builder builder = new VcnConfig.Builder(context);
|
||||
|
||||
for (VcnGatewayConnectionConfig gatewayConnectionConfig : GATEWAY_CONNECTION_CONFIGS) {
|
||||
builder.addGatewayConnectionConfig(gatewayConnectionConfig);
|
||||
@@ -47,10 +55,24 @@ public class VcnConfigTest {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
doReturn(TEST_PACKAGE_NAME).when(mContext).getOpPackageName();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderConstructorRequiresContext() {
|
||||
try {
|
||||
new VcnConfig.Builder(null);
|
||||
fail("Expected exception due to null context");
|
||||
} catch (NullPointerException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilderRequiresGatewayConnectionConfig() {
|
||||
try {
|
||||
new VcnConfig.Builder().build();
|
||||
new VcnConfig.Builder(mContext).build();
|
||||
fail("Expected exception due to no VcnGatewayConnectionConfigs provided");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
@@ -58,21 +80,22 @@ public class VcnConfigTest {
|
||||
|
||||
@Test
|
||||
public void testBuilderAndGetters() {
|
||||
final VcnConfig config = buildTestConfig();
|
||||
final VcnConfig config = buildTestConfig(mContext);
|
||||
|
||||
assertEquals(TEST_PACKAGE_NAME, config.getProvisioningPackageName());
|
||||
assertEquals(GATEWAY_CONNECTION_CONFIGS, config.getGatewayConnectionConfigs());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPersistableBundle() {
|
||||
final VcnConfig config = buildTestConfig();
|
||||
final VcnConfig config = buildTestConfig(mContext);
|
||||
|
||||
assertEquals(config, new VcnConfig(config.toPersistableBundle()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParceling() {
|
||||
final VcnConfig config = buildTestConfig();
|
||||
final VcnConfig config = buildTestConfig(mContext);
|
||||
|
||||
Parcel parcel = Parcel.obtain();
|
||||
config.writeToParcel(parcel, 0);
|
||||
|
||||
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.argThat;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.eq;
|
||||
@@ -31,6 +32,7 @@ import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.vcn.VcnConfig;
|
||||
@@ -67,9 +69,20 @@ import java.util.UUID;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class VcnManagementServiceTest {
|
||||
private static final String TEST_PACKAGE_NAME =
|
||||
VcnManagementServiceTest.class.getPackage().getName();
|
||||
private static final ParcelUuid TEST_UUID_1 = new ParcelUuid(new UUID(0, 0));
|
||||
private static final ParcelUuid TEST_UUID_2 = new ParcelUuid(new UUID(1, 1));
|
||||
private static final VcnConfig TEST_VCN_CONFIG = VcnConfigTest.buildTestConfig();
|
||||
private static final VcnConfig TEST_VCN_CONFIG;
|
||||
private static final int TEST_UID = Process.FIRST_APPLICATION_UID;
|
||||
|
||||
static {
|
||||
final Context mockConfigContext = mock(Context.class);
|
||||
doReturn(TEST_PACKAGE_NAME).when(mockConfigContext).getOpPackageName();
|
||||
|
||||
TEST_VCN_CONFIG = VcnConfigTest.buildTestConfig(mockConfigContext);
|
||||
}
|
||||
|
||||
private static final Map<ParcelUuid, VcnConfig> TEST_VCN_CONFIG_MAP =
|
||||
Collections.unmodifiableMap(Collections.singletonMap(TEST_UUID_1, TEST_VCN_CONFIG));
|
||||
|
||||
@@ -104,6 +117,7 @@ public class VcnManagementServiceTest {
|
||||
private final ConnectivityManager mConnMgr = mock(ConnectivityManager.class);
|
||||
private final TelephonyManager mTelMgr = mock(TelephonyManager.class);
|
||||
private final SubscriptionManager mSubMgr = mock(SubscriptionManager.class);
|
||||
private final AppOpsManager mAppOpsMgr = mock(AppOpsManager.class);
|
||||
private final VcnContext mVcnContext = mock(VcnContext.class);
|
||||
private final PersistableBundleUtils.LockingReadWriteHelper mConfigReadWriteHelper =
|
||||
mock(PersistableBundleUtils.LockingReadWriteHelper.class);
|
||||
@@ -117,9 +131,12 @@ public class VcnManagementServiceTest {
|
||||
setupSystemService(mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class);
|
||||
setupSystemService(
|
||||
mSubMgr, Context.TELEPHONY_SUBSCRIPTION_SERVICE, SubscriptionManager.class);
|
||||
setupSystemService(mAppOpsMgr, Context.APP_OPS_SERVICE, AppOpsManager.class);
|
||||
|
||||
doReturn(TEST_PACKAGE_NAME).when(mMockContext).getOpPackageName();
|
||||
|
||||
doReturn(mTestLooper.getLooper()).when(mMockDeps).getLooper();
|
||||
doReturn(Process.FIRST_APPLICATION_UID).when(mMockDeps).getBinderCallingUid();
|
||||
doReturn(TEST_UID).when(mMockDeps).getBinderCallingUid();
|
||||
doReturn(mVcnContext)
|
||||
.when(mMockDeps)
|
||||
.newVcnContext(
|
||||
@@ -213,6 +230,16 @@ public class VcnManagementServiceTest {
|
||||
final TelephonySubscriptionSnapshot snapshot = mock(TelephonySubscriptionSnapshot.class);
|
||||
doReturn(activeSubscriptionGroups).when(snapshot).getActiveSubscriptionGroups();
|
||||
|
||||
final Set<String> privilegedPackages =
|
||||
(activeSubscriptionGroups == null || activeSubscriptionGroups.isEmpty())
|
||||
? Collections.emptySet()
|
||||
: Collections.singleton(TEST_PACKAGE_NAME);
|
||||
doReturn(true)
|
||||
.when(snapshot)
|
||||
.packageHasPermissionsForSubscriptionGroup(
|
||||
argThat(val -> activeSubscriptionGroups.contains(val)),
|
||||
eq(TEST_PACKAGE_NAME));
|
||||
|
||||
final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
|
||||
cb.onNewSnapshot(snapshot);
|
||||
}
|
||||
@@ -227,7 +254,7 @@ public class VcnManagementServiceTest {
|
||||
}
|
||||
|
||||
private Vcn startAndGetVcnInstance(ParcelUuid uuid) {
|
||||
mVcnMgmtSvc.setVcnConfig(uuid, TEST_VCN_CONFIG);
|
||||
mVcnMgmtSvc.setVcnConfig(uuid, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
|
||||
return mVcnMgmtSvc.getAllVcns().get(uuid);
|
||||
}
|
||||
|
||||
@@ -302,7 +329,7 @@ public class VcnManagementServiceTest {
|
||||
doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid();
|
||||
|
||||
try {
|
||||
mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig());
|
||||
mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
|
||||
fail("Expected IllegalStateException exception for system server");
|
||||
} catch (IllegalStateException expected) {
|
||||
}
|
||||
@@ -310,12 +337,12 @@ public class VcnManagementServiceTest {
|
||||
|
||||
@Test
|
||||
public void testSetVcnConfigRequiresSystemUser() throws Exception {
|
||||
doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, Process.FIRST_APPLICATION_UID))
|
||||
doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, TEST_UID))
|
||||
.when(mMockDeps)
|
||||
.getBinderCallingUid();
|
||||
|
||||
try {
|
||||
mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig());
|
||||
mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
|
||||
fail("Expected security exception for non system user");
|
||||
} catch (SecurityException expected) {
|
||||
}
|
||||
@@ -326,16 +353,25 @@ public class VcnManagementServiceTest {
|
||||
setupMockedCarrierPrivilege(false);
|
||||
|
||||
try {
|
||||
mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig());
|
||||
mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
|
||||
fail("Expected security exception for missing carrier privileges");
|
||||
} catch (SecurityException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetVcnConfigMismatchedPackages() throws Exception {
|
||||
try {
|
||||
mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, "IncorrectPackage");
|
||||
fail("Expected exception due to mismatched packages in config and method call");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetVcnConfig() throws Exception {
|
||||
// Use a different UUID to simulate a new VCN config.
|
||||
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG);
|
||||
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
|
||||
assertEquals(TEST_VCN_CONFIG, mVcnMgmtSvc.getConfigs().get(TEST_UUID_2));
|
||||
verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
|
||||
}
|
||||
@@ -353,7 +389,7 @@ public class VcnManagementServiceTest {
|
||||
|
||||
@Test
|
||||
public void testClearVcnConfigRequiresSystemUser() throws Exception {
|
||||
doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, Process.FIRST_APPLICATION_UID))
|
||||
doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, TEST_UID))
|
||||
.when(mMockDeps)
|
||||
.getBinderCallingUid();
|
||||
|
||||
@@ -385,7 +421,7 @@ public class VcnManagementServiceTest {
|
||||
@Test
|
||||
public void testSetVcnConfigClearVcnConfigStartsUpdatesAndTeardsDownVcns() throws Exception {
|
||||
// Use a different UUID to simulate a new VCN config.
|
||||
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG);
|
||||
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
|
||||
final Map<ParcelUuid, Vcn> vcnInstances = mVcnMgmtSvc.getAllVcns();
|
||||
final Vcn vcnInstance = vcnInstances.get(TEST_UUID_2);
|
||||
assertEquals(1, vcnInstances.size());
|
||||
@@ -396,7 +432,7 @@ public class VcnManagementServiceTest {
|
||||
verify(mMockDeps).newVcn(eq(mVcnContext), eq(TEST_UUID_2), eq(TEST_VCN_CONFIG));
|
||||
|
||||
// Verify Vcn is updated if it was previously started
|
||||
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG);
|
||||
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
|
||||
verify(vcnInstance).updateConfig(TEST_VCN_CONFIG);
|
||||
|
||||
// Verify Vcn is stopped if it was already started
|
||||
|
||||
@@ -30,6 +30,7 @@ import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
@@ -37,6 +38,10 @@ import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.emptySet;
|
||||
import static java.util.Collections.singletonMap;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -49,6 +54,7 @@ import android.telephony.CarrierConfigManager;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
@@ -63,6 +69,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
@@ -71,12 +78,16 @@ import java.util.UUID;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class TelephonySubscriptionTrackerTest {
|
||||
private static final String PACKAGE_NAME =
|
||||
TelephonySubscriptionTrackerTest.class.getPackage().getName();
|
||||
private static final ParcelUuid TEST_PARCEL_UUID = new ParcelUuid(UUID.randomUUID());
|
||||
private static final int TEST_SIM_SLOT_INDEX = 1;
|
||||
private static final int TEST_SUBSCRIPTION_ID_1 = 2;
|
||||
private static final SubscriptionInfo TEST_SUBINFO_1 = mock(SubscriptionInfo.class);
|
||||
private static final int TEST_SUBSCRIPTION_ID_2 = 3;
|
||||
private static final SubscriptionInfo TEST_SUBINFO_2 = mock(SubscriptionInfo.class);
|
||||
private static final Map<ParcelUuid, Set<String>> TEST_PRIVILEGED_PACKAGES =
|
||||
Collections.singletonMap(TEST_PARCEL_UUID, Collections.singleton(PACKAGE_NAME));
|
||||
private static final Map<Integer, ParcelUuid> TEST_SUBID_TO_GROUP_MAP;
|
||||
|
||||
static {
|
||||
@@ -91,6 +102,7 @@ public class TelephonySubscriptionTrackerTest {
|
||||
@NonNull private final Handler mHandler;
|
||||
@NonNull private final TelephonySubscriptionTracker.Dependencies mDeps;
|
||||
|
||||
@NonNull private final TelephonyManager mTelephonyManager;
|
||||
@NonNull private final SubscriptionManager mSubscriptionManager;
|
||||
@NonNull private final CarrierConfigManager mCarrierConfigManager;
|
||||
|
||||
@@ -103,9 +115,15 @@ public class TelephonySubscriptionTrackerTest {
|
||||
mHandler = new Handler(mTestLooper.getLooper());
|
||||
mDeps = mock(TelephonySubscriptionTracker.Dependencies.class);
|
||||
|
||||
mTelephonyManager = mock(TelephonyManager.class);
|
||||
mSubscriptionManager = mock(SubscriptionManager.class);
|
||||
mCarrierConfigManager = mock(CarrierConfigManager.class);
|
||||
|
||||
doReturn(Context.TELEPHONY_SERVICE)
|
||||
.when(mContext)
|
||||
.getSystemServiceName(TelephonyManager.class);
|
||||
doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
|
||||
|
||||
doReturn(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
|
||||
.when(mContext)
|
||||
.getSystemServiceName(SubscriptionManager.class);
|
||||
@@ -140,6 +158,9 @@ public class TelephonySubscriptionTrackerTest {
|
||||
doReturn(Arrays.asList(TEST_SUBINFO_1, TEST_SUBINFO_2))
|
||||
.when(mSubscriptionManager)
|
||||
.getAllSubscriptionInfoList();
|
||||
|
||||
doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
|
||||
setPrivilegedPackagesForMock(Collections.singletonList(PACKAGE_NAME));
|
||||
}
|
||||
|
||||
private IntentFilter getIntentFilter() {
|
||||
@@ -167,13 +188,15 @@ public class TelephonySubscriptionTrackerTest {
|
||||
return intent;
|
||||
}
|
||||
|
||||
private TelephonySubscriptionSnapshot buildExpectedSnapshot(Set<ParcelUuid> activeSubGroups) {
|
||||
return buildExpectedSnapshot(TEST_SUBID_TO_GROUP_MAP, activeSubGroups);
|
||||
private TelephonySubscriptionSnapshot buildExpectedSnapshot(
|
||||
Map<ParcelUuid, Set<String>> privilegedPackages) {
|
||||
return buildExpectedSnapshot(TEST_SUBID_TO_GROUP_MAP, privilegedPackages);
|
||||
}
|
||||
|
||||
private TelephonySubscriptionSnapshot buildExpectedSnapshot(
|
||||
Map<Integer, ParcelUuid> subIdToGroupMap, Set<ParcelUuid> activeSubGroups) {
|
||||
return new TelephonySubscriptionSnapshot(subIdToGroupMap, activeSubGroups);
|
||||
Map<Integer, ParcelUuid> subIdToGroupMap,
|
||||
Map<ParcelUuid, Set<String>> privilegedPackages) {
|
||||
return new TelephonySubscriptionSnapshot(subIdToGroupMap, privilegedPackages);
|
||||
}
|
||||
|
||||
private void verifyNoActiveSubscriptions() {
|
||||
@@ -186,6 +209,10 @@ public class TelephonySubscriptionTrackerTest {
|
||||
Collections.singletonMap(TEST_SIM_SLOT_INDEX, TEST_SUBSCRIPTION_ID_1));
|
||||
}
|
||||
|
||||
private void setPrivilegedPackagesForMock(@NonNull List<String> privilegedPackages) {
|
||||
doReturn(privilegedPackages).when(mTelephonyManager).getPackagesWithCarrierPrivileges();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegister() throws Exception {
|
||||
verify(mContext)
|
||||
@@ -223,15 +250,30 @@ public class TelephonySubscriptionTrackerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnSubscriptionsChangedFired_WithReadySubIds() throws Exception {
|
||||
public void testOnSubscriptionsChangedFired_WithReadySubidsNoPrivilegedPackages()
|
||||
throws Exception {
|
||||
setupReadySubIds();
|
||||
setPrivilegedPackagesForMock(Collections.emptyList());
|
||||
|
||||
final OnSubscriptionsChangedListener listener = getOnSubscriptionsChangedListener();
|
||||
listener.onSubscriptionsChanged();
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
final Map<ParcelUuid, Set<String>> privilegedPackages =
|
||||
Collections.singletonMap(TEST_PARCEL_UUID, new ArraySet<>());
|
||||
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(privilegedPackages)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnSubscriptionsChangedFired_WithReadySubidsAndPrivilegedPackages()
|
||||
throws Exception {
|
||||
setupReadySubIds();
|
||||
|
||||
final OnSubscriptionsChangedListener listener = getOnSubscriptionsChangedListener();
|
||||
listener.onSubscriptionsChanged();
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
final Set<ParcelUuid> activeSubGroups = Collections.singleton(TEST_PARCEL_UUID);
|
||||
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(activeSubGroups)));
|
||||
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -239,8 +281,7 @@ public class TelephonySubscriptionTrackerTest {
|
||||
mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
final Set<ParcelUuid> activeSubGroups = Collections.singleton(TEST_PARCEL_UUID);
|
||||
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(activeSubGroups)));
|
||||
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -253,8 +294,7 @@ public class TelephonySubscriptionTrackerTest {
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
// Expect an empty snapshot
|
||||
verify(mCallback).onNewSnapshot(
|
||||
eq(buildExpectedSnapshot(Collections.emptyMap(), Collections.emptySet())));
|
||||
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(emptyMap(), emptyMap())));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -281,41 +321,57 @@ public class TelephonySubscriptionTrackerTest {
|
||||
|
||||
@Test
|
||||
public void testSubscriptionsClearedAfterValidTriggersCallbacks() throws Exception {
|
||||
final Set<ParcelUuid> activeSubGroups = Collections.singleton(TEST_PARCEL_UUID);
|
||||
|
||||
mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
|
||||
mTestLooper.dispatchAll();
|
||||
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(activeSubGroups)));
|
||||
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
|
||||
assertNotNull(
|
||||
mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
|
||||
|
||||
doReturn(Collections.emptyList()).when(mSubscriptionManager).getAllSubscriptionInfoList();
|
||||
mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
|
||||
mTestLooper.dispatchAll();
|
||||
verify(mCallback).onNewSnapshot(
|
||||
eq(buildExpectedSnapshot(Collections.emptyMap(), Collections.emptySet())));
|
||||
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(emptyMap(), emptyMap())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSlotClearedAfterValidTriggersCallbacks() throws Exception {
|
||||
final Set<ParcelUuid> activeSubGroups = Collections.singleton(TEST_PARCEL_UUID);
|
||||
|
||||
mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(true));
|
||||
mTestLooper.dispatchAll();
|
||||
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(activeSubGroups)));
|
||||
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
|
||||
assertNotNull(
|
||||
mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
|
||||
|
||||
mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false));
|
||||
mTestLooper.dispatchAll();
|
||||
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(Collections.emptySet())));
|
||||
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(emptyMap())));
|
||||
assertNull(mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangingPrivilegedPackagesAfterValidTriggersCallbacks() throws Exception {
|
||||
setupReadySubIds();
|
||||
|
||||
// Setup initial "valid" state
|
||||
final OnSubscriptionsChangedListener listener = getOnSubscriptionsChangedListener();
|
||||
listener.onSubscriptionsChanged();
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(TEST_PRIVILEGED_PACKAGES)));
|
||||
|
||||
// Simulate a loss of carrier privileges
|
||||
setPrivilegedPackagesForMock(Collections.emptyList());
|
||||
listener.onSubscriptionsChanged();
|
||||
mTestLooper.dispatchAll();
|
||||
|
||||
verify(mCallback)
|
||||
.onNewSnapshot(
|
||||
eq(buildExpectedSnapshot(singletonMap(TEST_PARCEL_UUID, emptySet()))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTelephonySubscriptionSnapshotGetGroupForSubId() throws Exception {
|
||||
final TelephonySubscriptionSnapshot snapshot =
|
||||
new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, Collections.emptySet());
|
||||
new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, emptyMap());
|
||||
|
||||
assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_1));
|
||||
assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_2));
|
||||
@@ -324,7 +380,7 @@ public class TelephonySubscriptionTrackerTest {
|
||||
@Test
|
||||
public void testTelephonySubscriptionSnapshotGetAllSubIdsInGroup() throws Exception {
|
||||
final TelephonySubscriptionSnapshot snapshot =
|
||||
new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, Collections.emptySet());
|
||||
new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, emptyMap());
|
||||
|
||||
assertEquals(
|
||||
new ArraySet<>(Arrays.asList(TEST_SUBSCRIPTION_ID_1, TEST_SUBSCRIPTION_ID_2)),
|
||||
|
||||
Reference in New Issue
Block a user