Merge changes I03be15d6,Ie1671925,Ie87f4aa3,I24474419,I15bc9aaf am: 6d02108df5

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1569845

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I271653dfbcffcccc49884c8c5ae345c242580abd
This commit is contained in:
Cody Kesting
2021-02-04 18:26:09 +00:00
committed by Automerger Merge Worker
11 changed files with 480 additions and 109 deletions

View File

@@ -290,8 +290,9 @@ public class VcnManagementService extends IVcnManagementService.Stub {
public Vcn newVcn( public Vcn newVcn(
@NonNull VcnContext vcnContext, @NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup, @NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config) { @NonNull VcnConfig config,
return new Vcn(vcnContext, subscriptionGroup, config); @NonNull TelephonySubscriptionSnapshot snapshot) {
return new Vcn(vcnContext, subscriptionGroup, config, snapshot);
} }
/** Gets the subId indicated by the given {@link WifiInfo}. */ /** Gets the subId indicated by the given {@link WifiInfo}. */
@@ -382,6 +383,7 @@ public class VcnManagementService extends IVcnManagementService.Stub {
// delay) // delay)
for (Entry<ParcelUuid, Vcn> entry : mVcns.entrySet()) { for (Entry<ParcelUuid, Vcn> entry : mVcns.entrySet()) {
final VcnConfig config = mConfigs.get(entry.getKey()); final VcnConfig config = mConfigs.get(entry.getKey());
if (config == null if (config == null
|| !snapshot.packageHasPermissionsForSubscriptionGroup( || !snapshot.packageHasPermissionsForSubscriptionGroup(
entry.getKey(), config.getProvisioningPackageName())) { entry.getKey(), config.getProvisioningPackageName())) {
@@ -395,16 +397,40 @@ public class VcnManagementService extends IVcnManagementService.Stub {
// correct instance is torn down. This could happen as a result of a // correct instance is torn down. This could happen as a result of a
// Carrier App manually removing/adding a VcnConfig. // Carrier App manually removing/adding a VcnConfig.
if (mVcns.get(uuidToTeardown) == instanceToTeardown) { if (mVcns.get(uuidToTeardown) == instanceToTeardown) {
mVcns.remove(uuidToTeardown).teardownAsynchronously(); stopVcnLocked(uuidToTeardown);
} }
} }
}, instanceToTeardown, CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS); }, instanceToTeardown, CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
} else {
// If this VCN's status has not changed, update it with the new snapshot
entry.getValue().updateSubscriptionSnapshot(mLastSnapshot);
} }
} }
} }
} }
} }
@GuardedBy("mLock")
private void stopVcnLocked(@NonNull ParcelUuid uuidToTeardown) {
final Vcn vcnToTeardown = mVcns.remove(uuidToTeardown);
if (vcnToTeardown == null) {
return;
}
vcnToTeardown.teardownAsynchronously();
// Now that the VCN is removed, notify all registered listeners to refresh their
// UnderlyingNetworkPolicy.
notifyAllPolicyListenersLocked();
}
@GuardedBy("mLock")
private void notifyAllPolicyListenersLocked() {
for (final PolicyListenerBinderDeath policyListener : mRegisteredPolicyListeners.values()) {
Binder.withCleanCallingIdentity(() -> policyListener.mListener.onPolicyChanged());
}
}
@GuardedBy("mLock") @GuardedBy("mLock")
private void startVcnLocked(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) { private void startVcnLocked(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) {
Slog.v(TAG, "Starting VCN config for subGrp: " + subscriptionGroup); Slog.v(TAG, "Starting VCN config for subGrp: " + subscriptionGroup);
@@ -412,8 +438,12 @@ public class VcnManagementService extends IVcnManagementService.Stub {
// TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active // TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active
// VCN. // VCN.
final Vcn newInstance = mDeps.newVcn(mVcnContext, subscriptionGroup, config); final Vcn newInstance = mDeps.newVcn(mVcnContext, subscriptionGroup, config, mLastSnapshot);
mVcns.put(subscriptionGroup, newInstance); mVcns.put(subscriptionGroup, newInstance);
// Now that a new VCN has started, notify all registered listeners to refresh their
// UnderlyingNetworkPolicy.
notifyAllPolicyListenersLocked();
} }
@GuardedBy("mLock") @GuardedBy("mLock")
@@ -476,9 +506,7 @@ public class VcnManagementService extends IVcnManagementService.Stub {
synchronized (mLock) { synchronized (mLock) {
mConfigs.remove(subscriptionGroup); mConfigs.remove(subscriptionGroup);
if (mVcns.containsKey(subscriptionGroup)) { stopVcnLocked(subscriptionGroup);
mVcns.remove(subscriptionGroup).teardownAsynchronously();
}
writeConfigsToDiskLocked(); writeConfigsToDiskLocked();
} }

View File

@@ -28,16 +28,15 @@ import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier; import android.net.TelephonyNetworkSpecifier;
import android.os.Handler; import android.os.Handler;
import android.os.ParcelUuid; import android.os.ParcelUuid;
import android.telephony.SubscriptionInfo; import android.util.ArrayMap;
import android.telephony.SubscriptionManager;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import java.util.List; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
@@ -55,19 +54,18 @@ public class UnderlyingNetworkTracker {
@NonNull private final VcnContext mVcnContext; @NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup; @NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final Set<Integer> mRequiredUnderlyingNetworkCapabilities;
@NonNull private final UnderlyingNetworkTrackerCallback mCb; @NonNull private final UnderlyingNetworkTrackerCallback mCb;
@NonNull private final Dependencies mDeps; @NonNull private final Dependencies mDeps;
@NonNull private final Handler mHandler; @NonNull private final Handler mHandler;
@NonNull private final ConnectivityManager mConnectivityManager; @NonNull private final ConnectivityManager mConnectivityManager;
@NonNull private final SubscriptionManager mSubscriptionManager;
@NonNull private final SparseArray<NetworkCallback> mCellBringupCallbacks = new SparseArray<>(); @NonNull private final Map<Integer, NetworkCallback> mCellBringupCallbacks = new ArrayMap<>();
@NonNull private final NetworkCallback mWifiBringupCallback = new NetworkBringupCallback(); @NonNull private final NetworkCallback mWifiBringupCallback = new NetworkBringupCallback();
@NonNull private final NetworkCallback mRouteSelectionCallback = new RouteSelectionCallback(); @NonNull private final NetworkCallback mRouteSelectionCallback = new RouteSelectionCallback();
@NonNull private final Set<Integer> mSubIds = new ArraySet<>(); @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
private boolean mIsRunning = true;
@NonNull private final Set<Integer> mRequiredUnderlyingNetworkCapabilities;
@Nullable private UnderlyingNetworkRecord mCurrentRecord; @Nullable private UnderlyingNetworkRecord mCurrentRecord;
@Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress; @Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress;
@@ -75,11 +73,13 @@ public class UnderlyingNetworkTracker {
public UnderlyingNetworkTracker( public UnderlyingNetworkTracker(
@NonNull VcnContext vcnContext, @NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup, @NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull Set<Integer> requiredUnderlyingNetworkCapabilities, @NonNull Set<Integer> requiredUnderlyingNetworkCapabilities,
@NonNull UnderlyingNetworkTrackerCallback cb) { @NonNull UnderlyingNetworkTrackerCallback cb) {
this( this(
vcnContext, vcnContext,
subscriptionGroup, subscriptionGroup,
snapshot,
requiredUnderlyingNetworkCapabilities, requiredUnderlyingNetworkCapabilities,
cb, cb,
new Dependencies()); new Dependencies());
@@ -88,11 +88,13 @@ public class UnderlyingNetworkTracker {
private UnderlyingNetworkTracker( private UnderlyingNetworkTracker(
@NonNull VcnContext vcnContext, @NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup, @NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull Set<Integer> requiredUnderlyingNetworkCapabilities, @NonNull Set<Integer> requiredUnderlyingNetworkCapabilities,
@NonNull UnderlyingNetworkTrackerCallback cb, @NonNull UnderlyingNetworkTrackerCallback cb,
@NonNull Dependencies deps) { @NonNull Dependencies deps) {
mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext"); mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext");
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup"); mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
mRequiredUnderlyingNetworkCapabilities = mRequiredUnderlyingNetworkCapabilities =
Objects.requireNonNull( Objects.requireNonNull(
requiredUnderlyingNetworkCapabilities, requiredUnderlyingNetworkCapabilities,
@@ -103,7 +105,6 @@ public class UnderlyingNetworkTracker {
mHandler = new Handler(mVcnContext.getLooper()); mHandler = new Handler(mVcnContext.getLooper());
mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class); mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class);
mSubscriptionManager = mVcnContext.getContext().getSystemService(SubscriptionManager.class);
registerNetworkRequests(); registerNetworkRequests();
} }
@@ -149,34 +150,47 @@ public class UnderlyingNetworkTracker {
private void updateSubIdsAndCellularRequests() { private void updateSubIdsAndCellularRequests() {
mVcnContext.ensureRunningOnLooperThread(); mVcnContext.ensureRunningOnLooperThread();
Set<Integer> prevSubIds = new ArraySet<>(mSubIds); // Don't bother re-filing NetworkRequests if this Tracker has been torn down.
mSubIds.clear(); if (!mIsRunning) {
return;
}
// Ensure NetworkRequests filed for all current subIds in mSubscriptionGroup final Set<Integer> subIdsInSubGroup = mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup);
// STOPSHIP: b/177364490 use TelephonySubscriptionSnapshot to avoid querying Telephony
List<SubscriptionInfo> subInfos =
mSubscriptionManager.getSubscriptionsInGroup(mSubscriptionGroup);
for (SubscriptionInfo subInfo : subInfos) { // new subIds to track = (updated list of subIds) - (currently tracked subIds)
final int subId = subInfo.getSubscriptionId(); final Set<Integer> subIdsToRegister = new ArraySet<>(subIdsInSubGroup);
mSubIds.add(subId); subIdsToRegister.removeAll(mCellBringupCallbacks.keySet());
if (!mCellBringupCallbacks.contains(subId)) { // subIds to stop tracking = (currently tracked subIds) - (updated list of subIds)
final Set<Integer> subIdsToUnregister = new ArraySet<>(mCellBringupCallbacks.keySet());
subIdsToUnregister.removeAll(subIdsInSubGroup);
for (final int subId : subIdsToRegister) {
final NetworkBringupCallback cb = new NetworkBringupCallback(); final NetworkBringupCallback cb = new NetworkBringupCallback();
mCellBringupCallbacks.put(subId, cb); mCellBringupCallbacks.put(subId, cb);
mConnectivityManager.requestBackgroundNetwork( mConnectivityManager.requestBackgroundNetwork(
getCellNetworkRequestForSubId(subId), mHandler, cb); getCellNetworkRequestForSubId(subId), mHandler, cb);
} }
}
// unregister all NetworkCallbacks for outdated subIds for (final int subId : subIdsToUnregister) {
for (final int subId : prevSubIds) { final NetworkCallback cb = mCellBringupCallbacks.remove(subId);
if (!mSubIds.contains(subId)) {
final NetworkCallback cb = mCellBringupCallbacks.removeReturnOld(subId);
mConnectivityManager.unregisterNetworkCallback(cb); mConnectivityManager.unregisterNetworkCallback(cb);
} }
} }
/**
* Update this UnderlyingNetworkTracker's TelephonySubscriptionSnapshot.
*
* <p>Updating the TelephonySubscriptionSnapshot will cause this UnderlyingNetworkTracker to
* 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");
mLastSnapshot = snapshot;
updateSubIdsAndCellularRequests();
} }
/** Tears down this Tracker, and releases all underlying network requests. */ /** Tears down this Tracker, and releases all underlying network requests. */
@@ -186,11 +200,12 @@ public class UnderlyingNetworkTracker {
mConnectivityManager.unregisterNetworkCallback(mWifiBringupCallback); mConnectivityManager.unregisterNetworkCallback(mWifiBringupCallback);
mConnectivityManager.unregisterNetworkCallback(mRouteSelectionCallback); mConnectivityManager.unregisterNetworkCallback(mRouteSelectionCallback);
for (final int subId : mSubIds) { for (final NetworkCallback cb : mCellBringupCallbacks.values()) {
final NetworkCallback cb = mCellBringupCallbacks.removeReturnOld(subId);
mConnectivityManager.unregisterNetworkCallback(cb); mConnectivityManager.unregisterNetworkCallback(cb);
} }
mSubIds.clear(); mCellBringupCallbacks.clear();
mIsRunning = false;
} }
/** Returns whether the currently selected Network matches the given network. */ /** Returns whether the currently selected Network matches the given network. */

View File

@@ -27,9 +27,16 @@ import android.os.Message;
import android.os.ParcelUuid; import android.os.ParcelUuid;
import android.util.Slog; import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
/** /**
* Represents an single instance of a VCN. * Represents an single instance of a VCN.
@@ -63,6 +70,15 @@ public class Vcn extends Handler {
*/ */
private static final int MSG_EVENT_NETWORK_REQUESTED = MSG_EVENT_BASE + 1; private static final int MSG_EVENT_NETWORK_REQUESTED = MSG_EVENT_BASE + 1;
/**
* The TelephonySubscriptionSnapshot tracked by VcnManagementService has changed.
*
* <p>This updated snapshot should be cached locally and passed to all VcnGatewayConnections.
*
* @param obj TelephonySubscriptionSnapshot
*/
private static final int MSG_EVENT_SUBSCRIPTIONS_CHANGED = MSG_EVENT_BASE + 2;
/** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */ /** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE; private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
@@ -76,20 +92,24 @@ public class Vcn extends Handler {
new HashMap<>(); new HashMap<>();
@NonNull private VcnConfig mConfig; @NonNull private VcnConfig mConfig;
@NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
private boolean mIsRunning = true; private boolean mIsRunning = true;
public Vcn( public Vcn(
@NonNull VcnContext vcnContext, @NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup, @NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config) { @NonNull VcnConfig config,
this(vcnContext, subscriptionGroup, config, new Dependencies()); @NonNull TelephonySubscriptionSnapshot snapshot) {
this(vcnContext, subscriptionGroup, config, snapshot, new Dependencies());
} }
private Vcn( @VisibleForTesting(visibility = Visibility.PRIVATE)
public Vcn(
@NonNull VcnContext vcnContext, @NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup, @NonNull ParcelUuid subscriptionGroup,
@NonNull VcnConfig config, @NonNull VcnConfig config,
@NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull Dependencies deps) { @NonNull Dependencies deps) {
super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
mVcnContext = vcnContext; mVcnContext = vcnContext;
@@ -98,6 +118,7 @@ public class Vcn extends Handler {
mRequestListener = new VcnNetworkRequestListener(); mRequestListener = new VcnNetworkRequestListener();
mConfig = Objects.requireNonNull(config, "Missing config"); mConfig = Objects.requireNonNull(config, "Missing config");
mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
// Register to receive cached and future NetworkRequests // Register to receive cached and future NetworkRequests
mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener); mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener);
@@ -110,11 +131,24 @@ public class Vcn extends Handler {
sendMessage(obtainMessage(MSG_EVENT_CONFIG_UPDATED, config)); sendMessage(obtainMessage(MSG_EVENT_CONFIG_UPDATED, config));
} }
/** Asynchronously updates the Subscription snapshot for this VCN. */
public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
Objects.requireNonNull(snapshot, "Missing snapshot");
sendMessage(obtainMessage(MSG_EVENT_SUBSCRIPTIONS_CHANGED, snapshot));
}
/** Asynchronously tears down this Vcn instance, including VcnGatewayConnection(s) */ /** Asynchronously tears down this Vcn instance, including VcnGatewayConnection(s) */
public void teardownAsynchronously() { public void teardownAsynchronously() {
sendMessageAtFrontOfQueue(obtainMessage(MSG_CMD_TEARDOWN)); sendMessageAtFrontOfQueue(obtainMessage(MSG_CMD_TEARDOWN));
} }
/** Get current Gateways for testing purposes */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public Set<VcnGatewayConnection> getVcnGatewayConnections() {
return Collections.unmodifiableSet(new HashSet<>(mVcnGatewayConnections.values()));
}
private class VcnNetworkRequestListener implements VcnNetworkProvider.NetworkRequestListener { private class VcnNetworkRequestListener implements VcnNetworkProvider.NetworkRequestListener {
@Override @Override
public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) { public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
@@ -137,6 +171,9 @@ public class Vcn extends Handler {
case MSG_EVENT_NETWORK_REQUESTED: case MSG_EVENT_NETWORK_REQUESTED:
handleNetworkRequested((NetworkRequest) msg.obj, msg.arg1, msg.arg2); handleNetworkRequested((NetworkRequest) msg.obj, msg.arg1, msg.arg2);
break; break;
case MSG_EVENT_SUBSCRIPTIONS_CHANGED:
handleSubscriptionsChanged((TelephonySubscriptionSnapshot) msg.obj);
break;
case MSG_CMD_TEARDOWN: case MSG_CMD_TEARDOWN:
handleTeardown(); handleTeardown();
break; break;
@@ -192,13 +229,26 @@ public class Vcn extends Handler {
"Bringing up new VcnGatewayConnection for request " + request.requestId); "Bringing up new VcnGatewayConnection for request " + request.requestId);
final VcnGatewayConnection vcnGatewayConnection = final VcnGatewayConnection vcnGatewayConnection =
new VcnGatewayConnection( mDeps.newVcnGatewayConnection(
mVcnContext, mSubscriptionGroup, gatewayConnectionConfig); mVcnContext,
mSubscriptionGroup,
mLastSnapshot,
gatewayConnectionConfig);
mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection); mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection);
} }
} }
} }
private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) {
mLastSnapshot = snapshot;
if (mIsRunning) {
for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
}
}
}
private boolean requestSatisfiedByGatewayConnectionConfig( private boolean requestSatisfiedByGatewayConnectionConfig(
@NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) { @NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) {
final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(); final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
@@ -221,5 +271,17 @@ public class Vcn extends Handler {
return 52; return 52;
} }
private static class Dependencies {} /** External dependencies used by Vcn, for injection in tests */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class Dependencies {
/** Builds a new VcnGatewayConnection */
public VcnGatewayConnection newVcnGatewayConnection(
VcnContext vcnContext,
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
VcnGatewayConnectionConfig connectionConfig) {
return new VcnGatewayConnection(
vcnContext, subscriptionGroup, snapshot, connectionConfig);
}
}
} }

