Merge changes Ib0690243,I0cabce70,I8f13a0d8,I81efeaa4,I488f6208

* changes:
  Switch to using NetworkScore instead of legacy integer
  Remove use of network scores, and provider IDs in VcnNetworkProvider
  Improve dump detail
  Disable INTERNET/DUN when mobile data toggled off
  Remove hidden connectivity method access in FrameworksVcnTests
This commit is contained in:
Benedict Wong
2021-04-20 20:37:15 +00:00
committed by Gerrit Code Review
13 changed files with 460 additions and 150 deletions

View File

@@ -936,13 +936,31 @@ public class VcnManagementService extends IVcnManagementService.Stub {
pw.println("VcnManagementService dump:"); pw.println("VcnManagementService dump:");
pw.increaseIndent(); pw.increaseIndent();
pw.println("mNetworkProvider:");
pw.increaseIndent();
mNetworkProvider.dump(pw); mNetworkProvider.dump(pw);
pw.decreaseIndent();
pw.println();
pw.println("mTrackingNetworkCallback:");
pw.increaseIndent();
mTrackingNetworkCallback.dump(pw);
pw.decreaseIndent();
pw.println();
synchronized (mLock) { synchronized (mLock) {
pw.println("mLastSnapshot:");
pw.increaseIndent();
mLastSnapshot.dump(pw);
pw.decreaseIndent();
pw.println();
pw.println("mVcns:"); pw.println("mVcns:");
pw.increaseIndent();
for (Vcn vcn : mVcns.values()) { for (Vcn vcn : mVcns.values()) {
vcn.dump(pw); vcn.dump(pw);
} }
pw.decreaseIndent();
pw.println(); pw.println();
} }
@@ -1004,6 +1022,24 @@ public class VcnManagementService extends IVcnManagementService.Stub {
return false; return false;
} }
/** Dumps the state of this snapshot for logging and debugging purposes. */
public void dump(IndentingPrintWriter pw) {
pw.println("TrackingNetworkCallback:");
pw.increaseIndent();
pw.println("mCaps:");
pw.increaseIndent();
synchronized (mCaps) {
for (Entry<Network, NetworkCapabilities> entry : mCaps.entrySet()) {
pw.println(entry.getKey() + ": " + entry.getValue());
}
}
pw.decreaseIndent();
pw.println();
pw.decreaseIndent();
}
} }
/** VcnCallbackImpl for Vcn signals sent up to VcnManagementService. */ /** VcnCallbackImpl for Vcn signals sent up to VcnManagementService. */

View File

