Merge changes from topic "vcn-network-policy" am: e980914d42

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

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: If22d811bfa933cae37afdc15dea6f43275bd128b
This commit is contained in:
Cody Kesting
2021-02-02 21:48:34 +00:00
committed by Automerger Merge Worker
8 changed files with 867 additions and 28 deletions

View File

@@ -67,7 +67,6 @@ import java.util.concurrent.Executor;
public class VcnManager {
@NonNull private static final String TAG = VcnManager.class.getSimpleName();
/** @hide */
@VisibleForTesting
public static final Map<
VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>

View File

@@ -27,10 +27,12 @@ import android.content.Context;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.IVcnManagementService;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnUnderlyingNetworkPolicy;
import android.net.wifi.WifiInfo;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
@@ -291,6 +293,12 @@ public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull VcnConfig config) {
return new Vcn(vcnContext, subscriptionGroup, config);
}
/** Gets the subId indicated by the given {@link WifiInfo}. */
public int getSubIdForWifiInfo(@NonNull WifiInfo wifiInfo) {
// TODO(b/178501049): use the subId indicated by WifiInfo#getSubscriptionId
return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
}
}
/** Notifies the VcnManagementService that external dependencies can be set up. */
@@ -582,8 +590,36 @@ public class VcnManagementService extends IVcnManagementService.Stub {
"Must have permission NETWORK_FACTORY or be the SystemServer to get underlying"
+ " Network policies");
// TODO(b/175914059): implement policy generation once VcnManagementService is able to
// determine policies
// Defensive copy in case this call is in-process and the given NetworkCapabilities mutates
networkCapabilities = new NetworkCapabilities(networkCapabilities);
int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
&& networkCapabilities.getNetworkSpecifier() instanceof TelephonyNetworkSpecifier) {
TelephonyNetworkSpecifier telephonyNetworkSpecifier =
(TelephonyNetworkSpecifier) networkCapabilities.getNetworkSpecifier();
subId = telephonyNetworkSpecifier.getSubscriptionId();
} else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
&& networkCapabilities.getTransportInfo() instanceof WifiInfo) {
WifiInfo wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
subId = mDeps.getSubIdForWifiInfo(wifiInfo);
}
boolean isVcnManagedNetwork = false;
if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
synchronized (mLock) {
ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId);
// TODO(b/178140910): only mark the Network as VCN-managed if not in safe mode
if (mVcns.containsKey(subGroup)) {
isVcnManagedNetwork = true;
}
}
}
if (isVcnManagedNetwork) {
networkCapabilities.removeCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
}
return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities);
}

View File