View File

@@ -61,10 +61,12 @@ import android.os.ParcelUuid;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Slog; import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.State; import com.android.internal.util.State;
import com.android.internal.util.StateMachine; import com.android.internal.util.StateMachine;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord; import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback; import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
@@ -371,6 +373,16 @@ public class VcnGatewayConnection extends StateMachine {
*/ */
private static final int EVENT_TEARDOWN_TIMEOUT_EXPIRED = 8; private static final int EVENT_TEARDOWN_TIMEOUT_EXPIRED = 8;
/**
* Sent when this VcnGatewayConnection is notified of a change in TelephonySubscriptions.
*
* <p>Relevant in all states.
*
* @param arg1 The "all" token; this signal is always honored.
*/
// TODO(b/178426520): implement handling of this event
private static final int EVENT_SUBSCRIPTIONS_CHANGED = 9;
@VisibleForTesting(visibility = Visibility.PRIVATE) @VisibleForTesting(visibility = Visibility.PRIVATE)
@NonNull @NonNull
final DisconnectedState mDisconnectedState = new DisconnectedState(); final DisconnectedState mDisconnectedState = new DisconnectedState();
@@ -391,6 +403,11 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull @NonNull
final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState(); final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState();
@NonNull private final Object mLock = new Object();
@GuardedBy("mLock")
@NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
@NonNull private final VcnContext mVcnContext; @NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup; @NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker; @NonNull private final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
@@ -469,14 +486,16 @@ public class VcnGatewayConnection extends StateMachine {
public VcnGatewayConnection( public VcnGatewayConnection(
@NonNull VcnContext vcnContext, @NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup, @NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull VcnGatewayConnectionConfig connectionConfig) { @NonNull VcnGatewayConnectionConfig connectionConfig) {
this(vcnContext, subscriptionGroup, connectionConfig, new Dependencies()); this(vcnContext, subscriptionGroup, snapshot, connectionConfig, new Dependencies());
} }
@VisibleForTesting(visibility = Visibility.PRIVATE) @VisibleForTesting(visibility = Visibility.PRIVATE)
VcnGatewayConnection( VcnGatewayConnection(
@NonNull VcnContext vcnContext, @NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup, @NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull VcnGatewayConnectionConfig connectionConfig, @NonNull VcnGatewayConnectionConfig connectionConfig,
@NonNull Dependencies deps) { @NonNull Dependencies deps) {
super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
@@ -485,12 +504,17 @@ public class VcnGatewayConnection extends StateMachine {
mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig"); mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig");
mDeps = Objects.requireNonNull(deps, "Missing deps"); mDeps = Objects.requireNonNull(deps, "Missing deps");
synchronized (mLock) {
mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
}
mUnderlyingNetworkTrackerCallback = new VcnUnderlyingNetworkTrackerCallback(); mUnderlyingNetworkTrackerCallback = new VcnUnderlyingNetworkTrackerCallback();
mUnderlyingNetworkTracker = mUnderlyingNetworkTracker =
mDeps.newUnderlyingNetworkTracker( mDeps.newUnderlyingNetworkTracker(
mVcnContext, mVcnContext,
subscriptionGroup, subscriptionGroup,
mLastSnapshot,
mConnectionConfig.getAllUnderlyingCapabilities(), mConnectionConfig.getAllUnderlyingCapabilities(),
mUnderlyingNetworkTrackerCallback); mUnderlyingNetworkTrackerCallback);
mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class); mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class);
@@ -545,6 +569,25 @@ public class VcnGatewayConnection extends StateMachine {
mUnderlyingNetworkTracker.teardown(); mUnderlyingNetworkTracker.teardown();
} }
/**
* Notify this Gateway that subscriptions have changed.
*
* <p>This snapshot should be used to update any keepalive requests necessary for potential
* underlying Networks in this Gateway's subscription group.
*/
public void updateSubscriptionSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
Objects.requireNonNull(snapshot, "Missing snapshot");
// Vcn is the only user of this method and runs on the same Thread, but lock around
// mLastSnapshot to be technically correct.
synchronized (mLock) {
mLastSnapshot = snapshot;
mUnderlyingNetworkTracker.updateSubscriptionSnapshot(mLastSnapshot);
}
sendMessage(EVENT_SUBSCRIPTIONS_CHANGED, TOKEN_ALL);
}
private class VcnUnderlyingNetworkTrackerCallback implements UnderlyingNetworkTrackerCallback { private class VcnUnderlyingNetworkTrackerCallback implements UnderlyingNetworkTrackerCallback {
@Override @Override
public void onSelectedUnderlyingNetworkChanged( public void onSelectedUnderlyingNetworkChanged(
@@ -682,7 +725,8 @@ public class VcnGatewayConnection extends StateMachine {
case EVENT_TRANSFORM_CREATED: // Fallthrough case EVENT_TRANSFORM_CREATED: // Fallthrough
case EVENT_SETUP_COMPLETED: // Fallthrough case EVENT_SETUP_COMPLETED: // Fallthrough
case EVENT_DISCONNECT_REQUESTED: // Fallthrough case EVENT_DISCONNECT_REQUESTED: // Fallthrough
case EVENT_TEARDOWN_TIMEOUT_EXPIRED: case EVENT_TEARDOWN_TIMEOUT_EXPIRED: // Fallthrough
case EVENT_SUBSCRIPTIONS_CHANGED:
logUnexpectedEvent(msg.what); logUnexpectedEvent(msg.what);
break; break;
default: default:
@@ -1384,10 +1428,15 @@ public class VcnGatewayConnection extends StateMachine {
public UnderlyingNetworkTracker newUnderlyingNetworkTracker( public UnderlyingNetworkTracker newUnderlyingNetworkTracker(
VcnContext vcnContext, VcnContext vcnContext,
ParcelUuid subscriptionGroup, ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
Set<Integer> requiredUnderlyingNetworkCapabilities, Set<Integer> requiredUnderlyingNetworkCapabilities,
UnderlyingNetworkTrackerCallback callback) { UnderlyingNetworkTrackerCallback callback) {
return new UnderlyingNetworkTracker( return new UnderlyingNetworkTracker(
vcnContext, subscriptionGroup, requiredUnderlyingNetworkCapabilities, callback); vcnContext,
subscriptionGroup,
snapshot,
requiredUnderlyingNetworkCapabilities,
callback);
} }
/** Builds a new IkeSession. */ /** Builds a new IkeSession. */

View File

@@ -25,6 +25,9 @@ import android.util.ArrayMap;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Slog; import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
@@ -52,8 +55,13 @@ public class VcnNetworkProvider extends NetworkProvider {
super(context, looper, VcnNetworkProvider.class.getSimpleName()); super(context, looper, VcnNetworkProvider.class.getSimpleName());
} }
// Package-private /**
void registerListener(@NonNull NetworkRequestListener listener) { * Registers a NetworkRequestListener with this NetworkProvider.
*
* <p>Upon registering, the provided listener will receive all cached requests.
*/
@VisibleForTesting(visibility = Visibility.PACKAGE)
public void registerListener(@NonNull NetworkRequestListener listener) {
mListeners.add(listener); mListeners.add(listener);
// Send listener all cached requests // Send listener all cached requests
@@ -62,8 +70,9 @@ public class VcnNetworkProvider extends NetworkProvider {
} }
} }
// Package-private /** Unregisters the specified listener from receiving future NetworkRequests. */
void unregisterListener(@NonNull NetworkRequestListener listener) { @VisibleForTesting(visibility = Visibility.PACKAGE)
public void unregisterListener(@NonNull NetworkRequestListener listener) {
mListeners.remove(listener); mListeners.remove(listener);
} }

View File

@@ -184,7 +184,7 @@ public class VcnManagementServiceTest {
doAnswer((invocation) -> { doAnswer((invocation) -> {
// Mock-within a doAnswer is safe, because it doesn't actually run nested. // Mock-within a doAnswer is safe, because it doesn't actually run nested.
return mock(Vcn.class); return mock(Vcn.class);
}).when(mMockDeps).newVcn(any(), any(), any()); }).when(mMockDeps).newVcn(any(), any(), any(), any());
final PersistableBundle bundle = final PersistableBundle bundle =
PersistableBundleUtils.fromMap( PersistableBundleUtils.fromMap(
@@ -304,14 +304,17 @@ public class VcnManagementServiceTest {
@Test @Test
public void testTelephonyNetworkTrackerCallbackStartsInstances() throws Exception { public void testTelephonyNetworkTrackerCallbackStartsInstances() throws Exception {
TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1)); triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
verify(mMockDeps).newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG)); verify(mMockDeps)
.newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot));
} }
@Test @Test
public void testTelephonyNetworkTrackerCallbackStopsInstances() throws Exception { public void testTelephonyNetworkTrackerCallbackStopsInstances() throws Exception {
final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback(); final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2); final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet()); triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet());
@@ -319,6 +322,7 @@ public class VcnManagementServiceTest {
mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS); mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
mTestLooper.dispatchAll(); mTestLooper.dispatchAll();
verify(vcn).teardownAsynchronously(); verify(vcn).teardownAsynchronously();
verify(mMockPolicyListener).onPolicyChanged();
} }
@Test @Test
@@ -389,6 +393,7 @@ public class VcnManagementServiceTest {
mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
fail("Expected security exception for non system user"); fail("Expected security exception for non system user");
} catch (SecurityException expected) { } catch (SecurityException expected) {
verify(mMockPolicyListener, never()).onPolicyChanged();
} }
} }
@@ -400,6 +405,7 @@ public class VcnManagementServiceTest {
mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
fail("Expected security exception for missing carrier privileges"); fail("Expected security exception for missing carrier privileges");
} catch (SecurityException expected) { } catch (SecurityException expected) {
verify(mMockPolicyListener, never()).onPolicyChanged();
} }
} }
@@ -409,6 +415,7 @@ public class VcnManagementServiceTest {
mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, "IncorrectPackage"); mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, "IncorrectPackage");
fail("Expected exception due to mismatched packages in config and method call"); fail("Expected exception due to mismatched packages in config and method call");
} catch (IllegalArgumentException expected) { } catch (IllegalArgumentException expected) {
verify(mMockPolicyListener, never()).onPolicyChanged();
} }
} }
@@ -473,7 +480,12 @@ public class VcnManagementServiceTest {
verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class)); verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
// Verify Vcn is started // Verify Vcn is started
verify(mMockDeps).newVcn(eq(mVcnContext), eq(TEST_UUID_2), eq(TEST_VCN_CONFIG)); verify(mMockDeps)
.newVcn(
eq(mVcnContext),
eq(TEST_UUID_2),
eq(TEST_VCN_CONFIG),
eq(TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT));
// Verify Vcn is updated if it was previously started // Verify Vcn is updated if it was previously started
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
@@ -520,7 +532,7 @@ public class VcnManagementServiceTest {
Collections.singleton(subGroup), Collections.singletonMap(subId, subGroup)); Collections.singleton(subGroup), Collections.singletonMap(subId, subGroup));
} }
private void verifyMergedNetworkCapabilities( private void verifyMergedNetworkCapabilitiesIsVcnManaged(
NetworkCapabilities mergedCapabilities, @Transport int transportType) { NetworkCapabilities mergedCapabilities, @Transport int transportType) {
assertTrue(mergedCapabilities.hasTransport(transportType)); assertTrue(mergedCapabilities.hasTransport(transportType));
assertFalse( assertFalse(
@@ -529,7 +541,7 @@ public class VcnManagementServiceTest {
} }
@Test @Test
public void testGetUnderlyingNetworkPolicyTransportCell() throws Exception { public void testGetUnderlyingNetworkPolicyCellular() throws Exception {
setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2); setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
NetworkCapabilities nc = NetworkCapabilities nc =
@@ -542,12 +554,12 @@ public class VcnManagementServiceTest {
mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties()); mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
assertFalse(policy.isTeardownRequested()); assertFalse(policy.isTeardownRequested());
verifyMergedNetworkCapabilities( verifyMergedNetworkCapabilitiesIsVcnManaged(
policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_CELLULAR); policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_CELLULAR);
} }
@Test @Test
public void testGetUnderlyingNetworkPolicyTransportWifi() throws Exception { public void testGetUnderlyingNetworkPolicyWifi() throws Exception {
setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2); setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
WifiInfo wifiInfo = mock(WifiInfo.class); WifiInfo wifiInfo = mock(WifiInfo.class);
@@ -563,7 +575,7 @@ public class VcnManagementServiceTest {
mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties()); mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
assertFalse(policy.isTeardownRequested()); assertFalse(policy.isTeardownRequested());
verifyMergedNetworkCapabilities( verifyMergedNetworkCapabilitiesIsVcnManaged(
policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_WIFI); policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_WIFI);
} }
@@ -591,4 +603,35 @@ public class VcnManagementServiceTest {
mVcnMgmtSvc.getUnderlyingNetworkPolicy(new NetworkCapabilities(), new LinkProperties()); mVcnMgmtSvc.getUnderlyingNetworkPolicy(new NetworkCapabilities(), new LinkProperties());
} }
@Test
public void testSubscriptionSnapshotUpdateNotifiesVcn() {
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);
TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2));
verify(vcnInstance).updateSubscriptionSnapshot(eq(snapshot));
}
@Test
public void testAddNewVcnUpdatesPolicyListener() throws Exception {
mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
verify(mMockPolicyListener).onPolicyChanged();
}
@Test
public void testRemoveVcnUpdatesPolicyListener() throws Exception {
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2);
verify(mMockPolicyListener).onPolicyChanged();
}
} }

