diff options
10 files changed, 425 insertions, 47 deletions
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index f1b110ab29c8..40e4083c02db 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -104,6 +104,14 @@ public class VcnManager { // TODO: Add separate signal strength thresholds for 2.4 GHz and 5GHz + /** List of Carrier Config options to extract from Carrier Config bundles. @hide */ + @NonNull + public static final String[] VCN_RELATED_CARRIER_CONFIG_KEYS = + new String[] { + VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY, + VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY + }; + private static final Map< VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder> REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>(); diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java index 89470ec00a6c..5c305c6902af 100644 --- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java +++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java @@ -29,6 +29,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.net.vcn.VcnManager; import android.os.Handler; import android.os.HandlerExecutor; import android.os.ParcelUuid; @@ -47,6 +48,8 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.util.IndentingPrintWriter; +import com.android.server.vcn.util.PersistableBundleUtils; +import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import java.util.ArrayList; import java.util.Collections; @@ -95,6 +98,10 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { // TODO (Android T+): Add ability to handle multiple subIds per slot. @NonNull private final Map<Integer, Integer> mReadySubIdsBySlotId = new HashMap<>(); + + @NonNull + private final Map<Integer, PersistableBundleWrapper> mSubIdToCarrierConfigMap = new HashMap<>(); + @NonNull private final OnSubscriptionsChangedListener mSubscriptionChangedListener; @NonNull @@ -250,7 +257,10 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { final TelephonySubscriptionSnapshot newSnapshot = new TelephonySubscriptionSnapshot( - mDeps.getActiveDataSubscriptionId(), newSubIdToInfoMap, privilegedPackages); + mDeps.getActiveDataSubscriptionId(), + newSubIdToInfoMap, + mSubIdToCarrierConfigMap, + privilegedPackages); // If snapshot was meaningfully updated, fire the callback if (!newSnapshot.equals(mCurrentSnapshot)) { @@ -311,47 +321,77 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { } if (SubscriptionManager.isValidSubscriptionId(subId)) { - final PersistableBundle carrierConfigs = mCarrierConfigManager.getConfigForSubId(subId); - if (mDeps.isConfigForIdentifiedCarrier(carrierConfigs)) { + final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId); + if (mDeps.isConfigForIdentifiedCarrier(carrierConfig)) { mReadySubIdsBySlotId.put(slotId, subId); + + final PersistableBundle minimized = + PersistableBundleUtils.minimizeBundle( + carrierConfig, VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS); + if (minimized != null) { + mSubIdToCarrierConfigMap.put(subId, new PersistableBundleWrapper(minimized)); + } handleSubscriptionsChanged(); } } else { - mReadySubIdsBySlotId.remove(slotId); + final Integer oldSubid = mReadySubIdsBySlotId.remove(slotId); + if (oldSubid != null) { + mSubIdToCarrierConfigMap.remove(oldSubid); + } handleSubscriptionsChanged(); } } @VisibleForTesting(visibility = Visibility.PRIVATE) void setReadySubIdsBySlotId(Map<Integer, Integer> readySubIdsBySlotId) { + mReadySubIdsBySlotId.clear(); mReadySubIdsBySlotId.putAll(readySubIdsBySlotId); } @VisibleForTesting(visibility = Visibility.PRIVATE) + void setSubIdToCarrierConfigMap( + Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap) { + mSubIdToCarrierConfigMap.clear(); + mSubIdToCarrierConfigMap.putAll(subIdToCarrierConfigMap); + } + + @VisibleForTesting(visibility = Visibility.PRIVATE) Map<Integer, Integer> getReadySubIdsBySlotId() { return Collections.unmodifiableMap(mReadySubIdsBySlotId); } + @VisibleForTesting(visibility = Visibility.PRIVATE) + Map<Integer, PersistableBundleWrapper> getSubIdToCarrierConfigMap() { + return Collections.unmodifiableMap(mSubIdToCarrierConfigMap); + } + /** TelephonySubscriptionSnapshot is a class containing info about active subscriptions */ public static class TelephonySubscriptionSnapshot { private final int mActiveDataSubId; private final Map<Integer, SubscriptionInfo> mSubIdToInfoMap; + private final Map<Integer, PersistableBundleWrapper> mSubIdToCarrierConfigMap; private final Map<ParcelUuid, Set<String>> mPrivilegedPackages; public static final TelephonySubscriptionSnapshot EMPTY_SNAPSHOT = new TelephonySubscriptionSnapshot( - INVALID_SUBSCRIPTION_ID, Collections.emptyMap(), Collections.emptyMap()); + INVALID_SUBSCRIPTION_ID, + Collections.emptyMap(), + Collections.emptyMap(), + Collections.emptyMap()); @VisibleForTesting(visibility = Visibility.PRIVATE) TelephonySubscriptionSnapshot( int activeDataSubId, @NonNull Map<Integer, SubscriptionInfo> subIdToInfoMap, + @NonNull Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap, @NonNull Map<ParcelUuid, Set<String>> privilegedPackages) { mActiveDataSubId = activeDataSubId; Objects.requireNonNull(subIdToInfoMap, "subIdToInfoMap was null"); Objects.requireNonNull(privilegedPackages, "privilegedPackages was null"); + Objects.requireNonNull(subIdToCarrierConfigMap, "subIdToCarrierConfigMap was null"); mSubIdToInfoMap = Collections.unmodifiableMap(subIdToInfoMap); + mSubIdToCarrierConfigMap = Collections.unmodifiableMap(subIdToCarrierConfigMap); final Map<ParcelUuid, Set<String>> unmodifiableInnerSets = new ArrayMap<>(); for (Entry<ParcelUuid, Set<String>> entry : privilegedPackages.entrySet()) { @@ -423,9 +463,40 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { : false; } + /** + * Retrieves a carrier config for a subscription in the provided group. + * + * <p>This method will prioritize non-opportunistic subscriptions, but will use the a + * carrier config for an opportunistic subscription if no other subscriptions are found. + */ + @Nullable + public PersistableBundleWrapper getCarrierConfigForSubGrp(@NonNull ParcelUuid subGrp) { + PersistableBundleWrapper result = null; + + for (int subId : getAllSubIdsInGroup(subGrp)) { + final PersistableBundleWrapper config = mSubIdToCarrierConfigMap.get(subId); + if (config != null) { + result = config; + + // Attempt to use (any) non-opportunistic subscription. If this subscription is + // opportunistic, continue and try to find a non-opportunistic subscription, + // using the opportunistic ones as a last resort. + if (!isOpportunistic(subId)) { + return config; + } + } + } + + return result; + } + @Override public int hashCode() { - return Objects.hash(mActiveDataSubId, mSubIdToInfoMap, mPrivilegedPackages); + return Objects.hash( + mActiveDataSubId, + mSubIdToInfoMap, + mSubIdToCarrierConfigMap, + mPrivilegedPackages); } @Override @@ -438,6 +509,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { return mActiveDataSubId == other.mActiveDataSubId && mSubIdToInfoMap.equals(other.mSubIdToInfoMap) + && mSubIdToCarrierConfigMap.equals(other.mSubIdToCarrierConfigMap) && mPrivilegedPackages.equals(other.mPrivilegedPackages); } @@ -448,6 +520,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { pw.println("mActiveDataSubId: " + mActiveDataSubId); pw.println("mSubIdToInfoMap: " + mSubIdToInfoMap); + pw.println("mSubIdToCarrierConfigMap: " + mSubIdToCarrierConfigMap); pw.println("mPrivilegedPackages: " + mPrivilegedPackages); pw.decreaseIndent(); @@ -458,6 +531,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { return "TelephonySubscriptionSnapshot{ " + "mActiveDataSubId=" + mActiveDataSubId + ", mSubIdToInfoMap=" + mSubIdToInfoMap + + ", mSubIdToCarrierConfigMap=" + mSubIdToCarrierConfigMap + ", mPrivilegedPackages=" + mPrivilegedPackages + " }"; } diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java index c96c1ee01a6d..2f84fddc7278 100644 --- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java +++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java @@ -24,6 +24,7 @@ import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_FORBIDDEN; import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED; import static com.android.server.VcnManagementService.LOCAL_LOG; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import android.annotation.NonNull; import android.annotation.Nullable; @@ -34,7 +35,6 @@ import android.net.vcn.VcnManager; import android.net.vcn.VcnUnderlyingNetworkTemplate; import android.net.vcn.VcnWifiUnderlyingNetworkTemplate; import android.os.ParcelUuid; -import android.os.PersistableBundle; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.Slog; @@ -81,7 +81,7 @@ class NetworkPriorityClassifier { ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, UnderlyingNetworkRecord currentlySelected, - PersistableBundle carrierConfig) { + PersistableBundleWrapper carrierConfig) { // mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED if (networkRecord.isBlocked) { @@ -119,7 +119,7 @@ class NetworkPriorityClassifier { ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, UnderlyingNetworkRecord currentlySelected, - PersistableBundle carrierConfig) { + PersistableBundleWrapper carrierConfig) { final NetworkCapabilities caps = networkRecord.networkCapabilities; final boolean isSelectedUnderlyingNetwork = currentlySelected != null @@ -181,7 +181,7 @@ class NetworkPriorityClassifier { VcnWifiUnderlyingNetworkTemplate networkPriority, UnderlyingNetworkRecord networkRecord, UnderlyingNetworkRecord currentlySelected, - PersistableBundle carrierConfig) { + PersistableBundleWrapper carrierConfig) { final NetworkCapabilities caps = networkRecord.networkCapabilities; if (!caps.hasTransport(TRANSPORT_WIFI)) { @@ -204,7 +204,7 @@ class NetworkPriorityClassifier { private static boolean isWifiRssiAcceptable( UnderlyingNetworkRecord networkRecord, UnderlyingNetworkRecord currentlySelected, - PersistableBundle carrierConfig) { + PersistableBundleWrapper carrierConfig) { final NetworkCapabilities caps = networkRecord.networkCapabilities; final boolean isSelectedNetwork = currentlySelected != null @@ -314,7 +314,7 @@ class NetworkPriorityClassifier { return false; } - static int getWifiEntryRssiThreshold(@Nullable PersistableBundle carrierConfig) { + static int getWifiEntryRssiThreshold(@Nullable PersistableBundleWrapper carrierConfig) { if (carrierConfig != null) { return carrierConfig.getInt( VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY, @@ -323,7 +323,7 @@ class NetworkPriorityClassifier { return WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT; } - static int getWifiExitRssiThreshold(@Nullable PersistableBundle carrierConfig) { + static int getWifiExitRssiThreshold(@Nullable PersistableBundleWrapper carrierConfig) { if (carrierConfig != null) { return carrierConfig.getInt( VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY, diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java index a3babf7c9fff..d474c5d33a93 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java @@ -21,7 +21,7 @@ import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListen import static com.android.server.VcnManagementService.LOCAL_LOG; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiEntryRssiThreshold; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiExitRssiThreshold; -import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.isOpportunistic; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import android.annotation.NonNull; import android.annotation.Nullable; @@ -37,8 +37,6 @@ import android.net.vcn.VcnUnderlyingNetworkTemplate; import android.os.Handler; import android.os.HandlerExecutor; import android.os.ParcelUuid; -import android.os.PersistableBundle; -import android.telephony.CarrierConfigManager; import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.util.ArrayMap; @@ -51,7 +49,6 @@ import com.android.server.vcn.VcnContext; import com.android.server.vcn.util.LogUtils; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -87,7 +84,7 @@ public class UnderlyingNetworkController { @Nullable private UnderlyingNetworkListener mRouteSelectionCallback; @NonNull private TelephonySubscriptionSnapshot mLastSnapshot; - @Nullable private PersistableBundle mCarrierConfig; + @Nullable private PersistableBundleWrapper mCarrierConfig; private boolean mIsQuitting = false; @Nullable private UnderlyingNetworkRecord mCurrentRecord; @@ -124,25 +121,7 @@ public class UnderlyingNetworkController { .getSystemService(TelephonyManager.class) .registerTelephonyCallback(new HandlerExecutor(mHandler), mActiveDataSubIdListener); - // TODO: Listen for changes in carrier config that affect this. - for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) { - PersistableBundle config = - mVcnContext - .getContext() - .getSystemService(CarrierConfigManager.class) - .getConfigForSubId(subId); - - if (config != null) { - mCarrierConfig = config; - - // Attempt to use (any) non-opportunistic subscription. If this subscription is - // opportunistic, continue and try to find a non-opportunistic subscription, using - // the opportunistic ones as a last resort. - if (!isOpportunistic(mLastSnapshot, Collections.singleton(subId))) { - break; - } - } - } + mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup); registerOrUpdateNetworkRequests(); } @@ -334,6 +313,9 @@ public class UnderlyingNetworkController { final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot; mLastSnapshot = newSnapshot; + // Update carrier config + mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup); + // Only trigger re-registration if subIds in this group have changed if (oldSnapshot .getAllSubIdsInGroup(mSubscriptionGroup) diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java index 06f92805ad2b..319680e0b01c 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java @@ -16,6 +16,8 @@ package com.android.server.vcn.routeselection; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; + import android.annotation.NonNull; import android.annotation.Nullable; import android.net.LinkProperties; @@ -23,7 +25,6 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.vcn.VcnUnderlyingNetworkTemplate; import android.os.ParcelUuid; -import android.os.PersistableBundle; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting.Visibility; @@ -68,7 +69,7 @@ public class UnderlyingNetworkRecord { ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, UnderlyingNetworkRecord currentlySelected, - PersistableBundle carrierConfig) { + PersistableBundleWrapper carrierConfig) { // Never changes after the underlying network record is created. if (mPriorityClass == PRIORITY_CLASS_INVALID) { mPriorityClass = @@ -113,7 +114,7 @@ public class UnderlyingNetworkRecord { ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, UnderlyingNetworkRecord currentlySelected, - PersistableBundle carrierConfig) { + PersistableBundleWrapper carrierConfig) { return (left, right) -> { final int leftIndex = left.getOrCalculatePriorityClass( @@ -167,7 +168,7 @@ public class UnderlyingNetworkRecord { ParcelUuid subscriptionGroup, TelephonySubscriptionSnapshot snapshot, UnderlyingNetworkRecord currentlySelected, - PersistableBundle carrierConfig) { + PersistableBundleWrapper carrierConfig) { pw.println("UnderlyingNetworkRecord:"); pw.increaseIndent(); diff --git a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java index 1c675c228554..08e8eebb8740 100644 --- a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java +++ b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java @@ -28,11 +28,13 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; +import java.util.TreeSet; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -354,4 +356,182 @@ public class PersistableBundleUtils { } } } + + /** + * Returns a copy of the persistable bundle with only the specified keys + * + * <p>This allows for holding minimized copies for memory-saving purposes. + */ + @NonNull + public static PersistableBundle minimizeBundle( + @NonNull PersistableBundle bundle, String... keys) { + final PersistableBundle minimized = new PersistableBundle(); + + if (bundle == null) { + return minimized; + } + + for (String key : keys) { + if (bundle.containsKey(key)) { + final Object value = bundle.get(key); + if (value == null) { + continue; + } + + if (value instanceof Boolean) { + minimized.putBoolean(key, (Boolean) value); + } else if (value instanceof boolean[]) { + minimized.putBooleanArray(key, (boolean[]) value); + } else if (value instanceof Double) { + minimized.putDouble(key, (Double) value); + } else if (value instanceof double[]) { + minimized.putDoubleArray(key, (double[]) value); + } else if (value instanceof Integer) { + minimized.putInt(key, (Integer) value); + } else if (value instanceof int[]) { + minimized.putIntArray(key, (int[]) value); + } else if (value instanceof Long) { + minimized.putLong(key, (Long) value); + } else if (value instanceof long[]) { + minimized.putLongArray(key, (long[]) value); + } else if (value instanceof String) { + minimized.putString(key, (String) value); + } else if (value instanceof String[]) { + minimized.putStringArray(key, (String[]) value); + } else if (value instanceof PersistableBundle) { + minimized.putPersistableBundle(key, (PersistableBundle) value); + } else { + continue; + } + } + } + + return minimized; + } + + /** Builds a stable hashcode */ + public static int getHashCode(@Nullable PersistableBundle bundle) { + if (bundle == null) { + return -1; + } + + int iterativeHashcode = 0; + TreeSet<String> treeSet = new TreeSet<>(bundle.keySet()); + for (String key : treeSet) { + Object val = bundle.get(key); + if (val instanceof PersistableBundle) { + iterativeHashcode = + Objects.hash(iterativeHashcode, key, getHashCode((PersistableBundle) val)); + } else { + iterativeHashcode = Objects.hash(iterativeHashcode, key, val); + } + } + + return iterativeHashcode; + } + + /** Checks for persistable bundle equality */ + public static boolean isEqual( + @Nullable PersistableBundle left, @Nullable PersistableBundle right) { + // Check for pointer equality & null equality + if (Objects.equals(left, right)) { + return true; + } + + // If only one of the two is null, but not the other, not equal by definition. + if (Objects.isNull(left) != Objects.isNull(right)) { + return false; + } + + if (!left.keySet().equals(right.keySet())) { + return false; + } + + for (String key : left.keySet()) { + Object leftVal = left.get(key); + Object rightVal = right.get(key); + + // Check for equality + if (Objects.equals(leftVal, rightVal)) { + continue; + } else if (Objects.isNull(leftVal) != Objects.isNull(rightVal)) { + // If only one of the two is null, but not the other, not equal by definition. + return false; + } else if (!Objects.equals(leftVal.getClass(), rightVal.getClass())) { + // If classes are different, not equal by definition. + return false; + } + if (leftVal instanceof PersistableBundle) { + if (!isEqual((PersistableBundle) leftVal, (PersistableBundle) rightVal)) { + return false; + } + } else if (leftVal.getClass().isArray()) { + if (leftVal instanceof boolean[]) { + if (!Arrays.equals((boolean[]) leftVal, (boolean[]) rightVal)) { + return false; + } + } else if (leftVal instanceof double[]) { + if (!Arrays.equals((double[]) leftVal, (double[]) rightVal)) { + return false; + } + } else if (leftVal instanceof int[]) { + if (!Arrays.equals((int[]) leftVal, (int[]) rightVal)) { + return false; + } + } else if (leftVal instanceof long[]) { + if (!Arrays.equals((long[]) leftVal, (long[]) rightVal)) { + return false; + } + } else if (!Arrays.equals((Object[]) leftVal, (Object[]) rightVal)) { + return false; + } + } else { + if (!Objects.equals(leftVal, rightVal)) { + return false; + } + } + } + + return true; + } + + /** + * Wrapper class around PersistableBundles to allow equality comparisons + * + * <p>This class exposes the minimal getters to retrieve values. + */ + public static class PersistableBundleWrapper { + @NonNull private final PersistableBundle mBundle; + + public PersistableBundleWrapper(@NonNull PersistableBundle bundle) { + mBundle = Objects.requireNonNull(bundle, "Bundle was null"); + } + + /** + * Retrieves the integer associated with the provided key. + * + * @param key the string key to query + * @param defaultValue the value to return if key does not exist + * @return the int value, or the default + */ + public int getInt(String key, int defaultValue) { + return mBundle.getInt(key, defaultValue); + } + + @Override + public int hashCode() { + return getHashCode(mBundle); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PersistableBundleWrapper)) { + return false; + } + + final PersistableBundleWrapper other = (PersistableBundleWrapper) obj; + + return isEqual(mBundle, other.mBundle); + } + } } diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java index 5f606e1dab0c..09080be9ee41 100644 --- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java +++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java @@ -26,6 +26,7 @@ import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -50,9 +51,11 @@ import android.annotation.NonNull; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.net.vcn.VcnManager; import android.os.Handler; import android.os.HandlerExecutor; import android.os.ParcelUuid; +import android.os.PersistableBundle; import android.os.test.TestLooper; import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; @@ -104,6 +107,26 @@ public class TelephonySubscriptionTrackerTest { TEST_SUBID_TO_INFO_MAP = Collections.unmodifiableMap(subIdToGroupMap); } + private static final String TEST_CARRIER_CONFIG_KEY_1 = "TEST_CARRIER_CONFIG_KEY_1"; + private static final String TEST_CARRIER_CONFIG_KEY_2 = "TEST_CARRIER_CONFIG_KEY_2"; + private static final PersistableBundle TEST_CARRIER_CONFIG = new PersistableBundle(); + private static final PersistableBundleWrapper TEST_CARRIER_CONFIG_WRAPPER; + private static final Map<Integer, PersistableBundleWrapper> TEST_SUBID_TO_CARRIER_CONFIG_MAP; + + static { + TEST_CARRIER_CONFIG.putString( + VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY, + VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY); + TEST_CARRIER_CONFIG.putString( + VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY, + VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY); + TEST_CARRIER_CONFIG_WRAPPER = new PersistableBundleWrapper(TEST_CARRIER_CONFIG); + + final Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap = new HashMap<>(); + subIdToCarrierConfigMap.put(TEST_SUBSCRIPTION_ID_1, TEST_CARRIER_CONFIG_WRAPPER); + TEST_SUBID_TO_CARRIER_CONFIG_MAP = Collections.unmodifiableMap(subIdToCarrierConfigMap); + } + @NonNull private final Context mContext; @NonNull private final TestLooper mTestLooper; @NonNull private final Handler mHandler; @@ -144,6 +167,9 @@ public class TelephonySubscriptionTrackerTest { doReturn(mCarrierConfigManager) .when(mContext) .getSystemService(Context.CARRIER_CONFIG_SERVICE); + doReturn(TEST_CARRIER_CONFIG) + .when(mCarrierConfigManager) + .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1)); // subId 1, 2 are in same subGrp, only subId 1 is active doReturn(TEST_PARCEL_UUID).when(TEST_SUBINFO_1).getGroupUuid(); @@ -227,14 +253,24 @@ public class TelephonySubscriptionTrackerTest { private TelephonySubscriptionSnapshot buildExpectedSnapshot( Map<Integer, SubscriptionInfo> subIdToInfoMap, Map<ParcelUuid, Set<String>> privilegedPackages) { - return new TelephonySubscriptionSnapshot(0, subIdToInfoMap, privilegedPackages); + return buildExpectedSnapshot(0, subIdToInfoMap, privilegedPackages); } private TelephonySubscriptionSnapshot buildExpectedSnapshot( int activeSubId, Map<Integer, SubscriptionInfo> subIdToInfoMap, Map<ParcelUuid, Set<String>> privilegedPackages) { - return new TelephonySubscriptionSnapshot(activeSubId, subIdToInfoMap, privilegedPackages); + return buildExpectedSnapshot( + activeSubId, subIdToInfoMap, TEST_SUBID_TO_CARRIER_CONFIG_MAP, privilegedPackages); + } + + private TelephonySubscriptionSnapshot buildExpectedSnapshot( + int activeSubId, + Map<Integer, SubscriptionInfo> subIdToInfoMap, + Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap, + Map<ParcelUuid, Set<String>> privilegedPackages) { + return new TelephonySubscriptionSnapshot( + activeSubId, subIdToInfoMap, subIdToCarrierConfigMap, privilegedPackages); } private void verifyNoActiveSubscriptions() { @@ -245,6 +281,8 @@ public class TelephonySubscriptionTrackerTest { private void setupReadySubIds() { mTelephonySubscriptionTracker.setReadySubIdsBySlotId( Collections.singletonMap(TEST_SIM_SLOT_INDEX, TEST_SUBSCRIPTION_ID_1)); + mTelephonySubscriptionTracker.setSubIdToCarrierConfigMap( + Collections.singletonMap(TEST_SUBSCRIPTION_ID_1, TEST_CARRIER_CONFIG_WRAPPER)); } private void setPrivilegedPackagesForMock(@NonNull List<String> privilegedPackages) { @@ -300,6 +338,7 @@ public class TelephonySubscriptionTrackerTest { readySubIdsBySlotId.put(TEST_SIM_SLOT_INDEX + 1, TEST_SUBSCRIPTION_ID_1); mTelephonySubscriptionTracker.setReadySubIdsBySlotId(readySubIdsBySlotId); + mTelephonySubscriptionTracker.setSubIdToCarrierConfigMap(TEST_SUBID_TO_CARRIER_CONFIG_MAP); doReturn(1).when(mTelephonyManager).getActiveModemCount(); List<CarrierPrivilegesCallback> carrierPrivilegesCallbacks = @@ -464,8 +503,16 @@ public class TelephonySubscriptionTrackerTest { mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false)); mTestLooper.dispatchAll(); - verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(emptyMap()))); + verify(mCallback) + .onNewSnapshot( + eq( + buildExpectedSnapshot( + 0, TEST_SUBID_TO_INFO_MAP, emptyMap(), emptyMap()))); assertNull(mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX)); + assertNull( + mTelephonySubscriptionTracker + .getSubIdToCarrierConfigMap() + .get(TEST_SUBSCRIPTION_ID_1)); } @Test @@ -493,7 +540,7 @@ public class TelephonySubscriptionTrackerTest { public void testTelephonySubscriptionSnapshotGetGroupForSubId() throws Exception { final TelephonySubscriptionSnapshot snapshot = new TelephonySubscriptionSnapshot( - TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap()); + TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap(), emptyMap()); assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_1)); assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_2)); @@ -503,7 +550,7 @@ public class TelephonySubscriptionTrackerTest { public void testTelephonySubscriptionSnapshotGetAllSubIdsInGroup() throws Exception { final TelephonySubscriptionSnapshot snapshot = new TelephonySubscriptionSnapshot( - TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap()); + TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap(), emptyMap()); assertEquals( new ArraySet<>(Arrays.asList(TEST_SUBSCRIPTION_ID_1, TEST_SUBSCRIPTION_ID_2)), diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java index 3d95a9b32d4a..785bff167ad2 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -138,6 +138,7 @@ public class VcnGatewayConnectionTestBase { new TelephonySubscriptionSnapshot( TEST_SUB_ID, Collections.singletonMap(TEST_SUB_ID, TEST_SUB_INFO), + Collections.EMPTY_MAP, Collections.EMPTY_MAP); @NonNull protected final Context mContext; diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java index 6c849b5af888..b0d68952c39d 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java @@ -30,6 +30,7 @@ import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.ch import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesPriorityRule; import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesWifiPriorityRule; import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName; +import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -309,7 +310,9 @@ public class NetworkPriorityClassifierTest { wifiNetworkPriority, mWifiNetworkRecord, selectedNetworkRecord, - carrierConfig)); + carrierConfig == null + ? null + : new PersistableBundleWrapper(carrierConfig))); } @Test diff --git a/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java b/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java index a44a734a2dce..294f5c1f4842 100644 --- a/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java +++ b/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java @@ -18,6 +18,8 @@ package com.android.server.vcn.util; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import android.os.PersistableBundle; @@ -211,4 +213,84 @@ public class PersistableBundleUtilsTest { assertEquals(testInt, result); } + + private PersistableBundle getTestBundle() { + final PersistableBundle bundle = new PersistableBundle(); + + bundle.putBoolean(TEST_KEY + "Boolean", true); + bundle.putBooleanArray(TEST_KEY + "BooleanArray", new boolean[] {true, false}); + bundle.putDouble(TEST_KEY + "Double", 0.1); + bundle.putDoubleArray(TEST_KEY + "DoubleArray", new double[] {0.1, 0.2, 0.3}); + bundle.putInt(TEST_KEY + "Int", 1); + bundle.putIntArray(TEST_KEY + "IntArray", new int[] {1, 2}); + bundle.putLong(TEST_KEY + "Long", 5L); + bundle.putLongArray(TEST_KEY + "LongArray", new long[] {0L, -1L, -2L}); + bundle.putString(TEST_KEY + "String", "TEST"); + bundle.putStringArray(TEST_KEY + "StringArray", new String[] {"foo", "bar", "bas"}); + bundle.putPersistableBundle( + TEST_KEY + "PersistableBundle", + new TestClass(1, TEST_INT_ARRAY, TEST_STRING_PREFIX, new PersistableBundle()) + .toPersistableBundle()); + + return bundle; + } + + @Test + public void testMinimizeBundle() throws Exception { + final String[] minimizedKeys = + new String[] { + TEST_KEY + "Boolean", + TEST_KEY + "BooleanArray", + TEST_KEY + "Double", + TEST_KEY + "DoubleArray", + TEST_KEY + "Int", + TEST_KEY + "IntArray", + TEST_KEY + "Long", + TEST_KEY + "LongArray", + TEST_KEY + "String", + TEST_KEY + "StringArray", + TEST_KEY + "PersistableBundle" + }; + + final PersistableBundle testBundle = getTestBundle(); + testBundle.putBoolean(TEST_KEY + "Boolean2", true); + + final PersistableBundle minimized = + PersistableBundleUtils.minimizeBundle(testBundle, minimizedKeys); + + // Verify that the minimized bundle is NOT the same in size OR values due to the extra + // Boolean2 key + assertFalse(PersistableBundleUtils.isEqual(testBundle, minimized)); + + // Verify that removing the extra key from the source bundle results in equality. + testBundle.remove(TEST_KEY + "Boolean2"); + assertTrue(PersistableBundleUtils.isEqual(testBundle, minimized)); + } + + @Test + public void testEquality_identical() throws Exception { + final PersistableBundle left = getTestBundle(); + final PersistableBundle right = getTestBundle(); + + assertTrue(PersistableBundleUtils.isEqual(left, right)); + } + + @Test + public void testEquality_different() throws Exception { + final PersistableBundle left = getTestBundle(); + final PersistableBundle right = getTestBundle(); + + left.putBoolean(TEST_KEY + "Boolean2", true); + assertFalse(PersistableBundleUtils.isEqual(left, right)); + + left.remove(TEST_KEY + "Boolean2"); + assertTrue(PersistableBundleUtils.isEqual(left, right)); + } + + @Test + public void testEquality_null() throws Exception { + assertFalse(PersistableBundleUtils.isEqual(getTestBundle(), null)); + assertFalse(PersistableBundleUtils.isEqual(null, getTestBundle())); + assertTrue(PersistableBundleUtils.isEqual(null, null)); + } } |