Merge changes Ia764b341,I4e4b41bb,I61b262d8,Ie6ace6bd,I21e866c7, ...

am: b044b885cf

Change-Id: Ib54b132dae9e6ab4e01ee46a8afdcb2f50df2709
This commit is contained in:
Chalard Jean
2018-06-06 03:00:49 -07:00
committed by android-build-merger
6 changed files with 804 additions and 142 deletions

View File

@@ -17,6 +17,8 @@
package android.net;
import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.net.ConnectivityManager.NetworkCallback;
import android.os.Parcel;
import android.os.Parcelable;
@@ -61,15 +63,7 @@ public final class NetworkCapabilities implements Parcelable {
public NetworkCapabilities(NetworkCapabilities nc) {
if (nc != null) {
mNetworkCapabilities = nc.mNetworkCapabilities;
mTransportTypes = nc.mTransportTypes;
mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps;
mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
mNetworkSpecifier = nc.mNetworkSpecifier;
mSignalStrength = nc.mSignalStrength;
mUids = nc.mUids;
mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid;
mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities;
set(nc);
}
}
@@ -85,6 +79,24 @@ public final class NetworkCapabilities implements Parcelable {
mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
mUids = null;
mEstablishingVpnAppUid = INVALID_UID;
mSSID = null;
}
/**
* Set all contents of this object to the contents of a NetworkCapabilities.
* @hide
*/
public void set(NetworkCapabilities nc) {
mNetworkCapabilities = nc.mNetworkCapabilities;
mTransportTypes = nc.mTransportTypes;
mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps;
mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
mNetworkSpecifier = nc.mNetworkSpecifier;
mSignalStrength = nc.mSignalStrength;
setUids(nc.mUids); // Will make the defensive copy
mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid;
mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities;
mSSID = nc.mSSID;
}
/**
@@ -276,6 +288,7 @@ public final class NetworkCapabilities implements Parcelable {
* this network can be used by system apps to upload telemetry data.
* @hide
*/
@SystemApi
public static final int NET_CAPABILITY_OEM_PAID = 22;
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
@@ -424,6 +437,7 @@ public final class NetworkCapabilities implements Parcelable {
* @return an array of capability values for this instance.
* @hide
*/
@TestApi
public @NetCapability int[] getCapabilities() {
return BitUtils.unpackBits(mNetworkCapabilities);
}
@@ -688,6 +702,7 @@ public final class NetworkCapabilities implements Parcelable {
* @return an array of transport type values for this instance.
* @hide
*/
@TestApi
public @Transport int[] getTransportTypes() {
return BitUtils.unpackBits(mTransportTypes);
}
@@ -921,7 +936,7 @@ public final class NetworkCapabilities implements Parcelable {
/**
* Sets the signal strength. This is a signed integer, with higher values indicating a stronger
* signal. The exact units are bearer-dependent. For example, Wi-Fi uses the same RSSI units
* reported by WifiManager.
* reported by wifi code.
* <p>
* Note that when used to register a network callback, this specifies the minimum acceptable
* signal strength. When received as the state of an existing network it specifies the current
@@ -1053,7 +1068,7 @@ public final class NetworkCapabilities implements Parcelable {
}
/**
* Tests if the set of UIDs that this network applies to is the same of the passed set of UIDs.
* Tests if the set of UIDs that this network applies to is the same as the passed network.
* <p>
* This test only checks whether equal range objects are in both sets. It will
* return false if the ranges are not exactly the same, even if the covered UIDs
@@ -1143,6 +1158,62 @@ public final class NetworkCapabilities implements Parcelable {
mUids.addAll(nc.mUids);
}
/**
* The SSID of the network, or null if not applicable or unknown.
* <p>
* This is filled in by wifi code.
* @hide
*/
private String mSSID;
/**
* Sets the SSID of this network.
* @hide
*/
public NetworkCapabilities setSSID(String ssid) {
mSSID = ssid;
return this;
}
/**
* Gets the SSID of this network, or null if none or unknown.
* @hide
*/
public String getSSID() {
return mSSID;
}
/**
* Tests if the SSID of this network is the same as the SSID of the passed network.
* @hide
*/
public boolean equalsSSID(NetworkCapabilities nc) {
return Objects.equals(mSSID, nc.mSSID);
}
/**
* Check if the SSID requirements of this object are matched by the passed object.
* @hide
*/
public boolean satisfiedBySSID(NetworkCapabilities nc) {
return mSSID == null || mSSID.equals(nc.mSSID);
}
/**
* Combine SSIDs of the capabilities.
* <p>
* This is only legal if either the SSID of this object is null, or both SSIDs are
* equal.
* @hide
*/
private void combineSSIDs(NetworkCapabilities nc) {
if (mSSID != null && !mSSID.equals(nc.mSSID)) {
throw new IllegalStateException("Can't combine two SSIDs");
}
setSSID(nc.mSSID);
}
/**
* Combine a set of Capabilities to this one. Useful for coming up with the complete set.
* <p>
@@ -1158,6 +1229,7 @@ public final class NetworkCapabilities implements Parcelable {
combineSpecifiers(nc);
combineSignalStrength(nc);
combineUids(nc);
combineSSIDs(nc);
}
/**
@@ -1176,7 +1248,8 @@ public final class NetworkCapabilities implements Parcelable {
&& (onlyImmutable || satisfiedByLinkBandwidths(nc))
&& satisfiedBySpecifier(nc)
&& (onlyImmutable || satisfiedBySignalStrength(nc))
&& (onlyImmutable || satisfiedByUids(nc)));
&& (onlyImmutable || satisfiedByUids(nc))
&& (onlyImmutable || satisfiedBySSID(nc)));
}
/**
@@ -1265,7 +1338,8 @@ public final class NetworkCapabilities implements Parcelable {
&& equalsLinkBandwidths(that)
&& equalsSignalStrength(that)
&& equalsSpecifier(that)
&& equalsUids(that));
&& equalsUids(that)
&& equalsSSID(that));
}
@Override
@@ -1280,7 +1354,8 @@ public final class NetworkCapabilities implements Parcelable {
+ (mLinkDownBandwidthKbps * 19)
+ Objects.hashCode(mNetworkSpecifier) * 23
+ (mSignalStrength * 29)
+ Objects.hashCode(mUids) * 31;
+ Objects.hashCode(mUids) * 31
+ Objects.hashCode(mSSID) * 37;
}
@Override
@@ -1297,6 +1372,7 @@ public final class NetworkCapabilities implements Parcelable {
dest.writeParcelable((Parcelable) mNetworkSpecifier, flags);
dest.writeInt(mSignalStrength);
dest.writeArraySet(mUids);
dest.writeString(mSSID);
}
public static final Creator<NetworkCapabilities> CREATOR =
@@ -1314,6 +1390,7 @@ public final class NetworkCapabilities implements Parcelable {
netCap.mSignalStrength = in.readInt();
netCap.mUids = (ArraySet<UidRange>) in.readArraySet(
null /* ClassLoader, null for default */);
netCap.mSSID = in.readString();
return netCap;
}
@Override
@@ -1364,6 +1441,10 @@ public final class NetworkCapabilities implements Parcelable {
sb.append(" EstablishingAppUid: ").append(mEstablishingVpnAppUid);
}
if (null != mSSID) {
sb.append(" SSID: ").append(mSSID);
}
sb.append("]");
return sb.toString();
}

View File

@@ -17,6 +17,8 @@
package android.net;
import android.annotation.NonNull;
import android.net.NetworkCapabilities.NetCapability;
import android.net.NetworkCapabilities.Transport;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
@@ -165,9 +167,6 @@ public class NetworkRequest implements Parcelable {
* the requested network's required capabilities. Note that when searching
* for a network to satisfy a request, all capabilities requested must be
* satisfied.
* <p>
* If the given capability was previously added to the list of unwanted capabilities
* then the capability will also be removed from the list of unwanted capabilities.
*
* @param capability The capability to add.
* @return The builder to facilitate chaining
@@ -179,8 +178,7 @@ public class NetworkRequest implements Parcelable {
}
/**
* Removes (if found) the given capability from this builder instance from both required
* and unwanted capabilities lists.
* Removes (if found) the given capability from this builder instance.
*
* @param capability The capability to remove.
* @return The builder to facilitate chaining.
@@ -199,8 +197,7 @@ public class NetworkRequest implements Parcelable {
* @hide
*/
public Builder setCapabilities(NetworkCapabilities nc) {
mNetworkCapabilities.clearAll();
mNetworkCapabilities.combineCapabilities(nc);
mNetworkCapabilities.set(nc);
return this;
}
@@ -228,6 +225,7 @@ public class NetworkRequest implements Parcelable {
*
* @param capability The capability to add to unwanted capability list.
* @return The builder to facilitate chaining.
*
* @hide
*/
public Builder addUnwantedCapability(@NetworkCapabilities.NetCapability int capability) {
@@ -426,6 +424,29 @@ public class NetworkRequest implements Parcelable {
return type == Type.BACKGROUND_REQUEST;
}
/**
* @see Builder#addCapability(int)
*/
public boolean hasCapability(@NetCapability int capability) {
return networkCapabilities.hasCapability(capability);
}
/**
* @see Builder#addUnwantedCapability(int)
*
* @hide
*/
public boolean hasUnwantedCapability(@NetCapability int capability) {
return networkCapabilities.hasUnwantedCapability(capability);
}
/**
* @see Builder#addTransportType(int)
*/
public boolean hasTransport(@Transport int transportType) {
return networkCapabilities.hasTransport(transportType);
}
public String toString() {
return "NetworkRequest [ " + type + " id=" + requestId +
(legacyType != ConnectivityManager.TYPE_NONE ? ", legacyType=" + legacyType : "") +

View File

@@ -102,6 +102,8 @@ import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
@@ -234,8 +236,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
private KeyStore mKeyStore;
@VisibleForTesting
@GuardedBy("mVpns")
private final SparseArray<Vpn> mVpns = new SparseArray<Vpn>();
protected final SparseArray<Vpn> mVpns = new SparseArray<Vpn>();
// TODO: investigate if mLockdownEnabled can be removed and replaced everywhere by
// a direct call to LockdownVpnTracker.isEnabled().
@@ -494,24 +497,24 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static final int MAX_VALIDATION_LOGS = 10;
private static class ValidationLog {
final Network mNetwork;
final String mNetworkExtraInfo;
final String mName;
final ReadOnlyLocalLog mLog;
ValidationLog(Network network, String networkExtraInfo, ReadOnlyLocalLog log) {
ValidationLog(Network network, String name, ReadOnlyLocalLog log) {
mNetwork = network;
mNetworkExtraInfo = networkExtraInfo;
mName = name;
mLog = log;
}
}
private final ArrayDeque<ValidationLog> mValidationLogs =
new ArrayDeque<ValidationLog>(MAX_VALIDATION_LOGS);
private void addValidationLogs(ReadOnlyLocalLog log, Network network, String networkExtraInfo) {
private void addValidationLogs(ReadOnlyLocalLog log, Network network, String name) {
synchronized (mValidationLogs) {
while (mValidationLogs.size() >= MAX_VALIDATION_LOGS) {
mValidationLogs.removeLast();
}
mValidationLogs.addFirst(new ValidationLog(network, networkExtraInfo, log));
mValidationLogs.addFirst(new ValidationLog(network, name, log));
}
}
@@ -898,6 +901,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
deps);
}
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
netCap.setSingleUid(uid);
return netCap;
}
private NetworkRequest createDefaultInternetRequestForTransport(
int transportType, NetworkRequest.Type type) {
NetworkCapabilities netCap = new NetworkCapabilities();
@@ -1150,12 +1162,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
int vpnNetId = NETID_UNSET;
synchronized (mVpns) {
final Vpn vpn = mVpns.get(user);
// TODO : now that capabilities contain the UID, the appliesToUid test should
// be removed as the satisfying test below should be enough.
if (vpn != null && vpn.appliesToUid(uid)) vpnNetId = vpn.getNetId();
}
NetworkAgentInfo nai;
if (vpnNetId != NETID_UNSET) {
nai = getNetworkAgentInfoForNetId(vpnNetId);
if (nai != null) return nai.network;
if (nai != null) {
final NetworkCapabilities requiredCaps =
createDefaultNetworkCapabilitiesForUid(uid);
if (requiredCaps.satisfiedByNetworkCapabilities(nai.networkCapabilities)) {
return nai.network;
}
}
}
nai = getDefaultNetwork();
if (nai != null
@@ -1352,7 +1372,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (nai != null) {
synchronized (nai) {
if (nai.networkCapabilities != null) {
return networkCapabilitiesWithoutUidsUnlessAllowed(nai.networkCapabilities,
return networkCapabilitiesRestrictedForCallerPermissions(
nai.networkCapabilities,
Binder.getCallingPid(), Binder.getCallingUid());
}
}
@@ -1366,10 +1387,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network));
}
private NetworkCapabilities networkCapabilitiesWithoutUidsUnlessAllowed(
private NetworkCapabilities networkCapabilitiesRestrictedForCallerPermissions(
NetworkCapabilities nc, int callerPid, int callerUid) {
if (checkSettingsPermission(callerPid, callerUid)) return new NetworkCapabilities(nc);
return new NetworkCapabilities(nc).setUids(null);
final NetworkCapabilities newNc = new NetworkCapabilities(nc);
if (!checkSettingsPermission(callerPid, callerUid)) {
newNc.setUids(null);
newNc.setSSID(null);
}
return newNc;
}
private void restrictRequestUidsForCaller(NetworkCapabilities nc) {
@@ -1415,7 +1440,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
public boolean isActiveNetworkMetered() {
enforceAccessPermission();
final NetworkCapabilities caps = getNetworkCapabilities(getActiveNetwork());
final int uid = Binder.getCallingUid();
final NetworkCapabilities caps = getUnfilteredActiveNetworkState(uid).networkCapabilities;
if (caps != null) {
return !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
} else {
@@ -1737,6 +1763,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
ni != null ? ni.getState().toString() : "?");
} catch (RemoteException e) {
}
intent.addFlags(Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
}
try {
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL, options);
@@ -2032,7 +2059,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
synchronized (mValidationLogs) {
pw.println("mValidationLogs (most recent first):");
for (ValidationLog p : mValidationLogs) {
pw.println(p.mNetwork + " - " + p.mNetworkExtraInfo);
pw.println(p.mNetwork + " - " + p.mName);
pw.increaseIndent();
p.mLog.dump(fd, pw, args);
pw.decreaseIndent();
@@ -2377,94 +2404,107 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
// This is a no-op if it's called with a message designating a network that has
// already been destroyed, because its reference will not be found in the relevant
// maps.
private void handleAsyncChannelDisconnected(Message msg) {
NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
if (nai != null) {
if (DBG) {
log(nai.name() + " got DISCONNECTED, was satisfying " + nai.numNetworkRequests());
}
// A network agent has disconnected.
// TODO - if we move the logic to the network agent (have them disconnect
// because they lost all their requests or because their score isn't good)
// then they would disconnect organically, report their new state and then
// disconnect the channel.
if (nai.networkInfo.isConnected()) {
nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
null, null);
}
final boolean wasDefault = isDefaultNetwork(nai);
if (wasDefault) {
mDefaultInetConditionPublished = 0;
// Log default network disconnection before required book-keeping.
// Let rematchAllNetworksAndRequests() below record a new default network event
// if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence
// whose timestamps tell how long it takes to recover a default network.
long now = SystemClock.elapsedRealtime();
metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(now, null, nai);
}
notifyIfacesChangedForNetworkStats();
// TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied
// by other networks that are already connected. Perhaps that can be done by
// sending all CALLBACK_LOST messages (for requests, not listens) at the end
// of rematchAllNetworksAndRequests
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
mKeepaliveTracker.handleStopAllKeepalives(nai,
ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK);
for (String iface : nai.linkProperties.getAllInterfaceNames()) {
// Disable wakeup packet monitoring for each interface.
wakeupModifyInterface(iface, nai.networkCapabilities, false);
}
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
mNetworkAgentInfos.remove(msg.replyTo);
nai.maybeStopClat();
synchronized (mNetworkForNetId) {
// Remove the NetworkAgent, but don't mark the netId as
// available until we've told netd to delete it below.
mNetworkForNetId.remove(nai.network.netId);
}
// Remove all previously satisfied requests.
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest request = nai.requestAt(i);
NetworkAgentInfo currentNetwork = getNetworkForRequest(request.requestId);
if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) {
clearNetworkForRequest(request.requestId);
sendUpdatedScoreToFactories(request, 0);
}
}
nai.clearLingerState();
if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
removeDataActivityTracking(nai);
notifyLockdownVpn(nai);
ensureNetworkTransitionWakelock(nai.name());
}
mLegacyTypeTracker.remove(nai, wasDefault);
rematchAllNetworksAndRequests(null, 0);
mLingerMonitor.noteDisconnect(nai);
if (nai.created) {
// Tell netd to clean up the configuration for this network
// (routing rules, DNS, etc).
// This may be slow as it requires a lot of netd shelling out to ip and
// ip[6]tables to flush routes and remove the incoming packet mark rule, so do it
// after we've rematched networks with requests which should make a potential
// fallback network the default or requested a new network from the
// NetworkFactories, so network traffic isn't interrupted for an unnecessarily
// long time.
try {
mNetd.removeNetwork(nai.network.netId);
} catch (Exception e) {
loge("Exception removing network: " + e);
}
mDnsManager.removeNetwork(nai.network);
}
synchronized (mNetworkForNetId) {
mNetIdInUse.delete(nai.network.netId);
}
disconnectAndDestroyNetwork(nai);
} else {
NetworkFactoryInfo nfi = mNetworkFactoryInfos.remove(msg.replyTo);
if (DBG && nfi != null) log("unregisterNetworkFactory for " + nfi.name);
}
}
// Destroys a network, remove references to it from the internal state managed by
// ConnectivityService, free its interfaces and clean up.
// Must be called on the Handler thread.
private void disconnectAndDestroyNetwork(NetworkAgentInfo nai) {
if (DBG) {
log(nai.name() + " got DISCONNECTED, was satisfying " + nai.numNetworkRequests());
}
// A network agent has disconnected.
// TODO - if we move the logic to the network agent (have them disconnect
// because they lost all their requests or because their score isn't good)
// then they would disconnect organically, report their new state and then
// disconnect the channel.
if (nai.networkInfo.isConnected()) {
nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
null, null);
}
final boolean wasDefault = isDefaultNetwork(nai);
if (wasDefault) {
mDefaultInetConditionPublished = 0;
// Log default network disconnection before required book-keeping.
// Let rematchAllNetworksAndRequests() below record a new default network event
// if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence
// whose timestamps tell how long it takes to recover a default network.
long now = SystemClock.elapsedRealtime();
metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(now, null, nai);
}
notifyIfacesChangedForNetworkStats();
// TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied
// by other networks that are already connected. Perhaps that can be done by
// sending all CALLBACK_LOST messages (for requests, not listens) at the end
// of rematchAllNetworksAndRequests
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
mKeepaliveTracker.handleStopAllKeepalives(nai,
ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK);
for (String iface : nai.linkProperties.getAllInterfaceNames()) {
// Disable wakeup packet monitoring for each interface.
wakeupModifyInterface(iface, nai.networkCapabilities, false);
}
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
mNetworkAgentInfos.remove(nai.messenger);
nai.maybeStopClat();
synchronized (mNetworkForNetId) {
// Remove the NetworkAgent, but don't mark the netId as
// available until we've told netd to delete it below.
mNetworkForNetId.remove(nai.network.netId);
}
// Remove all previously satisfied requests.
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest request = nai.requestAt(i);
NetworkAgentInfo currentNetwork = getNetworkForRequest(request.requestId);
if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) {
clearNetworkForRequest(request.requestId);
sendUpdatedScoreToFactories(request, 0);
}
}
nai.clearLingerState();
if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
removeDataActivityTracking(nai);
notifyLockdownVpn(nai);
ensureNetworkTransitionWakelock(nai.name());
}
mLegacyTypeTracker.remove(nai, wasDefault);
if (!nai.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
updateAllVpnsCapabilities();
}
rematchAllNetworksAndRequests(null, 0);
mLingerMonitor.noteDisconnect(nai);
if (nai.created) {
// Tell netd to clean up the configuration for this network
// (routing rules, DNS, etc).
// This may be slow as it requires a lot of netd shelling out to ip and
// ip[6]tables to flush routes and remove the incoming packet mark rule, so do it
// after we've rematched networks with requests which should make a potential
// fallback network the default or requested a new network from the
// NetworkFactories, so network traffic isn't interrupted for an unnecessarily
// long time.
try {
mNetd.removeNetwork(nai.network.netId);
} catch (Exception e) {
loge("Exception removing network: " + e);
}
mDnsManager.removeNetwork(nai.network);
}
synchronized (mNetworkForNetId) {
mNetIdInUse.delete(nai.network.netId);
}
}
// If this method proves to be too slow then we can maintain a separate
// pendingIntent => NetworkRequestInfo map.
// This method assumes that every non-null PendingIntent maps to exactly 1 NetworkRequestInfo.
@@ -3707,6 +3747,26 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
/**
* Ask all VPN objects 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.
* TODO : Fix this and call updateCapabilities inline to remove out-of-order events.
*/
private void updateAllVpnsCapabilities() {
synchronized (mVpns) {
for (int i = 0; i < mVpns.size(); i++) {
final Vpn vpn = mVpns.valueAt(i);
vpn.updateCapabilities();
}
}
}
@Override
public boolean updateLockdownVpn() {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
@@ -4181,6 +4241,15 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
// This checks that the passed capabilities either do not request a specific SSID, or the
// calling app has permission to do so.
private void ensureSufficientPermissionsForRequest(NetworkCapabilities nc,
int callerPid, int callerUid) {
if (null != nc.getSSID() && !checkSettingsPermission(callerPid, callerUid)) {
throw new SecurityException("Insufficient permissions to request a specific SSID");
}
}
private ArrayList<Integer> getSignalStrengthThresholds(NetworkAgentInfo nai) {
final SortedSet<Integer> thresholds = new TreeSet();
synchronized (nai) {
@@ -4238,8 +4307,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// the default network request. This allows callers to keep track of
// the system default network.
if (type == NetworkRequest.Type.TRACK_DEFAULT) {
networkCapabilities = new NetworkCapabilities(mDefaultRequest.networkCapabilities);
networkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN);
networkCapabilities = createDefaultNetworkCapabilitiesForUid(Binder.getCallingUid());
enforceAccessPermission();
} else {
networkCapabilities = new NetworkCapabilities(networkCapabilities);
@@ -4250,6 +4318,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
enforceMeteredApnPolicy(networkCapabilities);
}
ensureRequestableCapabilities(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
Binder.getCallingPid(), Binder.getCallingUid());
// Set the UID range for this request to the single UID of the requester, or to an empty
// set of UIDs if the caller has the appropriate permission and UIDs have not been set.
// This will overwrite any allowed UIDs in the requested capabilities. Though there
@@ -4328,6 +4398,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
enforceNetworkRequestPermissions(networkCapabilities);
enforceMeteredApnPolicy(networkCapabilities);
ensureRequestableCapabilities(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
Binder.getCallingPid(), Binder.getCallingUid());
ensureValidNetworkSpecifier(networkCapabilities);
restrictRequestUidsForCaller(networkCapabilities);
@@ -4383,6 +4455,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
Binder.getCallingPid(), Binder.getCallingUid());
restrictRequestUidsForCaller(nc);
// Apps without the CHANGE_NETWORK_STATE permission can't use background networks, so
// make all their listens include NET_CAPABILITY_FOREGROUND. That way, they will get
@@ -4409,6 +4483,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
enforceAccessPermission();
}
ensureValidNetworkSpecifier(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
Binder.getCallingPid(), Binder.getCallingUid());
final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
restrictRequestUidsForCaller(nc);
@@ -4541,8 +4617,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
synchronized (this) {
nai.networkMonitor.systemReady = mSystemReady;
}
addValidationLogs(nai.networkMonitor.getValidationLogs(), nai.network,
networkInfo.getExtraInfo());
final String extraInfo = networkInfo.getExtraInfo();
final String name = TextUtils.isEmpty(extraInfo)
? nai.networkCapabilities.getSSID() : extraInfo;
addValidationLogs(nai.networkMonitor.getValidationLogs(), nai.network, name);
if (DBG) log("registerNetworkAgent " + nai);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));
return nai.network.netId;
@@ -4562,7 +4640,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties oldLp) {
LinkProperties newLp = networkAgent.linkProperties;
LinkProperties newLp = new LinkProperties(networkAgent.linkProperties);
int netId = networkAgent.network.netId;
// The NetworkAgentInfo does not know whether clatd is running on its network or not. Before
@@ -4596,6 +4674,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
// TODO - move this check to cover the whole function
if (!Objects.equals(newLp, oldLp)) {
synchronized (networkAgent) {
networkAgent.linkProperties = newLp;
}
notifyIfacesChangedForNetworkStats();
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
}
@@ -4846,12 +4927,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.
synchronized (mVpns) {
for (int i = 0; i < mVpns.size(); i++) {
final Vpn vpn = mVpns.valueAt(i);
vpn.updateCapabilities();
}
}
updateAllVpnsCapabilities();
}
}
@@ -4980,7 +5056,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
case ConnectivityManager.CALLBACK_CAP_CHANGED: {
// networkAgent can't be null as it has been accessed a few lines above.
final NetworkCapabilities nc = networkCapabilitiesWithoutUidsUnlessAllowed(
final NetworkCapabilities nc = networkCapabilitiesRestrictedForCallerPermissions(
networkAgent.networkCapabilities, nri.mPid, nri.mUid);
putParcelable(bundle, nc);
break;
@@ -5511,6 +5587,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
updateUids(networkAgent, networkAgent.networkCapabilities, null);
}
disconnectAndDestroyNetwork(networkAgent);
} else if ((oldInfo != null && oldInfo.getState() == NetworkInfo.State.SUSPENDED) ||
state == NetworkInfo.State.SUSPENDED) {
// going into or coming out of SUSPEND: rescore and notify
@@ -5812,4 +5889,61 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static int encodeBool(boolean b) {
return b ? 1 : 0;
}
}
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,
FileDescriptor err, String[] args, ShellCallback callback,
ResultReceiver resultReceiver) {
(new ShellCmd()).exec(this, in, out, err, args, callback, resultReceiver);
}
private class ShellCmd extends ShellCommand {
@Override
public int onCommand(String cmd) {
if (cmd == null) {
return handleDefaultCommands(cmd);
}
final PrintWriter pw = getOutPrintWriter();
try {
switch (cmd) {
case "airplane-mode":
final String action = getNextArg();
if ("enable".equals(action)) {
setAirplaneMode(true);
return 0;
} else if ("disable".equals(action)) {
setAirplaneMode(false);
return 0;
} else if (action == null) {
final ContentResolver cr = mContext.getContentResolver();
final int enabled = Settings.Global.getInt(cr,
Settings.Global.AIRPLANE_MODE_ON);
pw.println(enabled == 0 ? "disabled" : "enabled");
return 0;
} else {
onHelp();
return -1;
}
default:
return handleDefaultCommands(cmd);
}
} catch (Exception e) {
pw.println(e);
}
return -1;
}
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
pw.println("Connectivity service commands:");
pw.println(" help");
pw.println(" Print this help text.");
pw.println(" airplane-mode [enable|disable]");
pw.println(" Turn airplane mode on or off.");
pw.println(" airplane-mode");
pw.println(" Get airplane mode.");
}
}
}