@@ -43,6 +43,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.IndentingPrintWriter;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@@ -320,6 +321,17 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver {
&& mPrivilegedPackages.equals(other.mPrivilegedPackages); && mPrivilegedPackages.equals(other.mPrivilegedPackages);
} }
/** Dumps the state of this snapshot for logging and debugging purposes. */
public void dump(IndentingPrintWriter pw) {
pw.println("TelephonySubscriptionSnapshot:");
pw.increaseIndent();
pw.println("mSubIdToGroupMap: " + mSubIdToGroupMap);
pw.println("mPrivilegedPackages: " + mPrivilegedPackages);
pw.decreaseIndent();
}
@Override @Override
public String toString() { public String toString() {
return "TelephonySubscriptionSnapshot{ " return "TelephonySubscriptionSnapshot{ "

View File

@@ -31,6 +31,7 @@ import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import java.util.ArrayList; import java.util.ArrayList;
@@ -396,6 +397,18 @@ public class UnderlyingNetworkTracker {
return Objects.hash(network, networkCapabilities, linkProperties, isBlocked); return Objects.hash(network, networkCapabilities, linkProperties, isBlocked);
} }
/** Dumps the state of this record for logging and debugging purposes. */
public void dump(IndentingPrintWriter pw) {
pw.println("UnderlyingNetworkRecord:");
pw.increaseIndent();
pw.println("mNetwork: " + network);
pw.println("mNetworkCapabilities: " + networkCapabilities);
pw.println("mLinkProperties: " + linkProperties);
pw.decreaseIndent();
}
/** Builder to incrementally construct an UnderlyingNetworkRecord. */ /** Builder to incrementally construct an UnderlyingNetworkRecord. */
private static class Builder { private static class Builder {
@NonNull private final Network mNetwork; @NonNull private final Network mNetwork;

View File

@@ -16,6 +16,8 @@
package com.android.server.vcn; package com.android.server.vcn;
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
@@ -26,14 +28,21 @@ import static com.android.server.VcnManagementService.VDBG;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.net.NetworkRequest; import android.net.NetworkRequest;
import android.net.NetworkScore;
import android.net.Uri;
import android.net.vcn.VcnConfig; import android.net.vcn.VcnConfig;
import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnManager.VcnErrorCode; import android.net.vcn.VcnManager.VcnErrorCode;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.os.ParcelUuid; import android.os.ParcelUuid;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.ArraySet;
import android.util.Slog; import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
@@ -42,9 +51,11 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.server.VcnManagementService.VcnCallback; import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Objects; import java.util.Objects;
@@ -61,6 +72,11 @@ import java.util.Set;
public class Vcn extends Handler { public class Vcn extends Handler {
private static final String TAG = Vcn.class.getSimpleName(); private static final String TAG = Vcn.class.getSimpleName();
private static final int VCN_LEGACY_SCORE_INT = 52;
private static final List<Integer> CAPS_REQUIRING_MOBILE_DATA =
Arrays.asList(NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN);
private static final int MSG_EVENT_BASE = 0; private static final int MSG_EVENT_BASE = 0;
private static final int MSG_CMD_BASE = 100; private static final int MSG_CMD_BASE = 100;
@@ -110,6 +126,15 @@ public class Vcn extends Handler {
*/ */
private static final int MSG_EVENT_SAFE_MODE_STATE_CHANGED = MSG_EVENT_BASE + 4; private static final int MSG_EVENT_SAFE_MODE_STATE_CHANGED = MSG_EVENT_BASE + 4;
/**
* Triggers reevaluation of mobile data enabled conditions.
*
* <p>Upon this notification, the VCN will check if any of the underlying subIds have mobile
* data enabled. If not, the VCN will restart any GatewayConnections providing INTERNET or DUN
* with the current mobile data toggle status.
*/
private static final int MSG_EVENT_MOBILE_DATA_TOGGLED = MSG_EVENT_BASE + 5;
/** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */ /** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE; private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
@@ -118,6 +143,8 @@ public class Vcn extends Handler {
@NonNull private final Dependencies mDeps; @NonNull private final Dependencies mDeps;
@NonNull private final VcnNetworkRequestListener mRequestListener; @NonNull private final VcnNetworkRequestListener mRequestListener;
@NonNull private final VcnCallback mVcnCallback; @NonNull private final VcnCallback mVcnCallback;
@NonNull private final VcnContentResolver mContentResolver;
@NonNull private final ContentObserver mMobileDataSettingsObserver;
/** /**
* Map containing all VcnGatewayConnections and their VcnGatewayConnectionConfigs. * Map containing all VcnGatewayConnections and their VcnGatewayConnectionConfigs.
@@ -154,6 +181,8 @@ public class Vcn extends Handler {
// Accessed from different threads, but always under lock in VcnManagementService // Accessed from different threads, but always under lock in VcnManagementService
private volatile int mCurrentStatus = VCN_STATUS_CODE_ACTIVE; private volatile int mCurrentStatus = VCN_STATUS_CODE_ACTIVE;
private boolean mIsMobileDataEnabled = false;
public Vcn( public Vcn(
@NonNull VcnContext vcnContext, @NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup, @NonNull ParcelUuid subscriptionGroup,
@@ -177,10 +206,19 @@ public class Vcn extends Handler {
mVcnCallback = Objects.requireNonNull(vcnCallback, "Missing vcnCallback"); mVcnCallback = Objects.requireNonNull(vcnCallback, "Missing vcnCallback");
mDeps = Objects.requireNonNull(deps, "Missing deps"); mDeps = Objects.requireNonNull(deps, "Missing deps");
mRequestListener = new VcnNetworkRequestListener(); mRequestListener = new VcnNetworkRequestListener();
mContentResolver = mDeps.newVcnContentResolver(mVcnContext);
mMobileDataSettingsObserver = new VcnMobileDataContentObserver(this /* handler */);
final Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA);
mContentResolver.registerContentObserver(
uri, true /* notifyForDescendants */, mMobileDataSettingsObserver);
mConfig = Objects.requireNonNull(config, "Missing config"); mConfig = Objects.requireNonNull(config, "Missing config");
mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot"); mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
// Update mIsMobileDataEnabled before starting handling of NetworkRequests.
mIsMobileDataEnabled = getMobileDataStatus();
// Register to receive cached and future NetworkRequests // Register to receive cached and future NetworkRequests
mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener); mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener);
} }
@@ -230,10 +268,10 @@ public class Vcn extends Handler {
private class VcnNetworkRequestListener implements VcnNetworkProvider.NetworkRequestListener { private class VcnNetworkRequestListener implements VcnNetworkProvider.NetworkRequestListener {
@Override @Override
public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) { public void onNetworkRequested(@NonNull NetworkRequest request) {
Objects.requireNonNull(request, "Missing request"); Objects.requireNonNull(request, "Missing request");
sendMessage(obtainMessage(MSG_EVENT_NETWORK_REQUESTED, score, providerId, request)); sendMessage(obtainMessage(MSG_EVENT_NETWORK_REQUESTED, request));
} }
} }
@@ -249,7 +287,7 @@ public class Vcn extends Handler {
handleConfigUpdated((VcnConfig) msg.obj); handleConfigUpdated((VcnConfig) msg.obj);
break; break;
case MSG_EVENT_NETWORK_REQUESTED: case MSG_EVENT_NETWORK_REQUESTED:
handleNetworkRequested((NetworkRequest) msg.obj, msg.arg1, msg.arg2); handleNetworkRequested((NetworkRequest) msg.obj);
break; break;
case MSG_EVENT_SUBSCRIPTIONS_CHANGED: case MSG_EVENT_SUBSCRIPTIONS_CHANGED:
handleSubscriptionsChanged((TelephonySubscriptionSnapshot) msg.obj); handleSubscriptionsChanged((TelephonySubscriptionSnapshot) msg.obj);
@@ -260,6 +298,9 @@ public class Vcn extends Handler {
case MSG_EVENT_SAFE_MODE_STATE_CHANGED: case MSG_EVENT_SAFE_MODE_STATE_CHANGED:
handleSafeModeStatusChanged(); handleSafeModeStatusChanged();
break; break;
case MSG_EVENT_MOBILE_DATA_TOGGLED:
handleMobileDataToggled();
break;
case MSG_CMD_TEARDOWN: case MSG_CMD_TEARDOWN:
handleTeardown(); handleTeardown();
break; break;
@@ -327,25 +368,9 @@ public class Vcn extends Handler {
} }
} }
private void handleNetworkRequested( private void handleNetworkRequested(@NonNull NetworkRequest request) {
@NonNull NetworkRequest request, int score, int providerId) {
Slog.v(getLogTag(), "Received request " + request); Slog.v(getLogTag(), "Received request " + request);
if (score > getNetworkScore()) {
if (VDBG) {
Slog.v(
getLogTag(),
"Request already satisfied by higher-scoring ("
+ score
+ ") network from "
+ "provider "
+ providerId
+ ": "
+ request);
}
return;
}
// If preexisting VcnGatewayConnection(s) satisfy request, return // If preexisting VcnGatewayConnection(s) satisfy request, return
for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) { for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) {
if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) { if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
@@ -366,18 +391,37 @@ public class Vcn extends Handler {
if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) { if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
Slog.v(getLogTag(), "Bringing up new VcnGatewayConnection for request " + request); Slog.v(getLogTag(), "Bringing up new VcnGatewayConnection for request " + request);
if (getExposedCapabilitiesForMobileDataState(gatewayConnectionConfig).isEmpty()) {
// Skip; this network does not provide any services if mobile data is disabled.
continue;
}
final VcnGatewayConnection vcnGatewayConnection = final VcnGatewayConnection vcnGatewayConnection =
mDeps.newVcnGatewayConnection( mDeps.newVcnGatewayConnection(
mVcnContext, mVcnContext,
mSubscriptionGroup, mSubscriptionGroup,
mLastSnapshot, mLastSnapshot,
gatewayConnectionConfig, gatewayConnectionConfig,
new VcnGatewayStatusCallbackImpl(gatewayConnectionConfig)); new VcnGatewayStatusCallbackImpl(gatewayConnectionConfig),
mIsMobileDataEnabled);
mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection); mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection);
} }
} }
} }
private Set<Integer> getExposedCapabilitiesForMobileDataState(
VcnGatewayConnectionConfig gatewayConnectionConfig) {
if (mIsMobileDataEnabled) {
return gatewayConnectionConfig.getAllExposedCapabilities();
}
final Set<Integer> exposedCapsWithoutMobileData =
new ArraySet<>(gatewayConnectionConfig.getAllExposedCapabilities());
exposedCapsWithoutMobileData.removeAll(CAPS_REQUIRING_MOBILE_DATA);
return exposedCapsWithoutMobileData;
}
private void handleGatewayConnectionQuit(VcnGatewayConnectionConfig config) { private void handleGatewayConnectionQuit(VcnGatewayConnectionConfig config) {
Slog.v(getLogTag(), "VcnGatewayConnection quit: " + config); Slog.v(getLogTag(), "VcnGatewayConnection quit: " + config);
mVcnGatewayConnections.remove(config); mVcnGatewayConnections.remove(config);
@@ -396,12 +440,55 @@ public class Vcn extends Handler {
} }
} }
private void handleMobileDataToggled() {
final boolean oldMobileDataEnabledStatus = mIsMobileDataEnabled;
mIsMobileDataEnabled = getMobileDataStatus();
if (oldMobileDataEnabledStatus != mIsMobileDataEnabled) {
// Teardown any GatewayConnections that advertise INTERNET or DUN. If they provide other
// services, the VcnGatewayConnections will be restarted without advertising INTERNET or
// DUN.
for (Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry :
mVcnGatewayConnections.entrySet()) {
final VcnGatewayConnectionConfig gatewayConnectionConfig = entry.getKey();
final VcnGatewayConnection gatewayConnection = entry.getValue();
final Set<Integer> exposedCaps =
gatewayConnectionConfig.getAllExposedCapabilities();
if (exposedCaps.contains(NET_CAPABILITY_INTERNET)
|| exposedCaps.contains(NET_CAPABILITY_DUN)) {
if (gatewayConnection == null) {
Slog.wtf(
getLogTag(),
"Found gatewayConnectionConfig without GatewayConnection");
} else {
// TODO(b/184868850): Optimize by restarting NetworkAgents without teardown.
gatewayConnection.teardownAsynchronously();
}
}
}
}
}
private boolean getMobileDataStatus() {
final TelephonyManager genericTelMan =
mVcnContext.getContext().getSystemService(TelephonyManager.class);
for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
if (genericTelMan.createForSubscriptionId(subId).isDataEnabled()) {
return true;
}
}
return false;
}
private boolean isRequestSatisfiedByGatewayConnectionConfig( private boolean isRequestSatisfiedByGatewayConnectionConfig(
@NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) { @NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) {
final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(); final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
builder.addTransportType(TRANSPORT_CELLULAR); builder.addTransportType(TRANSPORT_CELLULAR);
builder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); builder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
for (int cap : config.getAllExposedCapabilities()) { for (int cap : getExposedCapabilitiesForMobileDataState(config)) {
builder.addCapability(cap); builder.addCapability(cap);
} }
@@ -432,12 +519,20 @@ public class Vcn extends Handler {
pw.decreaseIndent(); pw.decreaseIndent();
} }
@VisibleForTesting(visibility = Visibility.PRIVATE)
public boolean isMobileDataEnabled() {
return mIsMobileDataEnabled;
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
public void setMobileDataEnabled(boolean isMobileDataEnabled) {
mIsMobileDataEnabled = isMobileDataEnabled;
}
/** Retrieves the network score for a VCN Network */ /** Retrieves the network score for a VCN Network */
// Package visibility for use in VcnGatewayConnection // Package visibility for use in VcnGatewayConnection and VcnNetworkProvider
static int getNetworkScore() { static NetworkScore getNetworkScore() {
// TODO: STOPSHIP (b/173549607): Make this use new NetworkSelection, or some magic "max in return new NetworkScore.Builder().setLegacyInt(VCN_LEGACY_SCORE_INT).build();
// subGrp" value
return 52;
} }
/** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */ /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */
@@ -485,6 +580,17 @@ public class Vcn extends Handler {
} }
} }
private class VcnMobileDataContentObserver extends ContentObserver {
private VcnMobileDataContentObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
sendMessage(obtainMessage(MSG_EVENT_MOBILE_DATA_TOGGLED));
}
}
/** External dependencies used by Vcn, for injection in tests */ /** External dependencies used by Vcn, for injection in tests */
@VisibleForTesting(visibility = Visibility.PRIVATE) @VisibleForTesting(visibility = Visibility.PRIVATE)
public static class Dependencies { public static class Dependencies {
@@ -494,13 +600,36 @@ public class Vcn extends Handler {
ParcelUuid subscriptionGroup, ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot, TelephonySubscriptionSnapshot snapshot,
VcnGatewayConnectionConfig connectionConfig, VcnGatewayConnectionConfig connectionConfig,
VcnGatewayStatusCallback gatewayStatusCallback) { VcnGatewayStatusCallback gatewayStatusCallback,
boolean isMobileDataEnabled) {
return new VcnGatewayConnection( return new VcnGatewayConnection(
vcnContext, vcnContext,
subscriptionGroup, subscriptionGroup,
snapshot, snapshot,
connectionConfig, connectionConfig,
gatewayStatusCallback); gatewayStatusCallback,
isMobileDataEnabled);
}
/** Builds a new VcnContentResolver instance */
public VcnContentResolver newVcnContentResolver(VcnContext vcnContext) {
return new VcnContentResolver(vcnContext);
}
}
/** Proxy Implementation of NetworkAgent, used for testing. */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class VcnContentResolver {
private final ContentResolver mImpl;
public VcnContentResolver(VcnContext vcnContext) {
mImpl = vcnContext.getContext().getContentResolver();
}
/** Registers the content observer */
public void registerContentObserver(
@NonNull Uri uri, boolean notifyForDescendants, @NonNull ContentObserver observer) {
mImpl.registerContentObserver(uri, notifyForDescendants, observer);
} }
} }
} }

