Move applying underlying caps from Vpn to ConnectivityService. am: 220987b38d

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

Change-Id: I10147f9b86661243e654a16a760e183128493042
This commit is contained in:
Lorenzo Colitti
2020-11-28 03:20:40 +00:00
committed by Automerger Merge Worker
4 changed files with 143 additions and 29 deletions

View File

@@ -40,6 +40,7 @@ import java.lang.annotation.Retention;
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;
@@ -173,6 +174,14 @@ public abstract class NetworkAgent {
*/
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
@@ -217,7 +226,13 @@ public abstract class NetworkAgent {
* 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
@@ -649,6 +664,33 @@ public abstract class NetworkAgent {
queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties));
}
/**
* 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.

View File

@@ -2771,6 +2771,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
networkCapabilities = new NetworkCapabilities(networkCapabilities);
networkCapabilities.restrictCapabilitesForTestNetwork(nai.creatorUid);
}
processCapabilitiesFromAgent(nai, networkCapabilities);
updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities);
break;
}
@@ -2809,6 +2810,31 @@ public class ConnectivityService extends IConnectivityManager.Stub
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();
}
}
}
}
@@ -3394,7 +3420,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
mLegacyTypeTracker.remove(nai, wasDefault);
if (!nai.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
updateAllVpnsCapabilities();
propagateUnderlyingNetworkCapabilities();
}
rematchAllNetworksAndRequests();
mLingerMonitor.noteDisconnect(nai);
@@ -4774,22 +4800,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
/**
* 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);
}
}
}
@@ -5979,6 +6000,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
this, mNetd, mDnsResolver, mNMS, providerId, Binder.getCallingUid());
// Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
processCapabilitiesFromAgent(nai, nc);
nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
processLinkPropertiesFromAgent(nai, nai.linkProperties);
@@ -6019,6 +6041,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
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());
@@ -6314,6 +6342,30 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
/**
* 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);
}
/** Propagates to |nc| the capabilities declared by the underlying networks of |nai|. */
private void mixInUnderlyingCapabilities(NetworkAgentInfo nai, NetworkCapabilities nc) {
Network[] underlyingNetworks = nai.declaredUnderlyingNetworks;
Network defaultNetwork = getNetwork(getDefaultNetwork());
if (underlyingNetworks == null && defaultNetwork != null) {
// null underlying networks means to track the default.
underlyingNetworks = new Network[] { defaultNetwork };
}
// TODO(b/124469351): Get capabilities directly from ConnectivityService instead.
final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
Vpn.applyUnderlyingCapabilities(cm, underlyingNetworks, nc, nai.declaredMetered);
}
/**
* 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,
@@ -6367,6 +6419,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
newNc.addCapability(NET_CAPABILITY_NOT_ROAMING);
}
if (nai.supportsUnderlyingNetworks()) {
mixInUnderlyingCapabilities(nai, newNc);
}
return newNc;
}
@@ -6446,7 +6502,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
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)) {
@@ -6766,7 +6822,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
? 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) {
@@ -7228,7 +7284,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// 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;
}
@@ -7270,8 +7326,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
// doing.
updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
if (networkAgent.isVPN()) {
updateAllVpnsCapabilities();
if (networkAgent.supportsUnderlyingNetworks()) {
propagateUnderlyingNetworkCapabilities();
}
// Consider network even though it is not yet validated.
@@ -7528,13 +7584,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
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;
}

View File

@@ -132,6 +132,16 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
// 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 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
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

View File

@@ -1084,7 +1084,7 @@ public class ConnectivityServiceTest {
throws Exception {
if (mAgentRegistered) throw new IllegalStateException("already registered");
setUids(uids);
mConfig.isMetered = isAlwaysMetered;
if (!isAlwaysMetered) mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
mInterface = VPN_IFNAME;
mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp,
mNetworkCapabilities);
@@ -5053,6 +5053,13 @@ public class ConnectivityServiceTest {
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);
}
@Test