Merge "Add a test for TcpKeepalivePacketDataParcelable.toString()."
diff --git a/core/java/android/net/CaptivePortal.java b/core/java/android/net/CaptivePortal.java
index c2586fa..269bbf2 100644
--- a/core/java/android/net/CaptivePortal.java
+++ b/core/java/android/net/CaptivePortal.java
@@ -15,7 +15,6 @@
*/
package android.net;
-import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
@@ -24,8 +23,6 @@
import android.os.Parcelable;
import android.os.RemoteException;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-
/**
* A class allowing apps handling the {@link ConnectivityManager#ACTION_CAPTIVE_PORTAL_SIGN_IN}
* activity to indicate to the system different outcomes of captive portal sign in. This class is
@@ -75,17 +72,6 @@
private final IBinder mBinder;
/** @hide */
- @IntDef(value = {
- MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY,
- MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_DISMISSED,
- MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_UNWANTED,
- MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_WANTED_AS_IS,
- MetricsEvent.CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR,
- })
- public @interface EventId {
- }
-
- /** @hide */
public CaptivePortal(@NonNull IBinder binder) {
mBinder = binder;
}
@@ -176,7 +162,7 @@
* @hide
*/
@SystemApi
- public void logEvent(@EventId int eventId, @NonNull String packageName) {
+ public void logEvent(int eventId, @NonNull String packageName) {
try {
ICaptivePortal.Stub.asInterface(mBinder).logEvent(eventId, packageName);
} catch (RemoteException e) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index c28a901..cf5d4e5 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -684,7 +684,7 @@
* {@hide}
*/
@Deprecated
- @UnsupportedAppUsage
+ @SystemApi
public static final int TYPE_PROXY = 16;
/**
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index aa4b154..0941e7d 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -80,8 +80,9 @@
private final transient boolean mParcelSensitiveFields;
private static final int MIN_MTU = 68;
- /* package-visibility - Used in other files (such as Ikev2VpnProfile) as minimum iface MTU. */
- static final int MIN_MTU_V6 = 1280;
+
+ private static final int MIN_MTU_V6 = 1280;
+
private static final int MAX_MTU = 10000;
private static final int INET6_ADDR_LENGTH = 16;
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 44ebff9..0676ad4 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -40,6 +40,7 @@
import java.lang.annotation.RetentionPolicy;
import java.time.Duration;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -174,6 +175,14 @@
public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4;
/**
+ * Sent by the NetworkAgent to ConnectivityService to pass the current
+ * list of underlying networks.
+ * obj = array of Network objects
+ * @hide
+ */
+ public static final int EVENT_UNDERLYING_NETWORKS_CHANGED = BASE + 5;
+
+ /**
* Sent by ConnectivityService to the NetworkAgent to inform the agent of the
* networks status - whether we could use the network or could not, due to
* either a bad network configuration (no internet link) or captive portal.
@@ -217,7 +226,13 @@
* The key for the redirect URL in the Bundle argument of {@code CMD_REPORT_NETWORK_STATUS}.
* @hide
*/
- public static String REDIRECT_URL_KEY = "redirect URL";
+ public static final String REDIRECT_URL_KEY = "redirect URL";
+
+ /**
+ * Bundle key for the underlying networks in {@code EVENT_UNDERLYING_NETWORKS_CHANGED}.
+ * @hide
+ */
+ public static final String UNDERLYING_NETWORKS_KEY = "underlyingNetworks";
/**
* Sent by the NetworkAgent to ConnectivityService to indicate this network was
@@ -650,6 +665,33 @@
}
/**
+ * Must be called by the agent when the network's underlying networks change.
+ *
+ * <p>{@code networks} is one of the following:
+ * <ul>
+ * <li><strong>a non-empty array</strong>: an array of one or more {@link Network}s, in
+ * decreasing preference order. For example, if this VPN uses both wifi and mobile (cellular)
+ * networks to carry app traffic, but prefers or uses wifi more than mobile, wifi should appear
+ * first in the array.</li>
+ * <li><strong>an empty array</strong>: a zero-element array, meaning that the VPN has no
+ * underlying network connection, and thus, app traffic will not be sent or received.</li>
+ * <li><strong>null</strong>: (default) signifies that the VPN uses whatever is the system's
+ * default network. I.e., it doesn't use the {@code bindSocket} or {@code bindDatagramSocket}
+ * APIs mentioned above to send traffic over specific channels.</li>
+ * </ul>
+ *
+ * @param underlyingNetworks the new list of underlying networks.
+ * @see {@link VpnService.Builder#setUnderlyingNetworks(Network[])}
+ */
+ public final void setUnderlyingNetworks(@Nullable List<Network> underlyingNetworks) {
+ final ArrayList<Network> underlyingArray = (underlyingNetworks != null)
+ ? new ArrayList<>(underlyingNetworks) : null;
+ final Bundle bundle = new Bundle();
+ bundle.putParcelableArrayList(UNDERLYING_NETWORKS_KEY, underlyingArray);
+ queueOrSendMessage(EVENT_UNDERLYING_NETWORKS_CHANGED, bundle);
+ }
+
+ /**
* Inform ConnectivityService that this agent has now connected.
* Call {@link #unregister} to disconnect.
*/
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index f806b56..8dad11f 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -339,10 +339,14 @@
public static final int NET_CAPABILITY_PARTIAL_CONNECTIVITY = 24;
/**
+ * Indicates that this network is temporarily unmetered.
+ * <p>
* This capability will be set for networks that are generally metered, but are currently
* unmetered, e.g., because the user is in a particular area. This capability can be changed at
* any time. When it is removed, applications are responsible for stopping any data transfer
* that should not occur on a metered network.
+ * Note that most apps should use {@link #NET_CAPABILITY_NOT_METERED} instead. For more
+ * information, see https://developer.android.com/about/versions/11/features/5g#meteredness.
*/
public static final int NET_CAPABILITY_TEMPORARILY_NOT_METERED = 25;
@@ -370,8 +374,8 @@
| (1 << NET_CAPABILITY_FOREGROUND)
| (1 << NET_CAPABILITY_NOT_CONGESTED)
| (1 << NET_CAPABILITY_NOT_SUSPENDED)
- | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY
- | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+ | (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY)
+ | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED);
/**
* Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -708,6 +712,7 @@
if (ArrayUtils.contains(originalAdministratorUids, creatorUid)) {
setAdministratorUids(new int[] {creatorUid});
}
+ // There is no need to clear the UIDs, they have already been cleared by clearAll() above.
}
/**
@@ -801,7 +806,9 @@
*/
private static final int TEST_NETWORKS_ALLOWED_TRANSPORTS = 1 << TRANSPORT_TEST
// Test ethernet networks can be created with EthernetManager#setIncludeTestInterfaces
- | 1 << TRANSPORT_ETHERNET;
+ | 1 << TRANSPORT_ETHERNET
+ // Test VPN networks can be created but their UID ranges must be empty.
+ | 1 << TRANSPORT_VPN;
/**
* Adds the given transport type to this {@code NetworkCapability} instance.
@@ -1802,20 +1809,26 @@
sb.append(" OwnerUid: ").append(mOwnerUid);
}
- if (mAdministratorUids.length == 0) {
- sb.append(" AdministratorUids: ").append(Arrays.toString(mAdministratorUids));
+ if (!ArrayUtils.isEmpty(mAdministratorUids)) {
+ sb.append(" AdminUids: ").append(Arrays.toString(mAdministratorUids));
+ }
+
+ if (mRequestorUid != Process.INVALID_UID) {
+ sb.append(" RequestorUid: ").append(mRequestorUid);
+ }
+
+ if (mRequestorPackageName != null) {
+ sb.append(" RequestorPkg: ").append(mRequestorPackageName);
}
if (null != mSSID) {
sb.append(" SSID: ").append(mSSID);
}
- if (mPrivateDnsBroken) {
- sb.append(" Private DNS is broken");
- }
- sb.append(" RequestorUid: ").append(mRequestorUid);
- sb.append(" RequestorPackageName: ").append(mRequestorPackageName);
+ if (mPrivateDnsBroken) {
+ sb.append(" PrivateDnsBroken");
+ }
sb.append("]");
return sb.toString();
diff --git a/core/java/android/net/NetworkProvider.java b/core/java/android/net/NetworkProvider.java
index d31218d..14cb51c 100644
--- a/core/java/android/net/NetworkProvider.java
+++ b/core/java/android/net/NetworkProvider.java
@@ -51,13 +51,6 @@
public static final int ID_NONE = -1;
/**
- * A hardcoded ID for NetworkAgents representing VPNs. These agents are not created by any
- * provider, so they use this constant for clarity instead of NONE.
- * @hide only used by ConnectivityService.
- */
- public static final int ID_VPN = -2;
-
- /**
* The first providerId value that will be allocated.
* @hide only used by ConnectivityService.
*/
@@ -70,7 +63,7 @@
private final Messenger mMessenger;
private final String mName;
- private final ConnectivityManager mCm;
+ private final Context mContext;
private int mProviderId = ID_NONE;
@@ -85,8 +78,6 @@
*/
@SystemApi
public NetworkProvider(@NonNull Context context, @NonNull Looper looper, @NonNull String name) {
- mCm = ConnectivityManager.from(context);
-
Handler handler = new Handler(looper) {
@Override
public void handleMessage(Message m) {
@@ -102,6 +93,7 @@
}
}
};
+ mContext = context;
mMessenger = new Messenger(handler);
mName = name;
}
@@ -165,6 +157,6 @@
@SystemApi
@RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
public void declareNetworkRequestUnfulfillable(@NonNull NetworkRequest request) {
- mCm.declareNetworkRequestUnfulfillable(request);
+ ConnectivityManager.from(mContext).declareNetworkRequestUnfulfillable(request);
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index fba1e79..58f1db3 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -41,6 +41,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
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_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
@@ -139,7 +140,6 @@
import android.net.util.LinkPropertiesUtils.CompareResult;
import android.net.util.MultinetworkPolicyTracker;
import android.net.util.NetdService;
-import android.os.BasicShellCommandHandler;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -171,7 +171,6 @@
import android.util.LocalLog;
import android.util.Log;
import android.util.Pair;
-import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.Xml;
@@ -192,6 +191,7 @@
import com.android.internal.util.LocationPermissionChecker;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.XmlUtils;
+import com.android.modules.utils.BasicShellCommandHandler;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.AutodestructReference;
import com.android.server.connectivity.DataConnectionStats;
@@ -1972,7 +1972,7 @@
private void registerNetdEventCallback() {
final IIpConnectivityMetrics ipConnectivityMetrics = mDeps.getIpConnectivityMetrics();
if (ipConnectivityMetrics == null) {
- Slog.wtf(TAG, "Missing IIpConnectivityMetrics");
+ Log.wtf(TAG, "Missing IIpConnectivityMetrics");
return;
}
@@ -2438,7 +2438,7 @@
if (VDBG || DDBG) log("Setting MTU size: " + iface + ", " + mtu);
mNetd.interfaceSetMtu(iface, mtu);
} catch (RemoteException | ServiceSpecificException e) {
- Slog.e(TAG, "exception in interfaceSetMtu()" + e);
+ loge("exception in interfaceSetMtu()" + e);
}
}
@@ -2460,7 +2460,7 @@
if (tcpBufferSizes.equals(mCurrentTcpBufferSizes)) return;
try {
- if (VDBG || DDBG) Slog.d(TAG, "Setting tx/rx TCP buffers to " + tcpBufferSizes);
+ if (VDBG || DDBG) log("Setting tx/rx TCP buffers to " + tcpBufferSizes);
String rmemValues = String.join(" ", values[0], values[1], values[2]);
String wmemValues = String.join(" ", values[3], values[4], values[5]);
@@ -2697,10 +2697,16 @@
/**
* Return an array of all current NetworkRequest sorted by request id.
*/
- private NetworkRequestInfo[] requestsSortedById() {
+ @VisibleForTesting
+ protected NetworkRequestInfo[] requestsSortedById() {
NetworkRequestInfo[] requests = new NetworkRequestInfo[0];
requests = mNetworkRequests.values().toArray(requests);
- Arrays.sort(requests, Comparator.comparingInt(nri -> nri.request.requestId));
+ // Sort the array based off the NRI containing the min requestId in its requests.
+ Arrays.sort(requests,
+ Comparator.comparingInt(nri -> Collections.min(nri.mRequests,
+ Comparator.comparingInt(req -> req.requestId)).requestId
+ )
+ );
return requests;
}
@@ -2755,7 +2761,7 @@
case NetworkAgent.EVENT_NETWORK_CAPABILITIES_CHANGED: {
NetworkCapabilities networkCapabilities = (NetworkCapabilities) msg.obj;
if (networkCapabilities.hasConnectivityManagedCapability()) {
- Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
+ Log.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
}
if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
// Make sure the original object is not mutated. NetworkAgent normally
@@ -2765,6 +2771,7 @@
networkCapabilities = new NetworkCapabilities(networkCapabilities);
networkCapabilities.restrictCapabilitesForTestNetwork(nai.creatorUid);
}
+ processCapabilitiesFromAgent(nai, networkCapabilities);
updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities);
break;
}
@@ -2803,6 +2810,31 @@
mKeepaliveTracker.handleEventSocketKeepalive(nai, msg);
break;
}
+ case NetworkAgent.EVENT_UNDERLYING_NETWORKS_CHANGED: {
+ if (!nai.supportsUnderlyingNetworks()) {
+ Log.wtf(TAG, "Non-virtual networks cannot have underlying networks");
+ break;
+ }
+ final ArrayList<Network> underlying;
+ try {
+ underlying = ((Bundle) msg.obj).getParcelableArrayList(
+ NetworkAgent.UNDERLYING_NETWORKS_KEY);
+ } catch (NullPointerException | ClassCastException e) {
+ break;
+ }
+ final Network[] oldUnderlying = nai.declaredUnderlyingNetworks;
+ nai.declaredUnderlyingNetworks = (underlying != null)
+ ? underlying.toArray(new Network[0]) : null;
+
+ if (!Arrays.equals(oldUnderlying, nai.declaredUnderlyingNetworks)) {
+ if (DBG) {
+ log(nai.toShortString() + " changed underlying networks to "
+ + Arrays.toString(nai.declaredUnderlyingNetworks));
+ }
+ updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
+ notifyIfacesChangedForNetworkStats();
+ }
+ }
}
}
@@ -3034,7 +3066,7 @@
// Legacy version of notifyNetworkTestedWithExtras.
// Would only be called if the system has a NetworkStack module older than the
// framework, which does not happen in practice.
- Slog.wtf(TAG, "Deprecated notifyNetworkTested called: no action taken");
+ Log.wtf(TAG, "Deprecated notifyNetworkTested called: no action taken");
}
@Override
@@ -3388,7 +3420,7 @@
}
mLegacyTypeTracker.remove(nai, wasDefault);
if (!nai.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
- updateAllVpnsCapabilities();
+ propagateUnderlyingNetworkCapabilities();
}
rematchAllNetworksAndRequests();
mLingerMonitor.noteDisconnect(nai);
@@ -3511,7 +3543,7 @@
numRequests = nai.numForegroundNetworkRequests();
break;
default:
- Slog.wtf(TAG, "Invalid reason. Cannot happen.");
+ Log.wtf(TAG, "Invalid reason. Cannot happen.");
return true;
}
@@ -3673,7 +3705,7 @@
synchronized (mUidToNetworkRequestCount) {
final int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
if (requests < 1) {
- Slog.wtf(TAG, "BUG: too small request count " + requests + " for UID " + nri.mUid);
+ Log.wtf(TAG, "BUG: too small request count " + requests + " for UID " + nri.mUid);
} else if (requests == 1) {
mUidToNetworkRequestCount.removeAt(mUidToNetworkRequestCount.indexOfKey(nri.mUid));
} else {
@@ -3718,7 +3750,7 @@
}
if (!nai.networkAgentConfig.explicitlySelected) {
- Slog.wtf(TAG, "BUG: setAcceptUnvalidated non non-explicitly selected network");
+ Log.wtf(TAG, "BUG: setAcceptUnvalidated non non-explicitly selected network");
}
if (accept != nai.networkAgentConfig.acceptUnvalidated) {
@@ -3988,7 +4020,7 @@
highPriority = nai.networkAgentConfig.explicitlySelected;
break;
default:
- Slog.wtf(TAG, "Unknown notification type " + type);
+ Log.wtf(TAG, "Unknown notification type " + type);
return;
}
@@ -4310,7 +4342,7 @@
synchronized (this) {
if (!mNetTransitionWakeLock.isHeld()) {
mWakelockLogs.log(String.format("RELEASE: already released (%s)", event));
- Slog.w(TAG, "expected Net Transition WakeLock to be held");
+ Log.w(TAG, "expected Net Transition WakeLock to be held");
return;
}
mNetTransitionWakeLock.release();
@@ -4482,7 +4514,7 @@
@Override
public void onChange(boolean selfChange) {
- Slog.wtf(TAG, "Should never be reached.");
+ Log.wtf(TAG, "Should never be reached.");
}
@Override
@@ -4497,15 +4529,19 @@
}
private static void log(String s) {
- Slog.d(TAG, s);
+ Log.d(TAG, s);
+ }
+
+ private static void logw(String s) {
+ Log.w(TAG, s);
}
private static void loge(String s) {
- Slog.e(TAG, s);
+ Log.e(TAG, s);
}
private static void loge(String s, Throwable t) {
- Slog.e(TAG, s, t);
+ Log.e(TAG, s, t);
}
/**
@@ -4698,10 +4734,9 @@
if (mLockdownEnabled) {
return new VpnInfo[0];
}
-
List<VpnInfo> infoList = new ArrayList<>();
- for (int i = 0; i < mVpns.size(); i++) {
- VpnInfo info = createVpnInfo(mVpns.valueAt(i));
+ for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ VpnInfo info = createVpnInfo(nai);
if (info != null) {
infoList.add(info);
}
@@ -4714,13 +4749,10 @@
* @return VPN information for accounting, or null if we can't retrieve all required
* information, e.g underlying ifaces.
*/
- @Nullable
- private VpnInfo createVpnInfo(Vpn vpn) {
- VpnInfo info = vpn.getVpnInfo();
- if (info == null) {
- return null;
- }
- Network[] underlyingNetworks = vpn.getUnderlyingNetworks();
+ private VpnInfo createVpnInfo(NetworkAgentInfo nai) {
+ if (!nai.isVPN()) return null;
+
+ Network[] underlyingNetworks = nai.declaredUnderlyingNetworks;
// see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
// the underlyingNetworks list.
if (underlyingNetworks == null) {
@@ -4729,23 +4761,33 @@
underlyingNetworks = new Network[] { defaultNai.network };
}
}
- if (underlyingNetworks != null && underlyingNetworks.length > 0) {
- List<String> interfaces = new ArrayList<>();
- for (Network network : underlyingNetworks) {
- LinkProperties lp = getLinkProperties(network);
- if (lp != null) {
- for (String iface : lp.getAllInterfaceNames()) {
- if (!TextUtils.isEmpty(iface)) {
- interfaces.add(iface);
- }
- }
+
+ if (ArrayUtils.isEmpty(underlyingNetworks)) return null;
+
+ List<String> interfaces = new ArrayList<>();
+ for (Network network : underlyingNetworks) {
+ NetworkAgentInfo underlyingNai = getNetworkAgentInfoForNetwork(network);
+ if (underlyingNai == null) continue;
+ LinkProperties lp = underlyingNai.linkProperties;
+ for (String iface : lp.getAllInterfaceNames()) {
+ if (!TextUtils.isEmpty(iface)) {
+ interfaces.add(iface);
}
}
- if (!interfaces.isEmpty()) {
- info.underlyingIfaces = interfaces.toArray(new String[interfaces.size()]);
- }
}
- return info.underlyingIfaces == null ? null : info;
+
+ if (interfaces.isEmpty()) return null;
+
+ VpnInfo info = new VpnInfo();
+ info.ownerUid = nai.networkCapabilities.getOwnerUid();
+ info.vpnIface = nai.linkProperties.getInterfaceName();
+ // Must be non-null or NetworkStatsService will crash.
+ // Cannot happen in production code because Vpn only registers the NetworkAgent after the
+ // tun or ipsec interface is created.
+ if (info.vpnIface == null) return null;
+ info.underlyingIfaces = interfaces.toArray(new String[0]);
+
+ return info;
}
/**
@@ -4768,39 +4810,25 @@
}
/**
- * Ask all VPN objects to recompute and update their capabilities.
+ * Ask all networks with underlying networks to recompute and update their capabilities.
*
- * When underlying networks change, VPNs may have to update capabilities to reflect things
- * like the metered bit, their transports, and so on. This asks the VPN objects to update
- * their capabilities, and as this will cause them to send messages to the ConnectivityService
- * handler thread through their agent, this is asynchronous. When the capabilities objects
- * are computed they will be up-to-date as they are computed synchronously from here and
- * this is running on the ConnectivityService thread.
+ * When underlying networks change, such networks may have to update capabilities to reflect
+ * things like the metered bit, their transports, and so on. The capabilities are calculated
+ * immediately. This method runs on the ConnectivityService thread.
*/
- private void updateAllVpnsCapabilities() {
- Network defaultNetwork = getNetwork(getDefaultNetwork());
- synchronized (mVpns) {
- for (int i = 0; i < mVpns.size(); i++) {
- final Vpn vpn = mVpns.valueAt(i);
- NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
- updateVpnCapabilities(vpn, nc);
+ private void propagateUnderlyingNetworkCapabilities() {
+ ensureRunningOnConnectivityServiceThread();
+ for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ if (nai.supportsUnderlyingNetworks()) {
+ updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
}
}
}
- private void updateVpnCapabilities(Vpn vpn, @Nullable NetworkCapabilities nc) {
- ensureRunningOnConnectivityServiceThread();
- NetworkAgentInfo vpnNai = getNetworkAgentInfoForNetId(vpn.getNetId());
- if (vpnNai == null || nc == null) {
- return;
- }
- updateCapabilities(vpnNai.getCurrentScore(), vpnNai, nc);
- }
-
@Override
public boolean updateLockdownVpn() {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- Slog.w(TAG, "Lockdown VPN only available to AID_SYSTEM");
+ logw("Lockdown VPN only available to AID_SYSTEM");
return false;
}
@@ -4810,21 +4838,21 @@
if (mLockdownEnabled) {
byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN);
if (profileTag == null) {
- Slog.e(TAG, "Lockdown VPN configured but cannot be read from keystore");
+ loge("Lockdown VPN configured but cannot be read from keystore");
return false;
}
String profileName = new String(profileTag);
final VpnProfile profile = VpnProfile.decode(
profileName, mKeyStore.get(Credentials.VPN + profileName));
if (profile == null) {
- Slog.e(TAG, "Lockdown VPN configured invalid profile " + profileName);
+ loge("Lockdown VPN configured invalid profile " + profileName);
setLockdownTracker(null);
return true;
}
int user = UserHandle.getUserId(Binder.getCallingUid());
Vpn vpn = mVpns.get(user);
if (vpn == null) {
- Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown");
+ logw("VPN for user " + user + " not ready yet. Skipping lockdown");
return false;
}
setLockdownTracker(new LockdownVpnTracker(mContext, this, mHandler, vpn, profile));
@@ -4884,7 +4912,7 @@
if (vpn == null) {
// Shouldn't happen as all code paths that point here should have checked the Vpn
// exists already.
- Slog.wtf(TAG, "User " + userId + " has no Vpn configuration");
+ Log.wtf(TAG, "User " + userId + " has no Vpn configuration");
return false;
}
@@ -4900,7 +4928,7 @@
synchronized (mVpns) {
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
- Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+ logw("User " + userId + " has no Vpn configuration");
return false;
}
return vpn.isAlwaysOnPackageSupported(packageName, mKeyStore);
@@ -4921,7 +4949,7 @@
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
- Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+ logw("User " + userId + " has no Vpn configuration");
return false;
}
if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownWhitelist, mKeyStore)) {
@@ -4943,7 +4971,7 @@
synchronized (mVpns) {
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
- Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+ logw("User " + userId + " has no Vpn configuration");
return null;
}
return vpn.getAlwaysOnPackage();
@@ -4958,7 +4986,7 @@
synchronized (mVpns) {
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
- Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+ logw("User " + userId + " has no Vpn configuration");
return false;
}
return vpn.getLockdown();
@@ -4973,7 +5001,7 @@
synchronized (mVpns) {
Vpn vpn = mVpns.get(userId);
if (vpn == null) {
- Slog.w(TAG, "User " + userId + " has no Vpn configuration");
+ logw("User " + userId + " has no Vpn configuration");
return null;
}
return vpn.getLockdownAllowlist();
@@ -5107,7 +5135,7 @@
}
}
- private void onUserStart(int userId) {
+ private void onUserStarted(int userId) {
synchronized (mVpns) {
Vpn userVpn = mVpns.get(userId);
if (userVpn != null) {
@@ -5122,7 +5150,7 @@
}
}
- private void onUserStop(int userId) {
+ private void onUserStopped(int userId) {
synchronized (mVpns) {
Vpn userVpn = mVpns.get(userId);
if (userVpn == null) {
@@ -5136,35 +5164,29 @@
private void onUserAdded(int userId) {
mPermissionMonitor.onUserAdded(userId);
- Network defaultNetwork = getNetwork(getDefaultNetwork());
synchronized (mVpns) {
final int vpnsSize = mVpns.size();
for (int i = 0; i < vpnsSize; i++) {
Vpn vpn = mVpns.valueAt(i);
vpn.onUserAdded(userId);
- NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
- updateVpnCapabilities(vpn, nc);
}
}
}
private void onUserRemoved(int userId) {
mPermissionMonitor.onUserRemoved(userId);
- Network defaultNetwork = getNetwork(getDefaultNetwork());
synchronized (mVpns) {
final int vpnsSize = mVpns.size();
for (int i = 0; i < vpnsSize; i++) {
Vpn vpn = mVpns.valueAt(i);
vpn.onUserRemoved(userId);
- NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
- updateVpnCapabilities(vpn, nc);
}
}
}
private void onPackageReplaced(String packageName, int uid) {
if (TextUtils.isEmpty(packageName) || uid < 0) {
- Slog.wtf(TAG, "Invalid package in onPackageReplaced: " + packageName + " | " + uid);
+ Log.wtf(TAG, "Invalid package in onPackageReplaced: " + packageName + " | " + uid);
return;
}
final int userId = UserHandle.getUserId(uid);
@@ -5175,7 +5197,7 @@
}
// Legacy always-on VPN won't be affected since the package name is not set.
if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName)) {
- Slog.d(TAG, "Restarting always-on VPN package " + packageName + " for user "
+ log("Restarting always-on VPN package " + packageName + " for user "
+ userId);
vpn.startAlwaysOnVpn(mKeyStore);
}
@@ -5184,7 +5206,7 @@
private void onPackageRemoved(String packageName, int uid, boolean isReplacing) {
if (TextUtils.isEmpty(packageName) || uid < 0) {
- Slog.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid);
+ Log.wtf(TAG, "Invalid package in onPackageRemoved: " + packageName + " | " + uid);
return;
}
@@ -5196,7 +5218,7 @@
}
// Legacy always-on VPN won't be affected since the package name is not set.
if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) {
- Slog.d(TAG, "Removing always-on VPN package " + packageName + " for user "
+ log("Removing always-on VPN package " + packageName + " for user "
+ userId);
vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
}
@@ -5239,9 +5261,9 @@
if (userId == UserHandle.USER_NULL) return;
if (Intent.ACTION_USER_STARTED.equals(action)) {
- onUserStart(userId);
+ onUserStarted(userId);
} else if (Intent.ACTION_USER_STOPPED.equals(action)) {
- onUserStop(userId);
+ onUserStopped(userId);
} else if (Intent.ACTION_USER_ADDED.equals(action)) {
onUserAdded(userId);
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
@@ -5350,6 +5372,12 @@
}
}
+ private void ensureAllNetworkRequestsHaveType(List<NetworkRequest> requests) {
+ for (int i = 0; i < requests.size(); i++) {
+ ensureNetworkRequestHasType(requests.get(i));
+ }
+ }
+
private void ensureNetworkRequestHasType(NetworkRequest request) {
if (request.type == NetworkRequest.Type.NONE) {
throw new IllegalArgumentException(
@@ -5361,7 +5389,8 @@
* Tracks info about the requester.
* Also used to notice when the calling process dies so we can self-expire
*/
- private class NetworkRequestInfo implements IBinder.DeathRecipient {
+ @VisibleForTesting
+ protected class NetworkRequestInfo implements IBinder.DeathRecipient {
final List<NetworkRequest> mRequests;
final NetworkRequest request;
@@ -5380,7 +5409,7 @@
NetworkRequestInfo(NetworkRequest r, PendingIntent pi) {
request = r;
mRequests = initializeRequests(r);
- ensureNetworkRequestHasType(request);
+ ensureAllNetworkRequestsHaveType(mRequests);
mPendingIntent = pi;
messenger = null;
mBinder = null;
@@ -5394,7 +5423,7 @@
messenger = m;
request = r;
mRequests = initializeRequests(r);
- ensureNetworkRequestHasType(request);
+ ensureAllNetworkRequestsHaveType(mRequests);
mBinder = binder;
mPid = getCallingPid();
mUid = getCallingUid();
@@ -5418,6 +5447,19 @@
return Collections.unmodifiableList(tempRequests);
}
+ private NetworkRequest getSatisfiedRequest() {
+ if (mSatisfier == null) {
+ return null;
+ }
+
+ for (NetworkRequest req : mRequests) {
+ if (mSatisfier.isSatisfyingRequest(req.requestId)) {
+ return req;
+ }
+ }
+
+ return null;
+ }
private void enforceRequestCountLimit() {
synchronized (mUidToNetworkRequestCount) {
@@ -5436,14 +5478,16 @@
}
}
+ @Override
public void binderDied() {
log("ConnectivityService NetworkRequestInfo binderDied(" +
- request + ", " + mBinder + ")");
- releaseNetworkRequest(request);
+ mRequests + ", " + mBinder + ")");
+ releaseNetworkRequest(mRequests);
}
+ @Override
public String toString() {
- return "uid/pid:" + mUid + "/" + mPid + " " + request
+ return "uid/pid:" + mUid + "/" + mPid + " " + mRequests
+ (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
}
}
@@ -5763,6 +5807,12 @@
return mNextNetworkProviderId.getAndIncrement();
}
+ private void releaseNetworkRequest(List<NetworkRequest> networkRequests) {
+ for (int i = 0; i < networkRequests.size(); i++) {
+ releaseNetworkRequest(networkRequests.get(i));
+ }
+ }
+
@Override
public void releaseNetworkRequest(NetworkRequest networkRequest) {
ensureNetworkRequestHasType(networkRequest);
@@ -5784,7 +5834,7 @@
// Avoid creating duplicates. even if an app makes a direct AIDL call.
// This will never happen if an app calls ConnectivityManager#registerNetworkProvider,
// as that will throw if a duplicate provider is registered.
- Slog.e(TAG, "Attempt to register existing NetworkProviderInfo "
+ loge("Attempt to register existing NetworkProviderInfo "
+ mNetworkProviderInfos.get(npi.messenger).name);
return;
}
@@ -5854,7 +5904,6 @@
@GuardedBy("mBlockedAppUids")
private final HashSet<Integer> mBlockedAppUids = new HashSet<>();
- // Note: if mDefaultRequest is changed, NetworkMonitor needs to be updated.
@NonNull
private final NetworkRequest mDefaultRequest;
// The NetworkAgentInfo currently satisfying the default request, if any.
@@ -5926,13 +5975,29 @@
int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS);
+ } else {
+ enforceNetworkFactoryPermission();
+ }
+
+ final int uid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return registerNetworkAgentInternal(messenger, networkInfo, linkProperties,
+ networkCapabilities, currentScore, networkAgentConfig, providerId, uid);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private Network registerNetworkAgentInternal(Messenger messenger, NetworkInfo networkInfo,
+ LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
+ int currentScore, NetworkAgentConfig networkAgentConfig, int providerId, int uid) {
+ if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
// Strictly, sanitizing here is unnecessary as the capabilities will be sanitized in
// the call to mixInCapabilities below anyway, but sanitizing here means the NAI never
// sees capabilities that may be malicious, which might prevent mistakes in the future.
networkCapabilities = new NetworkCapabilities(networkCapabilities);
- networkCapabilities.restrictCapabilitesForTestNetwork(Binder.getCallingUid());
- } else {
- enforceNetworkFactoryPermission();
+ networkCapabilities.restrictCapabilitesForTestNetwork(uid);
}
LinkProperties lp = new LinkProperties(linkProperties);
@@ -5943,9 +6008,10 @@
final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
- this, mNetd, mDnsResolver, mNMS, providerId, Binder.getCallingUid());
+ this, mNetd, mDnsResolver, mNMS, providerId, uid);
// Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
+ processCapabilitiesFromAgent(nai, nc);
nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
processLinkPropertiesFromAgent(nai, nai.linkProperties);
@@ -5953,13 +6019,8 @@
final String name = TextUtils.isEmpty(extraInfo)
? nai.networkCapabilities.getSsid() : extraInfo;
if (DBG) log("registerNetworkAgent " + nai);
- final long token = Binder.clearCallingIdentity();
- try {
- mDeps.getNetworkStack().makeNetworkMonitor(
- nai.network, name, new NetworkMonitorCallbacks(nai));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ mDeps.getNetworkStack().makeNetworkMonitor(
+ nai.network, name, new NetworkMonitorCallbacks(nai));
// NetworkAgentInfo registration will finish when the NetworkMonitor is created.
// If the network disconnects or sends any other event before that, messages are deferred by
// NetworkAgent until nai.asyncChannel.connect(), which will be called when finalizing the
@@ -5986,6 +6047,12 @@
updateUids(nai, null, nai.networkCapabilities);
}
+ /**
+ * Called when receiving LinkProperties directly from a NetworkAgent.
+ * Stores into |nai| any data coming from the agent that might also be written to the network's
+ * LinkProperties by ConnectivityService itself. This ensures that the data provided by the
+ * agent is not lost when updateLinkProperties is called.
+ */
private void processLinkPropertiesFromAgent(NetworkAgentInfo nai, LinkProperties lp) {
lp.ensureDirectlyConnectedRoutes();
nai.clatd.setNat64PrefixFromRa(lp.getNat64Prefix());
@@ -6282,6 +6349,83 @@
}
/**
+ * Called when receiving NetworkCapabilities directly from a NetworkAgent.
+ * Stores into |nai| any data coming from the agent that might also be written to the network's
+ * NetworkCapabilities by ConnectivityService itself. This ensures that the data provided by the
+ * agent is not lost when updateCapabilities is called.
+ */
+ private void processCapabilitiesFromAgent(NetworkAgentInfo nai, NetworkCapabilities nc) {
+ nai.declaredMetered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
+ }
+
+ /** Modifies |caps| based on the capabilities of the specified underlying networks. */
+ @VisibleForTesting
+ void applyUnderlyingCapabilities(@Nullable Network[] underlyingNetworks,
+ @NonNull NetworkCapabilities caps, boolean declaredMetered) {
+ final Network defaultNetwork = getNetwork(getDefaultNetwork());
+ if (underlyingNetworks == null && defaultNetwork != null) {
+ // null underlying networks means to track the default.
+ underlyingNetworks = new Network[] { defaultNetwork };
+ }
+ int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN };
+ int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
+ int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
+ boolean metered = declaredMetered; // metered if any underlying is metered, or agentMetered
+ boolean roaming = false; // roaming if any underlying is roaming
+ boolean congested = false; // congested if any underlying is congested
+ boolean suspended = true; // suspended if all underlying are suspended
+
+ boolean hadUnderlyingNetworks = false;
+ if (null != underlyingNetworks) {
+ for (Network underlyingNetwork : underlyingNetworks) {
+ final NetworkAgentInfo underlying =
+ getNetworkAgentInfoForNetwork(underlyingNetwork);
+ if (underlying == null) continue;
+
+ final NetworkCapabilities underlyingCaps = underlying.networkCapabilities;
+ hadUnderlyingNetworks = true;
+ for (int underlyingType : underlyingCaps.getTransportTypes()) {
+ transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType);
+ }
+
+ // Merge capabilities of this underlying network. For bandwidth, assume the
+ // worst case.
+ downKbps = NetworkCapabilities.minBandwidth(downKbps,
+ underlyingCaps.getLinkDownstreamBandwidthKbps());
+ upKbps = NetworkCapabilities.minBandwidth(upKbps,
+ underlyingCaps.getLinkUpstreamBandwidthKbps());
+ // If this underlying network is metered, the VPN is metered (it may cost money
+ // to send packets on this network).
+ metered |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_METERED);
+ // If this underlying network is roaming, the VPN is roaming (the billing structure
+ // is different than the usual, local one).
+ roaming |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ // If this underlying network is congested, the VPN is congested (the current
+ // condition of the network affects the performance of this network).
+ congested |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_CONGESTED);
+ // If this network is not suspended, the VPN is not suspended (the VPN
+ // is able to transfer some data).
+ suspended &= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ }
+ }
+ if (!hadUnderlyingNetworks) {
+ // No idea what the underlying networks are; assume reasonable defaults
+ metered = true;
+ roaming = false;
+ congested = false;
+ suspended = false;
+ }
+
+ caps.setTransportTypes(transportTypes);
+ caps.setLinkDownstreamBandwidthKbps(downKbps);
+ caps.setLinkUpstreamBandwidthKbps(upKbps);
+ caps.setCapability(NET_CAPABILITY_NOT_METERED, !metered);
+ caps.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming);
+ caps.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested);
+ caps.setCapability(NET_CAPABILITY_NOT_SUSPENDED, !suspended);
+ }
+
+ /**
* Augments the NetworkCapabilities passed in by a NetworkAgent with capabilities that are
* maintained here that the NetworkAgent is not aware of (e.g., validated, captive portal,
* and foreground status).
@@ -6300,7 +6444,7 @@
// stop being matched by the updated agent.
String diff = nai.networkCapabilities.describeImmutableDifferences(nc);
if (!TextUtils.isEmpty(diff)) {
- Slog.wtf(TAG, "BUG: " + nai + " lost immutable capabilities:" + diff);
+ Log.wtf(TAG, "BUG: " + nai + " lost immutable capabilities:" + diff);
}
}
@@ -6334,6 +6478,10 @@
newNc.addCapability(NET_CAPABILITY_NOT_ROAMING);
}
+ if (nai.supportsUnderlyingNetworks()) {
+ applyUnderlyingCapabilities(nai.declaredUnderlyingNetworks, newNc, nai.declaredMetered);
+ }
+
return newNc;
}
@@ -6413,7 +6561,7 @@
if (!newNc.hasTransport(TRANSPORT_VPN)) {
// Tell VPNs about updated capabilities, since they may need to
// bubble those changes through.
- updateAllVpnsCapabilities();
+ propagateUnderlyingNetworkCapabilities();
}
if (!newNc.equalsTransportTypes(prevNc)) {
@@ -6733,7 +6881,7 @@
? newNetwork.linkProperties.getTcpBufferSizes() : null);
notifyIfacesChangedForNetworkStats();
// Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks.
- updateAllVpnsCapabilities();
+ propagateUnderlyingNetworkCapabilities();
}
private void processListenRequests(@NonNull final NetworkAgentInfo nai) {
@@ -6780,7 +6928,7 @@
}
public String toString() {
- return mRequest.request.requestId + " : "
+ return mRequest.mRequests.get(0).requestId + " : "
+ (null != mOldNetwork ? mOldNetwork.network.netId : "null")
+ " → " + (null != mNewNetwork ? mNewNetwork.network.netId : "null");
}
@@ -6856,7 +7004,7 @@
}
newSatisfier.unlingerRequest(nri.request);
if (!newSatisfier.addRequest(nri.request)) {
- Slog.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
+ Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
+ nri.request);
}
} else {
@@ -7195,7 +7343,7 @@
// onCapabilitiesUpdated being sent in updateAllVpnCapabilities below as
// the VPN would switch from its default, blank capabilities to those
// that reflect the capabilities of its underlying networks.
- updateAllVpnsCapabilities();
+ propagateUnderlyingNetworkCapabilities();
}
networkAgent.created = true;
}
@@ -7204,7 +7352,7 @@
networkAgent.everConnected = true;
if (networkAgent.linkProperties == null) {
- Slog.wtf(TAG, networkAgent.toShortString() + " connected with null LinkProperties");
+ Log.wtf(TAG, networkAgent.toShortString() + " connected with null LinkProperties");
}
// NetworkCapabilities need to be set before sending the private DNS config to
@@ -7237,8 +7385,8 @@
// doing.
updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
- if (networkAgent.isVPN()) {
- updateAllVpnsCapabilities();
+ if (networkAgent.supportsUnderlyingNetworks()) {
+ propagateUnderlyingNetworkCapabilities();
}
// Consider network even though it is not yet validated.
@@ -7495,13 +7643,6 @@
throwIfLockdownEnabled();
success = mVpns.get(user).setUnderlyingNetworks(networks);
}
- if (success) {
- mHandler.post(() -> {
- // Update VPN's capabilities based on updated underlying network set.
- updateAllVpnsCapabilities();
- notifyIfacesChangedForNetworkStats();
- });
- }
return success;
}
@@ -8059,8 +8200,10 @@
final NetworkRequestInfo nri = cbInfo.mRequestInfo;
- if (uid != nri.mUid) {
- if (VDBG) loge("Different uid than registrant attempting to unregister cb");
+ // Caller's UID must either be the registrants (if they are unregistering) or the System's
+ // (if the Binder died)
+ if (uid != nri.mUid && uid != Process.SYSTEM_UID) {
+ if (DBG) loge("Uid(" + uid + ") not registrant's (" + nri.mUid + ") or System's");
return;
}
@@ -8177,13 +8320,12 @@
return false;
}
- final Network[] underlyingNetworks;
- synchronized (mVpns) {
- final Vpn vpn = getVpnIfOwner(callbackUid);
- underlyingNetworks = (vpn == null) ? null : vpn.getUnderlyingNetworks();
- }
- if (underlyingNetworks != null) {
- if (Arrays.asList(underlyingNetworks).contains(nai.network)) return true;
+ for (NetworkAgentInfo virtual : mNetworkAgentInfos.values()) {
+ if (virtual.supportsUnderlyingNetworks()
+ && virtual.networkCapabilities.getOwnerUid() == callbackUid
+ && ArrayUtils.contains(virtual.declaredUnderlyingNetworks, nai.network)) {
+ return true;
+ }
}
// Administrator UIDs also contains the Owner UID
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index d6bd5a1..a45466d 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -29,9 +29,9 @@
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkProvider;
import android.net.RouteInfo;
import android.net.StringNetworkSpecifier;
import android.net.TestNetworkInterface;
@@ -62,7 +62,8 @@
/** @hide */
class TestNetworkService extends ITestNetworkManager.Stub {
@NonNull private static final String TAG = TestNetworkService.class.getSimpleName();
- @NonNull private static final String TEST_NETWORK_TYPE = "TEST_NETWORK";
+ @NonNull private static final String TEST_NETWORK_LOGTAG = "TestNetworkAgent";
+ @NonNull private static final String TEST_NETWORK_PROVIDER_NAME = TAG;
@NonNull private static final AtomicInteger sTestTunIndex = new AtomicInteger();
@NonNull private final Context mContext;
@@ -72,6 +73,9 @@
@NonNull private final HandlerThread mHandlerThread;
@NonNull private final Handler mHandler;
+ @NonNull private final ConnectivityManager mCm;
+ @NonNull private final NetworkProvider mNetworkProvider;
+
// Native method stubs
private static native int jniCreateTunTap(boolean isTun, @NonNull String iface);
@@ -85,6 +89,10 @@
mContext = Objects.requireNonNull(context, "missing Context");
mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService");
mNetd = Objects.requireNonNull(NetdService.getInstance(), "could not get netd instance");
+ mCm = mContext.getSystemService(ConnectivityManager.class);
+ mNetworkProvider = new NetworkProvider(mContext, mHandler.getLooper(),
+ TEST_NETWORK_PROVIDER_NAME);
+ mCm.registerNetworkProvider(mNetworkProvider);
}
/**
@@ -150,9 +158,6 @@
private static final int NETWORK_SCORE = 1; // Use a low, non-zero score.
private final int mUid;
- @NonNull private final NetworkInfo mNi;
- @NonNull private final NetworkCapabilities mNc;
- @NonNull private final LinkProperties mLp;
@GuardedBy("mBinderLock")
@NonNull
@@ -161,20 +166,18 @@
@NonNull private final Object mBinderLock = new Object();
private TestNetworkAgent(
- @NonNull Looper looper,
@NonNull Context context,
- @NonNull NetworkInfo ni,
+ @NonNull Looper looper,
+ @NonNull NetworkAgentConfig config,
@NonNull NetworkCapabilities nc,
@NonNull LinkProperties lp,
int uid,
- @NonNull IBinder binder)
+ @NonNull IBinder binder,
+ @NonNull NetworkProvider np)
throws RemoteException {
- super(looper, context, TEST_NETWORK_TYPE, ni, nc, lp, NETWORK_SCORE);
+ super(context, looper, TEST_NETWORK_LOGTAG, nc, lp, NETWORK_SCORE, config, np);
mUid = uid;
- mNi = ni;
- mNc = nc;
- mLp = lp;
synchronized (mBinderLock) {
mBinder = binder; // Binder null-checks in create()
@@ -203,9 +206,7 @@
}
private void teardown() {
- mNi.setDetailedState(DetailedState.DISCONNECTED, null, null);
- mNi.setIsAvailable(false);
- sendNetworkInfo(mNi);
+ unregister();
// Synchronize on mBinderLock to ensure that unlinkToDeath is never called more than
// once (otherwise it could throw an exception)
@@ -238,11 +239,6 @@
Objects.requireNonNull(context, "missing Context");
// iface and binder validity checked by caller
- // Build network info with special testing type
- NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_TEST, 0, TEST_NETWORK_TYPE, "");
- ni.setDetailedState(DetailedState.CONNECTED, null, null);
- ni.setIsAvailable(true);
-
// Build narrow set of NetworkCapabilities, useful only for testing
NetworkCapabilities nc = new NetworkCapabilities();
nc.clearAll(); // Remove default capabilities.
@@ -290,7 +286,12 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null, iface));
}
- return new TestNetworkAgent(looper, context, ni, nc, lp, callingUid, binder);
+ final TestNetworkAgent agent = new TestNetworkAgent(context, looper,
+ new NetworkAgentConfig.Builder().build(), nc, lp, callingUid, binder,
+ mNetworkProvider);
+ agent.register();
+ agent.markConnected();
+ return agent;
}
/**
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index c789186..1f0fb5e 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -45,8 +45,8 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Pair;
-import android.util.Slog;
import java.net.InetAddress;
import java.util.Arrays;
@@ -279,7 +279,7 @@
}
public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) {
- Slog.w(TAG, "updatePrivateDns(" + network + ", " + cfg + ")");
+ Log.w(TAG, "updatePrivateDns(" + network + ", " + cfg + ")");
return (cfg != null)
? mPrivateDnsMap.put(network.netId, cfg)
: mPrivateDnsMap.remove(network.netId);
@@ -389,7 +389,7 @@
mPrivateDnsValidationMap.remove(netId);
}
- Slog.d(TAG, String.format("sendDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, "
+ Log.d(TAG, String.format("sendDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, "
+ "%d, %d, %s, %s)", paramsParcel.netId, Arrays.toString(paramsParcel.servers),
Arrays.toString(paramsParcel.domains), paramsParcel.sampleValiditySeconds,
paramsParcel.successThreshold, paramsParcel.minSamples,
@@ -400,7 +400,7 @@
try {
mDnsResolver.setResolverConfiguration(paramsParcel);
} catch (RemoteException | ServiceSpecificException e) {
- Slog.e(TAG, "Error setting DNS configuration: " + e);
+ Log.e(TAG, "Error setting DNS configuration: " + e);
return;
}
}
@@ -431,8 +431,8 @@
DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS,
DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
if (mSampleValidity < 0 || mSampleValidity > 65535) {
- Slog.w(TAG, "Invalid sampleValidity=" + mSampleValidity + ", using default=" +
- DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
+ Log.w(TAG, "Invalid sampleValidity=" + mSampleValidity + ", using default="
+ + DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
mSampleValidity = DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS;
}
@@ -440,17 +440,17 @@
DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT,
DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
if (mSuccessThreshold < 0 || mSuccessThreshold > 100) {
- Slog.w(TAG, "Invalid successThreshold=" + mSuccessThreshold + ", using default=" +
- DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
+ Log.w(TAG, "Invalid successThreshold=" + mSuccessThreshold + ", using default="
+ + DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
mSuccessThreshold = DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT;
}
mMinSamples = getIntSetting(DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES);
mMaxSamples = getIntSetting(DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES);
if (mMinSamples < 0 || mMinSamples > mMaxSamples || mMaxSamples > 64) {
- Slog.w(TAG, "Invalid sample count (min, max)=(" + mMinSamples + ", " + mMaxSamples +
- "), using default=(" + DNS_RESOLVER_DEFAULT_MIN_SAMPLES + ", " +
- DNS_RESOLVER_DEFAULT_MAX_SAMPLES + ")");
+ Log.w(TAG, "Invalid sample count (min, max)=(" + mMinSamples + ", " + mMaxSamples
+ + "), using default=(" + DNS_RESOLVER_DEFAULT_MIN_SAMPLES + ", "
+ + DNS_RESOLVER_DEFAULT_MAX_SAMPLES + ")");
mMinSamples = DNS_RESOLVER_DEFAULT_MIN_SAMPLES;
mMaxSamples = DNS_RESOLVER_DEFAULT_MAX_SAMPLES;
}
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 163788f..d9c2e80 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -30,7 +30,7 @@
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
-import android.util.Slog;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
@@ -176,7 +176,7 @@
try {
mNMService.registerObserver(this);
} catch (RemoteException e) {
- Slog.e(TAG, "Can't register iface observer for clat on " + mNetwork.toShortString());
+ Log.e(TAG, "Can't register iface observer for clat on " + mNetwork.toShortString());
return;
}
@@ -185,7 +185,7 @@
try {
addrStr = mNetd.clatdStart(baseIface, mNat64PrefixInUse.toString());
} catch (RemoteException | ServiceSpecificException e) {
- Slog.e(TAG, "Error starting clatd on " + baseIface + ": " + e);
+ Log.e(TAG, "Error starting clatd on " + baseIface + ": " + e);
}
mIface = CLAT_PREFIX + baseIface;
mBaseIface = baseIface;
@@ -193,7 +193,7 @@
try {
mIPv6Address = (Inet6Address) InetAddresses.parseNumericAddress(addrStr);
} catch (ClassCastException | IllegalArgumentException | NullPointerException e) {
- Slog.e(TAG, "Invalid IPv6 address " + addrStr);
+ Log.e(TAG, "Invalid IPv6 address " + addrStr);
}
if (mPrefixDiscoveryRunning && !isPrefixDiscoveryNeeded()) {
stopPrefixDiscovery();
@@ -218,7 +218,7 @@
try {
mNMService.unregisterObserver(this);
} catch (RemoteException | IllegalStateException e) {
- Slog.e(TAG, "Error unregistering clatd observer on " + mBaseIface + ": " + e);
+ Log.e(TAG, "Error unregistering clatd observer on " + mBaseIface + ": " + e);
}
mNat64PrefixInUse = null;
mIface = null;
@@ -242,37 +242,37 @@
@VisibleForTesting
protected void start() {
if (isStarted()) {
- Slog.e(TAG, "startClat: already started");
+ Log.e(TAG, "startClat: already started");
return;
}
if (mNetwork.linkProperties == null) {
- Slog.e(TAG, "startClat: Can't start clat with null LinkProperties");
+ Log.e(TAG, "startClat: Can't start clat with null LinkProperties");
return;
}
String baseIface = mNetwork.linkProperties.getInterfaceName();
if (baseIface == null) {
- Slog.e(TAG, "startClat: Can't start clat on null interface");
+ Log.e(TAG, "startClat: Can't start clat on null interface");
return;
}
// TODO: should we only do this if mNetd.clatdStart() succeeds?
- Slog.i(TAG, "Starting clatd on " + baseIface);
+ Log.i(TAG, "Starting clatd on " + baseIface);
enterStartingState(baseIface);
}
@VisibleForTesting
protected void stop() {
if (!isStarted()) {
- Slog.e(TAG, "stopClat: already stopped");
+ Log.e(TAG, "stopClat: already stopped");
return;
}
- Slog.i(TAG, "Stopping clatd on " + mBaseIface);
+ Log.i(TAG, "Stopping clatd on " + mBaseIface);
try {
mNetd.clatdStop(mBaseIface);
} catch (RemoteException | ServiceSpecificException e) {
- Slog.e(TAG, "Error stopping clatd on " + mBaseIface + ": " + e);
+ Log.e(TAG, "Error stopping clatd on " + mBaseIface + ": " + e);
}
String iface = mIface;
@@ -294,7 +294,7 @@
try {
mDnsResolver.startPrefix64Discovery(getNetId());
} catch (RemoteException | ServiceSpecificException e) {
- Slog.e(TAG, "Error starting prefix discovery on netId " + getNetId() + ": " + e);
+ Log.e(TAG, "Error starting prefix discovery on netId " + getNetId() + ": " + e);
}
mPrefixDiscoveryRunning = true;
}
@@ -303,7 +303,7 @@
try {
mDnsResolver.stopPrefix64Discovery(getNetId());
} catch (RemoteException | ServiceSpecificException e) {
- Slog.e(TAG, "Error stopping prefix discovery on netId " + getNetId() + ": " + e);
+ Log.e(TAG, "Error stopping prefix discovery on netId " + getNetId() + ": " + e);
}
mPrefixDiscoveryRunning = false;
}
@@ -320,7 +320,7 @@
try {
mDnsResolver.setPrefix64(getNetId(), prefixString);
} catch (RemoteException | ServiceSpecificException e) {
- Slog.e(TAG, "Error setting NAT64 prefix on netId " + getNetId() + " to "
+ Log.e(TAG, "Error setting NAT64 prefix on netId " + getNetId() + " to "
+ prefix + ": " + e);
}
}
@@ -328,7 +328,7 @@
private void maybeHandleNat64PrefixChange() {
final IpPrefix newPrefix = selectNat64Prefix();
if (!Objects.equals(mNat64PrefixInUse, newPrefix)) {
- Slog.d(TAG, "NAT64 prefix changed from " + mNat64PrefixInUse + " to "
+ Log.d(TAG, "NAT64 prefix changed from " + mNat64PrefixInUse + " to "
+ newPrefix);
stop();
// It's safe to call update here, even though this method is called from update, because
@@ -418,7 +418,7 @@
return;
}
- Slog.d(TAG, "clatd running, updating NAI for " + mIface);
+ Log.d(TAG, "clatd running, updating NAI for " + mIface);
for (LinkProperties stacked: oldLp.getStackedLinks()) {
if (Objects.equals(mIface, stacked.getInterfaceName())) {
lp.addStackedLink(stacked);
@@ -451,7 +451,7 @@
return new LinkAddress(
InetAddresses.parseNumericAddress(config.ipv4Addr), config.prefixLength);
} catch (IllegalArgumentException | RemoteException | ServiceSpecificException e) {
- Slog.e(TAG, "Error getting link properties: " + e);
+ Log.e(TAG, "Error getting link properties: " + e);
return null;
}
}
@@ -480,11 +480,11 @@
LinkAddress clatAddress = getLinkAddress(iface);
if (clatAddress == null) {
- Slog.e(TAG, "clatAddress was null for stacked iface " + iface);
+ Log.e(TAG, "clatAddress was null for stacked iface " + iface);
return;
}
- Slog.i(TAG, String.format("interface %s is up, adding stacked link %s on top of %s",
+ Log.i(TAG, String.format("interface %s is up, adding stacked link %s on top of %s",
mIface, mIface, mBaseIface));
enterRunningState();
LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
@@ -503,7 +503,7 @@
return;
}
- Slog.i(TAG, "interface " + iface + " removed");
+ Log.i(TAG, "interface " + iface + " removed");
// If we're running, and the interface was removed, then we didn't call stop(), and it's
// likely that clatd crashed. Ensure we call stop() so we can start clatd again. Calling
// stop() will also update LinkProperties, and if clatd crashed, the LinkProperties update
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index a9f62d9..3270dd5 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -132,6 +132,16 @@
// TODO: make this private with a getter.
public NetworkCapabilities networkCapabilities;
public final NetworkAgentConfig networkAgentConfig;
+
+ // Underlying networks declared by the agent. Only set if supportsUnderlyingNetworks is true.
+ // The networks in this list might be declared by a VPN app using setUnderlyingNetworks and are
+ // not guaranteed to be current or correct, or even to exist.
+ public @Nullable Network[] declaredUnderlyingNetworks;
+
+ // Whether this network is always metered even if its underlying networks are unmetered.
+ // Only relevant if #supportsUnderlyingNetworks is true.
+ public boolean declaredMetered;
+
// Indicates if netd has been told to create this Network. From this point on the appropriate
// routing rules are setup and routes are added so packets can begin flowing over the Network.
// This is a sticky bit; once set it is never cleared.
@@ -474,10 +484,16 @@
networkCapabilities);
}
+ /** Whether this network is a VPN. */
public boolean isVPN() {
return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
}
+ /** Whether this network might have underlying networks. Currently only true for VPNs. */
+ public boolean supportsUnderlyingNetworks() {
+ return isVPN();
+ }
+
private int getCurrentScore(boolean pretendValidated) {
// TODO: We may want to refactor this into a NetworkScore class that takes a base score from
// the NetworkAgent and signals from the NetworkAgent and uses those signals to modify the
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index 7f4fb40..7795ed3 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -35,7 +35,7 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import android.util.Slog;
+import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.widget.Toast;
@@ -175,7 +175,7 @@
final int previousEventId = mNotificationTypeMap.get(id);
final NotificationType previousNotifyType = NotificationType.getFromId(previousEventId);
if (priority(previousNotifyType) > priority(notifyType)) {
- Slog.d(TAG, String.format(
+ Log.d(TAG, String.format(
"ignoring notification %s for network %s with existing notification %s",
notifyType, id, previousNotifyType));
return;
@@ -183,7 +183,7 @@
clearNotification(id);
if (DBG) {
- Slog.d(TAG, String.format(
+ Log.d(TAG, String.format(
"showNotification tag=%s event=%s transport=%s name=%s highPriority=%s",
tag, nameOf(eventId), getTransportName(transportType), name, highPriority));
}
@@ -253,7 +253,7 @@
// are sent, but they are not implemented yet.
return;
} else {
- Slog.wtf(TAG, "Unknown notification type " + notifyType + " on network transport "
+ Log.wtf(TAG, "Unknown notification type " + notifyType + " on network transport "
+ getTransportName(transportType));
return;
}
@@ -294,7 +294,7 @@
try {
mNotificationManager.notify(tag, eventId, notification);
} catch (NullPointerException npe) {
- Slog.d(TAG, "setNotificationVisible: visible notificationManager error", npe);
+ Log.d(TAG, "setNotificationVisible: visible notificationManager error", npe);
}
}
@@ -317,13 +317,13 @@
final String tag = tagFor(id);
final int eventId = mNotificationTypeMap.get(id);
if (DBG) {
- Slog.d(TAG, String.format("clearing notification tag=%s event=%s", tag,
+ Log.d(TAG, String.format("clearing notification tag=%s event=%s", tag,
nameOf(eventId)));
}
try {
mNotificationManager.cancel(tag, eventId);
} catch (NullPointerException npe) {
- Slog.d(TAG, String.format(
+ Log.d(TAG, String.format(
"failed to clear notification tag=%s event=%s", tag, nameOf(eventId)), npe);
}
mNotificationTypeMap.delete(id);
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index a75a80a..4c63eb4 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -174,7 +174,7 @@
netdPermsUids.put(uid, netdPermsUids.get(uid) | otherNetdPerms);
}
- List<UserInfo> users = mUserManager.getUsers(true); // exclude dying users
+ List<UserInfo> users = mUserManager.getAliveUsers();
if (users != null) {
for (UserInfo user : users) {
mUsers.add(user.id);
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index 26cc3ee..5cb3d94 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -35,7 +35,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.Slog;
+import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -168,7 +168,7 @@
proxyProperties = new ProxyInfo(host, port, exclList);
}
if (!proxyProperties.isValid()) {
- if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyProperties);
+ if (DBG) Log.d(TAG, "Invalid proxy properties, ignoring: " + proxyProperties);
return;
}
@@ -223,7 +223,7 @@
if (mPacManager.setCurrentProxyScriptUrl(proxyInfo) == PacManager.DONT_SEND_BROADCAST) {
return;
}
- if (DBG) Slog.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
+ if (DBG) Log.d(TAG, "sending Proxy Broadcast for " + proxyInfo);
Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
@@ -255,7 +255,7 @@
if (proxyInfo != null && (!TextUtils.isEmpty(proxyInfo.getHost()) ||
!Uri.EMPTY.equals(proxyInfo.getPacFileUrl()))) {
if (!proxyInfo.isValid()) {
- if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
+ if (DBG) Log.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
return;
}
mGlobalProxy = new ProxyInfo(proxyInfo);
@@ -296,7 +296,7 @@
synchronized (mProxyLock) {
if (Objects.equals(mDefaultProxy, proxyInfo)) return;
if (proxyInfo != null && !proxyInfo.isValid()) {
- if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
+ if (DBG) Log.d(TAG, "Invalid proxy properties, ignoring: " + proxyInfo);
return;
}
diff --git a/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt b/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt
index a67156a..a5e44d5 100644
--- a/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt
+++ b/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt
@@ -53,8 +53,13 @@
assertParcelSane(MatchAllNetworkSpecifier(), 0)
}
- @Test @IgnoreAfter(Build.VERSION_CODES.R)
- fun testCanBeSatisfiedBy_BeforeS() {
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ @IgnoreAfter(Build.VERSION_CODES.R)
+ // Only run this test on Android R.
+ // The method - satisfiedBy() has changed to canBeSatisfiedBy() starting from Android R, so the
+ // method - canBeSatisfiedBy() cannot be found when running this test on Android Q.
+ fun testCanBeSatisfiedBy_OnlyForR() {
// MatchAllNetworkSpecifier didn't follow its parent class to change the satisfiedBy() to
// canBeSatisfiedBy(), so if a caller calls MatchAllNetworkSpecifier#canBeSatisfiedBy(), the
// NetworkSpecifier#canBeSatisfiedBy() will be called actually, and false will be returned.
diff --git a/tests/net/common/java/android/net/NetworkProviderTest.kt b/tests/net/common/java/android/net/NetworkProviderTest.kt
index dd3f5be..77e9f12 100644
--- a/tests/net/common/java/android/net/NetworkProviderTest.kt
+++ b/tests/net/common/java/android/net/NetworkProviderTest.kt
@@ -33,6 +33,9 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verifyNoMoreInteractions
import java.util.UUID
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
@@ -87,8 +90,8 @@
) = seenEvents.poll(DEFAULT_TIMEOUT_MS) { it is T && predicate(it) }
}
- private fun createNetworkProvider(): TestNetworkProvider {
- return TestNetworkProvider(context, mHandlerThread.looper)
+ private fun createNetworkProvider(ctx: Context = context): TestNetworkProvider {
+ return TestNetworkProvider(ctx, mHandlerThread.looper)
}
@Test
@@ -169,7 +172,12 @@
@Test
fun testDeclareNetworkRequestUnfulfillable() {
- val provider = createNetworkProvider()
+ val mockContext = mock(Context::class.java)
+ val provider = createNetworkProvider(mockContext)
+ // ConnectivityManager not required at creation time
+ verifyNoMoreInteractions(mockContext)
+ doReturn(mCm).`when`(mockContext).getSystemService(Context.CONNECTIVITY_SERVICE)
+
mCm.registerNetworkProvider(provider)
val specifier = StringNetworkSpecifier(UUID.randomUUID().toString())
diff --git a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
index b77ed6a..cade5ba 100644
--- a/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
+++ b/tests/net/common/java/android/net/OemNetworkPreferencesTest.java
@@ -22,10 +22,13 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import android.os.Build;
import android.util.SparseArray;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -34,7 +37,8 @@
import java.util.ArrayList;
import java.util.List;
-@RunWith(AndroidJUnit4.class)
+@IgnoreUpTo(Build.VERSION_CODES.R)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
public class OemNetworkPreferencesTest {
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index dba1856..70f6386 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -200,7 +200,8 @@
nsInstrumentation.addHttpResponse(HttpResponse(httpProbeUrl, responseCode = 204))
nsInstrumentation.addHttpResponse(HttpResponse(httpsProbeUrl, responseCode = 204))
- val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), context)
+ val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), null /* ncTemplate */,
+ context)
networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS)
na.addCapability(NET_CAPABILITY_INTERNET)
@@ -238,7 +239,7 @@
val lp = LinkProperties()
lp.captivePortalApiUrl = Uri.parse(apiUrl)
- val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, lp, context)
+ val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, lp, null /* ncTemplate */, context)
networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS)
na.addCapability(NET_CAPABILITY_INTERNET)
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index 85704d0..2a24d1a 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -72,12 +72,12 @@
private long mKeepaliveResponseDelay = 0L;
private Integer mExpectedKeepaliveSlot = null;
- public NetworkAgentWrapper(int transport, LinkProperties linkProperties, Context context)
- throws Exception {
+ public NetworkAgentWrapper(int transport, LinkProperties linkProperties,
+ NetworkCapabilities ncTemplate, Context context) throws Exception {
final int type = transportToLegacyType(transport);
final String typeName = ConnectivityManager.getNetworkTypeName(type);
mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
- mNetworkCapabilities = new NetworkCapabilities();
+ mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities();
mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
mNetworkCapabilities.addTransportType(transport);
switch (transport) {
diff --git a/tests/net/integration/util/com/android/server/TestNetIdManager.kt b/tests/net/integration/util/com/android/server/TestNetIdManager.kt
index eb290dc..938a694 100644
--- a/tests/net/integration/util/com/android/server/TestNetIdManager.kt
+++ b/tests/net/integration/util/com/android/server/TestNetIdManager.kt
@@ -35,4 +35,5 @@
private val nextId = AtomicInteger(MAX_NET_ID)
override fun reserveNetId() = nextId.decrementAndGet()
override fun releaseNetId(id: Int) = Unit
+ fun peekNextNetId() = nextId.get() - 1
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 3f1fabf..7a1cb25 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -18,6 +18,8 @@
import static android.Manifest.permission.CHANGE_NETWORK_STATE;
import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
+import static android.content.Intent.ACTION_USER_ADDED;
+import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
@@ -54,8 +56,10 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
+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_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
@@ -259,7 +263,10 @@
import org.mockito.Spy;
import org.mockito.stubbing.Answer;
+import java.io.FileDescriptor;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import java.net.DatagramSocket;
import java.net.Inet4Address;
import java.net.Inet6Address;
@@ -322,6 +329,7 @@
private static final String MOBILE_IFNAME = "test_rmnet_data0";
private static final String WIFI_IFNAME = "test_wlan0";
private static final String WIFI_WOL_IFNAME = "test_wlan_wol";
+ private static final String VPN_IFNAME = "tun10042";
private static final String TEST_PACKAGE_NAME = "com.android.test.package";
private static final String[] EMPTY_STRING_ARRAY = new String[0];
@@ -339,6 +347,7 @@
private INetworkPolicyListener mPolicyListener;
private WrappedMultinetworkPolicyTracker mPolicyTracker;
private HandlerThread mAlarmManagerThread;
+ private TestNetIdManager mNetIdManager;
@Mock IIpConnectivityMetrics mIpConnectivityMetrics;
@Mock IpConnectivityMetrics.Logger mMetricsService;
@@ -617,12 +626,17 @@
private String mRedirectUrl;
TestNetworkAgentWrapper(int transport) throws Exception {
- this(transport, new LinkProperties());
+ this(transport, new LinkProperties(), null);
}
TestNetworkAgentWrapper(int transport, LinkProperties linkProperties)
throws Exception {
- super(transport, linkProperties, mServiceContext);
+ this(transport, linkProperties, null);
+ }
+
+ private TestNetworkAgentWrapper(int transport, LinkProperties linkProperties,
+ NetworkCapabilities ncTemplate) throws Exception {
+ super(transport, linkProperties, ncTemplate, mServiceContext);
// Waits for the NetworkAgent to be registered, which includes the creation of the
// NetworkMonitor.
@@ -1017,46 +1031,38 @@
}
}
+ private Set<UidRange> uidRangesForUid(int uid) {
+ final ArraySet<UidRange> ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
+ return ranges;
+ }
+
private static Looper startHandlerThreadAndReturnLooper() {
final HandlerThread handlerThread = new HandlerThread("MockVpnThread");
handlerThread.start();
return handlerThread.getLooper();
}
- private class MockVpn extends Vpn {
- // TODO : the interactions between this mock and the mock network agent are too
- // hard to get right at this moment, because it's unclear in which case which
- // target needs to get a method call or both, and in what order. It's because
- // MockNetworkAgent wants to manage its own NetworkCapabilities, but the Vpn
- // parent class of MockVpn agent wants that responsibility.
- // That being said inside the test it should be possible to make the interactions
- // harder to get wrong with precise speccing, judicious comments, helper methods
- // and a few sprinkled assertions.
-
- private boolean mConnected = false;
+ private class MockVpn extends Vpn implements TestableNetworkCallback.HasNetwork {
// Careful ! This is different from mNetworkAgent, because MockNetworkAgent does
// not inherit from NetworkAgent.
private TestNetworkAgentWrapper mMockNetworkAgent;
- private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
+ private boolean mAgentRegistered = false;
+ private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
private VpnInfo mVpnInfo;
- private Network[] mUnderlyingNetworks;
public MockVpn(int userId) {
super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
userId, mock(KeyStore.class));
- }
-
- public void setNetworkAgent(TestNetworkAgentWrapper agent) {
- agent.waitForIdle(TIMEOUT_MS);
- mMockNetworkAgent = agent;
- mNetworkAgent = agent.getNetworkAgent();
- mNetworkCapabilities.set(agent.getNetworkCapabilities());
+ mConfig = new VpnConfig();
}
public void setUids(Set<UidRange> uids) {
mNetworkCapabilities.setUids(uids);
- updateCapabilities(null /* defaultNetwork */);
+ if (mAgentRegistered) {
+ mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities, true);
+ }
}
public void setVpnType(int vpnType) {
@@ -1064,21 +1070,13 @@
}
@Override
+ public Network getNetwork() {
+ return (mMockNetworkAgent == null) ? null : mMockNetworkAgent.getNetwork();
+ }
+
+ @Override
public int getNetId() {
- if (mMockNetworkAgent == null) {
- return NETID_UNSET;
- }
- return mMockNetworkAgent.getNetwork().netId;
- }
-
- @Override
- public boolean appliesToUid(int uid) {
- return mConnected; // Trickery to simplify testing.
- }
-
- @Override
- protected boolean isCallerEstablishedOwnerLocked() {
- return mConnected; // Similar trickery
+ return (mMockNetworkAgent == null) ? NETID_UNSET : mMockNetworkAgent.getNetwork().netId;
}
@Override
@@ -1086,41 +1084,76 @@
return mVpnType;
}
- private void connect(boolean isAlwaysMetered) {
+ private void registerAgent(boolean isAlwaysMetered, Set<UidRange> uids, LinkProperties lp)
+ throws Exception {
+ if (mAgentRegistered) throw new IllegalStateException("already registered");
+ setUids(uids);
+ if (!isAlwaysMetered) mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
+ mInterface = VPN_IFNAME;
+ mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp,
+ mNetworkCapabilities);
+ mMockNetworkAgent.waitForIdle(TIMEOUT_MS);
+ verify(mNetworkManagementService, times(1))
+ .addVpnUidRanges(eq(mMockVpn.getNetId()), eq(uids.toArray(new UidRange[0])));
+ verify(mNetworkManagementService, never())
+ .removeVpnUidRanges(eq(mMockVpn.getNetId()), any());
+ mAgentRegistered = true;
mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
- mConnected = true;
- mConfig = new VpnConfig();
- mConfig.isMetered = isAlwaysMetered;
+ mNetworkAgent = mMockNetworkAgent.getNetworkAgent();
}
- public void connectAsAlwaysMetered() {
- connect(true /* isAlwaysMetered */);
+ private void registerAgent(Set<UidRange> uids) throws Exception {
+ registerAgent(false /* isAlwaysMetered */, uids, new LinkProperties());
}
- public void connect() {
- connect(false /* isAlwaysMetered */);
+ private void connect(boolean validated, boolean hasInternet, boolean isStrictMode) {
+ mMockNetworkAgent.connect(validated, hasInternet, isStrictMode);
}
- @Override
- public NetworkCapabilities updateCapabilities(Network defaultNetwork) {
- if (!mConnected) return null;
- super.updateCapabilities(defaultNetwork);
- // Because super.updateCapabilities will update the capabilities of the agent but
- // not the mock agent, the mock agent needs to know about them.
- copyCapabilitiesToNetworkAgent();
- return new NetworkCapabilities(mNetworkCapabilities);
+ private void connect(boolean validated) {
+ mMockNetworkAgent.connect(validated);
}
- private void copyCapabilitiesToNetworkAgent() {
- if (null != mMockNetworkAgent) {
- mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities,
- false /* sendToConnectivityService */);
- }
+ private TestNetworkAgentWrapper getAgent() {
+ return mMockNetworkAgent;
+ }
+
+ public void establish(LinkProperties lp, int uid, Set<UidRange> ranges, boolean validated,
+ boolean hasInternet, boolean isStrictMode) throws Exception {
+ mNetworkCapabilities.setOwnerUid(uid);
+ mNetworkCapabilities.setAdministratorUids(new int[]{uid});
+ registerAgent(false, ranges, lp);
+ connect(validated, hasInternet, isStrictMode);
+ waitForIdle();
+ }
+
+ public void establish(LinkProperties lp, int uid, Set<UidRange> ranges) throws Exception {
+ establish(lp, uid, ranges, true, true, false);
+ }
+
+ public void establishForMyUid(LinkProperties lp) throws Exception {
+ final int uid = Process.myUid();
+ establish(lp, uid, uidRangesForUid(uid), true, true, false);
+ }
+
+ public void establishForMyUid(boolean validated, boolean hasInternet, boolean isStrictMode)
+ throws Exception {
+ final int uid = Process.myUid();
+ establish(new LinkProperties(), uid, uidRangesForUid(uid), validated, hasInternet,
+ isStrictMode);
+ }
+
+ public void establishForMyUid() throws Exception {
+ establishForMyUid(new LinkProperties());
+ }
+
+ public void sendLinkProperties(LinkProperties lp) {
+ mMockNetworkAgent.sendLinkProperties(lp);
}
public void disconnect() {
- mConnected = false;
- mConfig = null;
+ if (mMockNetworkAgent != null) mMockNetworkAgent.disconnect();
+ mAgentRegistered = false;
}
@Override
@@ -1133,18 +1166,6 @@
private synchronized void setVpnInfo(VpnInfo vpnInfo) {
mVpnInfo = vpnInfo;
}
-
- @Override
- public synchronized Network[] getUnderlyingNetworks() {
- if (mUnderlyingNetworks != null) return mUnderlyingNetworks;
-
- return super.getUnderlyingNetworks();
- }
-
- /** Don't override behavior for {@link Vpn#setUnderlyingNetworks}. */
- private synchronized void overrideUnderlyingNetworks(Network[] underlyingNetworks) {
- mUnderlyingNetworks = underlyingNetworks;
- }
}
private void mockVpn(int uid) {
@@ -1207,12 +1228,14 @@
@Before
public void setUp() throws Exception {
+ mNetIdManager = new TestNetIdManager();
+
mContext = InstrumentationRegistry.getContext();
MockitoAnnotations.initMocks(this);
when(mMetricsService.defaultNetworkMetrics()).thenReturn(mDefaultNetworkMetrics);
- when(mUserManager.getUsers(eq(true))).thenReturn(
+ when(mUserManager.getAliveUsers()).thenReturn(
Arrays.asList(new UserInfo[] {
new UserInfo(VPN_USER, "", 0),
}));
@@ -1277,7 +1300,7 @@
doNothing().when(mSystemProperties).setTcpInitRwnd(anyInt());
final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class);
doReturn(mCsHandlerThread).when(deps).makeHandlerThread();
- doReturn(new TestNetIdManager()).when(deps).makeNetIdManager();
+ doReturn(mNetIdManager).when(deps).makeNetIdManager();
doReturn(mNetworkStack).when(deps).getNetworkStack();
doReturn(mSystemProperties).when(deps).getSystemProperties();
doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
@@ -1335,6 +1358,9 @@
mEthernetNetworkAgent.disconnect();
mEthernetNetworkAgent = null;
}
+ mMockVpn.disconnect();
+ waitForIdle();
+
FakeSettingsProvider.clearSettingsProvider();
mCsHandlerThread.quitSafely();
@@ -3218,20 +3244,12 @@
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
- final int uid = Process.myUid();
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true);
- mMockVpn.connect();
- defaultNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
+ mMockVpn.establishForMyUid();
+ defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- vpnNetworkAgent.disconnect();
- defaultNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ mMockVpn.disconnect();
+ defaultNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
}
@@ -4808,13 +4826,52 @@
mCm.unregisterNetworkCallback(networkCallback);
}
+ private <T> void assertSameElementsNoDuplicates(T[] expected, T[] actual) {
+ // Easier to implement than a proper "assertSameElements" method that also correctly deals
+ // with duplicates.
+ final String msg = Arrays.toString(expected) + " != " + Arrays.toString(actual);
+ assertEquals(msg, expected.length, actual.length);
+ Set expectedSet = new ArraySet<>(Arrays.asList(expected));
+ assertEquals("expected contains duplicates", expectedSet.size(), expected.length);
+ // actual cannot have duplicates because it's the same length and has the same elements.
+ Set actualSet = new ArraySet<>(Arrays.asList(actual));
+ assertEquals(expectedSet, actualSet);
+ }
+
+ private void expectForceUpdateIfaces(Network[] networks, String defaultIface,
+ Integer vpnUid, String vpnIfname, String[] underlyingIfaces) throws Exception {
+ ArgumentCaptor<Network[]> networksCaptor = ArgumentCaptor.forClass(Network[].class);
+ ArgumentCaptor<VpnInfo[]> vpnInfosCaptor = ArgumentCaptor.forClass(VpnInfo[].class);
+
+ verify(mStatsService, atLeastOnce()).forceUpdateIfaces(networksCaptor.capture(),
+ any(NetworkState[].class), eq(defaultIface), vpnInfosCaptor.capture());
+
+ assertSameElementsNoDuplicates(networksCaptor.getValue(), networks);
+
+ VpnInfo[] infos = vpnInfosCaptor.getValue();
+ if (vpnUid != null) {
+ assertEquals("Should have exactly one VPN:", 1, infos.length);
+ VpnInfo info = infos[0];
+ assertEquals("Unexpected VPN owner:", (int) vpnUid, info.ownerUid);
+ assertEquals("Unexpected VPN interface:", vpnIfname, info.vpnIface);
+ assertSameElementsNoDuplicates(underlyingIfaces, info.underlyingIfaces);
+ } else {
+ assertEquals(0, infos.length);
+ return;
+ }
+ }
+
+ private void expectForceUpdateIfaces(Network[] networks, String defaultIface) throws Exception {
+ expectForceUpdateIfaces(networks, defaultIface, null, null, new String[0]);
+ }
+
@Test
public void testStatsIfacesChanged() throws Exception {
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()};
- Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()};
+ final Network[] onlyCell = new Network[] {mCellNetworkAgent.getNetwork()};
+ final Network[] onlyWifi = new Network[] {mWiFiNetworkAgent.getNetwork()};
LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
@@ -4825,9 +4882,7 @@
mCellNetworkAgent.connect(false);
mCellNetworkAgent.sendLinkProperties(cellLp);
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
reset(mStatsService);
// Default network switch should update ifaces.
@@ -4835,32 +4890,24 @@
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
waitForIdle();
assertEquals(wifiLp, mService.getActiveLinkProperties());
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyWifi), any(NetworkState[].class), eq(WIFI_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyWifi, WIFI_IFNAME);
reset(mStatsService);
// Disconnect should update ifaces.
mWiFiNetworkAgent.disconnect();
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class),
- eq(MOBILE_IFNAME), eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
reset(mStatsService);
// Metered change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
reset(mStatsService);
mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
reset(mStatsService);
// Captive portal change shouldn't update ifaces
@@ -4874,9 +4921,130 @@
// Roaming change should update ifaces
mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
waitForIdle();
- verify(mStatsService, atLeastOnce())
- .forceUpdateIfaces(eq(onlyCell), any(NetworkState[].class), eq(MOBILE_IFNAME),
- eq(new VpnInfo[0]));
+ expectForceUpdateIfaces(onlyCell, MOBILE_IFNAME);
+ reset(mStatsService);
+
+ // Test VPNs.
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(VPN_IFNAME);
+
+ mMockVpn.establishForMyUid(lp);
+
+ final Network[] cellAndVpn = new Network[] {
+ mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork()};
+
+ // A VPN with default (null) underlying networks sets the underlying network's interfaces...
+ expectForceUpdateIfaces(cellAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{MOBILE_IFNAME});
+
+ // ...and updates them as the default network switches.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false);
+ mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+ final Network[] onlyNull = new Network[]{null};
+ final Network[] wifiAndVpn = new Network[] {
+ mWiFiNetworkAgent.getNetwork(), mMockVpn.getNetwork()};
+ final Network[] cellAndWifi = new Network[] {
+ mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork()};
+ final Network[] cellNullAndWifi = new Network[] {
+ mCellNetworkAgent.getNetwork(), null, mWiFiNetworkAgent.getNetwork()};
+
+ waitForIdle();
+ assertEquals(wifiLp, mService.getActiveLinkProperties());
+ expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{WIFI_IFNAME});
+ reset(mStatsService);
+
+ // A VPN that sets its underlying networks passes the underlying interfaces, and influences
+ // the default interface sent to NetworkStatsService by virtue of applying to the system
+ // server UID (or, in this test, to the test's UID). This is the reason for sending
+ // MOBILE_IFNAME even though the default network is wifi.
+ // TODO: fix this to pass in the actual default network interface. Whether or not the VPN
+ // applies to the system server UID should not have any bearing on network stats.
+ mService.setUnderlyingNetworksForVpn(onlyCell);
+ waitForIdle();
+ expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{MOBILE_IFNAME});
+ reset(mStatsService);
+
+ mService.setUnderlyingNetworksForVpn(cellAndWifi);
+ waitForIdle();
+ expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{MOBILE_IFNAME, WIFI_IFNAME});
+ reset(mStatsService);
+
+ // Null underlying networks are ignored.
+ mService.setUnderlyingNetworksForVpn(cellNullAndWifi);
+ waitForIdle();
+ expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{MOBILE_IFNAME, WIFI_IFNAME});
+ reset(mStatsService);
+
+ // If an underlying network disconnects, that interface should no longer be underlying.
+ // This doesn't actually work because disconnectAndDestroyNetwork only notifies
+ // NetworkStatsService before the underlying network is actually removed. So the underlying
+ // network will only be removed if notifyIfacesChangedForNetworkStats is called again. This
+ // could result in incorrect data usage measurements if the interface used by the
+ // disconnected network is reused by a system component that does not register an agent for
+ // it (e.g., tethering).
+ mCellNetworkAgent.disconnect();
+ waitForIdle();
+ assertNull(mService.getLinkProperties(mCellNetworkAgent.getNetwork()));
+ expectForceUpdateIfaces(wifiAndVpn, MOBILE_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{MOBILE_IFNAME, WIFI_IFNAME});
+
+ // Confirm that we never tell NetworkStatsService that cell is no longer the underlying
+ // network for the VPN...
+ verify(mStatsService, never()).forceUpdateIfaces(any(Network[].class),
+ any(NetworkState[].class), any() /* anyString() doesn't match null */,
+ argThat(infos -> infos[0].underlyingIfaces.length == 1
+ && WIFI_IFNAME.equals(infos[0].underlyingIfaces[0])));
+ verifyNoMoreInteractions(mStatsService);
+ reset(mStatsService);
+
+ // ... but if something else happens that causes notifyIfacesChangedForNetworkStats to be
+ // called again, it does. For example, connect Ethernet, but with a low score, such that it
+ // does not become the default network.
+ mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
+ mEthernetNetworkAgent.adjustScore(-40);
+ mEthernetNetworkAgent.connect(false);
+ waitForIdle();
+ verify(mStatsService).forceUpdateIfaces(any(Network[].class),
+ any(NetworkState[].class), any() /* anyString() doesn't match null */,
+ argThat(vpnInfos -> vpnInfos[0].underlyingIfaces.length == 1
+ && WIFI_IFNAME.equals(vpnInfos[0].underlyingIfaces[0])));
+ mEthernetNetworkAgent.disconnect();
+ waitForIdle();
+ reset(mStatsService);
+
+ // When a VPN declares no underlying networks (i.e., no connectivity), getAllVpnInfo
+ // does not return the VPN, so CS does not pass it to NetworkStatsService. This causes
+ // NetworkStatsFactory#adjustForTunAnd464Xlat not to attempt any VPN data migration, which
+ // is probably a performance improvement (though it's very unlikely that a VPN would declare
+ // no underlying networks).
+ // Also, for the same reason as above, the active interface passed in is null.
+ mService.setUnderlyingNetworksForVpn(new Network[0]);
+ waitForIdle();
+ expectForceUpdateIfaces(wifiAndVpn, null);
+ reset(mStatsService);
+
+ // Specifying only a null underlying network is the same as no networks.
+ mService.setUnderlyingNetworksForVpn(onlyNull);
+ waitForIdle();
+ expectForceUpdateIfaces(wifiAndVpn, null);
+ reset(mStatsService);
+
+ // Specifying networks that are all disconnected is the same as specifying no networks.
+ mService.setUnderlyingNetworksForVpn(onlyCell);
+ waitForIdle();
+ expectForceUpdateIfaces(wifiAndVpn, null);
+ reset(mStatsService);
+
+ // Passing in null again means follow the default network again.
+ mService.setUnderlyingNetworksForVpn(null);
+ waitForIdle();
+ expectForceUpdateIfaces(wifiAndVpn, WIFI_IFNAME, Process.myUid(), VPN_IFNAME,
+ new String[]{WIFI_IFNAME});
reset(mStatsService);
}
@@ -5232,6 +5400,158 @@
}
@Test
+ public void testApplyUnderlyingCapabilities() throws Exception {
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mCellNetworkAgent.connect(false /* validated */);
+ mWiFiNetworkAgent.connect(false /* validated */);
+
+ final NetworkCapabilities cellNc = new NetworkCapabilities()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+ .setLinkDownstreamBandwidthKbps(10);
+ final NetworkCapabilities wifiNc = new NetworkCapabilities()
+ .addTransportType(TRANSPORT_WIFI)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_METERED)
+ .addCapability(NET_CAPABILITY_NOT_ROAMING)
+ .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+ .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ .setLinkUpstreamBandwidthKbps(20);
+ mCellNetworkAgent.setNetworkCapabilities(cellNc, true /* sendToConnectivityService */);
+ mWiFiNetworkAgent.setNetworkCapabilities(wifiNc, true /* sendToConnectivityService */);
+ waitForIdle();
+
+ final Network mobile = mCellNetworkAgent.getNetwork();
+ final Network wifi = mWiFiNetworkAgent.getNetwork();
+
+ final NetworkCapabilities initialCaps = new NetworkCapabilities();
+ initialCaps.addCapability(NET_CAPABILITY_INTERNET);
+ initialCaps.removeCapability(NET_CAPABILITY_NOT_VPN);
+
+ final NetworkCapabilities withNoUnderlying = new NetworkCapabilities();
+ withNoUnderlying.addCapability(NET_CAPABILITY_INTERNET);
+ withNoUnderlying.addCapability(NET_CAPABILITY_NOT_CONGESTED);
+ withNoUnderlying.addCapability(NET_CAPABILITY_NOT_ROAMING);
+ withNoUnderlying.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ withNoUnderlying.addTransportType(TRANSPORT_VPN);
+ withNoUnderlying.removeCapability(NET_CAPABILITY_NOT_VPN);
+
+ final NetworkCapabilities withMobileUnderlying = new NetworkCapabilities(withNoUnderlying);
+ withMobileUnderlying.addTransportType(TRANSPORT_CELLULAR);
+ withMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_ROAMING);
+ withMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ withMobileUnderlying.setLinkDownstreamBandwidthKbps(10);
+
+ final NetworkCapabilities withWifiUnderlying = new NetworkCapabilities(withNoUnderlying);
+ withWifiUnderlying.addTransportType(TRANSPORT_WIFI);
+ withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED);
+ withWifiUnderlying.setLinkUpstreamBandwidthKbps(20);
+
+ final NetworkCapabilities withWifiAndMobileUnderlying =
+ new NetworkCapabilities(withNoUnderlying);
+ withWifiAndMobileUnderlying.addTransportType(TRANSPORT_CELLULAR);
+ withWifiAndMobileUnderlying.addTransportType(TRANSPORT_WIFI);
+ withWifiAndMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED);
+ withWifiAndMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_ROAMING);
+ withWifiAndMobileUnderlying.setLinkDownstreamBandwidthKbps(10);
+ withWifiAndMobileUnderlying.setLinkUpstreamBandwidthKbps(20);
+
+ NetworkCapabilities caps = new NetworkCapabilities(initialCaps);
+ final boolean notDeclaredMetered = false;
+ mService.applyUnderlyingCapabilities(new Network[]{}, caps, notDeclaredMetered);
+ assertEquals(withNoUnderlying, caps);
+
+ caps = new NetworkCapabilities(initialCaps);
+ mService.applyUnderlyingCapabilities(new Network[]{null}, caps, notDeclaredMetered);
+ assertEquals(withNoUnderlying, caps);
+
+ caps = new NetworkCapabilities(initialCaps);
+ mService.applyUnderlyingCapabilities(new Network[]{mobile}, caps, notDeclaredMetered);
+ assertEquals(withMobileUnderlying, caps);
+
+ mService.applyUnderlyingCapabilities(new Network[]{wifi}, caps, notDeclaredMetered);
+ assertEquals(withWifiUnderlying, caps);
+
+ final boolean isDeclaredMetered = true;
+ withWifiUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED);
+ caps = new NetworkCapabilities(initialCaps);
+ mService.applyUnderlyingCapabilities(new Network[]{wifi}, caps, isDeclaredMetered);
+ assertEquals(withWifiUnderlying, caps);
+
+ caps = new NetworkCapabilities(initialCaps);
+ mService.applyUnderlyingCapabilities(new Network[]{mobile, wifi}, caps, isDeclaredMetered);
+ assertEquals(withWifiAndMobileUnderlying, caps);
+
+ withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED);
+ caps = new NetworkCapabilities(initialCaps);
+ mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi},
+ caps, notDeclaredMetered);
+ assertEquals(withWifiAndMobileUnderlying, caps);
+
+ caps = new NetworkCapabilities(initialCaps);
+ mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi},
+ caps, notDeclaredMetered);
+ assertEquals(withWifiAndMobileUnderlying, caps);
+
+ mService.applyUnderlyingCapabilities(null, caps, notDeclaredMetered);
+ assertEquals(withWifiUnderlying, caps);
+ }
+
+ @Test
+ public void testVpnConnectDisconnectUnderlyingNetwork() throws Exception {
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN).build();
+
+ mCm.registerNetworkCallback(request, callback);
+
+ // Bring up a VPN that specifies an underlying network that does not exist yet.
+ // Note: it's sort of meaningless for a VPN app to declare a network that doesn't exist yet,
+ // (and doing so is difficult without using reflection) but it's good to test that the code
+ // behaves approximately correctly.
+ mMockVpn.establishForMyUid(false, true, false);
+ final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
+ mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork});
+ callback.expectAvailableCallbacksUnvalidated(mMockVpn);
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasTransport(TRANSPORT_VPN));
+ assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasTransport(TRANSPORT_WIFI));
+
+ // Make that underlying network connect, and expect to see its capabilities immediately
+ // reflected in the VPN's capabilities.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ assertEquals(wifiNetwork, mWiFiNetworkAgent.getNetwork());
+ mWiFiNetworkAgent.connect(false);
+ // TODO: the callback for the VPN happens before any callbacks are called for the wifi
+ // network that has just connected. There appear to be two issues here:
+ // 1. The VPN code will accept an underlying network as soon as getNetworkCapabilities() for
+ // it returns non-null (which happens very early, during handleRegisterNetworkAgent).
+ // This is not correct because that that point the network is not connected and cannot
+ // pass any traffic.
+ // 2. When a network connects, updateNetworkInfo propagates underlying network capabilities
+ // before rematching networks.
+ // Given that this scenario can't really happen, this is probably fine for now.
+ callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasTransport(TRANSPORT_VPN));
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasTransport(TRANSPORT_WIFI));
+
+ // Disconnect the network, and expect to see the VPN capabilities change accordingly.
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ callback.expectCapabilitiesThat(mMockVpn, (nc) ->
+ nc.getTransportTypes().length == 1 && nc.hasTransport(TRANSPORT_VPN));
+
+ mMockVpn.disconnect();
+ mCm.unregisterNetworkCallback(callback);
+ }
+
+ @Test
public void testVpnNetworkActive() throws Exception {
final int uid = Process.myUid();
@@ -5265,42 +5585,32 @@
vpnNetworkCallback.assertNoCallback();
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
+ final Set<UidRange> ranges = uidRangesForUid(uid);
+ mMockVpn.registerAgent(ranges);
+ mService.setUnderlyingNetworksForVpn(new Network[0]);
+
// VPN networks do not satisfy the default request and are automatically validated
// by NetworkMonitor
assertFalse(NetworkMonitorUtils.isValidationRequired(
- vpnNetworkAgent.getNetworkCapabilities()));
- vpnNetworkAgent.setNetworkValid(false /* isStrictMode */);
+ mMockVpn.getAgent().getNetworkCapabilities()));
+ mMockVpn.getAgent().setNetworkValid(false /* isStrictMode */);
- vpnNetworkAgent.connect(false);
- mMockVpn.connect();
- mMockVpn.setUnderlyingNetworks(new Network[0]);
+ mMockVpn.connect(false);
- genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
+ genericNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
- defaultCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
- assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
-
- genericNetworkCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
- genericNotVpnNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent, nc -> null == nc.getUids());
- defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
ranges.clear();
- vpnNetworkAgent.setUids(ranges);
+ mMockVpn.setUids(ranges);
- genericNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
// TODO : The default network callback should actually get a LOST call here (also see the
// comment below for AVAILABLE). This is because ConnectivityService does not look at UID
@@ -5308,19 +5618,18 @@
// can't currently update their UIDs without disconnecting, so this does not matter too
// much, but that is the reason the test here has to check for an update to the
// capabilities instead of the expected LOST then AVAILABLE.
- defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
ranges.add(new UidRange(uid, uid));
mMockVpn.setUids(ranges);
- vpnNetworkAgent.setUids(ranges);
- genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
+ genericNetworkCallback.expectAvailableCallbacksValidated(mMockVpn);
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
+ vpnNetworkCallback.expectAvailableCallbacksValidated(mMockVpn);
// TODO : Here like above, AVAILABLE would be correct, but because this can't actually
// happen outside of the test, ConnectivityService does not rematch callbacks.
- defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, vpnNetworkAgent);
+ defaultCallback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
mWiFiNetworkAgent.disconnect();
@@ -5330,13 +5639,13 @@
vpnNetworkCallback.assertNoCallback();
defaultCallback.assertNoCallback();
- vpnNetworkAgent.disconnect();
+ mMockVpn.disconnect();
- genericNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ genericNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
- vpnNetworkCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
- defaultCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ vpnNetworkCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
+ defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
assertEquals(null, mCm.getActiveNetwork());
mCm.unregisterNetworkCallback(genericNetworkCallback);
@@ -5358,20 +5667,13 @@
defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
- mMockVpn.connect();
defaultCallback.assertNoCallback();
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- vpnNetworkAgent.disconnect();
+ mMockVpn.disconnect();
defaultCallback.assertNoCallback();
mCm.unregisterNetworkCallback(defaultCallback);
@@ -5390,21 +5692,14 @@
defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */,
+ mMockVpn.establishForMyUid(true /* validated */, true /* hasInternet */,
false /* isStrictMode */);
- mMockVpn.connect();
- defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
+ defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
- vpnNetworkAgent.disconnect();
- defaultCallback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ mMockVpn.disconnect();
+ defaultCallback.expectCallback(CallbackEntry.LOST, mMockVpn);
defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
mCm.unregisterNetworkCallback(defaultCallback);
@@ -5422,44 +5717,36 @@
callback.assertNoCallback();
// Bring up a VPN that has the INTERNET capability, initially unvalidated.
- final int uid = Process.myUid();
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */,
+ mMockVpn.establishForMyUid(false /* validated */, true /* hasInternet */,
false /* isStrictMode */);
- mMockVpn.connect();
// Even though the VPN is unvalidated, it becomes the default network for our app.
- callback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
+ callback.expectAvailableCallbacksUnvalidated(mMockVpn);
callback.assertNoCallback();
- assertTrue(vpnNetworkAgent.getScore() > mEthernetNetworkAgent.getScore());
- assertEquals(ConnectivityConstants.VPN_DEFAULT_SCORE, vpnNetworkAgent.getScore());
- assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertTrue(mMockVpn.getAgent().getScore() > mEthernetNetworkAgent.getScore());
+ assertEquals(ConnectivityConstants.VPN_DEFAULT_SCORE, mMockVpn.getAgent().getScore());
+ assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
- NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+ NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
assertFalse(nc.hasCapability(NET_CAPABILITY_VALIDATED));
assertTrue(nc.hasCapability(NET_CAPABILITY_INTERNET));
assertFalse(NetworkMonitorUtils.isValidationRequired(
- vpnNetworkAgent.getNetworkCapabilities()));
+ mMockVpn.getAgent().getNetworkCapabilities()));
assertTrue(NetworkMonitorUtils.isPrivateDnsValidationRequired(
- vpnNetworkAgent.getNetworkCapabilities()));
+ mMockVpn.getAgent().getNetworkCapabilities()));
// Pretend that the VPN network validates.
- vpnNetworkAgent.setNetworkValid(false /* isStrictMode */);
- vpnNetworkAgent.mNetworkMonitor.forceReevaluation(Process.myUid());
+ mMockVpn.getAgent().setNetworkValid(false /* isStrictMode */);
+ mMockVpn.getAgent().mNetworkMonitor.forceReevaluation(Process.myUid());
// Expect to see the validated capability, but no other changes, because the VPN is already
// the default network for the app.
- callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, vpnNetworkAgent);
+ callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mMockVpn);
callback.assertNoCallback();
- vpnNetworkAgent.disconnect();
- callback.expectCallback(CallbackEntry.LOST, vpnNetworkAgent);
+ mMockVpn.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mMockVpn);
callback.expectAvailableCallbacksValidated(mEthernetNetworkAgent);
}
@@ -5481,21 +5768,15 @@
mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
mCellNetworkAgent.connect(true);
- final TestNetworkAgentWrapper vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.connect();
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
- vpnNetworkCallback.expectAvailableCallbacks(vpnNetworkAgent.getNetwork(),
+ vpnNetworkCallback.expectAvailableCallbacks(mMockVpn.getNetwork(),
false /* suspended */, false /* validated */, false /* blocked */, TIMEOUT_MS);
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent.getNetwork(), TIMEOUT_MS,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn.getNetwork(), TIMEOUT_MS,
nc -> nc.hasCapability(NET_CAPABILITY_VALIDATED));
- final NetworkCapabilities nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+ final NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
assertTrue(nc.hasTransport(TRANSPORT_VPN));
assertTrue(nc.hasTransport(TRANSPORT_CELLULAR));
assertFalse(nc.hasTransport(TRANSPORT_WIFI));
@@ -5517,18 +5798,11 @@
mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
vpnNetworkCallback.assertNoCallback();
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.connect();
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
- vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
- nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+ vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
assertTrue(nc.hasTransport(TRANSPORT_VPN));
assertFalse(nc.hasTransport(TRANSPORT_CELLULAR));
assertFalse(nc.hasTransport(TRANSPORT_WIFI));
@@ -5545,7 +5819,7 @@
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5559,7 +5833,7 @@
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5569,7 +5843,7 @@
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5577,27 +5851,27 @@
// Remove NOT_SUSPENDED from the only network and observe VPN is now suspended.
mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
- vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, vpnNetworkAgent);
+ vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
// Add NOT_SUSPENDED again and observe VPN is no longer suspended.
mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
- vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, vpnNetworkAgent);
+ vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
// Use Wifi but not cell. Note the VPN is now unmetered and not suspended.
mService.setUnderlyingNetworksForVpn(
new Network[] { mWiFiNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5607,7 +5881,7 @@
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5620,7 +5894,7 @@
// Stop using WiFi. The VPN is suspended again.
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5634,7 +5908,7 @@
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
@@ -5645,14 +5919,14 @@
// Disconnect cell. Receive update without even removing the dead network from the
// underlying networks – it's dead anyway. Not metered any more.
mCellNetworkAgent.disconnect();
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& caps.hasCapability(NET_CAPABILITY_NOT_METERED));
// Disconnect wifi too. No underlying networks means this is now metered.
mWiFiNetworkAgent.disconnect();
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
@@ -5673,18 +5947,11 @@
mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
vpnNetworkCallback.assertNoCallback();
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.connect();
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */,
+ mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
- vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
- nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+ vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
assertTrue(nc.hasTransport(TRANSPORT_VPN));
assertFalse(nc.hasTransport(TRANSPORT_CELLULAR));
assertFalse(nc.hasTransport(TRANSPORT_WIFI));
@@ -5696,7 +5963,7 @@
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
@@ -5706,7 +5973,7 @@
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mWiFiNetworkAgent.connect(true);
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& caps.hasCapability(NET_CAPABILITY_NOT_METERED));
@@ -5718,7 +5985,7 @@
// Disconnect wifi too. Now we have no default network.
mWiFiNetworkAgent.disconnect();
- vpnNetworkCallback.expectCapabilitiesThat(vpnNetworkAgent,
+ vpnNetworkCallback.expectCapabilitiesThat(mMockVpn,
(caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED));
@@ -5727,6 +5994,86 @@
}
@Test
+ public void testVpnRestrictedUsers() throws Exception {
+ // NETWORK_SETTINGS is necessary to see the UID ranges in NetworkCapabilities.
+ mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS,
+ PERMISSION_GRANTED);
+
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .build();
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, callback);
+
+ // Bring up a VPN
+ mMockVpn.establishForMyUid();
+ callback.expectAvailableThenValidatedCallbacks(mMockVpn);
+ callback.assertNoCallback();
+
+ final int uid = Process.myUid();
+ NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
+ assertNotNull("nc=" + nc, nc.getUids());
+ assertEquals(nc.getUids(), uidRangesForUid(uid));
+
+ // Set an underlying network and expect to see the VPN transports change.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ callback.expectCapabilitiesThat(mMockVpn, (caps)
+ -> caps.hasTransport(TRANSPORT_VPN)
+ && caps.hasTransport(TRANSPORT_WIFI));
+ callback.expectCapabilitiesThat(mWiFiNetworkAgent, (caps)
+ -> caps.hasCapability(NET_CAPABILITY_VALIDATED));
+
+ // Create a fake restricted profile whose parent is our user ID.
+ final int userId = UserHandle.getUserId(uid);
+ final int restrictedUserId = userId + 1;
+ final UserInfo info = new UserInfo(restrictedUserId, "user", UserInfo.FLAG_RESTRICTED);
+ info.restrictedProfileParentId = userId;
+ assertTrue(info.isRestricted());
+ when(mUserManager.getUserInfo(restrictedUserId)).thenReturn(info);
+ final Intent addedIntent = new Intent(ACTION_USER_ADDED);
+ addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId);
+
+ // Send a USER_ADDED broadcast for it.
+ // The BroadcastReceiver for this broadcast checks that is being run on the handler thread.
+ final Handler handler = new Handler(mCsHandlerThread.getLooper());
+ handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
+
+ // Expect that the VPN UID ranges contain both |uid| and the UID range for the newly-added
+ // restricted user.
+ callback.expectCapabilitiesThat(mMockVpn, (caps)
+ -> caps.getUids().size() == 2
+ && caps.getUids().contains(new UidRange(uid, uid))
+ && caps.getUids().contains(UidRange.createForUser(restrictedUserId))
+ && caps.hasTransport(TRANSPORT_VPN)
+ && caps.hasTransport(TRANSPORT_WIFI));
+
+ // Change the VPN's capabilities somehow (specifically, disconnect wifi).
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ callback.expectCapabilitiesThat(mMockVpn, (caps)
+ -> caps.getUids().size() == 2
+ && caps.getUids().contains(new UidRange(uid, uid))
+ && caps.getUids().contains(UidRange.createForUser(restrictedUserId))
+ && caps.hasTransport(TRANSPORT_VPN)
+ && !caps.hasTransport(TRANSPORT_WIFI));
+
+ // Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
+ final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
+ removedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId);
+ handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
+
+ // Expect that the VPN gains the UID range for the restricted user, and that the capability
+ // change made just before that (i.e., loss of TRANSPORT_WIFI) is preserved.
+ callback.expectCapabilitiesThat(mMockVpn, (caps)
+ -> caps.getUids().size() == 1
+ && caps.getUids().contains(new UidRange(uid, uid))
+ && caps.hasTransport(TRANSPORT_VPN)
+ && !caps.hasTransport(TRANSPORT_WIFI));
+ }
+
+ @Test
public void testIsActiveNetworkMeteredOverWifi() throws Exception {
// Returns true by default when no network is available.
assertTrue(mCm.isActiveNetworkMetered());
@@ -5761,18 +6108,10 @@
assertTrue(mCm.isActiveNetworkMetered());
// Connect VPN network. By default it is using current default network (Cell).
- TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- final int uid = Process.myUid();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true);
- mMockVpn.connect();
- waitForIdle();
+ mMockVpn.establishForMyUid();
+
// Ensure VPN is now the active network.
- assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// Expect VPN to be metered.
assertTrue(mCm.isActiveNetworkMetered());
@@ -5783,7 +6122,7 @@
mWiFiNetworkAgent.connect(true);
waitForIdle();
// VPN should still be the active network.
- assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// Expect VPN to be unmetered as it should now be using WiFi (new default).
assertFalse(mCm.isActiveNetworkMetered());
@@ -5801,7 +6140,6 @@
// VPN without any underlying networks is treated as metered.
assertTrue(mCm.isActiveNetworkMetered());
- vpnNetworkAgent.disconnect();
mMockVpn.disconnect();
}
@@ -5822,18 +6160,10 @@
assertFalse(mCm.isActiveNetworkMetered());
// Connect VPN network.
- TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- final int uid = Process.myUid();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true);
- mMockVpn.connect();
- waitForIdle();
+ mMockVpn.establishForMyUid();
+
// Ensure VPN is now the active network.
- assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// VPN is using Cell
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork() });
@@ -5873,7 +6203,6 @@
// VPN without underlying networks is treated as metered.
assertTrue(mCm.isActiveNetworkMetered());
- vpnNetworkAgent.disconnect();
mMockVpn.disconnect();
}
@@ -5888,17 +6217,11 @@
assertFalse(mCm.isActiveNetworkMetered());
// Connect VPN network.
- TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- final int uid = Process.myUid();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(true);
- mMockVpn.connectAsAlwaysMetered();
+ mMockVpn.registerAgent(true /* isAlwaysMetered */, uidRangesForUid(Process.myUid()),
+ new LinkProperties());
+ mMockVpn.connect(true);
waitForIdle();
- assertEquals(vpnNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
// VPN is tracking current platform default (WiFi).
mService.setUnderlyingNetworksForVpn(null);
@@ -5922,7 +6245,7 @@
assertTrue(mCm.isActiveNetworkMetered());
- vpnNetworkAgent.disconnect();
+ mMockVpn.disconnect();
}
@Test
@@ -6654,34 +6977,21 @@
waitForIdle();
assertNull(mService.getProxyForNetwork(null));
- // Set up a VPN network with a proxy
- final int uid = Process.myUid();
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN);
- final ArraySet<UidRange> ranges = new ArraySet<>();
- ranges.add(new UidRange(uid, uid));
- mMockVpn.setUids(ranges);
+ // Connect a VPN network with a proxy.
LinkProperties testLinkProperties = new LinkProperties();
testLinkProperties.setHttpProxy(testProxyInfo);
- vpnNetworkAgent.sendLinkProperties(testLinkProperties);
- waitForIdle();
-
- // Connect to VPN with proxy
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- vpnNetworkAgent.connect(true);
- mMockVpn.connect();
- waitForIdle();
+ mMockVpn.establishForMyUid(testLinkProperties);
// Test that the VPN network returns a proxy, and the WiFi does not.
- assertEquals(testProxyInfo, mService.getProxyForNetwork(vpnNetworkAgent.getNetwork()));
+ assertEquals(testProxyInfo, mService.getProxyForNetwork(mMockVpn.getNetwork()));
assertEquals(testProxyInfo, mService.getProxyForNetwork(null));
assertNull(mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork()));
// Test that the VPN network returns no proxy when it is set to null.
testLinkProperties.setHttpProxy(null);
- vpnNetworkAgent.sendLinkProperties(testLinkProperties);
+ mMockVpn.sendLinkProperties(testLinkProperties);
waitForIdle();
- assertNull(mService.getProxyForNetwork(vpnNetworkAgent.getNetwork()));
+ assertNull(mService.getProxyForNetwork(mMockVpn.getNetwork()));
assertNull(mService.getProxyForNetwork(null));
// Set WiFi proxy and check that the vpn proxy is still null.
@@ -6692,7 +7002,7 @@
// Disconnect from VPN and check that the active network, which is now the WiFi, has the
// correct proxy setting.
- vpnNetworkAgent.disconnect();
+ mMockVpn.disconnect();
waitForIdle();
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(testProxyInfo, mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork()));
@@ -6707,17 +7017,17 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
+ mMockVpn.establish(lp, VPN_UID, vpnRange);
- // Connected VPN should have interface rules set up. There are two expected invocations,
- // one during VPN uid update, one during VPN LinkProperties update
+ // A connected VPN should have interface rules set up. There are two expected invocations,
+ // one during the VPN initial connection, one during the VPN LinkProperties update.
ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class);
verify(mMockNetd, times(2)).firewallAddUidInterfaceRules(eq("tun0"), uidCaptor.capture());
assertContainsExactly(uidCaptor.getAllValues().get(0), APP1_UID, APP2_UID);
assertContainsExactly(uidCaptor.getAllValues().get(1), APP1_UID, APP2_UID);
assertTrue(mService.mPermissionMonitor.getVpnUidRanges("tun0").equals(vpnRange));
- vpnNetworkAgent.disconnect();
+ mMockVpn.disconnect();
waitForIdle();
// Disconnected VPN should have interface rules removed
@@ -6734,8 +7044,7 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(
- lp, Process.SYSTEM_UID, vpnRange);
+ mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
// Legacy VPN should not have interface rules set up
verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
@@ -6750,8 +7059,7 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(
- lp, Process.SYSTEM_UID, vpnRange);
+ mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
// IPv6 unreachable route should not be misinterpreted as a default route
verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
@@ -6765,7 +7073,7 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID, vpnRange);
+ mMockVpn.establish(lp, VPN_UID, vpnRange);
// Connected VPN should have interface rules set up. There are two expected invocations,
// one during VPN uid update, one during VPN LinkProperties update
@@ -6777,7 +7085,7 @@
reset(mMockNetd);
InOrder inOrder = inOrder(mMockNetd);
lp.setInterfaceName("tun1");
- vpnNetworkAgent.sendLinkProperties(lp);
+ mMockVpn.sendLinkProperties(lp);
waitForIdle();
// VPN handover (switch to a new interface) should result in rules being updated (old rules
// removed first, then new rules added)
@@ -6790,7 +7098,7 @@
lp = new LinkProperties();
lp.setInterfaceName("tun1");
lp.addRoute(new RouteInfo(new IpPrefix("192.0.2.0/24"), null, "tun1"));
- vpnNetworkAgent.sendLinkProperties(lp);
+ mMockVpn.sendLinkProperties(lp);
waitForIdle();
// VPN not routing everything should no longer have interface filtering rules
verify(mMockNetd).firewallRemoveUidInterfaceRules(uidCaptor.capture());
@@ -6801,7 +7109,7 @@
lp.setInterfaceName("tun1");
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
- vpnNetworkAgent.sendLinkProperties(lp);
+ mMockVpn.sendLinkProperties(lp);
waitForIdle();
// Back to routing all IPv6 traffic should have filtering rules
verify(mMockNetd).firewallAddUidInterfaceRules(eq("tun1"), uidCaptor.capture());
@@ -6816,8 +7124,7 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final UidRange vpnRange = UidRange.createForUser(VPN_USER);
- final TestNetworkAgentWrapper vpnNetworkAgent = establishVpn(lp, VPN_UID,
- Collections.singleton(vpnRange));
+ mMockVpn.establish(lp, VPN_UID, Collections.singleton(vpnRange));
reset(mMockNetd);
InOrder inOrder = inOrder(mMockNetd);
@@ -6826,7 +7133,7 @@
final Set<UidRange> newRanges = new HashSet<>(Arrays.asList(
new UidRange(vpnRange.start, APP1_UID - 1),
new UidRange(APP1_UID + 1, vpnRange.stop)));
- vpnNetworkAgent.setUids(newRanges);
+ mMockVpn.setUids(newRanges);
waitForIdle();
ArgumentCaptor<int[]> uidCaptor = ArgumentCaptor.forClass(int[].class);
@@ -6967,7 +7274,7 @@
private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
throws Exception {
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
- establishVpn(new LinkProperties(), vpnOwnerUid, vpnRange);
+ mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
mMockVpn.setVpnType(vpnType);
final VpnInfo vpnInfo = new VpnInfo();
@@ -7048,19 +7355,6 @@
mService.getConnectionOwnerUid(getTestConnectionInfo());
}
- private TestNetworkAgentWrapper establishVpn(
- LinkProperties lp, int ownerUid, Set<UidRange> vpnRange) throws Exception {
- final TestNetworkAgentWrapper
- vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp);
- vpnNetworkAgent.getNetworkCapabilities().setOwnerUid(ownerUid);
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.connect();
- mMockVpn.setUids(vpnRange);
- vpnNetworkAgent.connect(true);
- waitForIdle();
- return vpnNetworkAgent;
- }
-
private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid) {
final PackageInfo packageInfo = new PackageInfo();
if (hasSystemPermission) {
@@ -7240,22 +7534,23 @@
setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION);
+
+ mMockVpn.establishForMyUid();
+
+ // Wait for networks to connect and broadcasts to be sent before removing permissions.
+ waitForIdle();
mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
- // setUp() calls mockVpn() which adds a VPN with the Test Runner's uid. Configure it to be
- // active
- final VpnInfo info = new VpnInfo();
- info.ownerUid = Process.myUid();
- info.vpnIface = "interface";
- mMockVpn.setVpnInfo(info);
- mMockVpn.overrideUnderlyingNetworks(new Network[] {network});
+ assertTrue(mService.setUnderlyingNetworksForVpn(new Network[] {network}));
+ waitForIdle();
assertTrue(
"Active VPN permission not applied",
mService.checkConnectivityDiagnosticsPermissions(
Process.myPid(), Process.myUid(), naiWithoutUid,
mContext.getOpPackageName()));
- mMockVpn.overrideUnderlyingNetworks(null);
+ assertTrue(mService.setUnderlyingNetworksForVpn(null));
+ waitForIdle();
assertFalse(
"VPN shouldn't receive callback on non-underlying network",
mService.checkConnectivityDiagnosticsPermissions(
@@ -7276,8 +7571,6 @@
Manifest.permission.ACCESS_FINE_LOCATION);
mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
- // Disconnect mock vpn so the uid check on NetworkAgentInfo is tested
- mMockVpn.disconnect();
assertTrue(
"NetworkCapabilities administrator uid permission not applied",
mService.checkConnectivityDiagnosticsPermissions(
@@ -7480,4 +7773,39 @@
mCm.unregisterNetworkCallback(networkCallback);
}
+
+ @Test
+ public void testDumpDoesNotCrash() {
+ StringWriter stringWriter = new StringWriter();
+
+ mService.dump(new FileDescriptor(), new PrintWriter(stringWriter), new String[0]);
+
+ assertFalse(stringWriter.toString().isEmpty());
+ }
+
+ @Test
+ public void testRequestsSortedByIdSortsCorrectly() {
+ final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback();
+ final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
+ final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest genericRequest = new NetworkRequest.Builder()
+ .clearCapabilities().build();
+ final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+ final NetworkRequest cellRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build();
+ mCm.registerNetworkCallback(genericRequest, genericNetworkCallback);
+ mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
+ mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+
+ ConnectivityService.NetworkRequestInfo[] nriOutput = mService.requestsSortedById();
+
+ assertTrue(nriOutput.length > 1);
+ for (int i = 0; i < nriOutput.length - 1; i++) {
+ boolean isRequestIdInOrder =
+ nriOutput[i].mRequests.get(0).requestId
+ < nriOutput[i + 1].mRequests.get(0).requestId;
+ assertTrue(isRequestIdInOrder);
+ }
+ }
}
diff --git a/tests/net/java/com/android/server/NetworkManagementServiceTest.java b/tests/net/java/com/android/server/NetworkManagementServiceTest.java
index 968b307..ea763d2 100644
--- a/tests/net/java/com/android/server/NetworkManagementServiceTest.java
+++ b/tests/net/java/com/android/server/NetworkManagementServiceTest.java
@@ -16,6 +16,12 @@
package com.android.server;
+import static android.util.DebugUtils.valueToString;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -29,15 +35,19 @@
import android.net.INetd;
import android.net.INetdUnsolicitedEventListener;
import android.net.LinkAddress;
+import android.net.NetworkPolicyManager;
import android.os.BatteryStats;
import android.os.Binder;
import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.ArrayMap;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.app.IBatteryStats;
-import com.android.server.NetworkManagementService.SystemServices;
+import com.android.server.NetworkManagementService.Dependencies;
import com.android.server.net.BaseNetworkObserver;
import org.junit.After;
@@ -49,13 +59,14 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.function.BiFunction;
+
/**
* Tests for {@link NetworkManagementService}.
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
public class NetworkManagementServiceTest {
-
private NetworkManagementService mNMService;
@Mock private Context mContext;
@@ -66,7 +77,9 @@
@Captor
private ArgumentCaptor<INetdUnsolicitedEventListener> mUnsolListenerCaptor;
- private final SystemServices mServices = new SystemServices() {
+ private final MockDependencies mDeps = new MockDependencies();
+
+ private final class MockDependencies extends Dependencies {
@Override
public IBinder getService(String name) {
switch (name) {
@@ -76,14 +89,21 @@
throw new UnsupportedOperationException("Unknown service " + name);
}
}
+
@Override
public void registerLocalService(NetworkManagementInternal nmi) {
}
+
@Override
public INetd getNetd() {
return mNetdService;
}
- };
+
+ @Override
+ public int getCallingUid() {
+ return Process.SYSTEM_UID;
+ }
+ }
@Before
public void setUp() throws Exception {
@@ -91,7 +111,7 @@
doNothing().when(mNetdService)
.registerUnsolicitedEventListener(mUnsolListenerCaptor.capture());
// Start the service and wait until it connects to our socket.
- mNMService = NetworkManagementService.create(mContext, mServices);
+ mNMService = NetworkManagementService.create(mContext, mDeps);
}
@After
@@ -192,4 +212,105 @@
// Make sure nothing else was called.
verifyNoMoreInteractions(observer);
}
+
+ @Test
+ public void testFirewallEnabled() {
+ mNMService.setFirewallEnabled(true);
+ assertTrue(mNMService.isFirewallEnabled());
+
+ mNMService.setFirewallEnabled(false);
+ assertFalse(mNMService.isFirewallEnabled());
+ }
+
+ private static final int TEST_UID = 111;
+
+ @Test
+ public void testNetworkRestrictedDefault() {
+ assertFalse(mNMService.isNetworkRestricted(TEST_UID));
+ }
+
+ @Test
+ public void testMeteredNetworkRestrictions() throws RemoteException {
+ // Make sure the mocked netd method returns true.
+ doReturn(true).when(mNetdService).bandwidthEnableDataSaver(anyBoolean());
+
+ // Restrict usage of mobile data in background
+ mNMService.setUidMeteredNetworkDenylist(TEST_UID, true);
+ assertTrue("Should be true since mobile data usage is restricted",
+ mNMService.isNetworkRestricted(TEST_UID));
+
+ mNMService.setDataSaverModeEnabled(true);
+ verify(mNetdService).bandwidthEnableDataSaver(true);
+
+ mNMService.setUidMeteredNetworkDenylist(TEST_UID, false);
+ assertTrue("Should be true since data saver is on and the uid is not allowlisted",
+ mNMService.isNetworkRestricted(TEST_UID));
+
+ mNMService.setUidMeteredNetworkAllowlist(TEST_UID, true);
+ assertFalse("Should be false since data saver is on and the uid is allowlisted",
+ mNMService.isNetworkRestricted(TEST_UID));
+
+ // remove uid from allowlist and turn datasaver off again
+ mNMService.setUidMeteredNetworkAllowlist(TEST_UID, false);
+ mNMService.setDataSaverModeEnabled(false);
+ verify(mNetdService).bandwidthEnableDataSaver(false);
+ assertFalse("Network should not be restricted when data saver is off",
+ mNMService.isNetworkRestricted(TEST_UID));
+ }
+
+ @Test
+ public void testFirewallChains() {
+ final ArrayMap<Integer, ArrayMap<Integer, Boolean>> expected = new ArrayMap<>();
+ // Dozable chain
+ final ArrayMap<Integer, Boolean> isRestrictedForDozable = new ArrayMap<>();
+ isRestrictedForDozable.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true);
+ isRestrictedForDozable.put(INetd.FIREWALL_RULE_ALLOW, false);
+ isRestrictedForDozable.put(INetd.FIREWALL_RULE_DENY, true);
+ expected.put(INetd.FIREWALL_CHAIN_DOZABLE, isRestrictedForDozable);
+ // Powersaver chain
+ final ArrayMap<Integer, Boolean> isRestrictedForPowerSave = new ArrayMap<>();
+ isRestrictedForPowerSave.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true);
+ isRestrictedForPowerSave.put(INetd.FIREWALL_RULE_ALLOW, false);
+ isRestrictedForPowerSave.put(INetd.FIREWALL_RULE_DENY, true);
+ expected.put(INetd.FIREWALL_CHAIN_POWERSAVE, isRestrictedForPowerSave);
+ // Standby chain
+ final ArrayMap<Integer, Boolean> isRestrictedForStandby = new ArrayMap<>();
+ isRestrictedForStandby.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, false);
+ isRestrictedForStandby.put(INetd.FIREWALL_RULE_ALLOW, false);
+ isRestrictedForStandby.put(INetd.FIREWALL_RULE_DENY, true);
+ expected.put(INetd.FIREWALL_CHAIN_STANDBY, isRestrictedForStandby);
+ // Restricted mode chain
+ final ArrayMap<Integer, Boolean> isRestrictedForRestrictedMode = new ArrayMap<>();
+ isRestrictedForRestrictedMode.put(NetworkPolicyManager.FIREWALL_RULE_DEFAULT, true);
+ isRestrictedForRestrictedMode.put(INetd.FIREWALL_RULE_ALLOW, false);
+ isRestrictedForRestrictedMode.put(INetd.FIREWALL_RULE_DENY, true);
+ expected.put(INetd.FIREWALL_CHAIN_RESTRICTED, isRestrictedForRestrictedMode);
+
+ final int[] chains = {
+ INetd.FIREWALL_CHAIN_STANDBY,
+ INetd.FIREWALL_CHAIN_POWERSAVE,
+ INetd.FIREWALL_CHAIN_DOZABLE,
+ INetd.FIREWALL_CHAIN_RESTRICTED
+ };
+ final int[] states = {
+ INetd.FIREWALL_RULE_ALLOW,
+ INetd.FIREWALL_RULE_DENY,
+ NetworkPolicyManager.FIREWALL_RULE_DEFAULT
+ };
+ BiFunction<Integer, Integer, String> errorMsg = (chain, state) -> {
+ return String.format("Unexpected value for chain: %s and state: %s",
+ valueToString(INetd.class, "FIREWALL_CHAIN_", chain),
+ valueToString(INetd.class, "FIREWALL_RULE_", state));
+ };
+ for (int chain : chains) {
+ final ArrayMap<Integer, Boolean> expectedValues = expected.get(chain);
+ mNMService.setFirewallChainEnabled(chain, true);
+ for (int state : states) {
+ mNMService.setFirewallUidRule(chain, TEST_UID, state);
+ assertEquals(errorMsg.apply(chain, state),
+ expectedValues.get(state), mNMService.isNetworkRestricted(TEST_UID));
+ }
+ mNMService.setFirewallChainEnabled(chain, false);
+ }
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index 5a29c2c..de35f91 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -123,7 +123,7 @@
MockitoAnnotations.initMocks(this);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
- when(mUserManager.getUsers(eq(true))).thenReturn(
+ when(mUserManager.getAliveUsers()).thenReturn(
Arrays.asList(new UserInfo[] {
new UserInfo(MOCK_USER1, "", 0),
new UserInfo(MOCK_USER2, "", 0),
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index a553b58..337507a 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -21,15 +21,6 @@
import static android.content.pm.UserInfo.FLAG_PRIMARY;
import static android.content.pm.UserInfo.FLAG_RESTRICTED;
import static android.net.ConnectivityManager.NetworkCallback;
-import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
-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_METERED;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
-import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
-import static android.net.NetworkCapabilities.TRANSPORT_VPN;
-import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -41,6 +32,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
@@ -86,10 +78,10 @@
import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.INetworkManagementService;
-import android.os.Looper;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.test.TestLooper;
import android.provider.Settings;
import android.security.Credentials;
import android.security.KeyStore;
@@ -100,6 +92,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
+import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
import com.android.server.IpSecService;
@@ -223,6 +216,8 @@
.thenReturn(mNotificationManager);
when(mContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE)))
.thenReturn(mConnectivityManager);
+ when(mContext.getSystemServiceName(eq(ConnectivityManager.class)))
+ .thenReturn(Context.CONNECTIVITY_SERVICE);
when(mContext.getSystemService(eq(Context.IPSEC_SERVICE))).thenReturn(mIpSecManager);
when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent))
.thenReturn(Resources.getSystem().getString(
@@ -589,7 +584,7 @@
}
@Test
- public void testNotificationShownForAlwaysOnApp() {
+ public void testNotificationShownForAlwaysOnApp() throws Exception {
final UserHandle userHandle = UserHandle.of(primaryUser.id);
final Vpn vpn = createVpn(primaryUser.id);
setMockedUsers(primaryUser);
@@ -617,103 +612,6 @@
order.verify(mNotificationManager).cancel(anyString(), anyInt());
}
- @Test
- public void testCapabilities() {
- final Vpn vpn = createVpn(primaryUser.id);
- setMockedUsers(primaryUser);
-
- final Network mobile = new Network(1);
- final Network wifi = new Network(2);
-
- final Map<Network, NetworkCapabilities> networks = new HashMap<>();
- networks.put(
- mobile,
- new NetworkCapabilities()
- .addTransportType(TRANSPORT_CELLULAR)
- .addCapability(NET_CAPABILITY_INTERNET)
- .addCapability(NET_CAPABILITY_NOT_CONGESTED)
- .setLinkDownstreamBandwidthKbps(10));
- networks.put(
- wifi,
- new NetworkCapabilities()
- .addTransportType(TRANSPORT_WIFI)
- .addCapability(NET_CAPABILITY_INTERNET)
- .addCapability(NET_CAPABILITY_NOT_METERED)
- .addCapability(NET_CAPABILITY_NOT_ROAMING)
- .addCapability(NET_CAPABILITY_NOT_CONGESTED)
- .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
- .setLinkUpstreamBandwidthKbps(20));
- setMockedNetworks(networks);
-
- final NetworkCapabilities caps = new NetworkCapabilities();
-
- Vpn.applyUnderlyingCapabilities(
- mConnectivityManager, new Network[] {}, caps, false /* isAlwaysMetered */);
- assertTrue(caps.hasTransport(TRANSPORT_VPN));
- assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
- assertFalse(caps.hasTransport(TRANSPORT_WIFI));
- assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps());
- assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps());
- assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
- assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
- assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
-
- Vpn.applyUnderlyingCapabilities(
- mConnectivityManager,
- new Network[] {mobile},
- caps,
- false /* isAlwaysMetered */);
- assertTrue(caps.hasTransport(TRANSPORT_VPN));
- assertTrue(caps.hasTransport(TRANSPORT_CELLULAR));
- assertFalse(caps.hasTransport(TRANSPORT_WIFI));
- assertEquals(10, caps.getLinkDownstreamBandwidthKbps());
- assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps());
- assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
- assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
- assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
-
- Vpn.applyUnderlyingCapabilities(
- mConnectivityManager, new Network[] {wifi}, caps, false /* isAlwaysMetered */);
- assertTrue(caps.hasTransport(TRANSPORT_VPN));
- assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
- assertTrue(caps.hasTransport(TRANSPORT_WIFI));
- assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps());
- assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
- assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
- assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
- assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
-
- Vpn.applyUnderlyingCapabilities(
- mConnectivityManager, new Network[] {wifi}, caps, true /* isAlwaysMetered */);
- assertTrue(caps.hasTransport(TRANSPORT_VPN));
- assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
- assertTrue(caps.hasTransport(TRANSPORT_WIFI));
- assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps());
- assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
- assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
- assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
- assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
-
- Vpn.applyUnderlyingCapabilities(
- mConnectivityManager,
- new Network[] {mobile, wifi},
- caps,
- false /* isAlwaysMetered */);
- assertTrue(caps.hasTransport(TRANSPORT_VPN));
- assertTrue(caps.hasTransport(TRANSPORT_CELLULAR));
- assertTrue(caps.hasTransport(TRANSPORT_WIFI));
- assertEquals(10, caps.getLinkDownstreamBandwidthKbps());
- assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
- assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
- assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
- assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
- }
-
/**
* The profile name should NOT change between releases for backwards compatibility
*
@@ -1037,7 +935,7 @@
when(exception.getErrorType())
.thenReturn(IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED);
- final Vpn vpn = startLegacyVpn(mVpnProfile);
+ final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), (mVpnProfile));
final NetworkCallback cb = triggerOnAvailableAndGetCallback();
// Wait for createIkeSession() to be called before proceeding in order to ensure consistent
@@ -1048,20 +946,20 @@
ikeCb.onClosedExceptionally(exception);
verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb));
- assertEquals(DetailedState.FAILED, vpn.getNetworkInfo().getDetailedState());
+ assertEquals(LegacyVpnInfo.STATE_FAILED, vpn.getLegacyVpnInfo().state);
}
@Test
public void testStartPlatformVpnIllegalArgumentExceptionInSetup() throws Exception {
when(mIkev2SessionCreator.createIkeSession(any(), any(), any(), any(), any(), any()))
.thenThrow(new IllegalArgumentException());
- final Vpn vpn = startLegacyVpn(mVpnProfile);
+ final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), mVpnProfile);
final NetworkCallback cb = triggerOnAvailableAndGetCallback();
// Wait for createIkeSession() to be called before proceeding in order to ensure consistent
// state
verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb));
- assertEquals(DetailedState.FAILED, vpn.getNetworkInfo().getDetailedState());
+ assertEquals(LegacyVpnInfo.STATE_FAILED, vpn.getLegacyVpnInfo().state);
}
private void setAndVerifyAlwaysOnPackage(Vpn vpn, int uid, boolean lockdownEnabled) {
@@ -1100,8 +998,7 @@
// a subsequent CL.
}
- public Vpn startLegacyVpn(final VpnProfile vpnProfile) throws Exception {
- final Vpn vpn = createVpn(primaryUser.id);
+ private Vpn startLegacyVpn(final Vpn vpn, final VpnProfile vpnProfile) throws Exception {
setMockedUsers(primaryUser);
// Dummy egress interface
@@ -1118,7 +1015,7 @@
@Test
public void testStartPlatformVpn() throws Exception {
- startLegacyVpn(mVpnProfile);
+ startLegacyVpn(createVpn(primaryUser.id), mVpnProfile);
// TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in
// a subsequent patch.
}
@@ -1153,7 +1050,7 @@
legacyRunnerReady.open();
return new Network(102);
});
- final Vpn vpn = startLegacyVpn(profile);
+ final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), profile);
final TestDeps deps = (TestDeps) vpn.mDeps;
try {
// udppsk and 1701 are the values for TYPE_L2TP_IPSEC_PSK
@@ -1287,8 +1184,13 @@
doReturn(UserHandle.of(userId)).when(asUserContext).getUser();
when(mContext.createContextAsUser(eq(UserHandle.of(userId)), anyInt()))
.thenReturn(asUserContext);
- return new Vpn(Looper.myLooper(), mContext, new TestDeps(), mNetService,
+ final TestLooper testLooper = new TestLooper();
+ final Vpn vpn = new Vpn(testLooper.getLooper(), mContext, new TestDeps(), mNetService,
userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
+ verify(mConnectivityManager, times(1)).registerNetworkProvider(argThat(
+ provider -> provider.getName().contains("VpnNetworkProvider")
+ ));
+ return vpn;
}
private static void assertBlocked(Vpn vpn, int... uids) {
@@ -1318,15 +1220,14 @@
* @see UserManagerService#getUsers(boolean)
*/
doAnswer(invocation -> {
- final boolean excludeDying = (boolean) invocation.getArguments()[0];
final ArrayList<UserInfo> result = new ArrayList<>(users.length);
for (UserInfo ui : users) {
- if (!excludeDying || (ui.isEnabled() && !ui.partial)) {
+ if (ui.isEnabled() && !ui.partial) {
result.add(ui);
}
}
return result;
- }).when(mUserManager).getUsers(anyBoolean());
+ }).when(mUserManager).getAliveUsers();
doAnswer(invocation -> {
final int id = (int) invocation.getArguments()[0];