View File

@@ -16,6 +16,8 @@
package com.android.server.vcn; package com.android.server.vcn;
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
@@ -47,6 +49,7 @@ import android.net.NetworkAgent;
import android.net.NetworkAgentConfig; import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.net.NetworkProvider; import android.net.NetworkProvider;
import android.net.NetworkScore;
import android.net.RouteInfo; import android.net.RouteInfo;
import android.net.TelephonyNetworkSpecifier; import android.net.TelephonyNetworkSpecifier;
import android.net.Uri; import android.net.Uri;
@@ -517,6 +520,7 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback; @NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback;
@NonNull private final Dependencies mDeps; @NonNull private final Dependencies mDeps;
@NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback; @NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback;
private final boolean mIsMobileDataEnabled;
@NonNull private final IpSecManager mIpSecManager; @NonNull private final IpSecManager mIpSecManager;
@@ -626,13 +630,15 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull ParcelUuid subscriptionGroup, @NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot snapshot, @NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull VcnGatewayConnectionConfig connectionConfig, @NonNull VcnGatewayConnectionConfig connectionConfig,
@NonNull VcnGatewayStatusCallback gatewayStatusCallback) { @NonNull VcnGatewayStatusCallback gatewayStatusCallback,
boolean isMobileDataEnabled) {
this( this(
vcnContext, vcnContext,
subscriptionGroup, subscriptionGroup,
snapshot, snapshot,
connectionConfig, connectionConfig,
gatewayStatusCallback, gatewayStatusCallback,
isMobileDataEnabled,
new Dependencies()); new Dependencies());
} }
@@ -643,6 +649,7 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull TelephonySubscriptionSnapshot snapshot, @NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull VcnGatewayConnectionConfig connectionConfig, @NonNull VcnGatewayConnectionConfig connectionConfig,
@NonNull VcnGatewayStatusCallback gatewayStatusCallback, @NonNull VcnGatewayStatusCallback gatewayStatusCallback,
boolean isMobileDataEnabled,
@NonNull Dependencies deps) { @NonNull Dependencies deps) {
super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper()); super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
mVcnContext = vcnContext; mVcnContext = vcnContext;
@@ -650,6 +657,7 @@ public class VcnGatewayConnection extends StateMachine {
mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig"); mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig");
mGatewayStatusCallback = mGatewayStatusCallback =
Objects.requireNonNull(gatewayStatusCallback, "Missing gatewayStatusCallback"); Objects.requireNonNull(gatewayStatusCallback, "Missing gatewayStatusCallback");
mIsMobileDataEnabled = isMobileDataEnabled;
mDeps = Objects.requireNonNull(deps, "Missing deps"); mDeps = Objects.requireNonNull(deps, "Missing deps");
mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot"); mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
@@ -1502,7 +1510,7 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull VcnNetworkAgent agent, @NonNull VcnNetworkAgent agent,
@NonNull VcnChildSessionConfiguration childConfig) { @NonNull VcnChildSessionConfiguration childConfig) {
final NetworkCapabilities caps = final NetworkCapabilities caps =
buildNetworkCapabilities(mConnectionConfig, mUnderlying); buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled);
final LinkProperties lp = final LinkProperties lp =
buildConnectedLinkProperties( buildConnectedLinkProperties(
mConnectionConfig, tunnelIface, childConfig, mUnderlying); mConnectionConfig, tunnelIface, childConfig, mUnderlying);
@@ -1515,7 +1523,7 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull IpSecTunnelInterface tunnelIface, @NonNull IpSecTunnelInterface tunnelIface,
@NonNull VcnChildSessionConfiguration childConfig) { @NonNull VcnChildSessionConfiguration childConfig) {
final NetworkCapabilities caps = final NetworkCapabilities caps =
buildNetworkCapabilities(mConnectionConfig, mUnderlying); buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled);
final LinkProperties lp = final LinkProperties lp =
buildConnectedLinkProperties( buildConnectedLinkProperties(
mConnectionConfig, tunnelIface, childConfig, mUnderlying); mConnectionConfig, tunnelIface, childConfig, mUnderlying);
@@ -1843,7 +1851,8 @@ public class VcnGatewayConnection extends StateMachine {
@VisibleForTesting(visibility = Visibility.PRIVATE) @VisibleForTesting(visibility = Visibility.PRIVATE)
static NetworkCapabilities buildNetworkCapabilities( static NetworkCapabilities buildNetworkCapabilities(
@NonNull VcnGatewayConnectionConfig gatewayConnectionConfig, @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
@Nullable UnderlyingNetworkRecord underlying) { @Nullable UnderlyingNetworkRecord underlying,
boolean isMobileDataEnabled) {
final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(); final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
builder.addTransportType(TRANSPORT_CELLULAR); builder.addTransportType(TRANSPORT_CELLULAR);
@@ -1853,6 +1862,12 @@ public class VcnGatewayConnection extends StateMachine {
// Add exposed capabilities // Add exposed capabilities
for (int cap : gatewayConnectionConfig.getAllExposedCapabilities()) { for (int cap : gatewayConnectionConfig.getAllExposedCapabilities()) {
// Skip adding INTERNET or DUN if mobile data is disabled.
if (!isMobileDataEnabled
&& (cap == NET_CAPABILITY_INTERNET || cap == NET_CAPABILITY_DUN)) {
continue;
}
builder.addCapability(cap); builder.addCapability(cap);
} }
@@ -2040,6 +2055,12 @@ public class VcnGatewayConnection extends StateMachine {
"mNetworkAgent.getNetwork(): " "mNetworkAgent.getNetwork(): "
+ (mNetworkAgent == null ? null : mNetworkAgent.getNetwork())); + (mNetworkAgent == null ? null : mNetworkAgent.getNetwork()));
pw.println("mUnderlying:");
pw.increaseIndent();
mUnderlying.dump(pw);
pw.decreaseIndent();
pw.println();
pw.decreaseIndent(); pw.decreaseIndent();
} }
@@ -2183,7 +2204,7 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull String tag, @NonNull String tag,
@NonNull NetworkCapabilities caps, @NonNull NetworkCapabilities caps,
@NonNull LinkProperties lp, @NonNull LinkProperties lp,
@NonNull int score, @NonNull NetworkScore score,
@NonNull NetworkAgentConfig nac, @NonNull NetworkAgentConfig nac,
@NonNull NetworkProvider provider, @NonNull NetworkProvider provider,
@NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback, @NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback,
@@ -2324,7 +2345,7 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull String tag, @NonNull String tag,
@NonNull NetworkCapabilities caps, @NonNull NetworkCapabilities caps,
@NonNull LinkProperties lp, @NonNull LinkProperties lp,
@NonNull int score, @NonNull NetworkScore score,
@NonNull NetworkAgentConfig nac, @NonNull NetworkAgentConfig nac,
@NonNull NetworkProvider provider, @NonNull NetworkProvider provider,
@NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback, @NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback,

View File

@@ -23,7 +23,6 @@ import android.content.Context;
import android.net.NetworkProvider; import android.net.NetworkProvider;
import android.net.NetworkRequest; import android.net.NetworkRequest;
import android.os.Looper; import android.os.Looper;
import android.util.ArrayMap;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Slog; import android.util.Slog;
@@ -31,7 +30,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.IndentingPrintWriter;
import java.util.Objects;
import java.util.Set; import java.util.Set;
/** /**
@@ -48,11 +46,11 @@ public class VcnNetworkProvider extends NetworkProvider {
private final Set<NetworkRequestListener> mListeners = new ArraySet<>(); private final Set<NetworkRequestListener> mListeners = new ArraySet<>();
/** /**
* Cache of NetworkRequest(s), scores and network providers, keyed by NetworkRequest * Cache of NetworkRequest(s).
* *
* <p>NetworkRequests are immutable once created, and therefore can be used as stable keys. * <p>NetworkRequests are immutable once created, and therefore can be used as stable keys.
*/ */
private final ArrayMap<NetworkRequest, NetworkRequestEntry> mRequests = new ArrayMap<>(); private final Set<NetworkRequest> mRequests = new ArraySet<>();
public VcnNetworkProvider(Context context, Looper looper) { public VcnNetworkProvider(Context context, Looper looper) {
super(context, looper, VcnNetworkProvider.class.getSimpleName()); super(context, looper, VcnNetworkProvider.class.getSimpleName());
@@ -80,38 +78,28 @@ public class VcnNetworkProvider extends NetworkProvider {
/** Sends all cached NetworkRequest(s) to the specified listener. */ /** Sends all cached NetworkRequest(s) to the specified listener. */
@VisibleForTesting(visibility = Visibility.PACKAGE) @VisibleForTesting(visibility = Visibility.PACKAGE)
public void resendAllRequests(@NonNull NetworkRequestListener listener) { public void resendAllRequests(@NonNull NetworkRequestListener listener) {
for (NetworkRequestEntry entry : mRequests.values()) { for (NetworkRequest request : mRequests) {
notifyListenerForEvent(listener, entry); notifyListenerForEvent(listener, request);
} }
} }
private void notifyListenerForEvent( private void notifyListenerForEvent(
@NonNull NetworkRequestListener listener, @NonNull NetworkRequestEntry entry) { @NonNull NetworkRequestListener listener, @NonNull NetworkRequest request) {
listener.onNetworkRequested(entry.mRequest, entry.mScore, entry.mProviderId); listener.onNetworkRequested(request);
} }
@Override @Override
public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) { public void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId) {
if (VDBG) { if (VDBG) {
Slog.v( Slog.v(TAG, "Network requested: Request = " + request);
TAG,
"Network requested: Request = "
+ request
+ ", score = "
+ score
+ ", providerId = "
+ providerId);
} }
final NetworkRequestEntry entry = new NetworkRequestEntry(request, score, providerId); mRequests.add(request);
// NetworkRequests are immutable once created, and therefore can be used as stable keys.
mRequests.put(request, entry);
// TODO(b/176939047): Intelligently route requests to prioritized VcnInstances (based on // TODO(b/176939047): Intelligently route requests to prioritized VcnInstances (based on
// Default Data Sub, or similar) // Default Data Sub, or similar)
for (NetworkRequestListener listener : mListeners) { for (NetworkRequestListener listener : mListeners) {
notifyListenerForEvent(listener, entry); notifyListenerForEvent(listener, request);
} }
} }
@@ -120,37 +108,9 @@ public class VcnNetworkProvider extends NetworkProvider {
mRequests.remove(request); mRequests.remove(request);
} }
private static class NetworkRequestEntry {
public final NetworkRequest mRequest;
public final int mScore;
public final int mProviderId;
private NetworkRequestEntry(@NonNull NetworkRequest request, int score, int providerId) {
mRequest = Objects.requireNonNull(request, "Missing request");
mScore = score;
mProviderId = providerId;
}
/**
* Dumps the state of this NetworkRequestEntry for logging and debugging purposes.
*
* <p>PII and credentials MUST NEVER be dumped here.
*/
public void dump(IndentingPrintWriter pw) {
pw.println("NetworkRequestEntry:");
pw.increaseIndent();
pw.println("mRequest: " + mRequest);
pw.println("mScore: " + mScore);
pw.println("mProviderId: " + mProviderId);
pw.decreaseIndent();
}
}
// package-private // package-private
interface NetworkRequestListener { interface NetworkRequestListener {
void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId); void onNetworkRequested(@NonNull NetworkRequest request);
} }
/** /**
@@ -163,15 +123,19 @@ public class VcnNetworkProvider extends NetworkProvider {
pw.increaseIndent(); pw.increaseIndent();
pw.println("mListeners:"); pw.println("mListeners:");
pw.increaseIndent();
for (NetworkRequestListener listener : mListeners) { for (NetworkRequestListener listener : mListeners) {
pw.println(listener); pw.println(listener);
} }
pw.decreaseIndent();
pw.println(); pw.println();
pw.println("mRequests.values:"); pw.println("mRequests:");
for (NetworkRequestEntry entry : mRequests.values()) { pw.increaseIndent();
entry.dump(pw); for (NetworkRequest request : mRequests) {
pw.println(request);
} }
pw.decreaseIndent();
pw.println(); pw.println();
pw.decreaseIndent(); pw.decreaseIndent();

View File

@@ -39,6 +39,7 @@ import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any; 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.CALLS_REAL_METHODS;
import static org.mockito.Mockito.any; import static org.mockito.Mockito.any;
import static org.mockito.Mockito.argThat; import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
@@ -59,7 +60,6 @@ import android.net.ConnectivityManager;
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.Transport;
import android.net.NetworkRequest; import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier; import android.net.TelephonyNetworkSpecifier;
import android.net.vcn.IVcnStatusCallback; import android.net.vcn.IVcnStatusCallback;
@@ -657,7 +657,7 @@ public class VcnManagementServiceTest {
private void verifyMergedNetworkCapabilities( private void verifyMergedNetworkCapabilities(
NetworkCapabilities mergedCapabilities, NetworkCapabilities mergedCapabilities,
@Transport int transportType, int transportType,
boolean isVcnManaged, boolean isVcnManaged,
boolean isRestricted) { boolean isRestricted) {
assertTrue(mergedCapabilities.hasTransport(transportType)); assertTrue(mergedCapabilities.hasTransport(transportType));
@@ -779,7 +779,7 @@ public class VcnManagementServiceTest {
.registerNetworkCallback( .registerNetworkCallback(
eq(new NetworkRequest.Builder().clearCapabilities().build()), eq(new NetworkRequest.Builder().clearCapabilities().build()),
captor.capture()); captor.capture());
captor.getValue().onCapabilitiesChanged(new Network(0), caps); captor.getValue().onCapabilitiesChanged(mock(Network.class, CALLS_REAL_METHODS), caps);
} }
@Test @Test

View File

@@ -89,7 +89,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
mNetworkAgent = mock(VcnNetworkAgent.class); mNetworkAgent = mock(VcnNetworkAgent.class);
doReturn(mNetworkAgent) doReturn(mNetworkAgent)
.when(mDeps) .when(mDeps)
.newNetworkAgent(any(), any(), any(), any(), anyInt(), any(), any(), any(), any()); .newNetworkAgent(any(), any(), any(), any(), any(), any(), any(), any(), any());
mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1); mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
@@ -216,7 +216,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
any(), any(),
any(), any(),
any(), any(),
anyInt(), any(),
any(), any(),
any(), any(),
any(), any(),
@@ -244,7 +244,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
any(String.class), any(String.class),
ncCaptor.capture(), ncCaptor.capture(),
lpCaptor.capture(), lpCaptor.capture(),
anyInt(), any(),
argThat(nac -> nac.getLegacyType() == ConnectivityManager.TYPE_MOBILE), argThat(nac -> nac.getLegacyType() == ConnectivityManager.TYPE_MOBILE),
any(), any(),
any(), any(),
@@ -297,7 +297,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
startingInternalAddrs.equals(lp.getLinkAddresses()) startingInternalAddrs.equals(lp.getLinkAddresses())
&& Collections.singletonList(TEST_DNS_ADDR) && Collections.singletonList(TEST_DNS_ADDR)
.equals(lp.getDnsServers())), .equals(lp.getDnsServers())),
anyInt(), any(),
any(), any(),
any(), any(),
any(), any(),
@@ -356,7 +356,7 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection
any(), any(),
any(), any(),
any(), any(),
anyInt(), any(),
any(), any(),
any(), any(),
unwantedCallbackCaptor.capture(), unwantedCallbackCaptor.capture(),

View File

@@ -65,6 +65,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
TEST_SUBSCRIPTION_SNAPSHOT, TEST_SUBSCRIPTION_SNAPSHOT,
mConfig, mConfig,
mGatewayStatusCallback, mGatewayStatusCallback,
true /* isMobileDataEnabled */,
mDeps); mDeps);
vgc.setIsQuitting(true); vgc.setIsQuitting(true);