View File

@@ -42,8 +42,9 @@ import android.net.TelephonyNetworkSpecifier;
import android.os.ParcelUuid; import android.os.ParcelUuid;
import android.os.test.TestLooper; import android.os.test.TestLooper;
import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager; import android.util.ArraySet;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.UnderlyingNetworkTracker.NetworkBringupCallback; import com.android.server.vcn.UnderlyingNetworkTracker.NetworkBringupCallback;
import com.android.server.vcn.UnderlyingNetworkTracker.RouteSelectionCallback; import com.android.server.vcn.UnderlyingNetworkTracker.RouteSelectionCallback;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord; import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
@@ -58,13 +59,19 @@ import org.mockito.MockitoAnnotations;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.Set;
import java.util.UUID; import java.util.UUID;
public class UnderlyingNetworkTrackerTest { public class UnderlyingNetworkTrackerTest {
private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0)); private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
private static final int INITIAL_SUB_ID_1 = 1; private static final int INITIAL_SUB_ID_1 = 1;
private static final int INITIAL_SUB_ID_2 = 2; private static final int INITIAL_SUB_ID_2 = 2;
private static final int UPDATED_SUB_ID = 3;
private static final Set<Integer> INITIAL_SUB_IDS =
new ArraySet<>(Arrays.asList(INITIAL_SUB_ID_1, INITIAL_SUB_ID_2));
private static final Set<Integer> UPDATED_SUB_IDS =
new ArraySet<>(Arrays.asList(UPDATED_SUB_ID));
private static final NetworkCapabilities INITIAL_NETWORK_CAPABILITIES = private static final NetworkCapabilities INITIAL_NETWORK_CAPABILITIES =
new NetworkCapabilities.Builder() new NetworkCapabilities.Builder()
@@ -90,7 +97,7 @@ public class UnderlyingNetworkTrackerTest {
@Mock private Context mContext; @Mock private Context mContext;
@Mock private VcnNetworkProvider mVcnNetworkProvider; @Mock private VcnNetworkProvider mVcnNetworkProvider;
@Mock private ConnectivityManager mConnectivityManager; @Mock private ConnectivityManager mConnectivityManager;
@Mock private SubscriptionManager mSubscriptionManager; @Mock private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
@Mock private UnderlyingNetworkTrackerCallback mNetworkTrackerCb; @Mock private UnderlyingNetworkTrackerCallback mNetworkTrackerCb;
@Mock private Network mNetwork; @Mock private Network mNetwork;
@@ -113,23 +120,14 @@ public class UnderlyingNetworkTrackerTest {
mConnectivityManager, mConnectivityManager,
Context.CONNECTIVITY_SERVICE, Context.CONNECTIVITY_SERVICE,
ConnectivityManager.class); ConnectivityManager.class);
setupSystemService(
mContext,
mSubscriptionManager,
Context.TELEPHONY_SUBSCRIPTION_SERVICE,
SubscriptionManager.class);
List<SubscriptionInfo> initialSubInfos = when(mSubscriptionSnapshot.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(INITIAL_SUB_IDS);
Arrays.asList(
getSubscriptionInfoForSubId(INITIAL_SUB_ID_1),
getSubscriptionInfoForSubId(INITIAL_SUB_ID_2));
when(mSubscriptionManager.getSubscriptionsInGroup(eq(SUB_GROUP)))
.thenReturn(initialSubInfos);
mUnderlyingNetworkTracker = mUnderlyingNetworkTracker =
new UnderlyingNetworkTracker( new UnderlyingNetworkTracker(
mVcnContext, mVcnContext,
SUB_GROUP, SUB_GROUP,
mSubscriptionSnapshot,
Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET), Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET),
mNetworkTrackerCb); mNetworkTrackerCb);
} }
@@ -154,23 +152,45 @@ public class UnderlyingNetworkTrackerTest {
eq(getWifiRequest()), eq(getWifiRequest()),
any(), any(),
any(NetworkBringupCallback.class)); any(NetworkBringupCallback.class));
verify(mConnectivityManager) verifyBackgroundCellRequests(mSubscriptionSnapshot, SUB_GROUP, INITIAL_SUB_IDS);
.requestBackgroundNetwork(
eq(getCellRequestForSubId(INITIAL_SUB_ID_1)),
any(),
any(NetworkBringupCallback.class));
verify(mConnectivityManager)
.requestBackgroundNetwork(
eq(getCellRequestForSubId(INITIAL_SUB_ID_2)),
any(),
any(NetworkBringupCallback.class));
verify(mConnectivityManager) verify(mConnectivityManager)
.requestBackgroundNetwork( .requestBackgroundNetwork(
eq(getRouteSelectionRequest()), eq(getRouteSelectionRequest()),
any(), any(),
any(RouteSelectionCallback.class)); any(RouteSelectionCallback.class));
}
verify(mSubscriptionManager).getSubscriptionsInGroup(eq(SUB_GROUP)); private void verifyBackgroundCellRequests(
TelephonySubscriptionSnapshot snapshot,
ParcelUuid subGroup,
Set<Integer> expectedSubIds) {
verify(snapshot).getAllSubIdsInGroup(eq(subGroup));
for (final int subId : expectedSubIds) {
verify(mConnectivityManager)
.requestBackgroundNetwork(
eq(getCellRequestForSubId(subId)),
any(),
any(NetworkBringupCallback.class));
}
}
@Test
public void testUpdateSubscriptionSnapshot() {
// Verify initial cell background requests filed
verifyBackgroundCellRequests(mSubscriptionSnapshot, SUB_GROUP, INITIAL_SUB_IDS);
TelephonySubscriptionSnapshot subscriptionUpdate =
mock(TelephonySubscriptionSnapshot.class);
when(subscriptionUpdate.getAllSubIdsInGroup(eq(SUB_GROUP))).thenReturn(UPDATED_SUB_IDS);
mUnderlyingNetworkTracker.updateSubscriptionSnapshot(subscriptionUpdate);
// verify that initially-filed bringup requests are unregistered
verify(mConnectivityManager, times(INITIAL_SUB_IDS.size()))
.unregisterNetworkCallback(any(NetworkBringupCallback.class));
verifyBackgroundCellRequests(subscriptionUpdate, SUB_GROUP, UPDATED_SUB_IDS);
} }
private NetworkRequest getWifiRequest() { private NetworkRequest getWifiRequest() {

View File

@@ -44,7 +44,8 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
@Test @Test
public void testEnterWhileNotRunningTriggersQuit() throws Exception { public void testEnterWhileNotRunningTriggersQuit() throws Exception {
final VcnGatewayConnection vgc = final VcnGatewayConnection vgc =
new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps); new VcnGatewayConnection(
mVcnContext, TEST_SUB_GRP, TEST_SUBSCRIPTION_SNAPSHOT, mConfig, mDeps);
vgc.setIsRunning(false); vgc.setIsRunning(false);
vgc.transitionTo(vgc.mDisconnectedState); vgc.transitionTo(vgc.mDisconnectedState);

View File

@@ -24,10 +24,10 @@ import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
import android.content.Context;
import android.net.LinkProperties; import android.net.LinkProperties;
import android.net.Network; import android.net.Network;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
@@ -37,14 +37,15 @@ import android.net.vcn.VcnTransportInfo;
import android.net.wifi.WifiInfo; import android.net.wifi.WifiInfo;
import android.os.ParcelUuid; import android.os.ParcelUuid;
import android.os.Process; import android.os.Process;
import android.os.test.TestLooper;
import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionInfo;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4; import androidx.test.runner.AndroidJUnit4;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord; import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -56,8 +57,9 @@ import java.util.UUID;
/** Tests for TelephonySubscriptionTracker */ /** Tests for TelephonySubscriptionTracker */
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@SmallTest @SmallTest
public class VcnGatewayConnectionTest { public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
private static final int TEST_UID = Process.myUid(); private static final int TEST_UID = Process.myUid();
private static final ParcelUuid TEST_PARCEL_UUID = new ParcelUuid(UUID.randomUUID()); 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_SIM_SLOT_INDEX = 1;
private static final int TEST_SUBSCRIPTION_ID_1 = 2; private static final int TEST_SUBSCRIPTION_ID_1 = 2;
@@ -73,17 +75,12 @@ public class VcnGatewayConnectionTest {
TEST_SUBID_TO_GROUP_MAP = Collections.unmodifiableMap(subIdToGroupMap); TEST_SUBID_TO_GROUP_MAP = Collections.unmodifiableMap(subIdToGroupMap);
} }
@NonNull private final Context mContext; private WifiInfo mWifiInfo;
@NonNull private final TestLooper mTestLooper;
@NonNull private final VcnNetworkProvider mVcnNetworkProvider; @Before
@NonNull private final VcnGatewayConnection.Dependencies mDeps; public void setUp() throws Exception {
@NonNull private final WifiInfo mWifiInfo; super.setUp();
public VcnGatewayConnectionTest() {
mContext = mock(Context.class);
mTestLooper = new TestLooper();
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
mDeps = mock(VcnGatewayConnection.Dependencies.class);
mWifiInfo = mock(WifiInfo.class); mWifiInfo = mock(WifiInfo.class);
} }
@@ -132,4 +129,13 @@ public class VcnGatewayConnectionTest {
public void testBuildNetworkCapabilitiesUnderlyingCell() throws Exception { public void testBuildNetworkCapabilitiesUnderlyingCell() throws Exception {
verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR); verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR);
} }
@Test
public void testSubscriptionSnapshotUpdateNotifiesUnderlyingNetworkTracker() {
final TelephonySubscriptionSnapshot updatedSnapshot =
mock(TelephonySubscriptionSnapshot.class);
mGatewayConnection.updateSubscriptionSnapshot(updatedSnapshot);
verify(mUnderlyingNetworkTracker).updateSubscriptionSnapshot(eq(updatedSnapshot));
}
} }