@@ -18,16 +18,28 @@ package com.android.server.vcn;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkCapabilities.NetCapability;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
import android.os.Handler;
import android.os.ParcelUuid;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
* Tracks a set of Networks underpinning a VcnGatewayConnection.
@@ -38,53 +50,385 @@ import java.util.Objects;
*
* @hide
*/
public class UnderlyingNetworkTracker extends Handler {
public class UnderlyingNetworkTracker {
@NonNull private static final String TAG = UnderlyingNetworkTracker.class.getSimpleName();
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@NonNull private final UnderlyingNetworkTrackerCallback mCb;
@NonNull private final Dependencies mDeps;
@NonNull private final Handler mHandler;
@NonNull private final ConnectivityManager mConnectivityManager;
@NonNull private final SubscriptionManager mSubscriptionManager;
@NonNull private final SparseArray<NetworkCallback> mCellBringupCallbacks = new SparseArray<>();
@NonNull private final NetworkCallback mWifiBringupCallback = new NetworkBringupCallback();
@NonNull private final NetworkCallback mRouteSelectionCallback = new RouteSelectionCallback();
@NonNull private final Set<Integer> mSubIds = new ArraySet<>();
@NonNull private final Set<Integer> mRequiredUnderlyingNetworkCapabilities;
@Nullable private UnderlyingNetworkRecord mCurrentRecord;
@Nullable private UnderlyingNetworkRecord.Builder mRecordInProgress;
public UnderlyingNetworkTracker(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull Set<Integer> requiredUnderlyingNetworkCapabilities,
@NonNull UnderlyingNetworkTrackerCallback cb) {
this(vcnContext, subscriptionGroup, cb, new Dependencies());
this(
vcnContext,
subscriptionGroup,
requiredUnderlyingNetworkCapabilities,
cb,
new Dependencies());
}
private UnderlyingNetworkTracker(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull Set<Integer> requiredUnderlyingNetworkCapabilities,
@NonNull UnderlyingNetworkTrackerCallback cb,
@NonNull Dependencies deps) {
super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
mVcnContext = vcnContext;
mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext");
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
mRequiredUnderlyingNetworkCapabilities =
Objects.requireNonNull(
requiredUnderlyingNetworkCapabilities,
"Missing requiredUnderlyingNetworkCapabilities");
mCb = Objects.requireNonNull(cb, "Missing cb");
mDeps = Objects.requireNonNull(deps, "Missing deps");
mHandler = new Handler(mVcnContext.getLooper());
mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class);
mSubscriptionManager = mVcnContext.getContext().getSystemService(SubscriptionManager.class);
registerNetworkRequests();
}
private void registerNetworkRequests() {
// register bringup requests for underlying Networks
mConnectivityManager.requestBackgroundNetwork(
getWifiNetworkRequest(), mHandler, mWifiBringupCallback);
updateSubIdsAndCellularRequests();
// register Network-selection request used to decide selected underlying Network
mConnectivityManager.requestBackgroundNetwork(
getNetworkRequestBase().build(), mHandler, mRouteSelectionCallback);
}
private NetworkRequest getWifiNetworkRequest() {
return getNetworkRequestBase().addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
}
private NetworkRequest getCellNetworkRequestForSubId(int subId) {
return getNetworkRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
.build();
}
private NetworkRequest.Builder getNetworkRequestBase() {
NetworkRequest.Builder requestBase = new NetworkRequest.Builder();
for (@NetCapability int capability : mRequiredUnderlyingNetworkCapabilities) {
requestBase.addCapability(capability);
}
return requestBase
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
.addUnwantedCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
}
/**
* Update the current subIds and Cellular bringup requests for this UnderlyingNetworkTracker.
*/
private void updateSubIdsAndCellularRequests() {
mVcnContext.ensureRunningOnLooperThread();
Set<Integer> prevSubIds = new ArraySet<>(mSubIds);
mSubIds.clear();
// Ensure NetworkRequests filed for all current subIds in mSubscriptionGroup
// STOPSHIP: b/177364490 use TelephonySubscriptionSnapshot to avoid querying Telephony
List<SubscriptionInfo> subInfos =
mSubscriptionManager.getSubscriptionsInGroup(mSubscriptionGroup);
for (SubscriptionInfo subInfo : subInfos) {
final int subId = subInfo.getSubscriptionId();
mSubIds.add(subId);
if (!mCellBringupCallbacks.contains(subId)) {
final NetworkBringupCallback cb = new NetworkBringupCallback();
mCellBringupCallbacks.put(subId, cb);
mConnectivityManager.requestBackgroundNetwork(
getCellNetworkRequestForSubId(subId), mHandler, cb);
}
}
// unregister all NetworkCallbacks for outdated subIds
for (final int subId : prevSubIds) {
if (!mSubIds.contains(subId)) {
final NetworkCallback cb = mCellBringupCallbacks.removeReturnOld(subId);
mConnectivityManager.unregisterNetworkCallback(cb);
}
}
}
/** Tears down this Tracker, and releases all underlying network requests. */
public void teardown() {}
public void teardown() {
mVcnContext.ensureRunningOnLooperThread();
/** An record of a single underlying network, caching relevant fields. */
mConnectivityManager.unregisterNetworkCallback(mWifiBringupCallback);
mConnectivityManager.unregisterNetworkCallback(mRouteSelectionCallback);
for (final int subId : mSubIds) {
final NetworkCallback cb = mCellBringupCallbacks.removeReturnOld(subId);
mConnectivityManager.unregisterNetworkCallback(cb);
}
mSubIds.clear();
}
/** 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);
}
/** 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()) {
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);
}
}
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;
}
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;
}
mRecordInProgress.setNetworkCapabilities(networkCapabilities);
maybeNotifyCallback();
}
private void handleNetworkSuspended(@NonNull Network network, boolean isSuspended) {
mVcnContext.ensureRunningOnLooperThread();
if (!isSameNetwork(mRecordInProgress, network)) {
Slog.wtf(TAG, "Invalid update to isSuspended");
return;
}
final NetworkCapabilities newCaps =
new NetworkCapabilities(mRecordInProgress.getNetworkCapabilities());
if (isSuspended) {
newCaps.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
} else {
newCaps.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
}
handleCapabilitiesChanged(network, newCaps);
}
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();
}
/**
* NetworkBringupCallback is used to keep background, VCN-managed Networks from being reaped.
*
* <p>NetworkBringupCallback only exists to prevent matching (VCN-managed) Networks from being
* reaped, and no action is taken on any events firing.
*/
@VisibleForTesting
class NetworkBringupCallback extends NetworkCallback {}
/**
* RouteSelectionCallback is used to select the "best" underlying Network.
*
* <p>The "best" network is determined by ConnectivityService, which is treated as a source of
* truth.
*/
@VisibleForTesting
class RouteSelectionCallback extends NetworkCallback {
@Override
public void onAvailable(@NonNull Network network) {
handleNetworkAvailable(network);
}
@Override
public void onLost(@NonNull Network network) {
handleNetworkLost(network);
}
@Override
public void onCapabilitiesChanged(
@NonNull Network network, @NonNull NetworkCapabilities networkCapabilities) {
handleCapabilitiesChanged(network, networkCapabilities);
}
@Override
public void onNetworkSuspended(@NonNull Network network) {
handleNetworkSuspended(network, true /* isSuspended */);
}
@Override
public void onNetworkResumed(@NonNull Network network) {
handleNetworkSuspended(network, false /* isSuspended */);
}
@Override
public void onLinkPropertiesChanged(
@NonNull Network network, @NonNull LinkProperties linkProperties) {
handlePropertiesChanged(network, linkProperties);
}
@Override
public void onBlockedStatusChanged(@NonNull Network network, boolean isBlocked) {
handleNetworkBlocked(network, isBlocked);
}
}
/** A record of a single underlying network, caching relevant fields. */
public static class UnderlyingNetworkRecord {
@NonNull public final Network network;
@NonNull public final NetworkCapabilities networkCapabilities;
@NonNull public final LinkProperties linkProperties;
public final boolean blocked;
public final boolean isBlocked;
@VisibleForTesting(visibility = Visibility.PRIVATE)
UnderlyingNetworkRecord(
@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities,
@NonNull LinkProperties linkProperties,
boolean blocked) {
boolean isBlocked) {
this.network = network;
this.networkCapabilities = networkCapabilities;
this.linkProperties = linkProperties;
this.blocked = blocked;
this.isBlocked = isBlocked;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof UnderlyingNetworkRecord)) return false;
final UnderlyingNetworkRecord that = (UnderlyingNetworkRecord) o;
return network.equals(that.network)
&& networkCapabilities.equals(that.networkCapabilities)
&& linkProperties.equals(that.linkProperties)
&& isBlocked == that.isBlocked;
}
@Override
public int hashCode() {
return Objects.hash(network, networkCapabilities, linkProperties, isBlocked);
}
/** Builder to incrementally construct an UnderlyingNetworkRecord. */
private static class Builder {
@NonNull private final Network mNetwork;
@Nullable private NetworkCapabilities mNetworkCapabilities;
@Nullable private LinkProperties mLinkProperties;
boolean mIsBlocked;
boolean mWasIsBlockedSet;
private Builder(@NonNull Network network) {
mNetwork = network;
}
@NonNull
private Network getNetwork() {
return mNetwork;
}
private void setNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
mNetworkCapabilities = networkCapabilities;
}
@Nullable
private NetworkCapabilities getNetworkCapabilities() {
return mNetworkCapabilities;
}
private void setLinkProperties(@NonNull LinkProperties linkProperties) {
mLinkProperties = linkProperties;
}
private void setIsBlocked(boolean isBlocked) {
mIsBlocked = isBlocked;
mWasIsBlockedSet = true;
}
private boolean isValid() {
return mNetworkCapabilities != null && mLinkProperties != null && mWasIsBlockedSet;
}
private UnderlyingNetworkRecord build() {
return new UnderlyingNetworkRecord(
mNetwork, mNetworkCapabilities, mLinkProperties, mIsBlocked);
}
}
}
@@ -95,9 +439,10 @@ public class UnderlyingNetworkTracker extends Handler {
*
* <p>This callback does NOT signal a mobility event.
*
* @param underlying The details of the new underlying network
* @param underlyingNetworkRecord The details of the new underlying network
*/
void onSelectedUnderlyingNetworkChanged(@Nullable UnderlyingNetworkRecord underlying);
void onSelectedUnderlyingNetworkChanged(
@Nullable UnderlyingNetworkRecord underlyingNetworkRecord);
}
private static class Dependencies {}

