Custom network selection
This change adds a basic prioritization framework in the VCN, and signal strength thresholds for WiFi. Bug: 188104094 Test: atest FrameworksVcnTests Change-Id: Iee4e3dbecf1102ddc127a8c8daf08a00b6fccbbd
This commit is contained in:
@@ -74,6 +74,36 @@ import java.util.concurrent.Executor;
|
||||
public class VcnManager {
|
||||
@NonNull private static final String TAG = VcnManager.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* Key for WiFi entry RSSI thresholds
|
||||
*
|
||||
* <p>The VCN will only migrate to a Carrier WiFi network that has a signal strength greater
|
||||
* than, or equal to this threshold.
|
||||
*
|
||||
* <p>WARNING: The VCN does not listen for changes to this key made after VCN startup.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public static final String VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY =
|
||||
"vcn_network_selection_wifi_entry_rssi_threshold";
|
||||
|
||||
/**
|
||||
* Key for WiFi entry RSSI thresholds
|
||||
*
|
||||
* <p>If the VCN's selected Carrier WiFi network has a signal strength less than this threshold,
|
||||
* the VCN will attempt to migrate away from the Carrier WiFi network.
|
||||
*
|
||||
* <p>WARNING: The VCN does not listen for changes to this key made after VCN startup.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public static final String VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY =
|
||||
"vcn_network_selection_wifi_exit_rssi_threshold";
|
||||
|
||||
// TODO: Add separate signal strength thresholds for 2.4 GHz and 5GHz
|
||||
|
||||
private static final Map<
|
||||
VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder>
|
||||
REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -145,7 +145,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
|
||||
*/
|
||||
public void handleSubscriptionsChanged() {
|
||||
final Map<ParcelUuid, Set<String>> privilegedPackages = new HashMap<>();
|
||||
final Map<Integer, ParcelUuid> newSubIdToGroupMap = new HashMap<>();
|
||||
final Map<Integer, SubscriptionInfo> newSubIdToInfoMap = new HashMap<>();
|
||||
|
||||
final List<SubscriptionInfo> allSubs = mSubscriptionManager.getAllSubscriptionInfoList();
|
||||
if (allSubs == null) {
|
||||
@@ -160,7 +160,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
|
||||
}
|
||||
|
||||
// Build subId -> subGrp cache
|
||||
newSubIdToGroupMap.put(subInfo.getSubscriptionId(), subInfo.getGroupUuid());
|
||||
newSubIdToInfoMap.put(subInfo.getSubscriptionId(), subInfo);
|
||||
|
||||
// Update subscription groups that are both ready, and active. For a group to be
|
||||
// considered active, both of the following must be true:
|
||||
@@ -186,7 +186,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
|
||||
}
|
||||
|
||||
final TelephonySubscriptionSnapshot newSnapshot =
|
||||
new TelephonySubscriptionSnapshot(newSubIdToGroupMap, privilegedPackages);
|
||||
new TelephonySubscriptionSnapshot(newSubIdToInfoMap, privilegedPackages);
|
||||
|
||||
// If snapshot was meaningfully updated, fire the callback
|
||||
if (!newSnapshot.equals(mCurrentSnapshot)) {
|
||||
@@ -245,7 +245,7 @@ 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 Map<Integer, SubscriptionInfo> mSubIdToInfoMap;
|
||||
private final Map<ParcelUuid, Set<String>> mPrivilegedPackages;
|
||||
|
||||
public static final TelephonySubscriptionSnapshot EMPTY_SNAPSHOT =
|
||||
@@ -253,12 +253,12 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
TelephonySubscriptionSnapshot(
|
||||
@NonNull Map<Integer, ParcelUuid> subIdToGroupMap,
|
||||
@NonNull Map<Integer, SubscriptionInfo> subIdToInfoMap,
|
||||
@NonNull Map<ParcelUuid, Set<String>> privilegedPackages) {
|
||||
Objects.requireNonNull(subIdToGroupMap, "subIdToGroupMap was null");
|
||||
Objects.requireNonNull(subIdToInfoMap, "subIdToInfoMap was null");
|
||||
Objects.requireNonNull(privilegedPackages, "privilegedPackages was null");
|
||||
|
||||
mSubIdToGroupMap = Collections.unmodifiableMap(subIdToGroupMap);
|
||||
mSubIdToInfoMap = Collections.unmodifiableMap(subIdToInfoMap);
|
||||
|
||||
final Map<ParcelUuid, Set<String>> unmodifiableInnerSets = new ArrayMap<>();
|
||||
for (Entry<ParcelUuid, Set<String>> entry : privilegedPackages.entrySet()) {
|
||||
@@ -285,7 +285,9 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
|
||||
/** Returns the Subscription Group for a given subId. */
|
||||
@Nullable
|
||||
public ParcelUuid getGroupForSubId(int subId) {
|
||||
return mSubIdToGroupMap.get(subId);
|
||||
return mSubIdToInfoMap.containsKey(subId)
|
||||
? mSubIdToInfoMap.get(subId).getGroupUuid()
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -295,8 +297,8 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
|
||||
public Set<Integer> getAllSubIdsInGroup(ParcelUuid subGrp) {
|
||||
final Set<Integer> subIds = new ArraySet<>();
|
||||
|
||||
for (Entry<Integer, ParcelUuid> entry : mSubIdToGroupMap.entrySet()) {
|
||||
if (subGrp.equals(entry.getValue())) {
|
||||
for (Entry<Integer, SubscriptionInfo> entry : mSubIdToInfoMap.entrySet()) {
|
||||
if (subGrp.equals(entry.getValue().getGroupUuid())) {
|
||||
subIds.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
@@ -304,9 +306,17 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
|
||||
return subIds;
|
||||
}
|
||||
|
||||
/** Checks if the requested subscription is opportunistic */
|
||||
@NonNull
|
||||
public boolean isOpportunistic(int subId) {
|
||||
return mSubIdToInfoMap.containsKey(subId)
|
||||
? mSubIdToInfoMap.get(subId).isOpportunistic()
|
||||
: false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mSubIdToGroupMap, mPrivilegedPackages);
|
||||
return Objects.hash(mSubIdToInfoMap, mPrivilegedPackages);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -317,7 +327,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
|
||||
|
||||
final TelephonySubscriptionSnapshot other = (TelephonySubscriptionSnapshot) obj;
|
||||
|
||||
return mSubIdToGroupMap.equals(other.mSubIdToGroupMap)
|
||||
return mSubIdToInfoMap.equals(other.mSubIdToInfoMap)
|
||||
&& mPrivilegedPackages.equals(other.mPrivilegedPackages);
|
||||
}
|
||||
|
||||
@@ -326,7 +336,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
|
||||
pw.println("TelephonySubscriptionSnapshot:");
|
||||
pw.increaseIndent();
|
||||
|
||||
pw.println("mSubIdToGroupMap: " + mSubIdToGroupMap);
|
||||
pw.println("mSubIdToInfoMap: " + mSubIdToInfoMap);
|
||||
pw.println("mPrivilegedPackages: " + mPrivilegedPackages);
|
||||
|
||||
pw.decreaseIndent();
|
||||
@@ -335,7 +345,7 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TelephonySubscriptionSnapshot{ "
|
||||
+ "mSubIdToGroupMap=" + mSubIdToGroupMap
|
||||
+ "mSubIdToInfoMap=" + mSubIdToInfoMap
|
||||
+ ", mPrivilegedPackages=" + mPrivilegedPackages
|
||||
+ " }";
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
package com.android.server.vcn;
|
||||
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
|
||||
import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.net.ConnectivityManager;
|
||||
@@ -25,8 +29,16 @@ import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
import android.net.NetworkRequest;
|
||||
import android.net.TelephonyNetworkSpecifier;
|
||||
import android.net.vcn.VcnManager;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerExecutor;
|
||||
import android.os.ParcelUuid;
|
||||
import android.os.PersistableBundle;
|
||||
import android.telephony.CarrierConfigManager;
|
||||
import android.telephony.SubscriptionManager;
|
||||
import android.telephony.TelephonyCallback;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -35,9 +47,13 @@ import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* Tracks a set of Networks underpinning a VcnGatewayConnection.
|
||||
@@ -51,6 +67,45 @@ import java.util.Set;
|
||||
public class UnderlyingNetworkTracker {
|
||||
@NonNull private static final String TAG = UnderlyingNetworkTracker.class.getSimpleName();
|
||||
|
||||
/**
|
||||
* Minimum signal strength for a WiFi network to be eligible for switching to
|
||||
*
|
||||
* <p>A network that satisfies this is eligible to become the selected underlying network with
|
||||
* no additional conditions
|
||||
*/
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
static final int WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT = -70;
|
||||
|
||||
/**
|
||||
* Minimum signal strength to continue using a WiFi network
|
||||
*
|
||||
* <p>A network that satisfies the conditions may ONLY continue to be used if it is already
|
||||
* selected as the underlying network. A WiFi network satisfying this condition, but NOT the
|
||||
* prospective-network RSSI threshold CANNOT be switched to.
|
||||
*/
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
static final int WIFI_EXIT_RSSI_THRESHOLD_DEFAULT = -74;
|
||||
|
||||
/** Priority for any cellular network for which the subscription is listed as opportunistic */
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
static final int PRIORITY_OPPORTUNISTIC_CELLULAR = 0;
|
||||
|
||||
/** Priority for any WiFi network which is in use, and satisfies the in-use RSSI threshold */
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
static final int PRIORITY_WIFI_IN_USE = 1;
|
||||
|
||||
/** Priority for any WiFi network which satisfies the prospective-network RSSI threshold */
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
static final int PRIORITY_WIFI_PROSPECTIVE = 2;
|
||||
|
||||
/** Priority for any standard macro cellular network */
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
static final int PRIORITY_MACRO_CELLULAR = 3;
|
||||
|
||||
/** Priority for any other networks (including unvalidated, etc) */
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
static final int PRIORITY_ANY = Integer.MAX_VALUE;
|
||||
|
||||
@NonNull private final VcnContext mVcnContext;
|
||||
@NonNull private final ParcelUuid mSubscriptionGroup;
|
||||
@NonNull private final Set<Integer> mRequiredUnderlyingNetworkCapabilities;
|
||||
@@ -58,12 +113,17 @@ public class UnderlyingNetworkTracker {
|
||||
@NonNull private final Dependencies mDeps;
|
||||
@NonNull private final Handler mHandler;
|
||||
@NonNull private final ConnectivityManager mConnectivityManager;
|
||||
@NonNull private final TelephonyCallback mActiveDataSubIdListener =
|
||||
new VcnActiveDataSubscriptionIdListener();
|
||||
|
||||
@NonNull private final List<NetworkCallback> mCellBringupCallbacks = new ArrayList<>();
|
||||
@Nullable private NetworkCallback mWifiBringupCallback;
|
||||
@Nullable private NetworkCallback mRouteSelectionCallback;
|
||||
@Nullable private NetworkCallback mWifiEntryRssiThresholdCallback;
|
||||
@Nullable private NetworkCallback mWifiExitRssiThresholdCallback;
|
||||
@Nullable private UnderlyingNetworkListener mRouteSelectionCallback;
|
||||
|
||||
@NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
|
||||
@Nullable private PersistableBundle mCarrierConfig;
|
||||
private boolean mIsQuitting = false;
|
||||
|
||||
@Nullable private UnderlyingNetworkRecord mCurrentRecord;
|
||||
@@ -104,6 +164,30 @@ public class UnderlyingNetworkTracker {
|
||||
mHandler = new Handler(mVcnContext.getLooper());
|
||||
|
||||
mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class);
|
||||
mVcnContext
|
||||
.getContext()
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerOrUpdateNetworkRequests();
|
||||
}
|
||||
@@ -111,16 +195,30 @@ public class UnderlyingNetworkTracker {
|
||||
private void registerOrUpdateNetworkRequests() {
|
||||
NetworkCallback oldRouteSelectionCallback = mRouteSelectionCallback;
|
||||
NetworkCallback oldWifiCallback = mWifiBringupCallback;
|
||||
NetworkCallback oldWifiEntryRssiThresholdCallback = mWifiEntryRssiThresholdCallback;
|
||||
NetworkCallback oldWifiExitRssiThresholdCallback = mWifiExitRssiThresholdCallback;
|
||||
List<NetworkCallback> oldCellCallbacks = new ArrayList<>(mCellBringupCallbacks);
|
||||
mCellBringupCallbacks.clear();
|
||||
|
||||
// Register new callbacks. Make-before-break; always register new callbacks before removal
|
||||
// of old callbacks
|
||||
if (!mIsQuitting) {
|
||||
mRouteSelectionCallback = new RouteSelectionCallback();
|
||||
mConnectivityManager.requestBackgroundNetwork(
|
||||
mRouteSelectionCallback = new UnderlyingNetworkListener();
|
||||
mConnectivityManager.registerNetworkCallback(
|
||||
getRouteSelectionRequest(), mRouteSelectionCallback, mHandler);
|
||||
|
||||
mWifiEntryRssiThresholdCallback = new NetworkBringupCallback();
|
||||
mConnectivityManager.registerNetworkCallback(
|
||||
getWifiEntryRssiThresholdNetworkRequest(),
|
||||
mWifiEntryRssiThresholdCallback,
|
||||
mHandler);
|
||||
|
||||
mWifiExitRssiThresholdCallback = new NetworkBringupCallback();
|
||||
mConnectivityManager.registerNetworkCallback(
|
||||
getWifiExitRssiThresholdNetworkRequest(),
|
||||
mWifiExitRssiThresholdCallback,
|
||||
mHandler);
|
||||
|
||||
mWifiBringupCallback = new NetworkBringupCallback();
|
||||
mConnectivityManager.requestBackgroundNetwork(
|
||||
getWifiNetworkRequest(), mWifiBringupCallback, mHandler);
|
||||
@@ -135,6 +233,8 @@ public class UnderlyingNetworkTracker {
|
||||
} else {
|
||||
mRouteSelectionCallback = null;
|
||||
mWifiBringupCallback = null;
|
||||
mWifiEntryRssiThresholdCallback = null;
|
||||
mWifiExitRssiThresholdCallback = null;
|
||||
// mCellBringupCallbacks already cleared above.
|
||||
}
|
||||
|
||||
@@ -145,6 +245,12 @@ public class UnderlyingNetworkTracker {
|
||||
if (oldWifiCallback != null) {
|
||||
mConnectivityManager.unregisterNetworkCallback(oldWifiCallback);
|
||||
}
|
||||
if (oldWifiEntryRssiThresholdCallback != null) {
|
||||
mConnectivityManager.unregisterNetworkCallback(oldWifiEntryRssiThresholdCallback);
|
||||
}
|
||||
if (oldWifiExitRssiThresholdCallback != null) {
|
||||
mConnectivityManager.unregisterNetworkCallback(oldWifiExitRssiThresholdCallback);
|
||||
}
|
||||
for (NetworkCallback cellBringupCallback : oldCellCallbacks) {
|
||||
mConnectivityManager.unregisterNetworkCallback(cellBringupCallback);
|
||||
}
|
||||
@@ -168,6 +274,8 @@ public class UnderlyingNetworkTracker {
|
||||
}
|
||||
|
||||
return getBaseNetworkRequestBuilder()
|
||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
|
||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
|
||||
.setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
|
||||
.build();
|
||||
}
|
||||
@@ -188,6 +296,38 @@ public class UnderlyingNetworkTracker {
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the WiFi entry threshold signal strength request
|
||||
*
|
||||
* <p>This request ensures that WiFi reports the crossing of the wifi entry RSSI threshold.
|
||||
* Without this request, WiFi rate-limits, and reports signal strength changes at too slow a
|
||||
* pace to effectively select a short-lived WiFi offload network.
|
||||
*/
|
||||
private NetworkRequest getWifiEntryRssiThresholdNetworkRequest() {
|
||||
return getBaseNetworkRequestBuilder()
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
||||
.setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
|
||||
// Ensure wifi updates signal strengths when crossing this threshold.
|
||||
.setSignalStrength(getWifiEntryRssiThreshold(mCarrierConfig))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the WiFi exit threshold signal strength request
|
||||
*
|
||||
* <p>This request ensures that WiFi reports the crossing of the wifi exit RSSI threshold.
|
||||
* Without this request, WiFi rate-limits, and reports signal strength changes at too slow a
|
||||
* pace to effectively select away from a failing WiFi network.
|
||||
*/
|
||||
private NetworkRequest getWifiExitRssiThresholdNetworkRequest() {
|
||||
return getBaseNetworkRequestBuilder()
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
||||
.setSubscriptionIds(mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))
|
||||
// Ensure wifi updates signal strengths when crossing this threshold.
|
||||
.setSignalStrength(getWifiExitRssiThreshold(mCarrierConfig))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a Cellular bringup request for a given subId
|
||||
*
|
||||
@@ -233,10 +373,18 @@ public class UnderlyingNetworkTracker {
|
||||
* reevaluate its NetworkBringupCallbacks. This may result in NetworkRequests being registered
|
||||
* or unregistered if the subIds mapped to the this Tracker's SubscriptionGroup change.
|
||||
*/
|
||||
public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
|
||||
Objects.requireNonNull(snapshot, "Missing snapshot");
|
||||
public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot newSnapshot) {
|
||||
Objects.requireNonNull(newSnapshot, "Missing newSnapshot");
|
||||
|
||||
mLastSnapshot = snapshot;
|
||||
final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot;
|
||||
mLastSnapshot = newSnapshot;
|
||||
|
||||
// Only trigger re-registration if subIds in this group have changed
|
||||
if (oldSnapshot
|
||||
.getAllSubIdsInGroup(mSubscriptionGroup)
|
||||
.equals(newSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))) {
|
||||
return;
|
||||
}
|
||||
registerOrUpdateNetworkRequests();
|
||||
}
|
||||
|
||||
@@ -247,88 +395,43 @@ public class UnderlyingNetworkTracker {
|
||||
|
||||
// Will unregister all existing callbacks, but not register new ones due to quitting flag.
|
||||
registerOrUpdateNetworkRequests();
|
||||
|
||||
mVcnContext
|
||||
.getContext()
|
||||
.getSystemService(TelephonyManager.class)
|
||||
.unregisterTelephonyCallback(mActiveDataSubIdListener);
|
||||
}
|
||||
|
||||
/** Returns whether the currently selected Network matches the given network. */
|
||||
private static boolean isSameNetwork(
|
||||
@Nullable UnderlyingNetworkRecord.Builder recordInProgress, @NonNull Network network) {
|
||||
return recordInProgress != null && recordInProgress.getNetwork().equals(network);
|
||||
}
|
||||
private void reevaluateNetworks() {
|
||||
TreeSet<UnderlyingNetworkRecord> sorted =
|
||||
new TreeSet<>(
|
||||
UnderlyingNetworkRecord.getComparator(
|
||||
mSubscriptionGroup, mLastSnapshot, mCurrentRecord, mCarrierConfig));
|
||||
sorted.addAll(mRouteSelectionCallback.getUnderlyingNetworks());
|
||||
|
||||
/** Notify the Callback if a full UnderlyingNetworkRecord exists. */
|
||||
private void maybeNotifyCallback() {
|
||||
// Only forward this update if a complete record has been received
|
||||
if (!mRecordInProgress.isValid()) {
|
||||
UnderlyingNetworkRecord candidate = sorted.isEmpty() ? null : sorted.first();
|
||||
if (Objects.equals(mCurrentRecord, candidate)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only forward this update if the updated record differs form the current record
|
||||
UnderlyingNetworkRecord updatedRecord = mRecordInProgress.build();
|
||||
if (!updatedRecord.equals(mCurrentRecord)) {
|
||||
mCurrentRecord = updatedRecord;
|
||||
|
||||
mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord);
|
||||
}
|
||||
mCurrentRecord = candidate;
|
||||
mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord);
|
||||
}
|
||||
|
||||
private void handleNetworkAvailable(@NonNull Network network) {
|
||||
mVcnContext.ensureRunningOnLooperThread();
|
||||
|
||||
mRecordInProgress = new UnderlyingNetworkRecord.Builder(network);
|
||||
}
|
||||
|
||||
private void handleNetworkLost(@NonNull Network network) {
|
||||
mVcnContext.ensureRunningOnLooperThread();
|
||||
|
||||
if (!isSameNetwork(mRecordInProgress, network)) {
|
||||
Slog.wtf(TAG, "Non-underlying Network lost");
|
||||
return;
|
||||
private static boolean isOpportunistic(
|
||||
@NonNull TelephonySubscriptionSnapshot snapshot, Set<Integer> subIds) {
|
||||
if (snapshot == null) {
|
||||
Slog.wtf(TAG, "Got null snapshot");
|
||||
return false;
|
||||
}
|
||||
|
||||
mRecordInProgress = null;
|
||||
mCurrentRecord = null;
|
||||
mCb.onSelectedUnderlyingNetworkChanged(null /* underlyingNetworkRecord */);
|
||||
}
|
||||
|
||||
private void handleCapabilitiesChanged(
|
||||
@NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
|
||||
mVcnContext.ensureRunningOnLooperThread();
|
||||
|
||||
if (!isSameNetwork(mRecordInProgress, network)) {
|
||||
Slog.wtf(TAG, "Invalid update to NetworkCapabilities");
|
||||
return;
|
||||
for (int subId : subIds) {
|
||||
if (snapshot.isOpportunistic(subId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
mRecordInProgress.setNetworkCapabilities(networkCapabilities);
|
||||
|
||||
maybeNotifyCallback();
|
||||
}
|
||||
|
||||
private void handlePropertiesChanged(
|
||||
@NonNull Network network, @NonNull LinkProperties linkProperties) {
|
||||
mVcnContext.ensureRunningOnLooperThread();
|
||||
|
||||
if (!isSameNetwork(mRecordInProgress, network)) {
|
||||
Slog.wtf(TAG, "Invalid update to LinkProperties");
|
||||
return;
|
||||
}
|
||||
|
||||
mRecordInProgress.setLinkProperties(linkProperties);
|
||||
|
||||
maybeNotifyCallback();
|
||||
}
|
||||
|
||||
private void handleNetworkBlocked(@NonNull Network network, boolean isBlocked) {
|
||||
mVcnContext.ensureRunningOnLooperThread();
|
||||
|
||||
if (!isSameNetwork(mRecordInProgress, network)) {
|
||||
Slog.wtf(TAG, "Invalid update to isBlocked");
|
||||
return;
|
||||
}
|
||||
|
||||
mRecordInProgress.setIsBlocked(isBlocked);
|
||||
|
||||
maybeNotifyCallback();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -347,36 +450,104 @@ public class UnderlyingNetworkTracker {
|
||||
* truth.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
class RouteSelectionCallback extends NetworkCallback {
|
||||
class UnderlyingNetworkListener extends NetworkCallback {
|
||||
private final Map<Network, UnderlyingNetworkRecord.Builder>
|
||||
mUnderlyingNetworkRecordBuilders = new ArrayMap<>();
|
||||
|
||||
private List<UnderlyingNetworkRecord> getUnderlyingNetworks() {
|
||||
final List<UnderlyingNetworkRecord> records = new ArrayList<>();
|
||||
|
||||
for (UnderlyingNetworkRecord.Builder builder :
|
||||
mUnderlyingNetworkRecordBuilders.values()) {
|
||||
if (builder.isValid()) {
|
||||
records.add(builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAvailable(@NonNull Network network) {
|
||||
handleNetworkAvailable(network);
|
||||
mUnderlyingNetworkRecordBuilders.put(
|
||||
network, new UnderlyingNetworkRecord.Builder(network));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLost(@NonNull Network network) {
|
||||
handleNetworkLost(network);
|
||||
mUnderlyingNetworkRecordBuilders.remove(network);
|
||||
|
||||
reevaluateNetworks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCapabilitiesChanged(
|
||||
@NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
|
||||
if (networkCapabilities.equals(mRecordInProgress.getNetworkCapabilities())) return;
|
||||
handleCapabilitiesChanged(network, networkCapabilities);
|
||||
final UnderlyingNetworkRecord.Builder builder =
|
||||
mUnderlyingNetworkRecordBuilders.get(network);
|
||||
if (builder == null) {
|
||||
Slog.wtf(TAG, "Got capabilities change for unknown key: " + network);
|
||||
return;
|
||||
}
|
||||
|
||||
builder.setNetworkCapabilities(networkCapabilities);
|
||||
if (builder.isValid()) {
|
||||
reevaluateNetworks();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLinkPropertiesChanged(
|
||||
@NonNull Network network, @NonNull LinkProperties linkProperties) {
|
||||
handlePropertiesChanged(network, linkProperties);
|
||||
final UnderlyingNetworkRecord.Builder builder =
|
||||
mUnderlyingNetworkRecordBuilders.get(network);
|
||||
if (builder == null) {
|
||||
Slog.wtf(TAG, "Got link properties change for unknown key: " + network);
|
||||
return;
|
||||
}
|
||||
|
||||
builder.setLinkProperties(linkProperties);
|
||||
if (builder.isValid()) {
|
||||
reevaluateNetworks();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlockedStatusChanged(@NonNull Network network, boolean isBlocked) {
|
||||
handleNetworkBlocked(network, isBlocked);
|
||||
final UnderlyingNetworkRecord.Builder builder =
|
||||
mUnderlyingNetworkRecordBuilders.get(network);
|
||||
if (builder == null) {
|
||||
Slog.wtf(TAG, "Got blocked status change for unknown key: " + network);
|
||||
return;
|
||||
}
|
||||
|
||||
builder.setIsBlocked(isBlocked);
|
||||
if (builder.isValid()) {
|
||||
reevaluateNetworks();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getWifiEntryRssiThreshold(@Nullable PersistableBundle carrierConfig) {
|
||||
if (carrierConfig != null) {
|
||||
return carrierConfig.getInt(
|
||||
VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
|
||||
WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT);
|
||||
}
|
||||
|
||||
return WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT;
|
||||
}
|
||||
|
||||
private static int getWifiExitRssiThreshold(@Nullable PersistableBundle carrierConfig) {
|
||||
if (carrierConfig != null) {
|
||||
return carrierConfig.getInt(
|
||||
VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
|
||||
WIFI_EXIT_RSSI_THRESHOLD_DEFAULT);
|
||||
}
|
||||
|
||||
return WIFI_EXIT_RSSI_THRESHOLD_DEFAULT;
|
||||
}
|
||||
|
||||
/** A record of a single underlying network, caching relevant fields. */
|
||||
public static class UnderlyingNetworkRecord {
|
||||
@NonNull public final Network network;
|
||||
@@ -413,6 +584,89 @@ public class UnderlyingNetworkTracker {
|
||||
return Objects.hash(network, networkCapabilities, linkProperties, isBlocked);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives networks a priority class, based on the following priorities:
|
||||
*
|
||||
* <ol>
|
||||
* <li>Opportunistic cellular
|
||||
* <li>Carrier WiFi, signal strength >= WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT
|
||||
* <li>Carrier WiFi, active network + signal strength >= WIFI_EXIT_RSSI_THRESHOLD_DEFAULT
|
||||
* <li>Macro cellular
|
||||
* <li>Any others
|
||||
* </ol>
|
||||
*/
|
||||
private int calculatePriorityClass(
|
||||
ParcelUuid subscriptionGroup,
|
||||
TelephonySubscriptionSnapshot snapshot,
|
||||
UnderlyingNetworkRecord currentlySelected,
|
||||
PersistableBundle carrierConfig) {
|
||||
final NetworkCapabilities caps = networkCapabilities;
|
||||
|
||||
// mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED
|
||||
|
||||
if (isBlocked) {
|
||||
Slog.wtf(TAG, "Network blocked for System Server: " + network);
|
||||
return PRIORITY_ANY;
|
||||
}
|
||||
|
||||
if (caps.hasTransport(TRANSPORT_CELLULAR)
|
||||
&& isOpportunistic(snapshot, caps.getSubscriptionIds())) {
|
||||
// If this carrier is the active data provider, ensure that opportunistic is only
|
||||
// ever prioritized if it is also the active data subscription. This ensures that
|
||||
// if an opportunistic subscription is still in the process of being switched to,
|
||||
// or switched away from, the VCN does not attempt to continue using it against the
|
||||
// decision made at the telephony layer. Failure to do so may result in the modem
|
||||
// switching back and forth.
|
||||
//
|
||||
// Allow the following two cases:
|
||||
// 1. Active subId is NOT in the group that this VCN is supporting
|
||||
// 2. This opportunistic subscription is for the active subId
|
||||
if (!snapshot.getAllSubIdsInGroup(subscriptionGroup)
|
||||
.contains(SubscriptionManager.getActiveDataSubscriptionId())
|
||||
|| caps.getSubscriptionIds()
|
||||
.contains(SubscriptionManager.getActiveDataSubscriptionId())) {
|
||||
return PRIORITY_OPPORTUNISTIC_CELLULAR;
|
||||
}
|
||||
}
|
||||
|
||||
if (caps.hasTransport(TRANSPORT_WIFI)) {
|
||||
if (caps.getSignalStrength() >= getWifiExitRssiThreshold(carrierConfig)
|
||||
&& currentlySelected != null
|
||||
&& network.equals(currentlySelected.network)) {
|
||||
return PRIORITY_WIFI_IN_USE;
|
||||
}
|
||||
|
||||
if (caps.getSignalStrength() >= getWifiEntryRssiThreshold(carrierConfig)) {
|
||||
return PRIORITY_WIFI_PROSPECTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
// Disallow opportunistic subscriptions from matching PRIORITY_MACRO_CELLULAR, as might
|
||||
// be the case when Default Data SubId (CBRS) != Active Data SubId (MACRO), as might be
|
||||
// the case if the Default Data SubId does not support certain services (eg voice
|
||||
// calling)
|
||||
if (caps.hasTransport(TRANSPORT_CELLULAR)
|
||||
&& !isOpportunistic(snapshot, caps.getSubscriptionIds())) {
|
||||
return PRIORITY_MACRO_CELLULAR;
|
||||
}
|
||||
|
||||
return PRIORITY_ANY;
|
||||
}
|
||||
|
||||
private static Comparator<UnderlyingNetworkRecord> getComparator(
|
||||
ParcelUuid subscriptionGroup,
|
||||
TelephonySubscriptionSnapshot snapshot,
|
||||
UnderlyingNetworkRecord currentlySelected,
|
||||
PersistableBundle carrierConfig) {
|
||||
return (left, right) -> {
|
||||
return Integer.compare(
|
||||
left.calculatePriorityClass(
|
||||
subscriptionGroup, snapshot, currentlySelected, carrierConfig),
|
||||
right.calculatePriorityClass(
|
||||
subscriptionGroup, snapshot, currentlySelected, carrierConfig));
|
||||
};
|
||||
}
|
||||
|
||||
/** Dumps the state of this record for logging and debugging purposes. */
|
||||
public void dump(IndentingPrintWriter pw) {
|
||||
pw.println("UnderlyingNetworkRecord:");
|
||||
@@ -434,6 +688,8 @@ public class UnderlyingNetworkTracker {
|
||||
boolean mIsBlocked;
|
||||
boolean mWasIsBlockedSet;
|
||||
|
||||
@Nullable private UnderlyingNetworkRecord mCached;
|
||||
|
||||
private Builder(@NonNull Network network) {
|
||||
mNetwork = network;
|
||||
}
|
||||
@@ -445,6 +701,7 @@ public class UnderlyingNetworkTracker {
|
||||
|
||||
private void setNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
|
||||
mNetworkCapabilities = networkCapabilities;
|
||||
mCached = null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -454,11 +711,13 @@ public class UnderlyingNetworkTracker {
|
||||
|
||||
private void setLinkProperties(@NonNull LinkProperties linkProperties) {
|
||||
mLinkProperties = linkProperties;
|
||||
mCached = null;
|
||||
}
|
||||
|
||||
private void setIsBlocked(boolean isBlocked) {
|
||||
mIsBlocked = isBlocked;
|
||||
mWasIsBlockedSet = true;
|
||||
mCached = null;
|
||||
}
|
||||
|
||||
private boolean isValid() {
|
||||
@@ -466,12 +725,30 @@ public class UnderlyingNetworkTracker {
|
||||
}
|
||||
|
||||
private UnderlyingNetworkRecord build() {
|
||||
return new UnderlyingNetworkRecord(
|
||||
mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked);
|
||||
if (!isValid()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Called build before UnderlyingNetworkRecord was valid");
|
||||
}
|
||||
|
||||
if (mCached == null) {
|
||||
mCached =
|
||||
new UnderlyingNetworkRecord(
|
||||
mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked);
|
||||
}
|
||||
|
||||
return mCached;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class VcnActiveDataSubscriptionIdListener extends TelephonyCallback
|
||||
implements ActiveDataSubscriptionIdListener {
|
||||
@Override
|
||||
public void onActiveDataSubscriptionIdChanged(int subId) {
|
||||
reevaluateNetworks();
|
||||
}
|
||||
}
|
||||
|
||||
/** Callbacks for being notified of the changes in, or to the selected underlying network. */
|
||||
public interface UnderlyingNetworkTrackerCallback {
|
||||
/**
|
||||
|
||||
@@ -88,13 +88,13 @@ public class TelephonySubscriptionTrackerTest {
|
||||
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;
|
||||
private static final Map<Integer, SubscriptionInfo> TEST_SUBID_TO_INFO_MAP;
|
||||
|
||||
static {
|
||||
final Map<Integer, ParcelUuid> subIdToGroupMap = new HashMap<>();
|
||||
subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_1, TEST_PARCEL_UUID);
|
||||
subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_2, TEST_PARCEL_UUID);
|
||||
TEST_SUBID_TO_GROUP_MAP = Collections.unmodifiableMap(subIdToGroupMap);
|
||||
final Map<Integer, SubscriptionInfo> subIdToGroupMap = new HashMap<>();
|
||||
subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_1, TEST_SUBINFO_1);
|
||||
subIdToGroupMap.put(TEST_SUBSCRIPTION_ID_2, TEST_SUBINFO_2);
|
||||
TEST_SUBID_TO_INFO_MAP = Collections.unmodifiableMap(subIdToGroupMap);
|
||||
}
|
||||
|
||||
@NonNull private final Context mContext;
|
||||
@@ -190,13 +190,13 @@ public class TelephonySubscriptionTrackerTest {
|
||||
|
||||
private TelephonySubscriptionSnapshot buildExpectedSnapshot(
|
||||
Map<ParcelUuid, Set<String>> privilegedPackages) {
|
||||
return buildExpectedSnapshot(TEST_SUBID_TO_GROUP_MAP, privilegedPackages);
|
||||
return buildExpectedSnapshot(TEST_SUBID_TO_INFO_MAP, privilegedPackages);
|
||||
}
|
||||
|
||||
private TelephonySubscriptionSnapshot buildExpectedSnapshot(
|
||||
Map<Integer, ParcelUuid> subIdToGroupMap,
|
||||
Map<Integer, SubscriptionInfo> subIdToInfoMap,
|
||||
Map<ParcelUuid, Set<String>> privilegedPackages) {
|
||||
return new TelephonySubscriptionSnapshot(subIdToGroupMap, privilegedPackages);
|
||||
return new TelephonySubscriptionSnapshot(subIdToInfoMap, privilegedPackages);
|
||||
}
|
||||
|
||||
private void verifyNoActiveSubscriptions() {
|
||||
@@ -371,7 +371,7 @@ public class TelephonySubscriptionTrackerTest {
|
||||
@Test
|
||||
public void testTelephonySubscriptionSnapshotGetGroupForSubId() throws Exception {
|
||||
final TelephonySubscriptionSnapshot snapshot =
|
||||
new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, emptyMap());
|
||||
new TelephonySubscriptionSnapshot(TEST_SUBID_TO_INFO_MAP, emptyMap());
|
||||
|
||||
assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_1));
|
||||
assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_2));
|
||||
@@ -380,7 +380,7 @@ public class TelephonySubscriptionTrackerTest {
|
||||
@Test
|
||||
public void testTelephonySubscriptionSnapshotGetAllSubIdsInGroup() throws Exception {
|
||||
final TelephonySubscriptionSnapshot snapshot =
|
||||
new TelephonySubscriptionSnapshot(TEST_SUBID_TO_GROUP_MAP, emptyMap());
|
||||
new TelephonySubscriptionSnapshot(TEST_SUBID_TO_INFO_MAP, emptyMap());
|
||||
|
||||
assertEquals(
|
||||
new ArraySet<>(Arrays.asList(TEST_SUBSCRIPTION_ID_1, TEST_SUBSCRIPTION_ID_2)),
|
||||
|
||||
@@ -42,12 +42,14 @@ import android.net.NetworkRequest;
|
||||
import android.net.TelephonyNetworkSpecifier;
|
||||
import android.os.ParcelUuid;
|
||||
import android.os.test.TestLooper;
|
||||
import android.telephony.CarrierConfigManager;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
|
||||
import com.android.server.vcn.UnderlyingNetworkTracker.NetworkBringupCallback;
|
||||
import com.android.server.vcn.UnderlyingNetworkTracker.RouteSelectionCallback;
|
||||
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkListener;
|
||||
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
|
||||
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
|
||||
|
||||
@@ -98,11 +100,13 @@ public class UnderlyingNetworkTrackerTest {
|
||||
@Mock private Context mContext;
|
||||
@Mock private VcnNetworkProvider mVcnNetworkProvider;
|
||||
@Mock private ConnectivityManager mConnectivityManager;
|
||||
@Mock private TelephonyManager mTelephonyManager;
|
||||
@Mock private CarrierConfigManager mCarrierConfigManager;
|
||||
@Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
|
||||
@Mock private UnderlyingNetworkTrackerCallback mNetworkTrackerCb;
|
||||
@Mock private Network mNetwork;
|
||||
|
||||
@Captor private ArgumentCaptor<RouteSelectionCallback> mRouteSelectionCallbackCaptor;
|
||||
@Captor private ArgumentCaptor<UnderlyingNetworkListener> mUnderlyingNetworkListenerCaptor;
|
||||
|
||||
private TestLooper mTestLooper;
|
||||
private VcnContext mVcnContext;
|
||||
@@ -127,6 +131,13 @@ public class UnderlyingNetworkTrackerTest {
|
||||
mConnectivityManager,
|
||||
Context.CONNECTIVITY_SERVICE,
|
||||
ConnectivityManager.class);
|
||||
setupSystemService(
|
||||
mContext, mTelephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class);
|
||||
setupSystemService(
|
||||
mContext,
|
||||
mCarrierConfigManager,
|
||||
Context.CARRIER_CONFIG_SERVICE,
|
||||
CarrierConfigManager.class);
|
||||
|
||||
when(mSubscriptionSnapshot.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(INITIAL_SUB_IDS);
|
||||
|
||||
@@ -163,26 +174,26 @@ public class UnderlyingNetworkTrackerTest {
|
||||
|
||||
@Test
|
||||
public void testNetworkCallbacksRegisteredOnStartupForTestMode() {
|
||||
final ConnectivityManager cm = mock(ConnectivityManager.class);
|
||||
setupSystemService(mContext, cm, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
|
||||
final VcnContext vcnContext =
|
||||
spy(
|
||||
new VcnContext(
|
||||
mContext,
|
||||
mTestLooper.getLooper(),
|
||||
mVcnNetworkProvider,
|
||||
true /* isInTestMode */));
|
||||
new VcnContext(
|
||||
mContext,
|
||||
mTestLooper.getLooper(),
|
||||
mVcnNetworkProvider,
|
||||
true /* isInTestMode */);
|
||||
|
||||
mUnderlyingNetworkTracker =
|
||||
new UnderlyingNetworkTracker(
|
||||
vcnContext,
|
||||
SUB_GROUP,
|
||||
mSubscriptionSnapshot,
|
||||
Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET),
|
||||
mNetworkTrackerCb);
|
||||
new UnderlyingNetworkTracker(
|
||||
vcnContext,
|
||||
SUB_GROUP,
|
||||
mSubscriptionSnapshot,
|
||||
Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET),
|
||||
mNetworkTrackerCb);
|
||||
|
||||
verify(mConnectivityManager)
|
||||
.requestBackgroundNetwork(
|
||||
verify(cm)
|
||||
.registerNetworkCallback(
|
||||
eq(getTestNetworkRequest(INITIAL_SUB_IDS)),
|
||||
any(RouteSelectionCallback.class),
|
||||
any(UnderlyingNetworkListener.class),
|
||||
any());
|
||||
}
|
||||
|
||||
@@ -200,9 +211,19 @@ public class UnderlyingNetworkTrackerTest {
|
||||
}
|
||||
|
||||
verify(mConnectivityManager)
|
||||
.requestBackgroundNetwork(
|
||||
.registerNetworkCallback(
|
||||
eq(getRouteSelectionRequest(expectedSubIds)),
|
||||
any(RouteSelectionCallback.class),
|
||||
any(UnderlyingNetworkListener.class),
|
||||
any());
|
||||
verify(mConnectivityManager)
|
||||
.registerNetworkCallback(
|
||||
eq(getWifiEntryRssiThresholdRequest(expectedSubIds)),
|
||||
any(NetworkBringupCallback.class),
|
||||
any());
|
||||
verify(mConnectivityManager)
|
||||
.registerNetworkCallback(
|
||||
eq(getWifiExitRssiThresholdRequest(expectedSubIds)),
|
||||
any(NetworkBringupCallback.class),
|
||||
any());
|
||||
}
|
||||
|
||||
@@ -218,9 +239,10 @@ public class UnderlyingNetworkTrackerTest {
|
||||
mUnderlyingNetworkTracker.updateSubscriptionSnapshot(subscriptionUpdate);
|
||||
|
||||
// verify that initially-filed bringup requests are unregistered (cell + wifi)
|
||||
verify(mConnectivityManager, times(INITIAL_SUB_IDS.size() + 1))
|
||||
verify(mConnectivityManager, times(INITIAL_SUB_IDS.size() + 3))
|
||||
.unregisterNetworkCallback(any(NetworkBringupCallback.class));
|
||||
verify(mConnectivityManager).unregisterNetworkCallback(any(RouteSelectionCallback.class));
|
||||
verify(mConnectivityManager)
|
||||
.unregisterNetworkCallback(any(UnderlyingNetworkListener.class));
|
||||
verifyNetworkRequestsRegistered(UPDATED_SUB_IDS);
|
||||
}
|
||||
|
||||
@@ -231,6 +253,24 @@ public class UnderlyingNetworkTrackerTest {
|
||||
.build();
|
||||
}
|
||||
|
||||
private NetworkRequest getWifiEntryRssiThresholdRequest(Set<Integer> netCapsSubIds) {
|
||||
// TODO (b/187991063): Add tests for carrier-config based thresholds
|
||||
return getExpectedRequestBase()
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
||||
.setSubscriptionIds(netCapsSubIds)
|
||||
.setSignalStrength(UnderlyingNetworkTracker.WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT)
|
||||
.build();
|
||||
}
|
||||
|
||||
private NetworkRequest getWifiExitRssiThresholdRequest(Set<Integer> netCapsSubIds) {
|
||||
// TODO (b/187991063): Add tests for carrier-config based thresholds
|
||||
return getExpectedRequestBase()
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
|
||||
.setSubscriptionIds(netCapsSubIds)
|
||||
.setSignalStrength(UnderlyingNetworkTracker.WIFI_EXIT_RSSI_THRESHOLD_DEFAULT)
|
||||
.build();
|
||||
}
|
||||
|
||||
private NetworkRequest getCellRequestForSubId(int subId) {
|
||||
return getExpectedRequestBase()
|
||||
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
|
||||
@@ -239,7 +279,11 @@ public class UnderlyingNetworkTrackerTest {
|
||||
}
|
||||
|
||||
private NetworkRequest getRouteSelectionRequest(Set<Integer> netCapsSubIds) {
|
||||
return getExpectedRequestBase().setSubscriptionIds(netCapsSubIds).build();
|
||||
return getExpectedRequestBase()
|
||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
|
||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
|
||||
.setSubscriptionIds(netCapsSubIds)
|
||||
.build();
|
||||
}
|
||||
|
||||
private NetworkRequest getTestNetworkRequest(Set<Integer> netCapsSubIds) {
|
||||
@@ -265,11 +309,12 @@ public class UnderlyingNetworkTrackerTest {
|
||||
public void testTeardown() {
|
||||
mUnderlyingNetworkTracker.teardown();
|
||||
|
||||
// Expect 3 NetworkBringupCallbacks to be unregistered: 1 for WiFi and 2 for Cellular (1x
|
||||
// for each subId)
|
||||
verify(mConnectivityManager, times(3))
|
||||
// Expect 5 NetworkBringupCallbacks to be unregistered: 1 for WiFi, 2 for Cellular (1x for
|
||||
// each subId), and 1 for each of the Wifi signal strength thresholds
|
||||
verify(mConnectivityManager, times(5))
|
||||
.unregisterNetworkCallback(any(NetworkBringupCallback.class));
|
||||
verify(mConnectivityManager).unregisterNetworkCallback(any(RouteSelectionCallback.class));
|
||||
verify(mConnectivityManager)
|
||||
.unregisterNetworkCallback(any(UnderlyingNetworkListener.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -302,19 +347,19 @@ public class UnderlyingNetworkTrackerTest {
|
||||
verifyRegistrationOnAvailableAndGetCallback();
|
||||
}
|
||||
|
||||
private RouteSelectionCallback verifyRegistrationOnAvailableAndGetCallback() {
|
||||
private UnderlyingNetworkListener verifyRegistrationOnAvailableAndGetCallback() {
|
||||
return verifyRegistrationOnAvailableAndGetCallback(INITIAL_NETWORK_CAPABILITIES);
|
||||
}
|
||||
|
||||
private RouteSelectionCallback verifyRegistrationOnAvailableAndGetCallback(
|
||||
private UnderlyingNetworkListener verifyRegistrationOnAvailableAndGetCallback(
|
||||
NetworkCapabilities networkCapabilities) {
|
||||
verify(mConnectivityManager)
|
||||
.requestBackgroundNetwork(
|
||||
.registerNetworkCallback(
|
||||
eq(getRouteSelectionRequest(INITIAL_SUB_IDS)),
|
||||
mRouteSelectionCallbackCaptor.capture(),
|
||||
mUnderlyingNetworkListenerCaptor.capture(),
|
||||
any());
|
||||
|
||||
RouteSelectionCallback cb = mRouteSelectionCallbackCaptor.getValue();
|
||||
UnderlyingNetworkListener cb = mUnderlyingNetworkListenerCaptor.getValue();
|
||||
cb.onAvailable(mNetwork);
|
||||
cb.onCapabilitiesChanged(mNetwork, networkCapabilities);
|
||||
cb.onLinkPropertiesChanged(mNetwork, INITIAL_LINK_PROPERTIES);
|
||||
@@ -332,7 +377,7 @@ public class UnderlyingNetworkTrackerTest {
|
||||
|
||||
@Test
|
||||
public void testRecordTrackerCallbackNotifiedForNetworkCapabilitiesChange() {
|
||||
RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
|
||||
UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
|
||||
|
||||
cb.onCapabilitiesChanged(mNetwork, UPDATED_NETWORK_CAPABILITIES);
|
||||
|
||||
@@ -347,7 +392,7 @@ public class UnderlyingNetworkTrackerTest {
|
||||
|
||||
@Test
|
||||
public void testRecordTrackerCallbackNotifiedForLinkPropertiesChange() {
|
||||
RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
|
||||
UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
|
||||
|
||||
cb.onLinkPropertiesChanged(mNetwork, UPDATED_LINK_PROPERTIES);
|
||||
|
||||
@@ -362,7 +407,7 @@ public class UnderlyingNetworkTrackerTest {
|
||||
|
||||
@Test
|
||||
public void testRecordTrackerCallbackNotifiedForNetworkSuspended() {
|
||||
RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
|
||||
UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
|
||||
|
||||
cb.onCapabilitiesChanged(mNetwork, SUSPENDED_NETWORK_CAPABILITIES);
|
||||
|
||||
@@ -381,7 +426,7 @@ public class UnderlyingNetworkTrackerTest {
|
||||
|
||||
@Test
|
||||
public void testRecordTrackerCallbackNotifiedForNetworkResumed() {
|
||||
RouteSelectionCallback cb =
|
||||
UnderlyingNetworkListener cb =
|
||||
verifyRegistrationOnAvailableAndGetCallback(SUSPENDED_NETWORK_CAPABILITIES);
|
||||
|
||||
cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES);
|
||||
@@ -401,7 +446,7 @@ public class UnderlyingNetworkTrackerTest {
|
||||
|
||||
@Test
|
||||
public void testRecordTrackerCallbackNotifiedForBlocked() {
|
||||
RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
|
||||
UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
|
||||
|
||||
cb.onBlockedStatusChanged(mNetwork, true /* isBlocked */);
|
||||
|
||||
@@ -416,7 +461,7 @@ public class UnderlyingNetworkTrackerTest {
|
||||
|
||||
@Test
|
||||
public void testRecordTrackerCallbackNotifiedForNetworkLoss() {
|
||||
RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
|
||||
UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
|
||||
|
||||
cb.onLost(mNetwork);
|
||||
|
||||
@@ -425,7 +470,7 @@ public class UnderlyingNetworkTrackerTest {
|
||||
|
||||
@Test
|
||||
public void testRecordTrackerCallbackIgnoresDuplicateRecord() {
|
||||
RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
|
||||
UnderlyingNetworkListener cb = verifyRegistrationOnAvailableAndGetCallback();
|
||||
|
||||
cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES);
|
||||
|
||||
@@ -433,4 +478,6 @@ public class UnderlyingNetworkTrackerTest {
|
||||
// UnderlyingNetworkRecord does not actually change
|
||||
verifyNoMoreInteractions(mNetworkTrackerCb);
|
||||
}
|
||||
|
||||
// TODO (b/187991063): Add tests for network prioritization
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ import android.net.vcn.VcnGatewayConnectionConfigTest;
|
||||
import android.os.ParcelUuid;
|
||||
import android.os.PowerManager;
|
||||
import android.os.test.TestLooper;
|
||||
import android.telephony.SubscriptionInfo;
|
||||
|
||||
import com.android.internal.util.State;
|
||||
import com.android.internal.util.WakeupMessage;
|
||||
@@ -73,6 +74,12 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class VcnGatewayConnectionTestBase {
|
||||
protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID());
|
||||
protected static final SubscriptionInfo TEST_SUB_INFO = mock(SubscriptionInfo.class);
|
||||
|
||||
static {
|
||||
doReturn(TEST_SUB_GRP).when(TEST_SUB_INFO).getGroupUuid();
|
||||
}
|
||||
|
||||
protected static final InetAddress TEST_DNS_ADDR =
|
||||
InetAddresses.parseNumericAddress("2001:DB8:0:1::");
|
||||
protected static final InetAddress TEST_DNS_ADDR_2 =
|
||||
@@ -116,7 +123,7 @@ public class VcnGatewayConnectionTestBase {
|
||||
|
||||
protected static final TelephonySubscriptionSnapshot TEST_SUBSCRIPTION_SNAPSHOT =
|
||||
new TelephonySubscriptionSnapshot(
|
||||
Collections.singletonMap(TEST_SUB_ID, TEST_SUB_GRP), Collections.EMPTY_MAP);
|
||||
Collections.singletonMap(TEST_SUB_ID, TEST_SUB_INFO), Collections.EMPTY_MAP);
|
||||
|
||||
@NonNull protected final Context mContext;
|
||||
@NonNull protected final TestLooper mTestLooper;
|
||||
|
||||
Reference in New Issue
Block a user