View File

@@ -42,10 +42,12 @@ import android.os.ParcelUuid;
import android.os.test.TestLooper; import android.os.test.TestLooper;
import com.android.server.IpSecService; import com.android.server.IpSecService;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import org.junit.Before; import org.junit.Before;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import java.util.Collections;
import java.util.UUID; import java.util.UUID;
public class VcnGatewayConnectionTestBase { public class VcnGatewayConnectionTestBase {
@@ -54,6 +56,7 @@ public class VcnGatewayConnectionTestBase {
protected static final int TEST_IPSEC_SPI_RESOURCE_ID = 1; protected static final int TEST_IPSEC_SPI_RESOURCE_ID = 1;
protected static final int TEST_IPSEC_TRANSFORM_RESOURCE_ID = 2; protected static final int TEST_IPSEC_TRANSFORM_RESOURCE_ID = 2;
protected static final int TEST_IPSEC_TUNNEL_RESOURCE_ID = 3; protected static final int TEST_IPSEC_TUNNEL_RESOURCE_ID = 3;
protected static final int TEST_SUB_ID = 5;
protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE"; protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE";
protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 = protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
new UnderlyingNetworkRecord( new UnderlyingNetworkRecord(
@@ -68,6 +71,10 @@ public class VcnGatewayConnectionTestBase {
new LinkProperties(), new LinkProperties(),
false /* blocked */); false /* blocked */);
protected static final TelephonySubscriptionSnapshot TEST_SUBSCRIPTION_SNAPSHOT =
new TelephonySubscriptionSnapshot(
Collections.singletonMap(TEST_SUB_ID, TEST_SUB_GRP), Collections.EMPTY_MAP);
@NonNull protected final Context mContext; @NonNull protected final Context mContext;
@NonNull protected final TestLooper mTestLooper; @NonNull protected final TestLooper mTestLooper;
@NonNull protected final VcnNetworkProvider mVcnNetworkProvider; @NonNull protected final VcnNetworkProvider mVcnNetworkProvider;
@@ -99,7 +106,7 @@ public class VcnGatewayConnectionTestBase {
doReturn(mUnderlyingNetworkTracker) doReturn(mUnderlyingNetworkTracker)
.when(mDeps) .when(mDeps)
.newUnderlyingNetworkTracker(any(), any(), any(), any()); .newUnderlyingNetworkTracker(any(), any(), any(), any(), any());
} }
@Before @Before
@@ -114,7 +121,9 @@ public class VcnGatewayConnectionTestBase {
mMockIkeSession = mock(VcnIkeSession.class); mMockIkeSession = mock(VcnIkeSession.class);
doReturn(mMockIkeSession).when(mDeps).newIkeSession(any(), any(), any(), any(), any()); doReturn(mMockIkeSession).when(mDeps).newIkeSession(any(), any(), any(), any(), any());
mGatewayConnection = new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps); mGatewayConnection =
new VcnGatewayConnection(
mVcnContext, TEST_SUB_GRP, TEST_SUBSCRIPTION_SNAPSHOT, mConfig, mDeps);
} }
protected IpSecTransform makeDummyIpSecTransform() throws Exception { protected IpSecTransform makeDummyIpSecTransform() throws Exception {

View File

@@ -0,0 +1,129 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.vcn;
import static org.junit.Assert.assertFalse;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.net.NetworkRequest;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import java.util.Set;
import java.util.UUID;
public class VcnTest {
private static final String PKG_NAME = VcnTest.class.getPackage().getName();
private static final ParcelUuid TEST_SUB_GROUP = new ParcelUuid(new UUID(0, 0));
private static final int NETWORK_SCORE = 0;
private static final int PROVIDER_ID = 5;
private Context mContext;
private VcnContext mVcnContext;
private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
private VcnNetworkProvider mVcnNetworkProvider;
private Vcn.Dependencies mDeps;
private TestLooper mTestLooper;
private VcnConfig mConfig;
private Vcn mVcn;
@Before
public void setUp() {
mContext = mock(Context.class);
mVcnContext = mock(VcnContext.class);
mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class);
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
mDeps = mock(Vcn.Dependencies.class);
mTestLooper = new TestLooper();
doReturn(PKG_NAME).when(mContext).getOpPackageName();
doReturn(mContext).when(mVcnContext).getContext();
doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
// Setup VcnGatewayConnection instance generation
doAnswer((invocation) -> {
// Mock-within a doAnswer is safe, because it doesn't actually run nested.
return mock(VcnGatewayConnection.class);
}).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any());
mConfig =
new VcnConfig.Builder(mContext)
.addGatewayConnectionConfig(
VcnGatewayConnectionConfigTest.buildTestConfig())
.build();
mVcn = new Vcn(mVcnContext, TEST_SUB_GROUP, mConfig, mSubscriptionSnapshot, mDeps);
}
private NetworkRequestListener verifyAndGetRequestListener() {
ArgumentCaptor<NetworkRequestListener> mNetworkRequestListenerCaptor =
ArgumentCaptor.forClass(NetworkRequestListener.class);
verify(mVcnNetworkProvider).registerListener(mNetworkRequestListenerCaptor.capture());
return mNetworkRequestListenerCaptor.getValue();
}
private NetworkRequest getNetworkRequestWithCapabilities(int[] networkCapabilities) {
final NetworkRequest.Builder builder = new NetworkRequest.Builder();
for (final int netCapability : networkCapabilities) {
builder.addCapability(netCapability);
}
return builder.build();
}
@Test
public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
final NetworkRequestListener requestListener = verifyAndGetRequestListener();
requestListener.onNetworkRequested(
getNetworkRequestWithCapabilities(VcnGatewayConnectionConfigTest.EXPOSED_CAPS),
NETWORK_SCORE,
PROVIDER_ID);
mTestLooper.dispatchAll();
final Set<VcnGatewayConnection> gatewayConnections = mVcn.getVcnGatewayConnections();
assertFalse(gatewayConnections.isEmpty());
final TelephonySubscriptionSnapshot updatedSnapshot =
mock(TelephonySubscriptionSnapshot.class);
mVcn.updateSubscriptionSnapshot(updatedSnapshot);
mTestLooper.dispatchAll();
for (final VcnGatewayConnection gateway : gatewayConnections) {
verify(gateway).updateSubscriptionSnapshot(eq(updatedSnapshot));
}
}
}