View File

@@ -55,4 +55,15 @@ public class VcnContext {
public VcnNetworkProvider getVcnNetworkProvider() {
return mVcnNetworkProvider;
}
/**
* Verifies that the caller is running on the VcnContext Thread.
*
* @throwsIllegalStateException if the caller is not running on the VcnContext Thread.
*/
public void ensureRunningOnLooperThread() {
if (getLooper().getThread() != Thread.currentThread()) {
throw new IllegalStateException("Not running on VcnMgmtSvc thread");
}
}
}

View File

@@ -65,6 +65,7 @@ import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
@@ -476,7 +477,10 @@ public class VcnGatewayConnection extends StateMachine {
mUnderlyingNetworkTracker =
mDeps.newUnderlyingNetworkTracker(
mVcnContext, subscriptionGroup, mUnderlyingNetworkTrackerCallback);
mVcnContext,
subscriptionGroup,
mConnectionConfig.getAllUnderlyingCapabilities(),
mUnderlyingNetworkTrackerCallback);
mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class);
IpSecTunnelInterface iface;
@@ -1134,8 +1138,10 @@ public class VcnGatewayConnection extends StateMachine {
public UnderlyingNetworkTracker newUnderlyingNetworkTracker(
VcnContext vcnContext,
ParcelUuid subscriptionGroup,
Set<Integer> requiredUnderlyingNetworkCapabilities,
UnderlyingNetworkTrackerCallback callback) {
return new UnderlyingNetworkTracker(vcnContext, subscriptionGroup, callback);
return new UnderlyingNetworkTracker(
vcnContext, subscriptionGroup, requiredUnderlyingNetworkCapabilities, callback);
}
/** Builds a new IkeSession. */

View File

@@ -22,7 +22,6 @@ import static com.android.server.vcn.VcnTestUtils.setupSystemService;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -30,6 +29,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
@@ -39,16 +39,20 @@ import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AppOpsManager;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.net.NetworkCapabilities.Transport;
import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnConfigTest;
import android.net.vcn.VcnUnderlyingNetworkPolicy;
import android.net.wifi.WifiInfo;
import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
@@ -253,7 +257,14 @@ public class VcnManagementServiceTest {
verify(mConfigReadWriteHelper).readFromDisk();
}
private void triggerSubscriptionTrackerCallback(Set<ParcelUuid> activeSubscriptionGroups) {
private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot(
Set<ParcelUuid> activeSubscriptionGroups) {
return triggerSubscriptionTrackerCbAndGetSnapshot(
activeSubscriptionGroups, Collections.emptyMap());
}
private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot(
Set<ParcelUuid> activeSubscriptionGroups, Map<Integer, ParcelUuid> subIdToGroupMap) {
final TelephonySubscriptionSnapshot snapshot = mock(TelephonySubscriptionSnapshot.class);
doReturn(activeSubscriptionGroups).when(snapshot).getActiveSubscriptionGroups();
@@ -267,8 +278,14 @@ public class VcnManagementServiceTest {
argThat(val -> activeSubscriptionGroups.contains(val)),
eq(TEST_PACKAGE_NAME));
doAnswer(invocation -> {
return subIdToGroupMap.get(invocation.getArgument(0));
}).when(snapshot).getGroupForSubId(anyInt());
final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
cb.onNewSnapshot(snapshot);
return snapshot;
}
private TelephonySubscriptionTrackerCallback getTelephonySubscriptionTrackerCallback() {
@@ -287,7 +304,7 @@ public class VcnManagementServiceTest {
@Test
public void testTelephonyNetworkTrackerCallbackStartsInstances() throws Exception {
triggerSubscriptionTrackerCallback(Collections.singleton(TEST_UUID_1));
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
verify(mMockDeps).newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG));
}
@@ -296,7 +313,7 @@ public class VcnManagementServiceTest {
final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
triggerSubscriptionTrackerCallback(Collections.emptySet());
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet());
// Verify teardown after delay
mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
@@ -311,13 +328,13 @@ public class VcnManagementServiceTest {
final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
// Simulate SIM unloaded
triggerSubscriptionTrackerCallback(Collections.emptySet());
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet());
// Simulate new SIM loaded right during teardown delay.
mTestLooper.moveTimeForward(
VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2);
mTestLooper.dispatchAll();
triggerSubscriptionTrackerCallback(Collections.singleton(TEST_UUID_2));
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2));
// Verify that even after the full timeout duration, the VCN instance is not torn down
mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
@@ -331,7 +348,7 @@ public class VcnManagementServiceTest {
final Vcn oldInstance = startAndGetVcnInstance(TEST_UUID_2);
// Simulate SIM unloaded
triggerSubscriptionTrackerCallback(Collections.emptySet());
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet());
// Config cleared, SIM reloaded & config re-added right before teardown delay, staring new
// vcnInstance.
@@ -496,14 +513,73 @@ public class VcnManagementServiceTest {
mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
}
private void setUpVcnSubscription(int subId, ParcelUuid subGroup) {
mVcnMgmtSvc.setVcnConfig(subGroup, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
triggerSubscriptionTrackerCbAndGetSnapshot(
Collections.singleton(subGroup), Collections.singletonMap(subId, subGroup));
}
private void verifyMergedNetworkCapabilities(
NetworkCapabilities mergedCapabilities, @Transport int transportType) {
assertTrue(mergedCapabilities.hasTransport(transportType));
assertFalse(
mergedCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED));
}
@Test
public void testGetUnderlyingNetworkPolicy() throws Exception {
public void testGetUnderlyingNetworkPolicyTransportCell() throws Exception {
setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
NetworkCapabilities nc =
new NetworkCapabilities.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID))
.build();
VcnUnderlyingNetworkPolicy policy =
mVcnMgmtSvc.getUnderlyingNetworkPolicy(
new NetworkCapabilities(), new LinkProperties());
mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
assertFalse(policy.isTeardownRequested());
assertNotNull(policy.getMergedNetworkCapabilities());
verifyMergedNetworkCapabilities(
policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_CELLULAR);
}
@Test
public void testGetUnderlyingNetworkPolicyTransportWifi() throws Exception {
setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2);
WifiInfo wifiInfo = mock(WifiInfo.class);
when(wifiInfo.makeCopy(anyBoolean())).thenReturn(wifiInfo);
when(mMockDeps.getSubIdForWifiInfo(eq(wifiInfo))).thenReturn(TEST_SUBSCRIPTION_ID);
NetworkCapabilities nc =
new NetworkCapabilities.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setTransportInfo(wifiInfo)
.build();
VcnUnderlyingNetworkPolicy policy =
mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
assertFalse(policy.isTeardownRequested());
verifyMergedNetworkCapabilities(
policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_WIFI);
}
@Test
public void testGetUnderlyingNetworkPolicyNonVcnNetwork() throws Exception {
NetworkCapabilities nc =
new NetworkCapabilities.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID))
.build();
VcnUnderlyingNetworkPolicy policy =
mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
assertFalse(policy.isTeardownRequested());
assertEquals(nc, policy.getMergedNetworkCapabilities());
}
@Test(expected = SecurityException.class)

