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

* changes:
  Let tests enumerate all transports/capabilities.
  Destroy networks as soon as they are disconnected.
  Fix a ConcurrentModificationException crash.
  Unify behavior of various cases of "no underlying networks"
  Fix: VPNs update caps upon underlying network disconnect.
  Add tests for setUnderlyingNetworks.
  Fix setCapabilities.
  Fix SSID not being logged by the validation logs
  Make sure getActiveNetwork is consistent with default callbacks
  Add tests for getActiveNetwork.
  Guard the SSID with NETWORK_SETTINGS
  Add a new ssid field in NetworkCapabilities.
  Notif manager and captive portal app to read SSID again
  Remove unwanted capability from the java-doc
  Actually @hide unwanted capability methods.
  API council requested tweaks to NetworkRequest.
  Add OEM_PAID capability to system api
  add airplane mode shell commands to connectivity
  Enable cleartext for captive portal apps
  Expose CONNECTIVITY_ACTION to Instant Apps
  fix isActiveNetworkMetered with VPNs
This commit is contained in:
Chalard Jean
2018-06-06 08:25:45 +00:00
committed by Gerrit Code Review
13 changed files with 842 additions and 161 deletions

View File

@@ -26057,6 +26057,8 @@ package android.net {
public class NetworkRequest implements android.os.Parcelable { public class NetworkRequest implements android.os.Parcelable {
method public int describeContents(); method public int describeContents();
method public boolean hasCapability(int);
method public boolean hasTransport(int);
method public void writeToParcel(android.os.Parcel, int); method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.NetworkRequest> CREATOR; field public static final android.os.Parcelable.Creator<android.net.NetworkRequest> CREATOR;
} }

View File

@@ -2636,6 +2636,10 @@ package android.net {
field public static final int ERROR_INVALID_NETWORK = 1; // 0x1 field public static final int ERROR_INVALID_NETWORK = 1; // 0x1
} }
public final class NetworkCapabilities implements android.os.Parcelable {
field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
}
public class NetworkKey implements android.os.Parcelable { public class NetworkKey implements android.os.Parcelable {
ctor public NetworkKey(android.net.WifiKey); ctor public NetworkKey(android.net.WifiKey);
method public int describeContents(); method public int describeContents();

View File

@@ -253,6 +253,11 @@ package android.net {
field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0 field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
} }
public final class NetworkCapabilities implements android.os.Parcelable {
method public int[] getCapabilities();
method public int[] getTransportTypes();
}
public class TrafficStats { public class TrafficStats {
method public static long getLoopbackRxBytes(); method public static long getLoopbackRxBytes();
method public static long getLoopbackRxPackets(); method public static long getLoopbackRxPackets();

View File

@@ -17,6 +17,8 @@
package android.net; package android.net;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.net.ConnectivityManager.NetworkCallback; import android.net.ConnectivityManager.NetworkCallback;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
@@ -60,15 +62,7 @@ public final class NetworkCapabilities implements Parcelable {
public NetworkCapabilities(NetworkCapabilities nc) { public NetworkCapabilities(NetworkCapabilities nc) {
if (nc != null) { if (nc != null) {
mNetworkCapabilities = nc.mNetworkCapabilities; set(nc);
mTransportTypes = nc.mTransportTypes;
mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps;
mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
mNetworkSpecifier = nc.mNetworkSpecifier;
mSignalStrength = nc.mSignalStrength;
mUids = nc.mUids;
mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid;
mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities;
} }
} }
@@ -84,6 +78,24 @@ public final class NetworkCapabilities implements Parcelable {
mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED; mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
mUids = null; mUids = null;
mEstablishingVpnAppUid = INVALID_UID; 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;
} }
/** /**
@@ -275,6 +287,7 @@ public final class NetworkCapabilities implements Parcelable {
* this network can be used by system apps to upload telemetry data. * this network can be used by system apps to upload telemetry data.
* @hide * @hide
*/ */
@SystemApi
public static final int NET_CAPABILITY_OEM_PAID = 22; public static final int NET_CAPABILITY_OEM_PAID = 22;
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS; private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
@@ -423,6 +436,7 @@ public final class NetworkCapabilities implements Parcelable {
* @return an array of capability values for this instance. * @return an array of capability values for this instance.
* @hide * @hide
*/ */
@TestApi
public @NetCapability int[] getCapabilities() { public @NetCapability int[] getCapabilities() {
return BitUtils.unpackBits(mNetworkCapabilities); return BitUtils.unpackBits(mNetworkCapabilities);
} }
@@ -687,6 +701,7 @@ public final class NetworkCapabilities implements Parcelable {
* @return an array of transport type values for this instance. * @return an array of transport type values for this instance.
* @hide * @hide
*/ */
@TestApi
public @Transport int[] getTransportTypes() { public @Transport int[] getTransportTypes() {
return BitUtils.unpackBits(mTransportTypes); return BitUtils.unpackBits(mTransportTypes);
} }
@@ -920,7 +935,7 @@ public final class NetworkCapabilities implements Parcelable {
/** /**
* Sets the signal strength. This is a signed integer, with higher values indicating a stronger * 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 * signal. The exact units are bearer-dependent. For example, Wi-Fi uses the same RSSI units
* reported by WifiManager. * reported by wifi code.
* <p> * <p>
* Note that when used to register a network callback, this specifies the minimum acceptable * 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 * signal strength. When received as the state of an existing network it specifies the current
@@ -1052,7 +1067,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> * <p>
* This test only checks whether equal range objects are in both sets. It will * 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 * return false if the ranges are not exactly the same, even if the covered UIDs
@@ -1142,6 +1157,62 @@ public final class NetworkCapabilities implements Parcelable {
mUids.addAll(nc.mUids); 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. * Combine a set of Capabilities to this one. Useful for coming up with the complete set.
* <p> * <p>
@@ -1157,6 +1228,7 @@ public final class NetworkCapabilities implements Parcelable {
combineSpecifiers(nc); combineSpecifiers(nc);
combineSignalStrength(nc); combineSignalStrength(nc);
combineUids(nc); combineUids(nc);
combineSSIDs(nc);
} }
/** /**
@@ -1175,7 +1247,8 @@ public final class NetworkCapabilities implements Parcelable {
&& (onlyImmutable || satisfiedByLinkBandwidths(nc)) && (onlyImmutable || satisfiedByLinkBandwidths(nc))
&& satisfiedBySpecifier(nc) && satisfiedBySpecifier(nc)
&& (onlyImmutable || satisfiedBySignalStrength(nc)) && (onlyImmutable || satisfiedBySignalStrength(nc))
&& (onlyImmutable || satisfiedByUids(nc))); && (onlyImmutable || satisfiedByUids(nc))
&& (onlyImmutable || satisfiedBySSID(nc)));
} }
/** /**
@@ -1264,7 +1337,8 @@ public final class NetworkCapabilities implements Parcelable {
&& equalsLinkBandwidths(that) && equalsLinkBandwidths(that)
&& equalsSignalStrength(that) && equalsSignalStrength(that)
&& equalsSpecifier(that) && equalsSpecifier(that)
&& equalsUids(that)); && equalsUids(that)
&& equalsSSID(that));
} }
@Override @Override
@@ -1279,7 +1353,8 @@ public final class NetworkCapabilities implements Parcelable {
+ (mLinkDownBandwidthKbps * 19) + (mLinkDownBandwidthKbps * 19)
+ Objects.hashCode(mNetworkSpecifier) * 23 + Objects.hashCode(mNetworkSpecifier) * 23
+ (mSignalStrength * 29) + (mSignalStrength * 29)
+ Objects.hashCode(mUids) * 31; + Objects.hashCode(mUids) * 31
+ Objects.hashCode(mSSID) * 37;
} }
@Override @Override
@@ -1296,6 +1371,7 @@ public final class NetworkCapabilities implements Parcelable {
dest.writeParcelable((Parcelable) mNetworkSpecifier, flags); dest.writeParcelable((Parcelable) mNetworkSpecifier, flags);
dest.writeInt(mSignalStrength); dest.writeInt(mSignalStrength);
dest.writeArraySet(mUids); dest.writeArraySet(mUids);
dest.writeString(mSSID);
} }
public static final Creator<NetworkCapabilities> CREATOR = public static final Creator<NetworkCapabilities> CREATOR =
@@ -1313,6 +1389,7 @@ public final class NetworkCapabilities implements Parcelable {
netCap.mSignalStrength = in.readInt(); netCap.mSignalStrength = in.readInt();
netCap.mUids = (ArraySet<UidRange>) in.readArraySet( netCap.mUids = (ArraySet<UidRange>) in.readArraySet(
null /* ClassLoader, null for default */); null /* ClassLoader, null for default */);
netCap.mSSID = in.readString();
return netCap; return netCap;
} }
@Override @Override
@@ -1363,6 +1440,10 @@ public final class NetworkCapabilities implements Parcelable {
sb.append(" EstablishingAppUid: ").append(mEstablishingVpnAppUid); sb.append(" EstablishingAppUid: ").append(mEstablishingVpnAppUid);
} }
if (null != mSSID) {
sb.append(" SSID: ").append(mSSID);
}
sb.append("]"); sb.append("]");
return sb.toString(); return sb.toString();
} }