View File

@@ -23,8 +23,10 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.net.NetworkCapabilities;
import android.net.wifi.WifiInfo;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -130,16 +132,17 @@ public class NetworkNotificationManager {
final String tag = tagFor(id);
final int eventId = notifyType.eventId;
final int transportType;
final String extraInfo;
final String name;
if (nai != null) {
transportType = getFirstTransportType(nai);
extraInfo = nai.networkInfo.getExtraInfo();
final String extraInfo = nai.networkInfo.getExtraInfo();
name = TextUtils.isEmpty(extraInfo) ? nai.networkCapabilities.getSSID() : extraInfo;
// Only notify for Internet-capable networks.
if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)) return;
} else {
// Legacy notifications.
transportType = TRANSPORT_CELLULAR;
extraInfo = null;
name = null;
}
// Clear any previous notification with lower priority, otherwise return. http://b/63676954.
@@ -156,9 +159,8 @@ public class NetworkNotificationManager {
if (DBG) {
Slog.d(TAG, String.format(
"showNotification tag=%s event=%s transport=%s extraInfo=%s highPrioriy=%s",
tag, nameOf(eventId), getTransportName(transportType), extraInfo,
highPriority));
"showNotification tag=%s event=%s transport=%s name=%s highPriority=%s",
tag, nameOf(eventId), getTransportName(transportType), name, highPriority));
}
Resources r = Resources.getSystem();
@@ -176,7 +178,8 @@ public class NetworkNotificationManager {
switch (transportType) {
case TRANSPORT_WIFI:
title = r.getString(R.string.wifi_available_sign_in, 0);
details = r.getString(R.string.network_available_sign_in_detailed, extraInfo);
details = r.getString(R.string.network_available_sign_in_detailed,
WifiInfo.removeDoubleQuotes(nai.networkCapabilities.getSSID()));
break;
case TRANSPORT_CELLULAR:
title = r.getString(R.string.network_available_sign_in, 0);
@@ -186,7 +189,7 @@ public class NetworkNotificationManager {
break;
default:
title = r.getString(R.string.network_available_sign_in, 0);
details = r.getString(R.string.network_available_sign_in_detailed, extraInfo);
details = r.getString(R.string.network_available_sign_in_detailed, name);
break;
}
} else if (notifyType == NotificationType.NETWORK_SWITCH) {

View File

@@ -40,12 +40,14 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.os.Parcel;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArraySet;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -54,6 +56,9 @@ import java.util.Set;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class NetworkCapabilitiesTest {
private static final String TEST_SSID = "TEST_SSID";
private static final String DIFFERENT_TEST_SSID = "DIFFERENT_TEST_SSID";
@Test
public void testMaybeMarkCapabilitiesRestricted() {
// verify EIMS is restricted
@@ -268,6 +273,8 @@ public class NetworkCapabilitiesTest {
.addCapability(NET_CAPABILITY_EIMS)
.addCapability(NET_CAPABILITY_NOT_METERED);
assertEqualsThroughMarshalling(netCap);
netCap.setSSID(TEST_SSID);
assertEqualsThroughMarshalling(netCap);
}
@Test
@@ -361,6 +368,27 @@ public class NetworkCapabilitiesTest {
assertTrue(nc1.equalsNetCapabilities(nc2));
}
@Test
public void testSSID() {
NetworkCapabilities nc1 = new NetworkCapabilities();
NetworkCapabilities nc2 = new NetworkCapabilities();
assertTrue(nc2.satisfiedBySSID(nc1));
nc1.setSSID(TEST_SSID);
assertTrue(nc2.satisfiedBySSID(nc1));
nc2.setSSID("different " + TEST_SSID);
assertFalse(nc2.satisfiedBySSID(nc1));
assertTrue(nc1.satisfiedByImmutableNetworkCapabilities(nc2));
assertFalse(nc1.satisfiedByNetworkCapabilities(nc2));
}
private ArraySet<UidRange> uidRange(int from, int to) {
final ArraySet<UidRange> range = new ArraySet<>(1);
range.add(new UidRange(from, to));
return range;
}
@Test
public void testCombineCapabilities() {
NetworkCapabilities nc1 = new NetworkCapabilities();
@@ -382,6 +410,35 @@ public class NetworkCapabilitiesTest {
// will never be satisfied.
assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_NOT_ROAMING));
nc1.setSSID(TEST_SSID);
nc2.combineCapabilities(nc1);
assertTrue(TEST_SSID.equals(nc2.getSSID()));
// Because they now have the same SSID, the following call should not throw
nc2.combineCapabilities(nc1);
nc1.setSSID(DIFFERENT_TEST_SSID);
try {
nc2.combineCapabilities(nc1);
fail("Expected IllegalStateException: can't combine different SSIDs");
} catch (IllegalStateException expected) {}
nc1.setSSID(TEST_SSID);
nc1.setUids(uidRange(10, 13));
assertNotEquals(nc1, nc2);
nc2.combineCapabilities(nc1); // Everything + 10~13 is still everything.
assertNotEquals(nc1, nc2);
nc1.combineCapabilities(nc2); // 10~13 + everything is everything.
assertEquals(nc1, nc2);
nc1.setUids(uidRange(10, 13));
nc2.setUids(uidRange(20, 23));
assertNotEquals(nc1, nc2);
nc1.combineCapabilities(nc2);
assertTrue(nc1.appliesToUid(12));
assertFalse(nc2.appliesToUid(12));
assertTrue(nc1.appliesToUid(22));
assertTrue(nc2.appliesToUid(22));
}
@Test
@@ -420,4 +477,38 @@ public class NetworkCapabilitiesTest {
p.setDataPosition(0);
assertEquals(NetworkCapabilities.CREATOR.createFromParcel(p), netCap);
}
@Test
public void testSet() {
NetworkCapabilities nc1 = new NetworkCapabilities();
NetworkCapabilities nc2 = new NetworkCapabilities();
nc1.addUnwantedCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
nc1.addCapability(NET_CAPABILITY_NOT_ROAMING);
assertNotEquals(nc1, nc2);
nc2.set(nc1);
assertEquals(nc1, nc2);
assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_CAPTIVE_PORTAL));
// This will effectively move NOT_ROAMING capability from required to unwanted for nc1.
nc1.addUnwantedCapability(NET_CAPABILITY_NOT_ROAMING);
nc1.setSSID(TEST_SSID);
nc2.set(nc1);
assertEquals(nc1, nc2);
// Contrary to combineCapabilities, set() will have removed the NOT_ROAMING capability
// from nc2.
assertFalse(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(nc2.hasUnwantedCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(TEST_SSID.equals(nc2.getSSID()));
nc1.setSSID(DIFFERENT_TEST_SSID);
nc2.set(nc1);
assertEquals(nc1, nc2);
assertTrue(DIFFERENT_TEST_SSID.equals(nc2.getSSID()));
nc1.setUids(uidRange(10, 13));
nc2.set(nc1); // Overwrites, as opposed to combineCapabilities
assertEquals(nc1, nc2);
}
}

View File

@@ -84,6 +84,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.net.CaptivePortal;
import android.net.ConnectivityManager;
@@ -111,6 +112,7 @@ import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.StringNetworkSpecifier;
import android.net.UidRange;
import android.net.VpnService;
import android.net.captiveportal.CaptivePortalProbeResult;
import android.net.metrics.IpConnectivityLog;
import android.net.util.MultinetworkPolicyTracker;
@@ -133,6 +135,7 @@ import android.test.mock.MockContentResolver;
import android.util.ArraySet;
import android.util.Log;
import com.android.internal.net.VpnConfig;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
@@ -196,6 +199,7 @@ public class ConnectivityServiceTest {
private MockNetworkAgent mWiFiNetworkAgent;
private MockNetworkAgent mCellNetworkAgent;
private MockNetworkAgent mEthernetNetworkAgent;
private MockVpn mMockVpn;
private Context mContext;
@Mock IpConnectivityMetrics.Logger mMetricsService;
@@ -477,6 +481,14 @@ public class ConnectivityServiceTest {
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
}
public void setNetworkCapabilities(NetworkCapabilities nc,
boolean sendToConnectivityService) {
mNetworkCapabilities.set(nc);
if (sendToConnectivityService) {
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
}
}
public void connectWithoutInternet() {
mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
@@ -506,6 +518,7 @@ public class ConnectivityServiceTest {
mWrappedNetworkMonitor.gen204ProbeResult = 204;
NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(mNetworkCapabilities.getTransportTypes()[0])
.clearCapabilities()
.build();
callback = new NetworkCallback() {
public void onCapabilitiesChanged(Network network,
@@ -591,6 +604,10 @@ public class ConnectivityServiceTest {
return mRedirectUrl;
}
public NetworkAgent getNetworkAgent() {
return mNetworkAgent;
}
public NetworkCapabilities getNetworkCapabilities() {
return mNetworkCapabilities;
}
@@ -723,6 +740,87 @@ public class ConnectivityServiceTest {
}
}
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;
// Careful ! This is different from mNetworkAgent, because MockNetworkAgent does
// not inherit from NetworkAgent.
private MockNetworkAgent mMockNetworkAgent;
public MockVpn(int userId) {
super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
userId);
}
public void setNetworkAgent(MockNetworkAgent agent) {
waitForIdle(agent, TIMEOUT_MS);
mMockNetworkAgent = agent;
mNetworkAgent = agent.getNetworkAgent();
mNetworkCapabilities.set(agent.getNetworkCapabilities());
}
public void setUids(Set<UidRange> uids) {
mNetworkCapabilities.setUids(uids);
updateCapabilities();
}
@Override
public int getNetId() {
return mMockNetworkAgent.getNetwork().netId;
}
@Override
public boolean appliesToUid(int uid) {
return mConnected; // Trickery to simplify testing.
}
@Override
protected boolean isCallerEstablishedOwnerLocked() {
return mConnected; // Similar trickery
}
public void connect() {
mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
mConnected = true;
mConfig = new VpnConfig();
}
@Override
public void updateCapabilities() {
if (!mConnected) return;
super.updateCapabilities();
// Because super.updateCapabilities will update the capabilities of the agent but not
// the mock agent, the mock agent needs to know about them.
copyCapabilitiesToNetworkAgent();
}
private void copyCapabilitiesToNetworkAgent() {
if (null != mMockNetworkAgent) {
mMockNetworkAgent.setNetworkCapabilities(mNetworkCapabilities,
false /* sendToConnectivityService */);
}
}
public void disconnect() {
mConnected = false;
mConfig = null;
}
}
private class FakeWakeupMessage extends WakeupMessage {
private static final int UNREASONABLY_LONG_WAIT = 1000;
@@ -889,6 +987,17 @@ public class ConnectivityServiceTest {
return mLastCreatedNetworkMonitor;
}
public void mockVpn(int uid) {
synchronized (mVpns) {
int userId = UserHandle.getUserId(uid);
mMockVpn = new MockVpn(userId);
// This has no effect unless the VPN is actually connected, because things like
// getActiveNetworkForUidInternal call getNetworkAgentInfoForNetId on the VPN
// netId, and check if that network is actually connected.
mVpns.put(userId, mMockVpn);
}
}
public void waitForIdle(int timeoutMs) {
waitForIdleHandler(mHandlerThread, timeoutMs);
}
@@ -932,8 +1041,9 @@ public class ConnectivityServiceTest {
mock(INetworkPolicyManager.class),
mock(IpConnectivityLog.class));
mService.systemReady();
mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
mService.systemReady();
mService.mockVpn(Process.myUid());
mCm.bindProcessToNetwork(null);
// Ensure that the default setting for Captive Portals is used for most tests
@@ -1346,6 +1456,7 @@ public class ConnectivityServiceTest {
private final static int TIMEOUT_MS = 100;
private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>();
private Network mLastAvailableNetwork;
protected void setLastCallback(CallbackState state, Network network, Object o) {
mCallbacks.offer(new CallbackInfo(state, network, o));
@@ -1353,6 +1464,7 @@ public class ConnectivityServiceTest {
@Override
public void onAvailable(Network network) {
mLastAvailableNetwork = network;
setLastCallback(CallbackState.AVAILABLE, network, null);
}
@@ -1388,9 +1500,14 @@ public class ConnectivityServiceTest {
@Override
public void onLost(Network network) {
mLastAvailableNetwork = null;
setLastCallback(CallbackState.LOST, network, null);
}
public Network getLastAvailableNetwork() {
return mLastAvailableNetwork;
}
CallbackInfo nextCallback(int timeoutMs) {
CallbackInfo cb = null;
try {
@@ -1526,7 +1643,8 @@ public class ConnectivityServiceTest {
void expectCapabilitiesLike(Predicate<NetworkCapabilities> fn, MockNetworkAgent agent) {
CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
assertTrue(fn.test((NetworkCapabilities) cbi.arg));
assertTrue("Received capabilities don't match expectations : " + cbi.arg,
fn.test((NetworkCapabilities) cbi.arg));
}
void assertNoCallback() {
@@ -1657,6 +1775,7 @@ public class ConnectivityServiceTest {
callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.connect(true);
// We get AVAILABLE on wifi when wifi connects and satisfies our unmetered request.
@@ -1667,6 +1786,7 @@ public class ConnectivityServiceTest {
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mEthernetNetworkAgent.connect(true);
callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
@@ -1675,11 +1795,13 @@ public class ConnectivityServiceTest {
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mEthernetNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
for (int i = 0; i < 4; i++) {
MockNetworkAgent oldNetwork, newNetwork;
@@ -1708,6 +1830,7 @@ public class ConnectivityServiceTest {
defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, mWiFiNetworkAgent);
defaultCallback.assertNoCallback();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Wifi no longer satisfies our listen, which is for an unmetered network.
// But because its score is 55, it's still up (and the default network).
@@ -1717,8 +1840,11 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.disconnect();
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mCellNetworkAgent.disconnect();
defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
mCm.unregisterNetworkCallback(callback);
waitForIdle();
@@ -1735,6 +1861,7 @@ public class ConnectivityServiceTest {
callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up wifi with a score of 20.
// Cell stays up because it would satisfy the default request if it validated.
@@ -1743,12 +1870,14 @@ public class ConnectivityServiceTest {
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up wifi with a score of 70.
// Cell is lingered because it would not satisfy any request, even if it validated.
@@ -1759,6 +1888,7 @@ public class ConnectivityServiceTest {
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Tear down wifi.
mWiFiNetworkAgent.disconnect();
@@ -1766,6 +1896,7 @@ public class ConnectivityServiceTest {
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up wifi, then validate it. Previous versions would immediately tear down cell, but
// it's arguably correct to linger it, since it was the default network before it validated.
@@ -1777,6 +1908,7 @@ public class ConnectivityServiceTest {
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
@@ -1785,12 +1917,15 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
// If a network is lingering, and we add and remove a request from it, resume lingering.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
@@ -1798,6 +1933,7 @@ public class ConnectivityServiceTest {
// TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
NetworkRequest cellRequest = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_CELLULAR).build();
@@ -1814,6 +1950,7 @@ public class ConnectivityServiceTest {
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Cell is now the default network. Pin it with a cell-specific request.
noopCallback = new NetworkCallback(); // Can't reuse NetworkCallbacks. http://b/20701525
@@ -1824,6 +1961,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// The default request is lingering on cell, but nothing happens to cell, and we send no
// callbacks for it, because it's kept up by cellRequest.
callback.assertNoCallback();
@@ -1847,6 +1985,7 @@ public class ConnectivityServiceTest {
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
trackDefaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Let linger run its course.
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent, lingerTimeoutMs);
@@ -2495,23 +2634,27 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up wifi and expect CALLBACK_AVAILABLE.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
cellNetworkCallback.assertNoCallback();
defaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring down cell. Expect no default network callback, since it wasn't the default.
mCellNetworkAgent.disconnect();
cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
defaultNetworkCallback.assertNoCallback();
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up cell. Expect no default network callback, since it won't be the default.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultNetworkCallback.assertNoCallback();
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring down wifi. Expect the default network callback to notified of LOST wifi
// followed by AVAILABLE cell.
@@ -2522,6 +2665,24 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.disconnect();
cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
defaultNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
final int uid = Process.myUid();
final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(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);
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
vpnNetworkAgent.disconnect();
defaultNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
}
@Test
@@ -4012,6 +4173,7 @@ public class ConnectivityServiceTest {
final TestNetworkCallback genericNotVpnNetworkCallback = new TestNetworkCallback();
final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
final NetworkRequest genericNotVpnRequest = new NetworkRequest.Builder().build();
final NetworkRequest genericRequest = new NetworkRequest.Builder()
.removeCapability(NET_CAPABILITY_NOT_VPN).build();
@@ -4024,6 +4186,8 @@ public class ConnectivityServiceTest {
mCm.registerNetworkCallback(genericNotVpnRequest, genericNotVpnNetworkCallback);
mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
mCm.registerDefaultNetworkCallback(defaultCallback);
defaultCallback.assertNoCallback();
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
@@ -4031,26 +4195,30 @@ public class ConnectivityServiceTest {
genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
genericNotVpnNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
vpnNetworkCallback.assertNoCallback();
// TODO : check callbacks agree with the return value of mCm.getActiveNetwork().
// Right now this is not possible because establish() is not adequately instrumented
// in this test.
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid));
vpnNetworkAgent.setUids(ranges);
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
vpnNetworkAgent.connect(false);
mMockVpn.connect();
genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
vpnNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
genericNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback();
vpnNetworkCallback.expectCapabilitiesLike(nc -> null == nc.getUids(), vpnNetworkAgent);
defaultCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
ranges.clear();
vpnNetworkAgent.setUids(ranges);
@@ -4060,13 +4228,24 @@ public class ConnectivityServiceTest {
wifiNetworkCallback.assertNoCallback();
vpnNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
// 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
// ranges at all when determining whether a network should be rematched. In practice, VPNs
// 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(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
ranges.add(new UidRange(uid, uid));
vpnNetworkAgent.setUids(ranges);
mMockVpn.setUids(ranges);
genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
vpnNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
// 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(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
mWiFiNetworkAgent.disconnect();
@@ -4074,6 +4253,7 @@ public class ConnectivityServiceTest {
genericNotVpnNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
wifiNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
vpnNetworkCallback.assertNoCallback();
defaultCallback.assertNoCallback();
vpnNetworkAgent.disconnect();
@@ -4081,9 +4261,161 @@ public class ConnectivityServiceTest {
genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback();
vpnNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
assertEquals(null, mCm.getActiveNetwork());
mCm.unregisterNetworkCallback(genericNetworkCallback);
mCm.unregisterNetworkCallback(wifiNetworkCallback);
mCm.unregisterNetworkCallback(vpnNetworkCallback);
mCm.unregisterNetworkCallback(defaultCallback);
}
@Test
public void testVpnWithAndWithoutInternet() {
final int uid = Process.myUid();
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
defaultCallback.assertNoCallback();
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(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.connect();
defaultCallback.assertNoCallback();
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
vpnNetworkAgent.disconnect();
defaultCallback.assertNoCallback();
vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */);
mMockVpn.connect();
defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
vpnNetworkAgent.disconnect();
defaultCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
ranges.clear();
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */);
mMockVpn.connect();
defaultCallback.assertNoCallback();
mCm.unregisterNetworkCallback(defaultCallback);
}
@Test
public void testVpnSetUnderlyingNetworks() {
final int uid = Process.myUid();
final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder()
.removeCapability(NET_CAPABILITY_NOT_VPN)
.addTransportType(TRANSPORT_VPN)
.build();
NetworkCapabilities nc;
mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
vpnNetworkCallback.assertNoCallback();
final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(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 */);
vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
assertTrue(nc.hasTransport(TRANSPORT_VPN));
assertFalse(nc.hasTransport(TRANSPORT_CELLULAR));
assertFalse(nc.hasTransport(TRANSPORT_WIFI));
// For safety reasons a VPN without underlying networks is considered metered.
assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
// Connect cell and use it as an underlying network.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
vpnNetworkAgent);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mWiFiNetworkAgent.connect(true);
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
vpnNetworkAgent);
// Don't disconnect, but note the VPN is not using wifi any more.
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
vpnNetworkAgent);
// Use Wifi but not cell. Note the VPN is now unmetered.
mService.setUnderlyingNetworksForVpn(
new Network[] { mWiFiNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& caps.hasCapability(NET_CAPABILITY_NOT_METERED),
vpnNetworkAgent);
// Use both again.
mService.setUnderlyingNetworksForVpn(
new Network[] { mCellNetworkAgent.getNetwork(), mWiFiNetworkAgent.getNetwork() });
vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
vpnNetworkAgent);
// 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.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& caps.hasCapability(NET_CAPABILITY_NOT_METERED),
vpnNetworkAgent);
// Disconnect wifi too. No underlying networks means this is now metered.
mWiFiNetworkAgent.disconnect();
vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
vpnNetworkAgent);
mMockVpn.disconnect();
}
}