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:
@@ -67,7 +67,6 @@ import java.util.concurrent.Executor;
|
|||||||
public class VcnManager {
|
public class VcnManager {
|
||||||
@NonNull private static final String TAG = VcnManager.class.getSimpleName();
|
@NonNull private static final String TAG = VcnManager.class.getSimpleName();
|
||||||
|
|
||||||
/** @hide */
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static final Map<
|
public static final Map<
|
||||||
VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
|
VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
|
||||||
|
|||||||
@@ -27,10 +27,12 @@ import android.content.Context;
|
|||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkCapabilities;
|
||||||
|
import android.net.TelephonyNetworkSpecifier;
|
||||||
import android.net.vcn.IVcnManagementService;
|
import android.net.vcn.IVcnManagementService;
|
||||||
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
|
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
|
||||||
import android.net.vcn.VcnConfig;
|
import android.net.vcn.VcnConfig;
|
||||||
import android.net.vcn.VcnUnderlyingNetworkPolicy;
|
import android.net.vcn.VcnUnderlyingNetworkPolicy;
|
||||||
|
import android.net.wifi.WifiInfo;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
@@ -291,6 +293,12 @@ public class VcnManagementService extends IVcnManagementService.Stub {
|
|||||||
@NonNull VcnConfig config) {
|
@NonNull VcnConfig config) {
|
||||||
return new Vcn(vcnContext, subscriptionGroup, 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. */
|
/** 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"
|
"Must have permission NETWORK_FACTORY or be the SystemServer to get underlying"
|
||||||
+ " Network policies");
|
+ " Network policies");
|
||||||
|
|
||||||
// TODO(b/175914059): implement policy generation once VcnManagementService is able to
|
// Defensive copy in case this call is in-process and the given NetworkCapabilities mutates
|
||||||
// determine policies
|
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);
|
return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,16 +18,28 @@ package com.android.server.vcn;
|
|||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.ConnectivityManager.NetworkCallback;
|
||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkCapabilities;
|
||||||
|
import android.net.NetworkCapabilities.NetCapability;
|
||||||
|
import android.net.NetworkRequest;
|
||||||
|
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.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;
|
||||||
import com.android.internal.annotations.VisibleForTesting.Visibility;
|
import com.android.internal.annotations.VisibleForTesting.Visibility;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tracks a set of Networks underpinning a VcnGatewayConnection.
|
* Tracks a set of Networks underpinning a VcnGatewayConnection.
|
||||||
@@ -38,53 +50,385 @@ import java.util.Objects;
|
|||||||
*
|
*
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public class UnderlyingNetworkTracker extends Handler {
|
public class UnderlyingNetworkTracker {
|
||||||
@NonNull private static final String TAG = UnderlyingNetworkTracker.class.getSimpleName();
|
@NonNull private static final String TAG = UnderlyingNetworkTracker.class.getSimpleName();
|
||||||
|
|
||||||
@NonNull private final VcnContext mVcnContext;
|
@NonNull private final VcnContext mVcnContext;
|
||||||
@NonNull private final ParcelUuid mSubscriptionGroup;
|
@NonNull private final ParcelUuid mSubscriptionGroup;
|
||||||
@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 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(
|
public UnderlyingNetworkTracker(
|
||||||
@NonNull VcnContext vcnContext,
|
@NonNull VcnContext vcnContext,
|
||||||
@NonNull ParcelUuid subscriptionGroup,
|
@NonNull ParcelUuid subscriptionGroup,
|
||||||
|
@NonNull Set<Integer> requiredUnderlyingNetworkCapabilities,
|
||||||
@NonNull UnderlyingNetworkTrackerCallback cb) {
|
@NonNull UnderlyingNetworkTrackerCallback cb) {
|
||||||
this(vcnContext, subscriptionGroup, cb, new Dependencies());
|
this(
|
||||||
|
vcnContext,
|
||||||
|
subscriptionGroup,
|
||||||
|
requiredUnderlyingNetworkCapabilities,
|
||||||
|
cb,
|
||||||
|
new Dependencies());
|
||||||
}
|
}
|
||||||
|
|
||||||
private UnderlyingNetworkTracker(
|
private UnderlyingNetworkTracker(
|
||||||
@NonNull VcnContext vcnContext,
|
@NonNull VcnContext vcnContext,
|
||||||
@NonNull ParcelUuid subscriptionGroup,
|
@NonNull ParcelUuid subscriptionGroup,
|
||||||
|
@NonNull Set<Integer> requiredUnderlyingNetworkCapabilities,
|
||||||
@NonNull UnderlyingNetworkTrackerCallback cb,
|
@NonNull UnderlyingNetworkTrackerCallback cb,
|
||||||
@NonNull Dependencies deps) {
|
@NonNull Dependencies deps) {
|
||||||
super(Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
|
mVcnContext = Objects.requireNonNull(vcnContext, "Missing vcnContext");
|
||||||
mVcnContext = vcnContext;
|
|
||||||
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
|
mSubscriptionGroup = Objects.requireNonNull(subscriptionGroup, "Missing subscriptionGroup");
|
||||||
|
mRequiredUnderlyingNetworkCapabilities =
|
||||||
|
Objects.requireNonNull(
|
||||||
|
requiredUnderlyingNetworkCapabilities,
|
||||||
|
"Missing requiredUnderlyingNetworkCapabilities");
|
||||||
mCb = Objects.requireNonNull(cb, "Missing cb");
|
mCb = Objects.requireNonNull(cb, "Missing cb");
|
||||||
mDeps = Objects.requireNonNull(deps, "Missing deps");
|
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. */
|
/** 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 {
|
public static class UnderlyingNetworkRecord {
|
||||||
@NonNull public final Network network;
|
@NonNull public final Network network;
|
||||||
@NonNull public final NetworkCapabilities networkCapabilities;
|
@NonNull public final NetworkCapabilities networkCapabilities;
|
||||||
@NonNull public final LinkProperties linkProperties;
|
@NonNull public final LinkProperties linkProperties;
|
||||||
public final boolean blocked;
|
public final boolean isBlocked;
|
||||||
|
|
||||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||||
UnderlyingNetworkRecord(
|
UnderlyingNetworkRecord(
|
||||||
@NonNull Network network,
|
@NonNull Network network,
|
||||||
@NonNull NetworkCapabilities networkCapabilities,
|
@NonNull NetworkCapabilities networkCapabilities,
|
||||||
@NonNull LinkProperties linkProperties,
|
@NonNull LinkProperties linkProperties,
|
||||||
boolean blocked) {
|
boolean isBlocked) {
|
||||||
this.network = network;
|
this.network = network;
|
||||||
this.networkCapabilities = networkCapabilities;
|
this.networkCapabilities = networkCapabilities;
|
||||||
this.linkProperties = linkProperties;
|
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.
|
* <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 {}
|
private static class Dependencies {}
|
||||||
|
|||||||
@@ -55,4 +55,15 @@ public class VcnContext {
|
|||||||
public VcnNetworkProvider getVcnNetworkProvider() {
|
public VcnNetworkProvider getVcnNetworkProvider() {
|
||||||
return mVcnNetworkProvider;
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ import java.net.Inet4Address;
|
|||||||
import java.net.Inet6Address;
|
import java.net.Inet6Address;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -476,7 +477,10 @@ public class VcnGatewayConnection extends StateMachine {
|
|||||||
|
|
||||||
mUnderlyingNetworkTracker =
|
mUnderlyingNetworkTracker =
|
||||||
mDeps.newUnderlyingNetworkTracker(
|
mDeps.newUnderlyingNetworkTracker(
|
||||||
mVcnContext, subscriptionGroup, mUnderlyingNetworkTrackerCallback);
|
mVcnContext,
|
||||||
|
subscriptionGroup,
|
||||||
|
mConnectionConfig.getAllUnderlyingCapabilities(),
|
||||||
|
mUnderlyingNetworkTrackerCallback);
|
||||||
mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class);
|
mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class);
|
||||||
|
|
||||||
IpSecTunnelInterface iface;
|
IpSecTunnelInterface iface;
|
||||||
@@ -1134,8 +1138,10 @@ public class VcnGatewayConnection extends StateMachine {
|
|||||||
public UnderlyingNetworkTracker newUnderlyingNetworkTracker(
|
public UnderlyingNetworkTracker newUnderlyingNetworkTracker(
|
||||||
VcnContext vcnContext,
|
VcnContext vcnContext,
|
||||||
ParcelUuid subscriptionGroup,
|
ParcelUuid subscriptionGroup,
|
||||||
|
Set<Integer> requiredUnderlyingNetworkCapabilities,
|
||||||
UnderlyingNetworkTrackerCallback callback) {
|
UnderlyingNetworkTrackerCallback callback) {
|
||||||
return new UnderlyingNetworkTracker(vcnContext, subscriptionGroup, callback);
|
return new UnderlyingNetworkTracker(
|
||||||
|
vcnContext, subscriptionGroup, requiredUnderlyingNetworkCapabilities, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Builds a new IkeSession. */
|
/** Builds a new IkeSession. */
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import static com.android.server.vcn.VcnTestUtils.setupSystemService;
|
|||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
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.anyInt;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.any;
|
import static org.mockito.Mockito.any;
|
||||||
|
import static org.mockito.Mockito.anyBoolean;
|
||||||
import static org.mockito.Mockito.argThat;
|
import static org.mockito.Mockito.argThat;
|
||||||
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.doNothing;
|
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.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.app.AppOpsManager;
|
import android.app.AppOpsManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkCapabilities;
|
||||||
|
import android.net.NetworkCapabilities.Transport;
|
||||||
|
import android.net.TelephonyNetworkSpecifier;
|
||||||
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
|
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
|
||||||
import android.net.vcn.VcnConfig;
|
import android.net.vcn.VcnConfig;
|
||||||
import android.net.vcn.VcnConfigTest;
|
import android.net.vcn.VcnConfigTest;
|
||||||
import android.net.vcn.VcnUnderlyingNetworkPolicy;
|
import android.net.vcn.VcnUnderlyingNetworkPolicy;
|
||||||
|
import android.net.wifi.WifiInfo;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.ParcelUuid;
|
import android.os.ParcelUuid;
|
||||||
import android.os.PersistableBundle;
|
import android.os.PersistableBundle;
|
||||||
@@ -253,7 +257,14 @@ public class VcnManagementServiceTest {
|
|||||||
verify(mConfigReadWriteHelper).readFromDisk();
|
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);
|
final TelephonySubscriptionSnapshot snapshot = mock(TelephonySubscriptionSnapshot.class);
|
||||||
doReturn(activeSubscriptionGroups).when(snapshot).getActiveSubscriptionGroups();
|
doReturn(activeSubscriptionGroups).when(snapshot).getActiveSubscriptionGroups();
|
||||||
|
|
||||||
@@ -267,8 +278,14 @@ public class VcnManagementServiceTest {
|
|||||||
argThat(val -> activeSubscriptionGroups.contains(val)),
|
argThat(val -> activeSubscriptionGroups.contains(val)),
|
||||||
eq(TEST_PACKAGE_NAME));
|
eq(TEST_PACKAGE_NAME));
|
||||||
|
|
||||||
|
doAnswer(invocation -> {
|
||||||
|
return subIdToGroupMap.get(invocation.getArgument(0));
|
||||||
|
}).when(snapshot).getGroupForSubId(anyInt());
|
||||||
|
|
||||||
final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
|
final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
|
||||||
cb.onNewSnapshot(snapshot);
|
cb.onNewSnapshot(snapshot);
|
||||||
|
|
||||||
|
return snapshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TelephonySubscriptionTrackerCallback getTelephonySubscriptionTrackerCallback() {
|
private TelephonySubscriptionTrackerCallback getTelephonySubscriptionTrackerCallback() {
|
||||||
@@ -287,7 +304,7 @@ public class VcnManagementServiceTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTelephonyNetworkTrackerCallbackStartsInstances() throws Exception {
|
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));
|
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 TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
|
||||||
final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
|
final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
|
||||||
|
|
||||||
triggerSubscriptionTrackerCallback(Collections.emptySet());
|
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet());
|
||||||
|
|
||||||
// Verify teardown after delay
|
// Verify teardown after delay
|
||||||
mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
|
mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
|
||||||
@@ -311,13 +328,13 @@ public class VcnManagementServiceTest {
|
|||||||
final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
|
final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
|
||||||
|
|
||||||
// Simulate SIM unloaded
|
// Simulate SIM unloaded
|
||||||
triggerSubscriptionTrackerCallback(Collections.emptySet());
|
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet());
|
||||||
|
|
||||||
// Simulate new SIM loaded right during teardown delay.
|
// Simulate new SIM loaded right during teardown delay.
|
||||||
mTestLooper.moveTimeForward(
|
mTestLooper.moveTimeForward(
|
||||||
VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2);
|
VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2);
|
||||||
mTestLooper.dispatchAll();
|
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
|
// Verify that even after the full timeout duration, the VCN instance is not torn down
|
||||||
mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
|
mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
|
||||||
@@ -331,7 +348,7 @@ public class VcnManagementServiceTest {
|
|||||||
final Vcn oldInstance = startAndGetVcnInstance(TEST_UUID_2);
|
final Vcn oldInstance = startAndGetVcnInstance(TEST_UUID_2);
|
||||||
|
|
||||||
// Simulate SIM unloaded
|
// Simulate SIM unloaded
|
||||||
triggerSubscriptionTrackerCallback(Collections.emptySet());
|
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet());
|
||||||
|
|
||||||
// Config cleared, SIM reloaded & config re-added right before teardown delay, staring new
|
// Config cleared, SIM reloaded & config re-added right before teardown delay, staring new
|
||||||
// vcnInstance.
|
// vcnInstance.
|
||||||
@@ -496,14 +513,73 @@ public class VcnManagementServiceTest {
|
|||||||
mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
|
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
|
@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 =
|
VcnUnderlyingNetworkPolicy policy =
|
||||||
mVcnMgmtSvc.getUnderlyingNetworkPolicy(
|
mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties());
|
||||||
new NetworkCapabilities(), new LinkProperties());
|
|
||||||
|
|
||||||
assertFalse(policy.isTeardownRequested());
|
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)
|
@Test(expected = SecurityException.class)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -94,7 +94,7 @@ public class VcnGatewayConnectionTestBase {
|
|||||||
|
|
||||||
doReturn(mUnderlyingNetworkTracker)
|
doReturn(mUnderlyingNetworkTracker)
|
||||||
.when(mDeps)
|
.when(mDeps)
|
||||||
.newUnderlyingNetworkTracker(any(), any(), any());
|
.newUnderlyingNetworkTracker(any(), any(), any(), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
|||||||
Reference in New Issue
Block a user