View File

@@ -16,6 +16,8 @@
package com.android.server.vcn; package com.android.server.vcn;
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
@@ -25,7 +27,10 @@ import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@@ -83,34 +88,46 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
super.setUp(); super.setUp();
mWifiInfo = mock(WifiInfo.class); mWifiInfo = mock(WifiInfo.class);
doReturn(mWifiInfo).when(mWifiInfo).makeCopy(anyLong());
} }
private void verifyBuildNetworkCapabilitiesCommon(int transportType) { private void verifyBuildNetworkCapabilitiesCommon(
final NetworkCapabilities underlyingCaps = new NetworkCapabilities(); int transportType, boolean isMobileDataEnabled) {
underlyingCaps.addTransportType(transportType); final NetworkCapabilities.Builder capBuilder = new NetworkCapabilities.Builder();
underlyingCaps.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); capBuilder.addTransportType(transportType);
underlyingCaps.addCapability(NET_CAPABILITY_NOT_METERED); capBuilder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
underlyingCaps.addCapability(NET_CAPABILITY_NOT_ROAMING); capBuilder.addCapability(NET_CAPABILITY_NOT_METERED);
capBuilder.addCapability(NET_CAPABILITY_NOT_ROAMING);
if (transportType == TRANSPORT_WIFI) { if (transportType == TRANSPORT_WIFI) {
underlyingCaps.setTransportInfo(mWifiInfo); capBuilder.setTransportInfo(mWifiInfo);
underlyingCaps.setOwnerUid(TEST_UID); capBuilder.setOwnerUid(TEST_UID);
} else if (transportType == TRANSPORT_CELLULAR) { } else if (transportType == TRANSPORT_CELLULAR) {
underlyingCaps.setAdministratorUids(new int[] {TEST_UID}); capBuilder.setNetworkSpecifier(
underlyingCaps.setNetworkSpecifier(
new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID_1)); new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID_1));
} }
capBuilder.setAdministratorUids(new int[] {TEST_UID});
UnderlyingNetworkRecord record = UnderlyingNetworkRecord record = new UnderlyingNetworkRecord(
new UnderlyingNetworkRecord( mock(Network.class, CALLS_REAL_METHODS),
new Network(0), underlyingCaps, new LinkProperties(), false); capBuilder.build(), new LinkProperties(), false);
final NetworkCapabilities vcnCaps = final NetworkCapabilities vcnCaps =
VcnGatewayConnection.buildNetworkCapabilities( VcnGatewayConnection.buildNetworkCapabilities(
VcnGatewayConnectionConfigTest.buildTestConfig(), record); VcnGatewayConnectionConfigTest.buildTestConfig(),
record,
isMobileDataEnabled);
assertTrue(vcnCaps.hasTransport(TRANSPORT_CELLULAR)); assertTrue(vcnCaps.hasTransport(TRANSPORT_CELLULAR));
assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_METERED)); assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_METERED));
assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING)); assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
for (int cap : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
if (cap == NET_CAPABILITY_INTERNET || cap == NET_CAPABILITY_DUN) {
assertEquals(isMobileDataEnabled, vcnCaps.hasCapability(cap));
} else {
assertTrue(vcnCaps.hasCapability(cap));
}
}
assertArrayEquals(new int[] {TEST_UID}, vcnCaps.getAdministratorUids()); assertArrayEquals(new int[] {TEST_UID}, vcnCaps.getAdministratorUids());
assertTrue(vcnCaps.getTransportInfo() instanceof VcnTransportInfo); assertTrue(vcnCaps.getTransportInfo() instanceof VcnTransportInfo);
@@ -124,12 +141,17 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
@Test @Test
public void testBuildNetworkCapabilitiesUnderlyingWifi() throws Exception { public void testBuildNetworkCapabilitiesUnderlyingWifi() throws Exception {
verifyBuildNetworkCapabilitiesCommon(TRANSPORT_WIFI); verifyBuildNetworkCapabilitiesCommon(TRANSPORT_WIFI, true /* isMobileDataEnabled */);
} }
@Test @Test
public void testBuildNetworkCapabilitiesUnderlyingCell() throws Exception { public void testBuildNetworkCapabilitiesUnderlyingCell() throws Exception {
verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR); verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR, true /* isMobileDataEnabled */);
}
@Test
public void testBuildNetworkCapabilitiesMobileDataDisabled() throws Exception {
verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR, false /* isMobileDataEnabled */);
} }
@Test @Test