View File

@@ -0,0 +1,366 @@
/*
* 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 com.android.server.vcn.VcnTestUtils.setupSystemService;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import com.android.server.vcn.UnderlyingNetworkTracker.NetworkBringupCallback;
import com.android.server.vcn.UnderlyingNetworkTracker.RouteSelectionCallback;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
public class UnderlyingNetworkTrackerTest {
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_2 = 2;
private static final NetworkCapabilities INITIAL_NETWORK_CAPABILITIES =
new NetworkCapabilities.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
.build();
private static final NetworkCapabilities SUSPENDED_NETWORK_CAPABILITIES =
new NetworkCapabilities.Builder(INITIAL_NETWORK_CAPABILITIES)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
.build();
private static final NetworkCapabilities UPDATED_NETWORK_CAPABILITIES =
new NetworkCapabilities.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.build();
private static final LinkProperties INITIAL_LINK_PROPERTIES =
getLinkPropertiesWithName("initial_iface");
private static final LinkProperties UPDATED_LINK_PROPERTIES =
getLinkPropertiesWithName("updated_iface");
@Mock private Context mContext;
@Mock private VcnNetworkProvider mVcnNetworkProvider;
@Mock private ConnectivityManager mConnectivityManager;
@Mock private SubscriptionManager mSubscriptionManager;
@Mock private UnderlyingNetworkTrackerCallback mNetworkTrackerCb;
@Mock private Network mNetwork;
@Captor private ArgumentCaptor<RouteSelectionCallback> mRouteSelectionCallbackCaptor;
private TestLooper mTestLooper;
private VcnContext mVcnContext;
private UnderlyingNetworkTracker mUnderlyingNetworkTracker;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mTestLooper = new TestLooper();
mVcnContext = spy(new VcnContext(mContext, mTestLooper.getLooper(), mVcnNetworkProvider));
doNothing().when(mVcnContext).ensureRunningOnLooperThread();
setupSystemService(
mContext,
mConnectivityManager,
Context.CONNECTIVITY_SERVICE,
ConnectivityManager.class);
setupSystemService(
mContext,
mSubscriptionManager,
Context.TELEPHONY_SUBSCRIPTION_SERVICE,
SubscriptionManager.class);
List<SubscriptionInfo> initialSubInfos =
Arrays.asList(
getSubscriptionInfoForSubId(INITIAL_SUB_ID_1),
getSubscriptionInfoForSubId(INITIAL_SUB_ID_2));
when(mSubscriptionManager.getSubscriptionsInGroup(eq(SUB_GROUP)))
.thenReturn(initialSubInfos);
mUnderlyingNetworkTracker =
new UnderlyingNetworkTracker(
mVcnContext,
SUB_GROUP,
Collections.singleton(NetworkCapabilities.NET_CAPABILITY_INTERNET),
mNetworkTrackerCb);
}
private static LinkProperties getLinkPropertiesWithName(String iface) {
LinkProperties linkProperties = new LinkProperties();
linkProperties.setInterfaceName(iface);
return linkProperties;
}
private SubscriptionInfo getSubscriptionInfoForSubId(int subId) {
SubscriptionInfo subInfo = mock(SubscriptionInfo.class);
when(subInfo.getSubscriptionId()).thenReturn(subId);
return subInfo;
}
@Test
public void testNetworkCallbacksRegisteredOnStartup() {
// verify NetworkCallbacks registered when instantiated
verify(mConnectivityManager)
.requestBackgroundNetwork(
eq(getWifiRequest()),
any(),
any(NetworkBringupCallback.class));
verify(mConnectivityManager)
.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)
.requestBackgroundNetwork(
eq(getRouteSelectionRequest()),
any(),
any(RouteSelectionCallback.class));
verify(mSubscriptionManager).getSubscriptionsInGroup(eq(SUB_GROUP));
}
private NetworkRequest getWifiRequest() {
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build();
}
private NetworkRequest getCellRequestForSubId(int subId) {
return getExpectedRequestBase()
.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
.setNetworkSpecifier(new TelephonyNetworkSpecifier(subId))
.build();
}
private NetworkRequest getRouteSelectionRequest() {
return getExpectedRequestBase().build();
}
private NetworkRequest.Builder getExpectedRequestBase() {
return new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
.addUnwantedCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
}
@Test
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))
.unregisterNetworkCallback(any(NetworkBringupCallback.class));
verify(mConnectivityManager).unregisterNetworkCallback(any(RouteSelectionCallback.class));
}
@Test
public void testUnderlyingNetworkRecordEquals() {
UnderlyingNetworkRecord recordA =
new UnderlyingNetworkRecord(
mNetwork,
INITIAL_NETWORK_CAPABILITIES,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
UnderlyingNetworkRecord recordB =
new UnderlyingNetworkRecord(
mNetwork,
INITIAL_NETWORK_CAPABILITIES,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
UnderlyingNetworkRecord recordC =
new UnderlyingNetworkRecord(
mNetwork,
UPDATED_NETWORK_CAPABILITIES,
UPDATED_LINK_PROPERTIES,
false /* isBlocked */);
assertEquals(recordA, recordB);
assertNotEquals(recordA, recordC);
}
@Test
public void testRecordTrackerCallbackNotifiedForNetworkChange() {
verifyRegistrationOnAvailableAndGetCallback();
}
private RouteSelectionCallback verifyRegistrationOnAvailableAndGetCallback() {
return verifyRegistrationOnAvailableAndGetCallback(INITIAL_NETWORK_CAPABILITIES);
}
private RouteSelectionCallback verifyRegistrationOnAvailableAndGetCallback(
NetworkCapabilities networkCapabilities) {
verify(mConnectivityManager)
.requestBackgroundNetwork(
eq(getRouteSelectionRequest()),
any(),
mRouteSelectionCallbackCaptor.capture());
RouteSelectionCallback cb = mRouteSelectionCallbackCaptor.getValue();
cb.onAvailable(mNetwork);
cb.onCapabilitiesChanged(mNetwork, networkCapabilities);
cb.onLinkPropertiesChanged(mNetwork, INITIAL_LINK_PROPERTIES);
cb.onBlockedStatusChanged(mNetwork, false /* isFalse */);
UnderlyingNetworkRecord expectedRecord =
new UnderlyingNetworkRecord(
mNetwork,
networkCapabilities,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
return cb;
}
@Test
public void testRecordTrackerCallbackNotifiedForNetworkCapabilitiesChange() {
RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
cb.onCapabilitiesChanged(mNetwork, UPDATED_NETWORK_CAPABILITIES);
UnderlyingNetworkRecord expectedRecord =
new UnderlyingNetworkRecord(
mNetwork,
UPDATED_NETWORK_CAPABILITIES,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
}
@Test
public void testRecordTrackerCallbackNotifiedForLinkPropertiesChange() {
RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
cb.onLinkPropertiesChanged(mNetwork, UPDATED_LINK_PROPERTIES);
UnderlyingNetworkRecord expectedRecord =
new UnderlyingNetworkRecord(
mNetwork,
INITIAL_NETWORK_CAPABILITIES,
UPDATED_LINK_PROPERTIES,
false /* isBlocked */);
verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
}
@Test
public void testRecordTrackerCallbackNotifiedForNetworkSuspended() {
RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
cb.onNetworkSuspended(mNetwork);
UnderlyingNetworkRecord expectedRecord =
new UnderlyingNetworkRecord(
mNetwork,
SUSPENDED_NETWORK_CAPABILITIES,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
}
@Test
public void testRecordTrackerCallbackNotifiedForNetworkResumed() {
RouteSelectionCallback cb =
verifyRegistrationOnAvailableAndGetCallback(SUSPENDED_NETWORK_CAPABILITIES);
cb.onNetworkResumed(mNetwork);
UnderlyingNetworkRecord expectedRecord =
new UnderlyingNetworkRecord(
mNetwork,
INITIAL_NETWORK_CAPABILITIES,
INITIAL_LINK_PROPERTIES,
false /* isBlocked */);
verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
}
@Test
public void testRecordTrackerCallbackNotifiedForBlocked() {
RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
cb.onBlockedStatusChanged(mNetwork, true /* isBlocked */);
UnderlyingNetworkRecord expectedRecord =
new UnderlyingNetworkRecord(
mNetwork,
INITIAL_NETWORK_CAPABILITIES,
INITIAL_LINK_PROPERTIES,
true /* isBlocked */);
verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(eq(expectedRecord));
}
@Test
public void testRecordTrackerCallbackNotifiedForNetworkLoss() {
RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
cb.onLost(mNetwork);
verify(mNetworkTrackerCb).onSelectedUnderlyingNetworkChanged(null);
}
@Test
public void testRecordTrackerCallbackIgnoresDuplicateRecord() {
RouteSelectionCallback cb = verifyRegistrationOnAvailableAndGetCallback();
cb.onCapabilitiesChanged(mNetwork, INITIAL_NETWORK_CAPABILITIES);
// Verify no more calls to the UnderlyingNetworkTrackerCallback when the
// UnderlyingNetworkRecord does not actually change
verifyNoMoreInteractions(mNetworkTrackerCb);
}
}

View File

@@ -94,7 +94,7 @@ public class VcnGatewayConnectionTestBase {
doReturn(mUnderlyingNetworkTracker)
.when(mDeps)
.newUnderlyingNetworkTracker(any(), any(), any());
.newUnderlyingNetworkTracker(any(), any(), any(), any());
}
@Before