View File

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

View File

@@ -23,8 +23,10 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" /> <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
<uses-permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS" /> <uses-permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS" />
<uses-permission android:name="android.permission.NETWORK_SETTINGS" />
<application android:label="@string/app_name" > <application android:label="@string/app_name"
android:usesCleartextTraffic="true">
<activity <activity
android:name="com.android.captiveportallogin.CaptivePortalLoginActivity" android:name="com.android.captiveportallogin.CaptivePortalLoginActivity"
android:label="@string/action_bar_label" android:label="@string/action_bar_label"

View File

@@ -29,17 +29,18 @@ import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback; import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network; import android.net.Network;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest; import android.net.NetworkRequest;
import android.net.Proxy; import android.net.Proxy;
import android.net.Uri; import android.net.Uri;
import android.net.captiveportal.CaptivePortalProbeSpec; import android.net.captiveportal.CaptivePortalProbeSpec;
import android.net.dns.ResolvUtil; import android.net.dns.ResolvUtil;
import android.net.http.SslError; import android.net.http.SslError;
import android.net.wifi.WifiInfo;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
import android.support.v4.widget.SwipeRefreshLayout; import android.support.v4.widget.SwipeRefreshLayout;
import android.text.TextUtils;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.Log; import android.util.Log;
import android.util.TypedValue; import android.util.TypedValue;
@@ -556,15 +557,12 @@ public class CaptivePortalLoginActivity extends Activity {
} }
private String getHeaderTitle() { private String getHeaderTitle() {
NetworkInfo info = mCm.getNetworkInfo(mNetwork);
if (info == null) {
return getString(R.string.action_bar_label);
}
NetworkCapabilities nc = mCm.getNetworkCapabilities(mNetwork); NetworkCapabilities nc = mCm.getNetworkCapabilities(mNetwork);
if (!nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { if (nc == null || TextUtils.isEmpty(nc.getSSID())
|| !nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
return getString(R.string.action_bar_label); return getString(R.string.action_bar_label);
} }
return getString(R.string.action_bar_title, info.getExtraInfo().replaceAll("^\"|\"$", "")); return getString(R.string.action_bar_title, WifiInfo.removeDoubleQuotes(nc.getSSID()));
} }
private String getHeaderSubtitle(URL url) { private String getHeaderSubtitle(URL url) {

View File

@@ -30,7 +30,8 @@
<application <application
android:label="@string/app_name" android:label="@string/app_name"
android:directBootAware="true"> android:directBootAware="true"
android:usesCleartextTraffic="true">
<receiver android:name="com.android.carrierdefaultapp.CarrierDefaultBroadcastReceiver"> <receiver android:name="com.android.carrierdefaultapp.CarrierDefaultBroadcastReceiver">
<intent-filter> <intent-filter>
<action android:name="com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED" /> <action android:name="com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED" />

View File

@@ -102,6 +102,8 @@ import android.os.RemoteException;
import android.os.ResultReceiver; import android.os.ResultReceiver;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.ServiceSpecificException; import android.os.ServiceSpecificException;
import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
@@ -234,8 +236,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
private KeyStore mKeyStore; private KeyStore mKeyStore;
@VisibleForTesting
@GuardedBy("mVpns") @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 // TODO: investigate if mLockdownEnabled can be removed and replaced everywhere by
// a direct call to LockdownVpnTracker.isEnabled(). // 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 final int MAX_VALIDATION_LOGS = 10;
private static class ValidationLog { private static class ValidationLog {
final Network mNetwork; final Network mNetwork;
final String mNetworkExtraInfo; final String mName;
final ReadOnlyLocalLog mLog; final ReadOnlyLocalLog mLog;
ValidationLog(Network network, String networkExtraInfo, ReadOnlyLocalLog log) { ValidationLog(Network network, String name, ReadOnlyLocalLog log) {
mNetwork = network; mNetwork = network;
mNetworkExtraInfo = networkExtraInfo; mName = name;
mLog = log; mLog = log;
} }
} }
private final ArrayDeque<ValidationLog> mValidationLogs = private final ArrayDeque<ValidationLog> mValidationLogs =
new ArrayDeque<ValidationLog>(MAX_VALIDATION_LOGS); 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) { synchronized (mValidationLogs) {
while (mValidationLogs.size() >= MAX_VALIDATION_LOGS) { while (mValidationLogs.size() >= MAX_VALIDATION_LOGS) {
mValidationLogs.removeLast(); 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); 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( private NetworkRequest createDefaultInternetRequestForTransport(
int transportType, NetworkRequest.Type type) { int transportType, NetworkRequest.Type type) {
NetworkCapabilities netCap = new NetworkCapabilities(); NetworkCapabilities netCap = new NetworkCapabilities();
@@ -1150,12 +1162,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
int vpnNetId = NETID_UNSET; int vpnNetId = NETID_UNSET;
synchronized (mVpns) { synchronized (mVpns) {
final Vpn vpn = mVpns.get(user); 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(); if (vpn != null && vpn.appliesToUid(uid)) vpnNetId = vpn.getNetId();
} }
NetworkAgentInfo nai; NetworkAgentInfo nai;
if (vpnNetId != NETID_UNSET) { if (vpnNetId != NETID_UNSET) {
nai = getNetworkAgentInfoForNetId(vpnNetId); 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(); nai = getDefaultNetwork();
if (nai != null if (nai != null
@@ -1352,7 +1372,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (nai != null) { if (nai != null) {
synchronized (nai) { synchronized (nai) {
if (nai.networkCapabilities != null) { if (nai.networkCapabilities != null) {
return networkCapabilitiesWithoutUidsUnlessAllowed(nai.networkCapabilities, return networkCapabilitiesRestrictedForCallerPermissions(
nai.networkCapabilities,
Binder.getCallingPid(), Binder.getCallingUid()); Binder.getCallingPid(), Binder.getCallingUid());
} }
} }
@@ -1366,10 +1387,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network)); return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network));
} }
private NetworkCapabilities networkCapabilitiesWithoutUidsUnlessAllowed( private NetworkCapabilities networkCapabilitiesRestrictedForCallerPermissions(
NetworkCapabilities nc, int callerPid, int callerUid) { NetworkCapabilities nc, int callerPid, int callerUid) {
if (checkSettingsPermission(callerPid, callerUid)) return new NetworkCapabilities(nc); final NetworkCapabilities newNc = new NetworkCapabilities(nc);
return new NetworkCapabilities(nc).setUids(null); if (!checkSettingsPermission(callerPid, callerUid)) {
newNc.setUids(null);
newNc.setSSID(null);
}
return newNc;
} }
private void restrictRequestUidsForCaller(NetworkCapabilities nc) { private void restrictRequestUidsForCaller(NetworkCapabilities nc) {
@@ -1415,7 +1440,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
public boolean isActiveNetworkMetered() { public boolean isActiveNetworkMetered() {
enforceAccessPermission(); enforceAccessPermission();
final NetworkCapabilities caps = getNetworkCapabilities(getActiveNetwork()); final int uid = Binder.getCallingUid();
final NetworkCapabilities caps = getUnfilteredActiveNetworkState(uid).networkCapabilities;
if (caps != null) { if (caps != null) {
return !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); return !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
} else { } else {
@@ -1737,6 +1763,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
ni != null ? ni.getState().toString() : "?"); ni != null ? ni.getState().toString() : "?");
} catch (RemoteException e) { } catch (RemoteException e) {
} }
intent.addFlags(Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
} }
try { try {
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL, options); mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL, options);
@@ -2032,7 +2059,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
synchronized (mValidationLogs) { synchronized (mValidationLogs) {
pw.println("mValidationLogs (most recent first):"); pw.println("mValidationLogs (most recent first):");
for (ValidationLog p : mValidationLogs) { for (ValidationLog p : mValidationLogs) {
pw.println(p.mNetwork + " - " + p.mNetworkExtraInfo); pw.println(p.mNetwork + " - " + p.mName);
pw.increaseIndent(); pw.increaseIndent();
p.mLog.dump(fd, pw, args); p.mLog.dump(fd, pw, args);
pw.decreaseIndent(); pw.decreaseIndent();
@@ -2377,9 +2404,23 @@ 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) { private void handleAsyncChannelDisconnected(Message msg) {
NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo); NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
if (nai != null) { if (nai != null) {
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) { if (DBG) {
log(nai.name() + " got DISCONNECTED, was satisfying " + nai.numNetworkRequests()); log(nai.name() + " got DISCONNECTED, was satisfying " + nai.numNetworkRequests());
} }
@@ -2415,7 +2456,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
wakeupModifyInterface(iface, nai.networkCapabilities, false); wakeupModifyInterface(iface, nai.networkCapabilities, false);
} }
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED); nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
mNetworkAgentInfos.remove(msg.replyTo); mNetworkAgentInfos.remove(nai.messenger);
nai.maybeStopClat(); nai.maybeStopClat();
synchronized (mNetworkForNetId) { synchronized (mNetworkForNetId) {
// Remove the NetworkAgent, but don't mark the netId as // Remove the NetworkAgent, but don't mark the netId as
@@ -2438,6 +2479,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
ensureNetworkTransitionWakelock(nai.name()); ensureNetworkTransitionWakelock(nai.name());
} }
mLegacyTypeTracker.remove(nai, wasDefault); mLegacyTypeTracker.remove(nai, wasDefault);
if (!nai.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
updateAllVpnsCapabilities();
}
rematchAllNetworksAndRequests(null, 0); rematchAllNetworksAndRequests(null, 0);
mLingerMonitor.noteDisconnect(nai); mLingerMonitor.noteDisconnect(nai);
if (nai.created) { if (nai.created) {
@@ -2459,10 +2503,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
synchronized (mNetworkForNetId) { synchronized (mNetworkForNetId) {
mNetIdInUse.delete(nai.network.netId); mNetIdInUse.delete(nai.network.netId);
} }
} else {
NetworkFactoryInfo nfi = mNetworkFactoryInfos.remove(msg.replyTo);
if (DBG && nfi != null) log("unregisterNetworkFactory for " + nfi.name);
}
} }
// If this method proves to be too slow then we can maintain a separate // If this method proves to be too slow then we can maintain a separate
@@ -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 @Override
public boolean updateLockdownVpn() { public boolean updateLockdownVpn() {
if (Binder.getCallingUid() != Process.SYSTEM_UID) { 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) { private ArrayList<Integer> getSignalStrengthThresholds(NetworkAgentInfo nai) {
final SortedSet<Integer> thresholds = new TreeSet(); final SortedSet<Integer> thresholds = new TreeSet();
synchronized (nai) { synchronized (nai) {
@@ -4238,8 +4307,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// the default network request. This allows callers to keep track of // the default network request. This allows callers to keep track of
// the system default network. // the system default network.
if (type == NetworkRequest.Type.TRACK_DEFAULT) { if (type == NetworkRequest.Type.TRACK_DEFAULT) {
networkCapabilities = new NetworkCapabilities(mDefaultRequest.networkCapabilities); networkCapabilities = createDefaultNetworkCapabilitiesForUid(Binder.getCallingUid());
networkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN);
enforceAccessPermission(); enforceAccessPermission();
} else { } else {
networkCapabilities = new NetworkCapabilities(networkCapabilities); networkCapabilities = new NetworkCapabilities(networkCapabilities);
@@ -4250,6 +4318,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
enforceMeteredApnPolicy(networkCapabilities); enforceMeteredApnPolicy(networkCapabilities);
} }
ensureRequestableCapabilities(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 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. // 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 // This will overwrite any allowed UIDs in the requested capabilities. Though there
@@ -4328,6 +4398,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
enforceNetworkRequestPermissions(networkCapabilities); enforceNetworkRequestPermissions(networkCapabilities);
enforceMeteredApnPolicy(networkCapabilities); enforceMeteredApnPolicy(networkCapabilities);
ensureRequestableCapabilities(networkCapabilities); ensureRequestableCapabilities(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
Binder.getCallingPid(), Binder.getCallingUid());
ensureValidNetworkSpecifier(networkCapabilities); ensureValidNetworkSpecifier(networkCapabilities);
restrictRequestUidsForCaller(networkCapabilities); restrictRequestUidsForCaller(networkCapabilities);
@@ -4383,6 +4455,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities); NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
Binder.getCallingPid(), Binder.getCallingUid());
restrictRequestUidsForCaller(nc); restrictRequestUidsForCaller(nc);
// Apps without the CHANGE_NETWORK_STATE permission can't use background networks, so // 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 // make all their listens include NET_CAPABILITY_FOREGROUND. That way, they will get
@@ -4409,6 +4483,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
enforceAccessPermission(); enforceAccessPermission();
} }
ensureValidNetworkSpecifier(networkCapabilities); ensureValidNetworkSpecifier(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
Binder.getCallingPid(), Binder.getCallingUid());
final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities); final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
restrictRequestUidsForCaller(nc); restrictRequestUidsForCaller(nc);
@@ -4541,8 +4617,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
synchronized (this) { synchronized (this) {
nai.networkMonitor.systemReady = mSystemReady; nai.networkMonitor.systemReady = mSystemReady;
} }
addValidationLogs(nai.networkMonitor.getValidationLogs(), nai.network, final String extraInfo = networkInfo.getExtraInfo();
networkInfo.getExtraInfo()); final String name = TextUtils.isEmpty(extraInfo)
? nai.networkCapabilities.getSSID() : extraInfo;
addValidationLogs(nai.networkMonitor.getValidationLogs(), nai.network, name);
if (DBG) log("registerNetworkAgent " + nai); if (DBG) log("registerNetworkAgent " + nai);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai)); mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));
return nai.network.netId; return nai.network.netId;
@@ -4562,7 +4640,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties oldLp) { private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties oldLp) {
LinkProperties newLp = networkAgent.linkProperties; LinkProperties newLp = new LinkProperties(networkAgent.linkProperties);
int netId = networkAgent.network.netId; int netId = networkAgent.network.netId;
// The NetworkAgentInfo does not know whether clatd is running on its network or not. Before // 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 // TODO - move this check to cover the whole function
if (!Objects.equals(newLp, oldLp)) { if (!Objects.equals(newLp, oldLp)) {
synchronized (networkAgent) {
networkAgent.linkProperties = newLp;
}
notifyIfacesChangedForNetworkStats(); notifyIfacesChangedForNetworkStats();
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED); notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
} }
@@ -4846,12 +4927,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (!newNc.hasTransport(TRANSPORT_VPN)) { if (!newNc.hasTransport(TRANSPORT_VPN)) {
// Tell VPNs about updated capabilities, since they may need to // Tell VPNs about updated capabilities, since they may need to
// bubble those changes through. // bubble those changes through.
synchronized (mVpns) { updateAllVpnsCapabilities();
for (int i = 0; i < mVpns.size(); i++) {
final Vpn vpn = mVpns.valueAt(i);
vpn.updateCapabilities();
}
}
} }
} }
@@ -4980,7 +5056,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
case ConnectivityManager.CALLBACK_CAP_CHANGED: { case ConnectivityManager.CALLBACK_CAP_CHANGED: {
// networkAgent can't be null as it has been accessed a few lines above. // 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); networkAgent.networkCapabilities, nri.mPid, nri.mUid);
putParcelable(bundle, nc); putParcelable(bundle, nc);
break; break;
@@ -5511,6 +5587,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
} }
updateUids(networkAgent, networkAgent.networkCapabilities, null); updateUids(networkAgent, networkAgent.networkCapabilities, null);
} }
disconnectAndDestroyNetwork(networkAgent);
} else if ((oldInfo != null && oldInfo.getState() == NetworkInfo.State.SUSPENDED) || } else if ((oldInfo != null && oldInfo.getState() == NetworkInfo.State.SUSPENDED) ||
state == NetworkInfo.State.SUSPENDED) { state == NetworkInfo.State.SUSPENDED) {
// going into or coming out of SUSPEND: rescore and notify // 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) { private static int encodeBool(boolean b) {
return b ? 1 : 0; 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.Intent;
import android.content.res.Resources; import android.content.res.Resources;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.net.wifi.WifiInfo;
import android.os.UserHandle; import android.os.UserHandle;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.SparseIntArray; import android.util.SparseIntArray;
@@ -130,16 +132,17 @@ public class NetworkNotificationManager {
final String tag = tagFor(id); final String tag = tagFor(id);
final int eventId = notifyType.eventId; final int eventId = notifyType.eventId;
final int transportType; final int transportType;
final String extraInfo; final String name;
if (nai != null) { if (nai != null) {
transportType = getFirstTransportType(nai); 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. // Only notify for Internet-capable networks.
if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)) return; if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_INTERNET)) return;
} else { } else {
// Legacy notifications. // Legacy notifications.
transportType = TRANSPORT_CELLULAR; transportType = TRANSPORT_CELLULAR;
extraInfo = null; name = null;
} }
// Clear any previous notification with lower priority, otherwise return. http://b/63676954. // Clear any previous notification with lower priority, otherwise return. http://b/63676954.
@@ -156,9 +159,8 @@ public class NetworkNotificationManager {
if (DBG) { if (DBG) {
Slog.d(TAG, String.format( Slog.d(TAG, String.format(
"showNotification tag=%s event=%s transport=%s extraInfo=%s highPrioriy=%s", "showNotification tag=%s event=%s transport=%s name=%s highPriority=%s",
tag, nameOf(eventId), getTransportName(transportType), extraInfo, tag, nameOf(eventId), getTransportName(transportType), name, highPriority));
highPriority));
} }
Resources r = Resources.getSystem(); Resources r = Resources.getSystem();
@@ -176,7 +178,8 @@ public class NetworkNotificationManager {
switch (transportType) { switch (transportType) {
case TRANSPORT_WIFI: case TRANSPORT_WIFI:
title = r.getString(R.string.wifi_available_sign_in, 0); 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; break;
case TRANSPORT_CELLULAR: case TRANSPORT_CELLULAR:
title = r.getString(R.string.network_available_sign_in, 0); title = r.getString(R.string.network_available_sign_in, 0);
@@ -186,7 +189,7 @@ public class NetworkNotificationManager {
break; break;
default: default:
title = r.getString(R.string.network_available_sign_in, 0); 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; break;
} }
} else if (notifyType == NotificationType.NETWORK_SWITCH) { } else if (notifyType == NotificationType.NETWORK_SWITCH) {

View File

@@ -172,10 +172,13 @@ public class Vpn {
private PendingIntent mStatusIntent; private PendingIntent mStatusIntent;
private volatile boolean mEnableTeardown = true; private volatile boolean mEnableTeardown = true;
private final INetworkManagementService mNetd; private final INetworkManagementService mNetd;
private VpnConfig mConfig; @VisibleForTesting
private NetworkAgent mNetworkAgent; protected VpnConfig mConfig;
@VisibleForTesting
protected NetworkAgent mNetworkAgent;
private final Looper mLooper; private final Looper mLooper;
private final NetworkCapabilities mNetworkCapabilities; @VisibleForTesting
protected final NetworkCapabilities mNetworkCapabilities;
private final SystemServices mSystemServices; private final SystemServices mSystemServices;
/** /**
@@ -316,15 +319,12 @@ public class Vpn {
boolean roaming = false; boolean roaming = false;
boolean congested = false; boolean congested = false;
if (ArrayUtils.isEmpty(underlyingNetworks)) { boolean hadUnderlyingNetworks = false;
// No idea what the underlying networks are; assume sane defaults if (null != underlyingNetworks) {
metered = true;
roaming = false;
congested = false;
} else {
for (Network underlying : underlyingNetworks) { for (Network underlying : underlyingNetworks) {
final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying); final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying);
if (underlyingCaps == null) continue; if (underlyingCaps == null) continue;
hadUnderlyingNetworks = true;
for (int underlyingType : underlyingCaps.getTransportTypes()) { for (int underlyingType : underlyingCaps.getTransportTypes()) {
transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType); transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType);
} }
@@ -340,6 +340,12 @@ public class Vpn {
congested |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_CONGESTED); congested |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_CONGESTED);
} }
} }
if (!hadUnderlyingNetworks) {
// No idea what the underlying networks are; assume sane defaults
metered = true;
roaming = false;
congested = false;
}
caps.setTransportTypes(transportTypes); caps.setTransportTypes(transportTypes);
caps.setLinkDownstreamBandwidthKbps(downKbps); caps.setLinkDownstreamBandwidthKbps(downKbps);
@@ -1071,7 +1077,8 @@ public class Vpn {
// Returns true if the VPN has been established and the calling UID is its owner. Used to check // Returns true if the VPN has been established and the calling UID is its owner. Used to check
// that a call to mutate VPN state is admissible. // that a call to mutate VPN state is admissible.
private boolean isCallerEstablishedOwnerLocked() { @VisibleForTesting
protected boolean isCallerEstablishedOwnerLocked() {
return isRunningLocked() && Binder.getCallingUid() == mOwnerUID; return isRunningLocked() && Binder.getCallingUid() == mOwnerUID;
} }

View File

@@ -40,12 +40,14 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.os.Parcel; import android.os.Parcel;
import android.support.test.runner.AndroidJUnit4; import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest; import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArraySet; import android.util.ArraySet;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -54,6 +56,9 @@ import java.util.Set;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@SmallTest @SmallTest
public class NetworkCapabilitiesTest { public class NetworkCapabilitiesTest {
private static final String TEST_SSID = "TEST_SSID";
private static final String DIFFERENT_TEST_SSID = "DIFFERENT_TEST_SSID";
@Test @Test
public void testMaybeMarkCapabilitiesRestricted() { public void testMaybeMarkCapabilitiesRestricted() {
// verify EIMS is restricted // verify EIMS is restricted
@@ -268,6 +273,8 @@ public class NetworkCapabilitiesTest {
.addCapability(NET_CAPABILITY_EIMS) .addCapability(NET_CAPABILITY_EIMS)
.addCapability(NET_CAPABILITY_NOT_METERED); .addCapability(NET_CAPABILITY_NOT_METERED);
assertEqualsThroughMarshalling(netCap); assertEqualsThroughMarshalling(netCap);
netCap.setSSID(TEST_SSID);
assertEqualsThroughMarshalling(netCap);
} }
@Test @Test
@@ -361,6 +368,27 @@ public class NetworkCapabilitiesTest {
assertTrue(nc1.equalsNetCapabilities(nc2)); 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 @Test
public void testCombineCapabilities() { public void testCombineCapabilities() {
NetworkCapabilities nc1 = new NetworkCapabilities(); NetworkCapabilities nc1 = new NetworkCapabilities();
@@ -382,6 +410,35 @@ public class NetworkCapabilitiesTest {
// will never be satisfied. // will never be satisfied.
assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING)); assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(nc2.hasUnwantedCapability(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 @Test
@@ -420,4 +477,38 @@ public class NetworkCapabilitiesTest {
p.setDataPosition(0); p.setDataPosition(0);
assertEquals(NetworkCapabilities.CREATOR.createFromParcel(p), netCap); 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.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.pm.UserInfo;
import android.content.res.Resources; import android.content.res.Resources;
import android.net.CaptivePortal; import android.net.CaptivePortal;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
@@ -111,6 +112,7 @@ import android.net.NetworkUtils;
import android.net.RouteInfo; import android.net.RouteInfo;
import android.net.StringNetworkSpecifier; import android.net.StringNetworkSpecifier;
import android.net.UidRange; import android.net.UidRange;
import android.net.VpnService;
import android.net.captiveportal.CaptivePortalProbeResult; import android.net.captiveportal.CaptivePortalProbeResult;
import android.net.metrics.IpConnectivityLog; import android.net.metrics.IpConnectivityLog;
import android.net.util.MultinetworkPolicyTracker; import android.net.util.MultinetworkPolicyTracker;
@@ -133,6 +135,7 @@ import android.test.mock.MockContentResolver;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Log; import android.util.Log;
import com.android.internal.net.VpnConfig;
import com.android.internal.util.ArrayUtils; import com.android.internal.util.ArrayUtils;
import com.android.internal.util.WakeupMessage; import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.BroadcastInterceptingContext;
@@ -196,6 +199,7 @@ public class ConnectivityServiceTest {
private MockNetworkAgent mWiFiNetworkAgent; private MockNetworkAgent mWiFiNetworkAgent;
private MockNetworkAgent mCellNetworkAgent; private MockNetworkAgent mCellNetworkAgent;
private MockNetworkAgent mEthernetNetworkAgent; private MockNetworkAgent mEthernetNetworkAgent;
private MockVpn mMockVpn;
private Context mContext; private Context mContext;
@Mock IpConnectivityMetrics.Logger mMetricsService; @Mock IpConnectivityMetrics.Logger mMetricsService;
@@ -477,6 +481,14 @@ public class ConnectivityServiceTest {
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
} }
public void setNetworkCapabilities(NetworkCapabilities nc,
boolean sendToConnectivityService) {
mNetworkCapabilities.set(nc);
if (sendToConnectivityService) {
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
}
}
public void connectWithoutInternet() { public void connectWithoutInternet() {
mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null); mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
mNetworkAgent.sendNetworkInfo(mNetworkInfo); mNetworkAgent.sendNetworkInfo(mNetworkInfo);
@@ -506,6 +518,7 @@ public class ConnectivityServiceTest {
mWrappedNetworkMonitor.gen204ProbeResult = 204; mWrappedNetworkMonitor.gen204ProbeResult = 204;
NetworkRequest request = new NetworkRequest.Builder() NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(mNetworkCapabilities.getTransportTypes()[0]) .addTransportType(mNetworkCapabilities.getTransportTypes()[0])
.clearCapabilities()
.build(); .build();
callback = new NetworkCallback() { callback = new NetworkCallback() {
public void onCapabilitiesChanged(Network network, public void onCapabilitiesChanged(Network network,
@@ -591,6 +604,10 @@ public class ConnectivityServiceTest {
return mRedirectUrl; return mRedirectUrl;
} }
public NetworkAgent getNetworkAgent() {
return mNetworkAgent;
}
public NetworkCapabilities getNetworkCapabilities() { public NetworkCapabilities getNetworkCapabilities() {
return mNetworkCapabilities; 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 class FakeWakeupMessage extends WakeupMessage {
private static final int UNREASONABLY_LONG_WAIT = 1000; private static final int UNREASONABLY_LONG_WAIT = 1000;
@@ -889,6 +987,17 @@ public class ConnectivityServiceTest {
return mLastCreatedNetworkMonitor; 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) { public void waitForIdle(int timeoutMs) {
waitForIdleHandler(mHandlerThread, timeoutMs); waitForIdleHandler(mHandlerThread, timeoutMs);
} }
@@ -932,8 +1041,9 @@ public class ConnectivityServiceTest {
mock(INetworkPolicyManager.class), mock(INetworkPolicyManager.class),
mock(IpConnectivityLog.class)); mock(IpConnectivityLog.class));
mService.systemReady();
mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService); mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService);
mService.systemReady();
mService.mockVpn(Process.myUid());
mCm.bindProcessToNetwork(null); mCm.bindProcessToNetwork(null);
// Ensure that the default setting for Captive Portals is used for most tests // 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 static int TIMEOUT_MS = 100;
private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>(); private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>();
private Network mLastAvailableNetwork;
protected void setLastCallback(CallbackState state, Network network, Object o) { protected void setLastCallback(CallbackState state, Network network, Object o) {
mCallbacks.offer(new CallbackInfo(state, network, o)); mCallbacks.offer(new CallbackInfo(state, network, o));
@@ -1353,6 +1464,7 @@ public class ConnectivityServiceTest {
@Override @Override
public void onAvailable(Network network) { public void onAvailable(Network network) {
mLastAvailableNetwork = network;
setLastCallback(CallbackState.AVAILABLE, network, null); setLastCallback(CallbackState.AVAILABLE, network, null);
} }
@@ -1388,9 +1500,14 @@ public class ConnectivityServiceTest {
@Override @Override
public void onLost(Network network) { public void onLost(Network network) {
mLastAvailableNetwork = null;
setLastCallback(CallbackState.LOST, network, null); setLastCallback(CallbackState.LOST, network, null);
} }
public Network getLastAvailableNetwork() {
return mLastAvailableNetwork;
}
CallbackInfo nextCallback(int timeoutMs) { CallbackInfo nextCallback(int timeoutMs) {
CallbackInfo cb = null; CallbackInfo cb = null;
try { try {
@@ -1526,7 +1643,8 @@ public class ConnectivityServiceTest {
void expectCapabilitiesLike(Predicate<NetworkCapabilities> fn, MockNetworkAgent agent) { void expectCapabilitiesLike(Predicate<NetworkCapabilities> fn, MockNetworkAgent agent) {
CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, 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() { void assertNoCallback() {
@@ -1657,6 +1775,7 @@ public class ConnectivityServiceTest {
callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.connect(true); mWiFiNetworkAgent.connect(true);
// We get AVAILABLE on wifi when wifi connects and satisfies our unmetered request. // 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); callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mEthernetNetworkAgent.connect(true); mEthernetNetworkAgent.connect(true);
callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent); callback.expectAvailableCallbacksUnvalidated(mEthernetNetworkAgent);
@@ -1675,11 +1795,13 @@ public class ConnectivityServiceTest {
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent); callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent); defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mEthernetNetworkAgent.disconnect(); mEthernetNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent); callback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent); defaultCallback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
MockNetworkAgent oldNetwork, newNetwork; MockNetworkAgent oldNetwork, newNetwork;
@@ -1708,6 +1830,7 @@ public class ConnectivityServiceTest {
defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, mWiFiNetworkAgent); defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, mWiFiNetworkAgent);
defaultCallback.assertNoCallback(); defaultCallback.assertNoCallback();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Wifi no longer satisfies our listen, which is for an unmetered network. // 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). // But because its score is 55, it's still up (and the default network).
@@ -1717,8 +1840,11 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.disconnect(); mWiFiNetworkAgent.disconnect();
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mCellNetworkAgent.disconnect(); mCellNetworkAgent.disconnect();
defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
waitForIdle();
assertEquals(null, mCm.getActiveNetwork());
mCm.unregisterNetworkCallback(callback); mCm.unregisterNetworkCallback(callback);
waitForIdle(); waitForIdle();
@@ -1735,6 +1861,7 @@ public class ConnectivityServiceTest {
callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); callback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up wifi with a score of 20. // Bring up wifi with a score of 20.
// Cell stays up because it would satisfy the default request if it validated. // Cell stays up because it would satisfy the default request if it validated.
@@ -1743,12 +1870,14 @@ public class ConnectivityServiceTest {
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect(); mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up wifi with a score of 70. // Bring up wifi with a score of 70.
// Cell is lingered because it would not satisfy any request, even if it validated. // 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); callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Tear down wifi. // Tear down wifi.
mWiFiNetworkAgent.disconnect(); mWiFiNetworkAgent.disconnect();
@@ -1766,6 +1896,7 @@ public class ConnectivityServiceTest {
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); defaultCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up wifi, then validate it. Previous versions would immediately tear down cell, but // 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. // 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); callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork()); assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect(); mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
@@ -1785,12 +1917,15 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.disconnect(); mCellNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mCellNetworkAgent); callback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
defaultCallback.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. // If a network is lingering, and we add and remove a request from it, resume lingering.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true); mCellNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); defaultCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true); mWiFiNetworkAgent.connect(true);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); defaultCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
@@ -1798,6 +1933,7 @@ public class ConnectivityServiceTest {
// TODO: Investigate sending validated before losing. // TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent); callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
NetworkRequest cellRequest = new NetworkRequest.Builder() NetworkRequest cellRequest = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_CELLULAR).build(); .addTransportType(TRANSPORT_CELLULAR).build();
@@ -1814,6 +1950,7 @@ public class ConnectivityServiceTest {
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent); defaultCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Cell is now the default network. Pin it with a cell-specific request. // Cell is now the default network. Pin it with a cell-specific request.
noopCallback = new NetworkCallback(); // Can't reuse NetworkCallbacks. http://b/20701525 noopCallback = new NetworkCallback(); // Can't reuse NetworkCallbacks. http://b/20701525
@@ -1824,6 +1961,7 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.connect(true); mWiFiNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent); callback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
defaultCallback.expectAvailableDoubleValidatedCallbacks(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 // 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. // callbacks for it, because it's kept up by cellRequest.
callback.assertNoCallback(); callback.assertNoCallback();
@@ -1847,6 +1985,7 @@ public class ConnectivityServiceTest {
callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent); callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
trackDefaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent); trackDefaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent); defaultCallback.expectAvailableDoubleValidatedCallbacks(mEthernetNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Let linger run its course. // Let linger run its course.
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent, lingerTimeoutMs); callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent, lingerTimeoutMs);
@@ -2495,23 +2634,27 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.connect(true); mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up wifi and expect CALLBACK_AVAILABLE. // Bring up wifi and expect CALLBACK_AVAILABLE.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true); mWiFiNetworkAgent.connect(true);
cellNetworkCallback.assertNoCallback(); cellNetworkCallback.assertNoCallback();
defaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent); defaultNetworkCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring down cell. Expect no default network callback, since it wasn't the default. // Bring down cell. Expect no default network callback, since it wasn't the default.
mCellNetworkAgent.disconnect(); mCellNetworkAgent.disconnect();
cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
defaultNetworkCallback.assertNoCallback(); defaultNetworkCallback.assertNoCallback();
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring up cell. Expect no default network callback, since it won't be the default. // Bring up cell. Expect no default network callback, since it won't be the default.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true); mCellNetworkAgent.connect(true);
cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
defaultNetworkCallback.assertNoCallback(); defaultNetworkCallback.assertNoCallback();
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// Bring down wifi. Expect the default network callback to notified of LOST wifi // Bring down wifi. Expect the default network callback to notified of LOST wifi
// followed by AVAILABLE cell. // followed by AVAILABLE cell.
@@ -2522,6 +2665,24 @@ public class ConnectivityServiceTest {
mCellNetworkAgent.disconnect(); mCellNetworkAgent.disconnect();
cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
defaultNetworkCallback.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 @Test
@@ -4012,6 +4173,7 @@ public class ConnectivityServiceTest {
final TestNetworkCallback genericNotVpnNetworkCallback = new TestNetworkCallback(); final TestNetworkCallback genericNotVpnNetworkCallback = new TestNetworkCallback();
final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback(); final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback(); final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
final NetworkRequest genericNotVpnRequest = new NetworkRequest.Builder().build(); final NetworkRequest genericNotVpnRequest = new NetworkRequest.Builder().build();
final NetworkRequest genericRequest = new NetworkRequest.Builder() final NetworkRequest genericRequest = new NetworkRequest.Builder()
.removeCapability(NET_CAPABILITY_NOT_VPN).build(); .removeCapability(NET_CAPABILITY_NOT_VPN).build();
@@ -4024,6 +4186,8 @@ public class ConnectivityServiceTest {
mCm.registerNetworkCallback(genericNotVpnRequest, genericNotVpnNetworkCallback); mCm.registerNetworkCallback(genericNotVpnRequest, genericNotVpnNetworkCallback);
mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
mCm.registerDefaultNetworkCallback(defaultCallback);
defaultCallback.assertNoCallback();
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false); mWiFiNetworkAgent.connect(false);
@@ -4031,26 +4195,30 @@ public class ConnectivityServiceTest {
genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
genericNotVpnNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); genericNotVpnNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
vpnNetworkCallback.assertNoCallback(); vpnNetworkCallback.assertNoCallback();
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
// 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.
final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
final ArraySet<UidRange> ranges = new ArraySet<>(); final ArraySet<UidRange> ranges = new ArraySet<>();
ranges.add(new UidRange(uid, uid)); ranges.add(new UidRange(uid, uid));
vpnNetworkAgent.setUids(ranges); mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
vpnNetworkAgent.connect(false); vpnNetworkAgent.connect(false);
mMockVpn.connect();
genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent); genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback(); genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback(); wifiNetworkCallback.assertNoCallback();
vpnNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent); vpnNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
defaultCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
genericNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent); genericNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback(); genericNotVpnNetworkCallback.assertNoCallback();
vpnNetworkCallback.expectCapabilitiesLike(nc -> null == nc.getUids(), vpnNetworkAgent); vpnNetworkCallback.expectCapabilitiesLike(nc -> null == nc.getUids(), vpnNetworkAgent);
defaultCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
ranges.clear(); ranges.clear();
vpnNetworkAgent.setUids(ranges); vpnNetworkAgent.setUids(ranges);
@@ -4060,13 +4228,24 @@ public class ConnectivityServiceTest {
wifiNetworkCallback.assertNoCallback(); wifiNetworkCallback.assertNoCallback();
vpnNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent); 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)); ranges.add(new UidRange(uid, uid));
vpnNetworkAgent.setUids(ranges); mMockVpn.setUids(ranges);
genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent); genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback(); genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback(); wifiNetworkCallback.assertNoCallback();
vpnNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent); 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(); mWiFiNetworkAgent.disconnect();
@@ -4074,6 +4253,7 @@ public class ConnectivityServiceTest {
genericNotVpnNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); genericNotVpnNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
wifiNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); wifiNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
vpnNetworkCallback.assertNoCallback(); vpnNetworkCallback.assertNoCallback();
defaultCallback.assertNoCallback();
vpnNetworkAgent.disconnect(); vpnNetworkAgent.disconnect();
@@ -4081,9 +4261,161 @@ public class ConnectivityServiceTest {
genericNotVpnNetworkCallback.assertNoCallback(); genericNotVpnNetworkCallback.assertNoCallback();
wifiNetworkCallback.assertNoCallback(); wifiNetworkCallback.assertNoCallback();
vpnNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent); vpnNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
assertEquals(null, mCm.getActiveNetwork());
mCm.unregisterNetworkCallback(genericNetworkCallback); mCm.unregisterNetworkCallback(genericNetworkCallback);
mCm.unregisterNetworkCallback(wifiNetworkCallback); mCm.unregisterNetworkCallback(wifiNetworkCallback);
mCm.unregisterNetworkCallback(vpnNetworkCallback); 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();
} }
} }