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:
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user