View File

@@ -26,6 +26,7 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@@ -93,7 +94,7 @@ public class VcnGatewayConnectionTestBase {
protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 = protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
new UnderlyingNetworkRecord( new UnderlyingNetworkRecord(
new Network(0), mock(Network.class, CALLS_REAL_METHODS),
new NetworkCapabilities(), new NetworkCapabilities(),
new LinkProperties(), new LinkProperties(),
false /* blocked */); false /* blocked */);
@@ -104,7 +105,7 @@ public class VcnGatewayConnectionTestBase {
protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_2 = protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_2 =
new UnderlyingNetworkRecord( new UnderlyingNetworkRecord(
new Network(1), mock(Network.class, CALLS_REAL_METHODS),
new NetworkCapabilities(), new NetworkCapabilities(),
new LinkProperties(), new LinkProperties(),
false /* blocked */); false /* blocked */);
@@ -201,6 +202,7 @@ public class VcnGatewayConnectionTestBase {
TEST_SUBSCRIPTION_SNAPSHOT, TEST_SUBSCRIPTION_SNAPSHOT,
mConfig, mConfig,
mGatewayStatusCallback, mGatewayStatusCallback,
true /* isMobileDataEnabled */,
mDeps); mDeps);
} }

View File

@@ -22,8 +22,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.content.Context; import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest; import android.net.NetworkRequest;
import android.os.test.TestLooper; import android.os.test.TestLooper;
@@ -44,10 +42,7 @@ import java.util.List;
@SmallTest @SmallTest
public class VcnNetworkProviderTest { public class VcnNetworkProviderTest {
private static final int TEST_SCORE_UNSATISFIED = 0; private static final int TEST_SCORE_UNSATISFIED = 0;
private static final int TEST_SCORE_HIGH = 100;
private static final int TEST_PROVIDER_ID = 1; private static final int TEST_PROVIDER_ID = 1;
private static final int TEST_LEGACY_TYPE = ConnectivityManager.TYPE_MOBILE;
private static final NetworkRequest.Type TEST_REQUEST_TYPE = NetworkRequest.Type.REQUEST;
@NonNull private final Context mContext; @NonNull private final Context mContext;
@NonNull private final TestLooper mTestLooper; @NonNull private final TestLooper mTestLooper;
@@ -72,17 +67,7 @@ public class VcnNetworkProviderTest {
final NetworkRequest request = mock(NetworkRequest.class); final NetworkRequest request = mock(NetworkRequest.class);
mVcnNetworkProvider.onNetworkRequested(request, TEST_SCORE_UNSATISFIED, TEST_PROVIDER_ID); mVcnNetworkProvider.onNetworkRequested(request, TEST_SCORE_UNSATISFIED, TEST_PROVIDER_ID);
verify(mListener).onNetworkRequested(request, TEST_SCORE_UNSATISFIED, TEST_PROVIDER_ID); verify(mListener).onNetworkRequested(request);
}
@Test
public void testRequestsPassedToRegisteredListeners_satisfiedByHighScoringProvider()
throws Exception {
mVcnNetworkProvider.registerListener(mListener);
final NetworkRequest request = mock(NetworkRequest.class);
mVcnNetworkProvider.onNetworkRequested(request, TEST_SCORE_HIGH, TEST_PROVIDER_ID);
verify(mListener).onNetworkRequested(request, TEST_SCORE_HIGH, TEST_PROVIDER_ID);
} }
@Test @Test
@@ -100,21 +85,22 @@ public class VcnNetworkProviderTest {
final List<NetworkRequest> requests = new ArrayList<>(); final List<NetworkRequest> requests = new ArrayList<>();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
// Build unique network requests; in this case, iterate down the capabilities as a way
// to unique-ify requests.
final NetworkRequest request = final NetworkRequest request =
new NetworkRequest( new NetworkRequest.Builder().clearCapabilities().addCapability(i).build();
new NetworkCapabilities(),
TEST_LEGACY_TYPE,
i /* requestId */,
TEST_REQUEST_TYPE);
requests.add(request); requests.add(request);
mVcnNetworkProvider.onNetworkRequested(request, i, i + 1); mVcnNetworkProvider.onNetworkRequested(request, i, i + 1);
} }
// Remove one, and verify that it is never sent to the listeners.
final NetworkRequest removed = requests.remove(0);
mVcnNetworkProvider.onNetworkRequestWithdrawn(removed);
mVcnNetworkProvider.registerListener(mListener); mVcnNetworkProvider.registerListener(mListener);
for (int i = 0; i < requests.size(); i++) { for (NetworkRequest request : requests) {
final NetworkRequest request = requests.get(i); verify(mListener).onNetworkRequested(request);
verify(mListener).onNetworkRequested(request, i, i + 1);
} }
verifyNoMoreInteractions(mListener); verifyNoMoreInteractions(mListener);
} }

View File

@@ -16,16 +16,24 @@
package com.android.server.vcn; package com.android.server.vcn;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA;
import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS; import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
import static com.android.server.vcn.Vcn.VcnContentResolver;
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.assertTrue;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doReturn;
@@ -35,12 +43,16 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import android.content.Context; import android.content.Context;
import android.database.ContentObserver;
import android.net.NetworkRequest; import android.net.NetworkRequest;
import android.net.Uri;
import android.net.vcn.VcnConfig; import android.net.vcn.VcnConfig;
import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest; import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid; import android.os.ParcelUuid;
import android.os.test.TestLooper; import android.os.test.TestLooper;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.ArraySet; import android.util.ArraySet;
import com.android.server.VcnManagementService.VcnCallback; import com.android.server.VcnManagementService.VcnCallback;
@@ -53,23 +65,31 @@ import org.junit.Test;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
public class VcnTest { public class VcnTest {
private static final String PKG_NAME = VcnTest.class.getPackage().getName(); private static final String PKG_NAME = VcnTest.class.getPackage().getName();
private static final ParcelUuid TEST_SUB_GROUP = new ParcelUuid(new UUID(0, 0)); private static final ParcelUuid TEST_SUB_GROUP = new ParcelUuid(new UUID(0, 0));
private static final int NETWORK_SCORE = 0; private static final boolean MOBILE_DATA_ENABLED = true;
private static final int PROVIDER_ID = 5; private static final Set<Integer> TEST_SUB_IDS_IN_GROUP =
new ArraySet<>(Arrays.asList(1, 2, 3));
private static final int[][] TEST_CAPS = private static final int[][] TEST_CAPS =
new int[][] { new int[][] {
new int[] {NET_CAPABILITY_MMS, NET_CAPABILITY_INTERNET}, new int[] {NET_CAPABILITY_IMS, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN},
new int[] {NET_CAPABILITY_DUN} new int[] {NET_CAPABILITY_CBS, NET_CAPABILITY_INTERNET},
new int[] {NET_CAPABILITY_FOTA, NET_CAPABILITY_DUN},
new int[] {NET_CAPABILITY_MMS}
}; };
private Context mContext; private Context mContext;
private VcnContext mVcnContext; private VcnContext mVcnContext;
private TelephonyManager mTelephonyManager;
private VcnContentResolver mContentResolver;
private TelephonySubscriptionSnapshot mSubscriptionSnapshot; private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
private VcnNetworkProvider mVcnNetworkProvider; private VcnNetworkProvider mVcnNetworkProvider;
private VcnCallback mVcnCallback; private VcnCallback mVcnCallback;
@@ -86,6 +106,9 @@ public class VcnTest {
public void setUp() { public void setUp() {
mContext = mock(Context.class); mContext = mock(Context.class);
mVcnContext = mock(VcnContext.class); mVcnContext = mock(VcnContext.class);
mTelephonyManager =
setupAndGetTelephonyManager(MOBILE_DATA_ENABLED /* isMobileDataEnabled */);
mContentResolver = mock(VcnContentResolver.class);
mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class); mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class);
mVcnNetworkProvider = mock(VcnNetworkProvider.class); mVcnNetworkProvider = mock(VcnNetworkProvider.class);
mVcnCallback = mock(VcnCallback.class); mVcnCallback = mock(VcnCallback.class);
@@ -97,12 +120,15 @@ public class VcnTest {
doReturn(mContext).when(mVcnContext).getContext(); doReturn(mContext).when(mVcnContext).getContext();
doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper(); doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider(); doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
doReturn(mContentResolver).when(mDeps).newVcnContentResolver(eq(mVcnContext));
// Setup VcnGatewayConnection instance generation // Setup VcnGatewayConnection instance generation
doAnswer((invocation) -> { doAnswer((invocation) -> {
// Mock-within a doAnswer is safe, because it doesn't actually run nested. // Mock-within a doAnswer is safe, because it doesn't actually run nested.
return mock(VcnGatewayConnection.class); return mock(VcnGatewayConnection.class);
}).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any(), any()); }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any(), any(), anyBoolean());
doReturn(TEST_SUB_IDS_IN_GROUP).when(mSubscriptionSnapshot).getAllSubIdsInGroup(any());
mGatewayStatusCallbackCaptor = ArgumentCaptor.forClass(VcnGatewayStatusCallback.class); mGatewayStatusCallbackCaptor = ArgumentCaptor.forClass(VcnGatewayStatusCallback.class);
@@ -123,6 +149,16 @@ public class VcnTest {
mDeps); mDeps);
} }
private TelephonyManager setupAndGetTelephonyManager(boolean isMobileDataEnabled) {
final TelephonyManager telephonyManager = mock(TelephonyManager.class);
VcnTestUtils.setupSystemService(
mContext, telephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class);
doReturn(telephonyManager).when(telephonyManager).createForSubscriptionId(anyInt());
doReturn(isMobileDataEnabled).when(telephonyManager).isDataEnabled();
return telephonyManager;
}
private NetworkRequestListener verifyAndGetRequestListener() { private NetworkRequestListener verifyAndGetRequestListener() {
ArgumentCaptor<NetworkRequestListener> mNetworkRequestListenerCaptor = ArgumentCaptor<NetworkRequestListener> mNetworkRequestListenerCaptor =
ArgumentCaptor.forClass(NetworkRequestListener.class); ArgumentCaptor.forClass(NetworkRequestListener.class);
@@ -139,7 +175,7 @@ public class VcnTest {
requestBuilder.addCapability(netCapability); requestBuilder.addCapability(netCapability);
} }
requestListener.onNetworkRequested(requestBuilder.build(), NETWORK_SCORE, PROVIDER_ID); requestListener.onNetworkRequested(requestBuilder.build());
mTestLooper.dispatchAll(); mTestLooper.dispatchAll();
} }
@@ -163,6 +199,39 @@ public class VcnTest {
} }
} }
@Test
public void testContentObserverRegistered() {
// Validate state from setUp()
final Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA);
verify(mContentResolver)
.registerContentObserver(eq(uri), eq(true), any(ContentObserver.class));
}
@Test
public void testMobileDataStateCheckedOnInitialization_enabled() {
// Validate state from setUp()
assertTrue(mVcn.isMobileDataEnabled());
verify(mTelephonyManager).isDataEnabled();
}
@Test
public void testMobileDataStateCheckedOnInitialization_disabled() {
// Build and setup new telephonyManager to ensure method call count is reset.
final TelephonyManager telephonyManager =
setupAndGetTelephonyManager(false /* isMobileDataEnabled */);
final Vcn vcn =
new Vcn(
mVcnContext,
TEST_SUB_GROUP,
mConfig,
mSubscriptionSnapshot,
mVcnCallback,
mDeps);
assertFalse(vcn.isMobileDataEnabled());
verify(mTelephonyManager).isDataEnabled();
}
@Test @Test
public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() { public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
verifyUpdateSubscriptionSnapshotNotifiesGatewayConnections(VCN_STATUS_CODE_ACTIVE); verifyUpdateSubscriptionSnapshotNotifiesGatewayConnections(VCN_STATUS_CODE_ACTIVE);
@@ -193,7 +262,8 @@ public class VcnTest {
eq(TEST_SUB_GROUP), eq(TEST_SUB_GROUP),
eq(mSubscriptionSnapshot), eq(mSubscriptionSnapshot),
any(), any(),
mGatewayStatusCallbackCaptor.capture()); mGatewayStatusCallbackCaptor.capture(),
eq(MOBILE_DATA_ENABLED));
return gatewayConnections; return gatewayConnections;
} }
@@ -256,20 +326,21 @@ public class VcnTest {
mTestLooper.dispatchAll(); mTestLooper.dispatchAll();
// Verify that the VCN requests the networkRequests be resent // Verify that the VCN requests the networkRequests be resent
assertEquals(1, mVcn.getVcnGatewayConnections().size()); assertEquals(gatewayConnections.size() - 1, mVcn.getVcnGatewayConnections().size());
verify(mVcnNetworkProvider).resendAllRequests(requestListener); verify(mVcnNetworkProvider).resendAllRequests(requestListener);
// Verify that the VcnGatewayConnection is restarted if a request exists for it // Verify that the VcnGatewayConnection is restarted if a request exists for it
triggerVcnRequestListeners(requestListener); triggerVcnRequestListeners(requestListener);
mTestLooper.dispatchAll(); mTestLooper.dispatchAll();
assertEquals(2, mVcn.getVcnGatewayConnections().size()); assertEquals(gatewayConnections.size(), mVcn.getVcnGatewayConnections().size());
verify(mDeps, times(gatewayConnections.size() + 1)) verify(mDeps, times(gatewayConnections.size() + 1))
.newVcnGatewayConnection( .newVcnGatewayConnection(
eq(mVcnContext), eq(mVcnContext),
eq(TEST_SUB_GROUP), eq(TEST_SUB_GROUP),
eq(mSubscriptionSnapshot), eq(mSubscriptionSnapshot),
any(), any(),
mGatewayStatusCallbackCaptor.capture()); mGatewayStatusCallbackCaptor.capture(),
anyBoolean());
} }
@Test @Test
@@ -286,7 +357,7 @@ public class VcnTest {
public void testUpdateConfigReevaluatesGatewayConnections() { public void testUpdateConfigReevaluatesGatewayConnections() {
final NetworkRequestListener requestListener = verifyAndGetRequestListener(); final NetworkRequestListener requestListener = verifyAndGetRequestListener();
startGatewaysAndGetGatewayConnections(requestListener); startGatewaysAndGetGatewayConnections(requestListener);
assertEquals(2, mVcn.getVcnGatewayConnectionConfigMap().size()); assertEquals(TEST_CAPS.length, mVcn.getVcnGatewayConnectionConfigMap().size());
// Create VcnConfig with only one VcnGatewayConnectionConfig so a gateway connection is torn // Create VcnConfig with only one VcnGatewayConnectionConfig so a gateway connection is torn
// down. Reuse existing VcnGatewayConnectionConfig so that the gateway connection name // down. Reuse existing VcnGatewayConnectionConfig so that the gateway connection name
@@ -309,4 +380,57 @@ public class VcnTest {
verify(removedGatewayConnection).teardownAsynchronously(); verify(removedGatewayConnection).teardownAsynchronously();
verify(mVcnNetworkProvider).resendAllRequests(requestListener); verify(mVcnNetworkProvider).resendAllRequests(requestListener);
} }
private void verifyMobileDataToggled(boolean startingToggleState, boolean endingToggleState) {
final ArgumentCaptor<ContentObserver> captor =
ArgumentCaptor.forClass(ContentObserver.class);
verify(mContentResolver).registerContentObserver(any(), anyBoolean(), captor.capture());
final ContentObserver contentObserver = captor.getValue();
// Start VcnGatewayConnections
mVcn.setMobileDataEnabled(startingToggleState);
triggerVcnRequestListeners(verifyAndGetRequestListener());
final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways =
mVcn.getVcnGatewayConnectionConfigMap();
// Trigger data toggle change.
doReturn(endingToggleState).when(mTelephonyManager).isDataEnabled();
contentObserver.onChange(false /* selfChange, ignored */);
mTestLooper.dispatchAll();
// Verify that data toggle changes restart ONLY INTERNET or DUN networks, and only if the
// toggle state changed.
for (Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry : gateways.entrySet()) {
final Set<Integer> exposedCaps = entry.getKey().getAllExposedCapabilities();
if (startingToggleState != endingToggleState
&& (exposedCaps.contains(NET_CAPABILITY_INTERNET)
|| exposedCaps.contains(NET_CAPABILITY_DUN))) {
verify(entry.getValue()).teardownAsynchronously();
} else {
verify(entry.getValue(), never()).teardownAsynchronously();
}
}
assertEquals(endingToggleState, mVcn.isMobileDataEnabled());
}
@Test
public void testMobileDataEnabled() {
verifyMobileDataToggled(false /* startingToggleState */, true /* endingToggleState */);
}
@Test
public void testMobileDataDisabled() {
verifyMobileDataToggled(true /* startingToggleState */, false /* endingToggleState */);
}
@Test
public void testMobileDataObserverFiredWithoutChanges_dataEnabled() {
verifyMobileDataToggled(false /* startingToggleState */, false /* endingToggleState */);
}
@Test
public void testMobileDataObserverFiredWithoutChanges_dataDisabled() {
verifyMobileDataToggled(true /* startingToggleState */, true /* endingToggleState */);
}
} }