Merge "[IT4.21] Update the label type of idle timer"
diff --git a/core/java/android/net/CaptivePortalData.java b/core/java/android/net/CaptivePortalData.java
index c443c75..18467fa 100644
--- a/core/java/android/net/CaptivePortalData.java
+++ b/core/java/android/net/CaptivePortalData.java
@@ -39,9 +39,11 @@
private final long mByteLimit;
private final long mExpiryTimeMillis;
private final boolean mCaptive;
+ private final String mVenueFriendlyName;
private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl,
- boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive) {
+ boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive,
+ String venueFriendlyName) {
mRefreshTimeMillis = refreshTimeMillis;
mUserPortalUrl = userPortalUrl;
mVenueInfoUrl = venueInfoUrl;
@@ -49,11 +51,12 @@
mByteLimit = byteLimit;
mExpiryTimeMillis = expiryTimeMillis;
mCaptive = captive;
+ mVenueFriendlyName = venueFriendlyName;
}
private CaptivePortalData(Parcel p) {
this(p.readLong(), p.readParcelable(null), p.readParcelable(null), p.readBoolean(),
- p.readLong(), p.readLong(), p.readBoolean());
+ p.readLong(), p.readLong(), p.readBoolean(), p.readString());
}
@Override
@@ -70,6 +73,7 @@
dest.writeLong(mByteLimit);
dest.writeLong(mExpiryTimeMillis);
dest.writeBoolean(mCaptive);
+ dest.writeString(mVenueFriendlyName);
}
/**
@@ -83,6 +87,7 @@
private long mBytesRemaining = -1;
private long mExpiryTime = -1;
private boolean mCaptive;
+ private String mVenueFriendlyName;
/**
* Create an empty builder.
@@ -100,7 +105,8 @@
.setSessionExtendable(data.mIsSessionExtendable)
.setBytesRemaining(data.mByteLimit)
.setExpiryTime(data.mExpiryTimeMillis)
- .setCaptive(data.mCaptive);
+ .setCaptive(data.mCaptive)
+ .setVenueFriendlyName(data.mVenueFriendlyName);
}
/**
@@ -167,12 +173,22 @@
}
/**
+ * Set the venue friendly name.
+ */
+ @NonNull
+ public Builder setVenueFriendlyName(@Nullable String venueFriendlyName) {
+ mVenueFriendlyName = venueFriendlyName;
+ return this;
+ }
+
+ /**
* Create a new {@link CaptivePortalData}.
*/
@NonNull
public CaptivePortalData build() {
return new CaptivePortalData(mRefreshTime, mUserPortalUrl, mVenueInfoUrl,
- mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive);
+ mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive,
+ mVenueFriendlyName);
}
}
@@ -232,6 +248,14 @@
return mCaptive;
}
+ /**
+ * Get the venue friendly name
+ */
+ @Nullable
+ public String getVenueFriendlyName() {
+ return mVenueFriendlyName;
+ }
+
@NonNull
public static final Creator<CaptivePortalData> CREATOR = new Creator<CaptivePortalData>() {
@Override
@@ -248,7 +272,7 @@
@Override
public int hashCode() {
return Objects.hash(mRefreshTimeMillis, mUserPortalUrl, mVenueInfoUrl,
- mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive);
+ mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive, mVenueFriendlyName);
}
@Override
@@ -261,7 +285,8 @@
&& mIsSessionExtendable == other.mIsSessionExtendable
&& mByteLimit == other.mByteLimit
&& mExpiryTimeMillis == other.mExpiryTimeMillis
- && mCaptive == other.mCaptive;
+ && mCaptive == other.mCaptive
+ && Objects.equals(mVenueFriendlyName, other.mVenueFriendlyName);
}
@Override
@@ -274,6 +299,7 @@
+ ", byteLimit: " + mByteLimit
+ ", expiryTime: " + mExpiryTimeMillis
+ ", captive: " + mCaptive
+ + ", venueFriendlyName: " + mVenueFriendlyName
+ "}";
}
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 1a37fb9..8597329 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -171,6 +171,8 @@
NET_CAPABILITY_PARTIAL_CONNECTIVITY,
NET_CAPABILITY_TEMPORARILY_NOT_METERED,
NET_CAPABILITY_OEM_PRIVATE,
+ NET_CAPABILITY_VEHICLE_INTERNAL,
+ NET_CAPABILITY_NOT_VCN_MANAGED,
})
public @interface NetCapability { }
@@ -357,8 +359,26 @@
@SystemApi
public static final int NET_CAPABILITY_OEM_PRIVATE = 26;
+ /**
+ * Indicates this is an internal vehicle network, meant to communicate with other
+ * automotive systems.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27;
+
+ /**
+ * Indicates that this network is not managed by a Virtual Carrier Network (VCN).
+ *
+ * TODO(b/177299683): Add additional clarifying javadoc.
+ * @hide
+ */
+ @SystemApi
+ public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28;
+
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_OEM_PRIVATE;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VCN_MANAGED;
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -375,7 +395,8 @@
| (1 << NET_CAPABILITY_NOT_CONGESTED)
| (1 << NET_CAPABILITY_NOT_SUSPENDED)
| (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY)
- | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+ | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
/**
* Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -384,16 +405,22 @@
* can get into a cycle where the NetworkFactory endlessly churns out NetworkAgents that then
* get immediately torn down because they do not have the requested capability.
*/
+ // Note that as a historical exception, the TRUSTED and NOT_VCN_MANAGED capabilities
+ // are mutable but requestable. Factories are responsible for not getting
+ // in an infinite loop about these.
private static final long NON_REQUESTABLE_CAPABILITIES =
- MUTABLE_CAPABILITIES & ~(1 << NET_CAPABILITY_TRUSTED);
+ MUTABLE_CAPABILITIES
+ & ~(1 << NET_CAPABILITY_TRUSTED)
+ & ~(1 << NET_CAPABILITY_NOT_VCN_MANAGED);
/**
* Capabilities that are set by default when the object is constructed.
*/
private static final long DEFAULT_CAPABILITIES =
- (1 << NET_CAPABILITY_NOT_RESTRICTED) |
- (1 << NET_CAPABILITY_TRUSTED) |
- (1 << NET_CAPABILITY_NOT_VPN);
+ (1 << NET_CAPABILITY_NOT_RESTRICTED)
+ | (1 << NET_CAPABILITY_TRUSTED)
+ | (1 << NET_CAPABILITY_NOT_VPN)
+ | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
/**
* Capabilities that suggest that a network is restricted.
@@ -401,15 +428,16 @@
*/
@VisibleForTesting
/* package */ static final long RESTRICTED_CAPABILITIES =
- (1 << NET_CAPABILITY_CBS) |
- (1 << NET_CAPABILITY_DUN) |
- (1 << NET_CAPABILITY_EIMS) |
- (1 << NET_CAPABILITY_FOTA) |
- (1 << NET_CAPABILITY_IA) |
- (1 << NET_CAPABILITY_IMS) |
- (1 << NET_CAPABILITY_RCS) |
- (1 << NET_CAPABILITY_XCAP) |
- (1 << NET_CAPABILITY_MCX);
+ (1 << NET_CAPABILITY_CBS)
+ | (1 << NET_CAPABILITY_DUN)
+ | (1 << NET_CAPABILITY_EIMS)
+ | (1 << NET_CAPABILITY_FOTA)
+ | (1 << NET_CAPABILITY_IA)
+ | (1 << NET_CAPABILITY_IMS)
+ | (1 << NET_CAPABILITY_MCX)
+ | (1 << NET_CAPABILITY_RCS)
+ | (1 << NET_CAPABILITY_VEHICLE_INTERNAL)
+ | (1 << NET_CAPABILITY_XCAP);
/**
* Capabilities that force network to be restricted.
@@ -452,7 +480,8 @@
| (1 << NET_CAPABILITY_NOT_VPN)
| (1 << NET_CAPABILITY_NOT_ROAMING)
| (1 << NET_CAPABILITY_NOT_CONGESTED)
- | (1 << NET_CAPABILITY_NOT_SUSPENDED);
+ | (1 << NET_CAPABILITY_NOT_SUSPENDED)
+ | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
/**
* Adds the given capability to this {@code NetworkCapability} instance.
@@ -1939,6 +1968,8 @@
case NET_CAPABILITY_PARTIAL_CONNECTIVITY: return "PARTIAL_CONNECTIVITY";
case NET_CAPABILITY_TEMPORARILY_NOT_METERED: return "TEMPORARILY_NOT_METERED";
case NET_CAPABILITY_OEM_PRIVATE: return "OEM_PRIVATE";
+ case NET_CAPABILITY_VEHICLE_INTERNAL: return "NET_CAPABILITY_VEHICLE_INTERNAL";
+ case NET_CAPABILITY_NOT_VCN_MANAGED: return "NOT_VCN_MANAGED";
default: return Integer.toString(capability);
}
}
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index dc16d74..f0c637c 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -40,6 +40,18 @@
*/
public class NetworkRequest implements Parcelable {
/**
+ * The first requestId value that will be allocated.
+ * @hide only used by ConnectivityService.
+ */
+ public static final int FIRST_REQUEST_ID = 1;
+
+ /**
+ * The requestId value that represents the absence of a request.
+ * @hide only used by ConnectivityService.
+ */
+ public static final int REQUEST_ID_NONE = -1;
+
+ /**
* The {@link NetworkCapabilities} that define this request.
* @hide
*/
diff --git a/service/Android.bp b/service/Android.bp
index a26f715..c8f3bd3 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -14,8 +14,8 @@
// limitations under the License.
//
-cc_defaults {
- name: "libservice-connectivity-defaults",
+cc_library_shared {
+ name: "libservice-connectivity",
// TODO: build against the NDK (sdk_version: "30" for example)
cflags: [
"-Wall",
@@ -26,6 +26,7 @@
srcs: [
"jni/com_android_server_TestNetworkService.cpp",
"jni/com_android_server_connectivity_Vpn.cpp",
+ "jni/onload.cpp",
],
shared_libs: [
"libbase",
@@ -35,27 +36,11 @@
// addresses, and remove dependency on libnetutils.
"libnetutils",
],
-}
-
-cc_library_shared {
- name: "libservice-connectivity",
- defaults: ["libservice-connectivity-defaults"],
- srcs: [
- "jni/onload.cpp",
- ],
apex_available: [
- // TODO: move this library to the tethering APEX and remove libservice-connectivity-static
- // "com.android.tethering",
+ "com.android.tethering",
],
}
-// Static library linked into libservices.core until libservice-connectivity can be loaded from
-// the tethering APEX instead.
-cc_library_static {
- name: "libservice-connectivity-static",
- defaults: ["libservice-connectivity-defaults"],
-}
-
java_library {
name: "service-connectivity",
srcs: [
@@ -75,5 +60,6 @@
],
apex_available: [
"//apex_available:platform",
+ "com.android.tethering",
],
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7ebc345..a330a74 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -56,12 +56,14 @@
import static android.net.NetworkPolicyManager.uidRulesToString;
import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired;
import static android.os.Process.INVALID_UID;
+import static android.os.Process.VPN_UID;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
import static java.util.Map.Entry;
import android.Manifest;
+import android.annotation.BoolRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AppOpsManager;
@@ -87,7 +89,6 @@
import android.net.IConnectivityDiagnosticsCallback;
import android.net.IConnectivityManager;
import android.net.IDnsResolver;
-import android.net.IIpConnectivityMetrics;
import android.net.INetd;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkMonitor;
@@ -154,7 +155,6 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -620,7 +620,7 @@
private LingerMonitor mLingerMonitor;
// sequence number of NetworkRequests
- private int mNextNetworkRequestId = 1;
+ private int mNextNetworkRequestId = NetworkRequest.FIRST_REQUEST_ID;
// Sequence number for NetworkProvider IDs.
private final AtomicInteger mNextNetworkProviderId = new AtomicInteger(
@@ -927,14 +927,6 @@
"no IpConnectivityMetrics service");
}
- /**
- * @see IpConnectivityMetrics
- */
- public IIpConnectivityMetrics getIpConnectivityMetrics() {
- return IIpConnectivityMetrics.Stub.asInterface(
- ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
- }
-
public IBatteryStats getBatteryStatsService() {
return BatteryStatsService.getService();
}
@@ -973,6 +965,10 @@
mDefaultWifiRequest = createDefaultInternetRequestForTransport(
NetworkCapabilities.TRANSPORT_WIFI, NetworkRequest.Type.BACKGROUND_REQUEST);
+ mDefaultVehicleRequest = createAlwaysOnRequestForCapability(
+ NetworkCapabilities.NET_CAPABILITY_VEHICLE_INTERNAL,
+ NetworkRequest.Type.BACKGROUND_REQUEST);
+
mHandlerThread = mDeps.makeHandlerThread();
mHandlerThread.start();
mHandler = new InternalHandler(mHandlerThread.getLooper());
@@ -1172,6 +1168,15 @@
return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId(), type);
}
+ private NetworkRequest createAlwaysOnRequestForCapability(int capability,
+ NetworkRequest.Type type) {
+ final NetworkCapabilities netCap = new NetworkCapabilities();
+ netCap.clearAll();
+ netCap.addCapability(capability);
+ netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
+ return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId(), type);
+ }
+
// Used only for testing.
// TODO: Delete this and either:
// 1. Give FakeSettingsProvider the ability to send settings change notifications (requires
@@ -1189,10 +1194,19 @@
mHandler.sendEmptyMessage(EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
}
+ private void handleAlwaysOnNetworkRequest(NetworkRequest networkRequest, @BoolRes int id) {
+ final boolean enable = mContext.getResources().getBoolean(id);
+ handleAlwaysOnNetworkRequest(networkRequest, enable);
+ }
+
private void handleAlwaysOnNetworkRequest(
NetworkRequest networkRequest, String settingName, boolean defaultValue) {
final boolean enable = toBool(Settings.Global.getInt(
mContext.getContentResolver(), settingName, encodeBool(defaultValue)));
+ handleAlwaysOnNetworkRequest(networkRequest, enable);
+ }
+
+ private void handleAlwaysOnNetworkRequest(NetworkRequest networkRequest, boolean enable) {
final boolean isEnabled = (mNetworkRequests.get(networkRequest) != null);
if (enable == isEnabled) {
return; // Nothing to do.
@@ -1209,9 +1223,11 @@
private void handleConfigureAlwaysOnNetworks() {
handleAlwaysOnNetworkRequest(
- mDefaultMobileDataRequest,Settings.Global.MOBILE_DATA_ALWAYS_ON, true);
+ mDefaultMobileDataRequest, Settings.Global.MOBILE_DATA_ALWAYS_ON, true);
handleAlwaysOnNetworkRequest(mDefaultWifiRequest, Settings.Global.WIFI_ALWAYS_REQUESTED,
false);
+ handleAlwaysOnNetworkRequest(mDefaultVehicleRequest,
+ com.android.internal.R.bool.config_vehicleInternalNetworkAlwaysRequested);
}
private void registerSettingsCallbacks() {
@@ -1238,6 +1254,8 @@
}
private synchronized int nextNetworkRequestId() {
+ // TODO: Consider handle wrapping and exclude {@link NetworkRequest#REQUEST_ID_NONE} if
+ // doing that.
return mNextNetworkRequestId++;
}
@@ -1329,15 +1347,20 @@
/**
* Check if UID should be blocked from using the specified network.
*/
- private boolean isNetworkWithLinkPropertiesBlocked(LinkProperties lp, int uid,
- boolean ignoreBlocked) {
+ private boolean isNetworkWithCapabilitiesBlocked(@Nullable final NetworkCapabilities nc,
+ final int uid, final boolean ignoreBlocked) {
// Networks aren't blocked when ignoring blocked status
if (ignoreBlocked) {
return false;
}
if (isUidBlockedByVpn(uid, mVpnBlockedUidRanges)) return true;
- final String iface = (lp == null ? "" : lp.getInterfaceName());
- return mPolicyManagerInternal.isUidNetworkingBlocked(uid, iface);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ final boolean metered = nc == null ? true : nc.isMetered();
+ return mPolicyManager.isUidNetworkingBlocked(uid, metered);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
private void maybeLogBlockedNetworkInfo(NetworkInfo ni, int uid) {
@@ -1375,12 +1398,13 @@
/**
* Apply any relevant filters to {@link NetworkState} for the given UID. For
* example, this may mark the network as {@link DetailedState#BLOCKED} based
- * on {@link #isNetworkWithLinkPropertiesBlocked}.
+ * on {@link #isNetworkWithCapabilitiesBlocked}.
*/
private void filterNetworkStateForUid(NetworkState state, int uid, boolean ignoreBlocked) {
if (state == null || state.networkInfo == null || state.linkProperties == null) return;
- if (isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid, ignoreBlocked)) {
+ if (isNetworkWithCapabilitiesBlocked(state.networkCapabilities, uid,
+ ignoreBlocked)) {
state.networkInfo.setDetailedState(DetailedState.BLOCKED, null, null);
}
synchronized (mVpns) {
@@ -1440,8 +1464,8 @@
}
}
nai = getDefaultNetwork();
- if (nai != null
- && isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid, ignoreBlocked)) {
+ if (nai != null && isNetworkWithCapabilitiesBlocked(
+ nai.networkCapabilities, uid, ignoreBlocked)) {
nai = null;
}
return nai != null ? nai.network : null;
@@ -1513,7 +1537,7 @@
enforceAccessPermission();
final int uid = mDeps.getCallingUid();
NetworkState state = getFilteredNetworkState(networkType, uid);
- if (!isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid, false)) {
+ if (!isNetworkWithCapabilitiesBlocked(state.networkCapabilities, uid, false)) {
return state.network;
}
return null;
@@ -2965,7 +2989,7 @@
case EVENT_CAPPORT_DATA_CHANGED: {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
if (nai == null) break;
- handleCaptivePortalDataUpdate(nai, (CaptivePortalData) msg.obj);
+ handleCapportApiDataUpdate(nai, (CaptivePortalData) msg.obj);
break;
}
}
@@ -2991,9 +3015,7 @@
}
if (valid != nai.lastValidated) {
if (wasDefault) {
- mDeps.getMetricsLogger()
- .defaultNetworkMetrics().logDefaultNetworkValidity(
- SystemClock.elapsedRealtime(), valid);
+ mMetricsLog.logDefaultNetworkValidity(valid);
}
final int oldScore = nai.getCurrentScore();
nai.lastValidated = valid;
@@ -3305,9 +3327,9 @@
handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
}
- private void handleCaptivePortalDataUpdate(@NonNull final NetworkAgentInfo nai,
+ private void handleCapportApiDataUpdate(@NonNull final NetworkAgentInfo nai,
@Nullable final CaptivePortalData data) {
- nai.captivePortalData = data;
+ nai.capportApiData = data;
// CaptivePortalData will be merged into LinkProperties from NetworkAgentInfo
handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
}
@@ -3421,7 +3443,9 @@
// if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence
// whose timestamps tell how long it takes to recover a default network.
long now = SystemClock.elapsedRealtime();
- mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(now, null, nai);
+ mMetricsLog.logDefaultNetworkEvent(null, 0, false,
+ null /* lp */, null /* nc */, nai.network, nai.getCurrentScore(),
+ nai.linkProperties, nai.networkCapabilities);
}
notifyIfacesChangedForNetworkStats();
// TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied
@@ -4487,7 +4511,8 @@
if (!nai.everConnected) {
return;
}
- if (isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid, false)) {
+ final NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
+ if (isNetworkWithCapabilitiesBlocked(nc, uid, false)) {
return;
}
nai.networkMonitor().forceReevaluation(uid);
@@ -5972,6 +5997,9 @@
// priority networks like ethernet are active.
private final NetworkRequest mDefaultWifiRequest;
+ // Request used to optionally keep vehicle internal network always active
+ private final NetworkRequest mDefaultVehicleRequest;
+
private NetworkAgentInfo getDefaultNetwork() {
return mDefaultNetworkNai;
}
@@ -6111,6 +6139,7 @@
private void processLinkPropertiesFromAgent(NetworkAgentInfo nai, LinkProperties lp) {
lp.ensureDirectlyConnectedRoutes();
nai.clatd.setNat64PrefixFromRa(lp.getNat64Prefix());
+ nai.networkAgentPortalData = lp.getCaptivePortalData();
}
private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
@@ -6154,9 +6183,11 @@
updateWakeOnLan(newLp);
- // Captive portal data is obtained from NetworkMonitor and stored in NetworkAgentInfo,
- // it is not contained in LinkProperties sent from NetworkAgents so needs to be merged here.
- newLp.setCaptivePortalData(networkAgent.captivePortalData);
+ // Captive portal data is obtained from NetworkMonitor and stored in NetworkAgentInfo.
+ // It is not always contained in the LinkProperties sent from NetworkAgents, and if it
+ // does, it needs to be merged here.
+ newLp.setCaptivePortalData(mergeCaptivePortalData(networkAgent.networkAgentPortalData,
+ networkAgent.capportApiData));
// TODO - move this check to cover the whole function
if (!Objects.equals(newLp, oldLp)) {
@@ -6176,6 +6207,57 @@
mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
}
+ /**
+ * @param naData captive portal data from NetworkAgent
+ * @param apiData captive portal data from capport API
+ */
+ @Nullable
+ private CaptivePortalData mergeCaptivePortalData(CaptivePortalData naData,
+ CaptivePortalData apiData) {
+ if (naData == null || apiData == null) {
+ return naData == null ? apiData : naData;
+ }
+ final CaptivePortalData.Builder captivePortalBuilder =
+ new CaptivePortalData.Builder(naData);
+
+ if (apiData.isCaptive()) {
+ captivePortalBuilder.setCaptive(true);
+ }
+ if (apiData.isSessionExtendable()) {
+ captivePortalBuilder.setSessionExtendable(true);
+ }
+ if (apiData.getExpiryTimeMillis() >= 0 || apiData.getByteLimit() >= 0) {
+ // Expiry time, bytes remaining, refresh time all need to come from the same source,
+ // otherwise data would be inconsistent. Prefer the capport API info if present,
+ // as it can generally be refreshed more often.
+ captivePortalBuilder.setExpiryTime(apiData.getExpiryTimeMillis());
+ captivePortalBuilder.setBytesRemaining(apiData.getByteLimit());
+ captivePortalBuilder.setRefreshTime(apiData.getRefreshTimeMillis());
+ } else if (naData.getExpiryTimeMillis() < 0 && naData.getByteLimit() < 0) {
+ // No source has time / bytes remaining information: surface the newest refresh time
+ // for other fields
+ captivePortalBuilder.setRefreshTime(
+ Math.max(naData.getRefreshTimeMillis(), apiData.getRefreshTimeMillis()));
+ }
+
+ // Prioritize the user portal URL from the network agent.
+ if (apiData.getUserPortalUrl() != null && (naData.getUserPortalUrl() == null
+ || TextUtils.isEmpty(naData.getUserPortalUrl().toSafeString()))) {
+ captivePortalBuilder.setUserPortalUrl(apiData.getUserPortalUrl());
+ }
+ // Prioritize the venue information URL from the network agent.
+ if (apiData.getVenueInfoUrl() != null && (naData.getVenueInfoUrl() == null
+ || TextUtils.isEmpty(naData.getVenueInfoUrl().toSafeString()))) {
+ captivePortalBuilder.setVenueInfoUrl(apiData.getVenueInfoUrl());
+
+ // Note that venue friendly name can only come from the network agent because it is not
+ // in use in RFC8908. However, if using the Capport venue URL, make sure that the
+ // friendly name is not set from the network agent.
+ captivePortalBuilder.setVenueFriendlyName(null);
+ }
+ return captivePortalBuilder.build();
+ }
+
private void wakeupModifyInterface(String iface, NetworkCapabilities caps, boolean add) {
// Marks are only available on WiFi interfaces. Checking for
// marks on unsupported interfaces is harmless.
@@ -6682,6 +6764,39 @@
return stableRanges;
}
+ private void maybeCloseSockets(NetworkAgentInfo nai, UidRangeParcel[] ranges,
+ int[] exemptUids) {
+ if (nai.isVPN() && !nai.networkAgentConfig.allowBypass) {
+ try {
+ mNetd.socketDestroy(ranges, exemptUids);
+ } catch (Exception e) {
+ loge("Exception in socket destroy: ", e);
+ }
+ }
+ }
+
+ private void updateUidRanges(boolean add, NetworkAgentInfo nai, Set<UidRange> uidRanges) {
+ int[] exemptUids = new int[2];
+ // TODO: Excluding VPN_UID is necessary in order to not to kill the TCP connection used
+ // by PPTP. Fix this by making Vpn set the owner UID to VPN_UID instead of system when
+ // starting a legacy VPN, and remove VPN_UID here. (b/176542831)
+ exemptUids[0] = VPN_UID;
+ exemptUids[1] = nai.networkCapabilities.getOwnerUid();
+ UidRangeParcel[] ranges = toUidRangeStableParcels(uidRanges);
+
+ maybeCloseSockets(nai, ranges, exemptUids);
+ try {
+ if (add) {
+ mNetd.networkAddUidRanges(nai.network.netId, ranges);
+ } else {
+ mNetd.networkRemoveUidRanges(nai.network.netId, ranges);
+ }
+ } catch (Exception e) {
+ loge("Exception while " + (add ? "adding" : "removing") + " uid ranges " + uidRanges +
+ " on netId " + nai.network.netId + ". " + e);
+ }
+ maybeCloseSockets(nai, ranges, exemptUids);
+ }
private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc,
NetworkCapabilities newNc) {
@@ -6701,12 +6816,21 @@
// in both ranges are not subject to any VPN routing rules. Adding new range before
// removing old range works because, unlike the filtering rules below, it's possible to
// add duplicate UID routing rules.
+ // TODO: calculate the intersection of add & remove. Imagining that we are trying to
+ // remove uid 3 from a set containing 1-5. Intersection of the prev and new sets is:
+ // [1-5] & [1-2],[4-5] == [3]
+ // Then we can do:
+ // maybeCloseSockets([3])
+ // mNetd.networkAddUidRanges([1-2],[4-5])
+ // mNetd.networkRemoveUidRanges([1-5])
+ // maybeCloseSockets([3])
+ // This can prevent the sockets of uid 1-2, 4-5 from being closed. It also reduce the
+ // number of binder calls from 6 to 4.
if (!newRanges.isEmpty()) {
- mNetd.networkAddUidRanges(nai.network.netId, toUidRangeStableParcels(newRanges));
+ updateUidRanges(true, nai, newRanges);
}
if (!prevRanges.isEmpty()) {
- mNetd.networkRemoveUidRanges(
- nai.network.netId, toUidRangeStableParcels(prevRanges));
+ updateUidRanges(false, nai, prevRanges);
}
final boolean wasFiltering = requiresVpnIsolation(nai, prevNc, nai.linkProperties);
final boolean shouldFilter = requiresVpnIsolation(nai, newNc, nai.linkProperties);
@@ -7081,11 +7205,11 @@
log(" accepting network in place of " + previousSatisfier.toShortString());
}
previousSatisfier.removeRequest(nri.request.requestId);
- previousSatisfier.lingerRequest(nri.request, now, mLingerDelayMs);
+ previousSatisfier.lingerRequest(nri.request.requestId, now, mLingerDelayMs);
} else {
if (VDBG || DDBG) log(" accepting network in place of null");
}
- newSatisfier.unlingerRequest(nri.request);
+ newSatisfier.unlingerRequest(nri.request.requestId);
if (!newSatisfier.addRequest(nri.request)) {
Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
+ nri.request);
@@ -7174,9 +7298,28 @@
updateDataActivityTracking(newDefaultNetwork, oldDefaultNetwork);
// Notify system services of the new default.
makeDefault(newDefaultNetwork);
+
// Log 0 -> X and Y -> X default network transitions, where X is the new default.
- mDeps.getMetricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
- now, newDefaultNetwork, oldDefaultNetwork);
+ final Network network = (newDefaultNetwork != null) ? newDefaultNetwork.network : null;
+ final int score = (newDefaultNetwork != null) ? newDefaultNetwork.getCurrentScore() : 0;
+ final boolean validated = newDefaultNetwork != null && newDefaultNetwork.lastValidated;
+ final LinkProperties lp = (newDefaultNetwork != null)
+ ? newDefaultNetwork.linkProperties : null;
+ final NetworkCapabilities nc = (newDefaultNetwork != null)
+ ? newDefaultNetwork.networkCapabilities : null;
+
+ final Network prevNetwork = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.network : null;
+ final int prevScore = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.getCurrentScore() : 0;
+ final LinkProperties prevLp = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.linkProperties : null;
+ final NetworkCapabilities prevNc = (oldDefaultNetwork != null)
+ ? oldDefaultNetwork.networkCapabilities : null;
+
+ mMetricsLog.logDefaultNetworkEvent(network, score, validated, lp, nc,
+ prevNetwork, prevScore, prevLp, prevNc);
+
// Have a new default network, release the transition wakelock in
scheduleReleaseNetworkTransitionWakelock();
}
diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
index f701688..0779f71 100644
--- a/services/core/java/com/android/server/ConnectivityServiceInitializer.java
+++ b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
@@ -35,6 +35,8 @@
public ConnectivityServiceInitializer(Context context) {
super(context);
+ // Load JNI libraries used by ConnectivityService and its dependencies
+ System.loadLibrary("service-connectivity");
// TODO: Define formal APIs to get the needed services.
mConnectivity = new ConnectivityService(context, getNetworkManagementService(),
getNetworkStatsService());
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index e8687e5..a08d066 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -242,6 +242,7 @@
nc.addTransportType(NetworkCapabilities.TRANSPORT_TEST);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
nc.setNetworkSpecifier(new StringNetworkSpecifier(iface));
nc.setAdministratorUids(administratorUids);
if (!isMetered) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 7bde4d5..b0a73f1 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -189,41 +189,46 @@
// Set to true when partial connectivity was detected.
public boolean partialConnectivity;
- // Captive portal info of the network, if any.
+ // Captive portal info of the network from RFC8908, if any.
// Obtained by ConnectivityService and merged into NetworkAgent-provided information.
- public CaptivePortalData captivePortalData;
+ public CaptivePortalData capportApiData;
// The UID of the remote entity that created this Network.
public final int creatorUid;
+ // Network agent portal info of the network, if any. This information is provided from
+ // non-RFC8908 sources, such as Wi-Fi Passpoint, which can provide information such as Venue
+ // URL, Terms & Conditions URL, and network friendly name.
+ public CaptivePortalData networkAgentPortalData;
+
// Networks are lingered when they become unneeded as a result of their NetworkRequests being
// satisfied by a higher-scoring network. so as to allow communication to wrap up before the
// network is taken down. This usually only happens to the default network. Lingering ends with
// either the linger timeout expiring and the network being taken down, or the network
// satisfying a request again.
public static class LingerTimer implements Comparable<LingerTimer> {
- public final NetworkRequest request;
+ public final int requestId;
public final long expiryMs;
- public LingerTimer(NetworkRequest request, long expiryMs) {
- this.request = request;
+ public LingerTimer(int requestId, long expiryMs) {
+ this.requestId = requestId;
this.expiryMs = expiryMs;
}
public boolean equals(Object o) {
if (!(o instanceof LingerTimer)) return false;
LingerTimer other = (LingerTimer) o;
- return (request.requestId == other.request.requestId) && (expiryMs == other.expiryMs);
+ return (requestId == other.requestId) && (expiryMs == other.expiryMs);
}
public int hashCode() {
- return Objects.hash(request.requestId, expiryMs);
+ return Objects.hash(requestId, expiryMs);
}
public int compareTo(LingerTimer other) {
return (expiryMs != other.expiryMs) ?
Long.compare(expiryMs, other.expiryMs) :
- Integer.compare(request.requestId, other.request.requestId);
+ Integer.compare(requestId, other.requestId);
}
public String toString() {
- return String.format("%s, expires %dms", request.toString(),
+ return String.format("%s, expires %dms", requestId,
expiryMs - SystemClock.elapsedRealtime());
}
}
@@ -693,7 +698,7 @@
updateRequestCounts(REMOVE, existing);
mNetworkRequests.remove(requestId);
if (existing.isRequest()) {
- unlingerRequest(existing);
+ unlingerRequest(existing.requestId);
}
}
@@ -839,33 +844,33 @@
}
/**
- * Sets the specified request to linger on this network for the specified time. Called by
+ * Sets the specified requestId to linger on this network for the specified time. Called by
* ConnectivityService when the request is moved to another network with a higher score.
*/
- public void lingerRequest(NetworkRequest request, long now, long duration) {
- if (mLingerTimerForRequest.get(request.requestId) != null) {
+ public void lingerRequest(int requestId, long now, long duration) {
+ if (mLingerTimerForRequest.get(requestId) != null) {
// Cannot happen. Once a request is lingering on a particular network, we cannot
// re-linger it unless that network becomes the best for that request again, in which
// case we should have unlingered it.
- Log.wtf(TAG, toShortString() + ": request " + request.requestId + " already lingered");
+ Log.wtf(TAG, toShortString() + ": request " + requestId + " already lingered");
}
final long expiryMs = now + duration;
- LingerTimer timer = new LingerTimer(request, expiryMs);
+ LingerTimer timer = new LingerTimer(requestId, expiryMs);
if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + toShortString());
mLingerTimers.add(timer);
- mLingerTimerForRequest.put(request.requestId, timer);
+ mLingerTimerForRequest.put(requestId, timer);
}
/**
* Cancel lingering. Called by ConnectivityService when a request is added to this network.
- * Returns true if the given request was lingering on this network, false otherwise.
+ * Returns true if the given requestId was lingering on this network, false otherwise.
*/
- public boolean unlingerRequest(NetworkRequest request) {
- LingerTimer timer = mLingerTimerForRequest.get(request.requestId);
+ public boolean unlingerRequest(int requestId) {
+ LingerTimer timer = mLingerTimerForRequest.get(requestId);
if (timer != null) {
if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + toShortString());
mLingerTimers.remove(timer);
- mLingerTimerForRequest.remove(request.requestId);
+ mLingerTimerForRequest.remove(requestId);
return true;
}
return false;
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index a762219..f6a2846 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -70,4 +70,7 @@
"android.test.base",
"android.test.mock",
],
+ jni_libs: [
+ "libservice-connectivity",
+ ],
}
diff --git a/tests/net/common/java/android/net/CaptivePortalDataTest.kt b/tests/net/common/java/android/net/CaptivePortalDataTest.kt
index bd1847b..8710d23 100644
--- a/tests/net/common/java/android/net/CaptivePortalDataTest.kt
+++ b/tests/net/common/java/android/net/CaptivePortalDataTest.kt
@@ -41,13 +41,14 @@
.setBytesRemaining(456L)
.setExpiryTime(789L)
.setCaptive(true)
+ .setVenueFriendlyName("venue friendly name")
.build()
private fun makeBuilder() = CaptivePortalData.Builder(data)
@Test
fun testParcelUnparcel() {
- assertParcelSane(data, fieldCount = 7)
+ assertParcelSane(data, fieldCount = 8)
assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build())
assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build())
@@ -66,6 +67,8 @@
assertNotEqualsAfterChange { it.setBytesRemaining(789L) }
assertNotEqualsAfterChange { it.setExpiryTime(12L) }
assertNotEqualsAfterChange { it.setCaptive(false) }
+ assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") }
+ assertNotEqualsAfterChange { it.setVenueFriendlyName(null) }
}
@Test
@@ -108,6 +111,11 @@
assertFalse(makeBuilder().setCaptive(false).build().isCaptive)
}
+ @Test
+ fun testVenueFriendlyName() {
+ assertEquals("venue friendly name", data.venueFriendlyName)
+ }
+
private fun CaptivePortalData.mutate(mutator: (CaptivePortalData.Builder) -> Unit) =
CaptivePortalData.Builder(this).apply { mutator(this) }.build()
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 0a48068..2e70be7 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -63,6 +63,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
@@ -161,7 +162,6 @@
import android.net.EthernetManager;
import android.net.IConnectivityDiagnosticsCallback;
import android.net.IDnsResolver;
-import android.net.IIpConnectivityMetrics;
import android.net.INetd;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
@@ -344,6 +344,11 @@
private static final String INTERFACE_NAME = "interface";
+ private static final String TEST_VENUE_URL_NA = "https://android.com/";
+ private static final String TEST_VENUE_URL_CAPPORT = "https://android.com/capport/";
+ private static final String TEST_FRIENDLY_NAME = "Network friendly name";
+ private static final String TEST_REDIRECT_URL = "http://example.com/firstPath";
+
private MockContext mServiceContext;
private HandlerThread mCsHandlerThread;
private ConnectivityService.Dependencies mDeps;
@@ -359,7 +364,6 @@
private HandlerThread mAlarmManagerThread;
private TestNetIdManager mNetIdManager;
- @Mock IIpConnectivityMetrics mIpConnectivityMetrics;
@Mock IpConnectivityMetrics.Logger mMetricsService;
@Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
@Mock DeviceIdleInternal mDeviceIdleInternal;
@@ -869,7 +873,7 @@
mProbesSucceeded = probesSucceeded;
}
- void notifyCaptivePortalDataChanged(CaptivePortalData data) {
+ void notifyCapportApiDataChanged(CaptivePortalData data) {
try {
mNmCallbacks.notifyCaptivePortalDataChanged(data);
} catch (RemoteException e) {
@@ -1199,6 +1203,8 @@
updateState(NetworkInfo.DetailedState.DISCONNECTED, "disconnect");
}
mAgentRegistered = false;
+ setUids(null);
+ mInterface = null;
}
@Override
@@ -1371,7 +1377,6 @@
doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
doReturn(mMetricsService).when(deps).getMetricsLogger();
doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt());
- doReturn(mIpConnectivityMetrics).when(deps).getIpConnectivityMetrics();
doReturn(mBatteryStatsService).when(deps).getBatteryStatsService();
doAnswer(inv -> {
mPolicyTracker = new WrappedMultinetworkPolicyTracker(
@@ -2005,7 +2010,7 @@
Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl()));
final CaptivePortalData expectedCapportData = sanitized ? null : capportData;
- mWiFiNetworkAgent.notifyCaptivePortalDataChanged(capportData);
+ mWiFiNetworkAgent.notifyCapportApiDataChanged(capportData);
callback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp ->
Objects.equals(expectedCapportData, lp.getCaptivePortalData()));
defaultCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp ->
@@ -3043,7 +3048,7 @@
.setBytesRemaining(12345L)
.build();
- mWiFiNetworkAgent.notifyCaptivePortalDataChanged(testData);
+ mWiFiNetworkAgent.notifyCapportApiDataChanged(testData);
captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
lp -> testData.equals(lp.getCaptivePortalData()));
@@ -3056,6 +3061,136 @@
lp -> testData.equals(lp.getCaptivePortalData()) && lp.getMtu() == 1234);
}
+ private TestNetworkCallback setupNetworkCallbackAndConnectToWifi() throws Exception {
+ // Grant NETWORK_SETTINGS permission to be able to receive LinkProperties change callbacks
+ // with sensitive (captive portal) data
+ mServiceContext.setPermission(
+ android.Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
+
+ final TestNetworkCallback captivePortalCallback = new TestNetworkCallback();
+ final NetworkRequest captivePortalRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build();
+ mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback);
+
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+
+ mWiFiNetworkAgent.connectWithCaptivePortal(TEST_REDIRECT_URL, false /* isStrictMode */);
+ captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ return captivePortalCallback;
+ }
+
+ private class CaptivePortalTestData {
+ CaptivePortalTestData(CaptivePortalData naData, CaptivePortalData capportData,
+ CaptivePortalData expectedMergedData) {
+ mNaData = naData;
+ mCapportData = capportData;
+ mExpectedMergedData = expectedMergedData;
+ }
+
+ public final CaptivePortalData mNaData;
+ public final CaptivePortalData mCapportData;
+ public final CaptivePortalData mExpectedMergedData;
+ }
+
+ private CaptivePortalTestData setupCaptivePortalData() {
+ final CaptivePortalData capportData = new CaptivePortalData.Builder()
+ .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL))
+ .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_CAPPORT))
+ .setExpiryTime(1000000L)
+ .setBytesRemaining(12345L)
+ .build();
+
+ final CaptivePortalData naData = new CaptivePortalData.Builder()
+ .setBytesRemaining(80802L)
+ .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA))
+ .setVenueFriendlyName(TEST_FRIENDLY_NAME).build();
+
+ final CaptivePortalData expectedMergedData = new CaptivePortalData.Builder()
+ .setUserPortalUrl(Uri.parse(TEST_REDIRECT_URL))
+ .setBytesRemaining(12345L)
+ .setExpiryTime(1000000L)
+ .setVenueInfoUrl(Uri.parse(TEST_VENUE_URL_NA))
+ .setVenueFriendlyName(TEST_FRIENDLY_NAME).build();
+
+ return new CaptivePortalTestData(naData, capportData, expectedMergedData);
+ }
+
+ @Test
+ public void testMergeCaptivePortalApiWithFriendlyNameAndVenueUrl() throws Exception {
+ final TestNetworkCallback captivePortalCallback = setupNetworkCallbackAndConnectToWifi();
+ final CaptivePortalTestData captivePortalTestData = setupCaptivePortalData();
+
+ // Baseline capport data
+ mWiFiNetworkAgent.notifyCapportApiDataChanged(captivePortalTestData.mCapportData);
+
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData()));
+
+ // Venue URL and friendly name from Network agent, confirm that API data gets precedence
+ // on the bytes remaining.
+ final LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setCaptivePortalData(captivePortalTestData.mNaData);
+ mWiFiNetworkAgent.sendLinkProperties(linkProperties);
+
+ // Make sure that the capport data is merged
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> captivePortalTestData.mExpectedMergedData.equals(lp.getCaptivePortalData()));
+
+ // Create a new LP with no Network agent capport data
+ final LinkProperties newLps = new LinkProperties();
+ newLps.setMtu(1234);
+ mWiFiNetworkAgent.sendLinkProperties(newLps);
+ // CaptivePortalData is not lost and has the original values when LPs are received from the
+ // NetworkAgent
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData())
+ && lp.getMtu() == 1234);
+
+ // Now send capport data only from the Network agent
+ mWiFiNetworkAgent.notifyCapportApiDataChanged(null);
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> lp.getCaptivePortalData() == null);
+
+ newLps.setCaptivePortalData(captivePortalTestData.mNaData);
+ mWiFiNetworkAgent.sendLinkProperties(newLps);
+
+ // Make sure that only the network agent capport data is available
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> captivePortalTestData.mNaData.equals(lp.getCaptivePortalData()));
+ }
+
+ @Test
+ public void testMergeCaptivePortalDataFromNetworkAgentFirstThenCapport() throws Exception {
+ final TestNetworkCallback captivePortalCallback = setupNetworkCallbackAndConnectToWifi();
+ final CaptivePortalTestData captivePortalTestData = setupCaptivePortalData();
+
+ // Venue URL and friendly name from Network agent, confirm that API data gets precedence
+ // on the bytes remaining.
+ final LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setCaptivePortalData(captivePortalTestData.mNaData);
+ mWiFiNetworkAgent.sendLinkProperties(linkProperties);
+
+ // Make sure that the data is saved correctly
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> captivePortalTestData.mNaData.equals(lp.getCaptivePortalData()));
+
+ // Expected merged data: Network agent data is preferred, and values that are not used by
+ // it are merged from capport data
+ mWiFiNetworkAgent.notifyCapportApiDataChanged(captivePortalTestData.mCapportData);
+
+ // Make sure that the Capport data is merged correctly
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> captivePortalTestData.mExpectedMergedData.equals(lp.getCaptivePortalData()));
+
+ // Now set the naData to null
+ linkProperties.setCaptivePortalData(null);
+ mWiFiNetworkAgent.sendLinkProperties(linkProperties);
+
+ // Make sure that the Capport data is retained correctly
+ captivePortalCallback.expectLinkPropertiesThat(mWiFiNetworkAgent,
+ lp -> captivePortalTestData.mCapportData.equals(lp.getCaptivePortalData()));
+ }
+
private NetworkRequest.Builder newWifiRequestBuilder() {
return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI);
}
@@ -3361,6 +3496,7 @@
assertEquals(null, mCm.getActiveNetwork());
mMockVpn.establishForMyUid();
+ assertUidRangesUpdatedForMyUid(true);
defaultNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
assertEquals(defaultNetworkCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -3624,51 +3760,55 @@
// Register the factory and expect it to start looking for a network.
testFactory.expectAddRequestsWithScores(0); // Score 0 as the request is not served yet.
testFactory.register();
- testFactory.waitForNetworkRequests(1);
- assertTrue(testFactory.getMyStartRequested());
- // Bring up wifi. The factory stops looking for a network.
- mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- // Score 60 - 40 penalty for not validated yet, then 60 when it validates
- testFactory.expectAddRequestsWithScores(20, 60);
- mWiFiNetworkAgent.connect(true);
- testFactory.waitForRequests();
- assertFalse(testFactory.getMyStartRequested());
+ try {
+ testFactory.waitForNetworkRequests(1);
+ assertTrue(testFactory.getMyStartRequested());
- ContentResolver cr = mServiceContext.getContentResolver();
+ // Bring up wifi. The factory stops looking for a network.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ // Score 60 - 40 penalty for not validated yet, then 60 when it validates
+ testFactory.expectAddRequestsWithScores(20, 60);
+ mWiFiNetworkAgent.connect(true);
+ testFactory.waitForRequests();
+ assertFalse(testFactory.getMyStartRequested());
- // Turn on mobile data always on. The factory starts looking again.
- testFactory.expectAddRequestsWithScores(0); // Always on requests comes up with score 0
- setAlwaysOnNetworks(true);
- testFactory.waitForNetworkRequests(2);
- assertTrue(testFactory.getMyStartRequested());
+ ContentResolver cr = mServiceContext.getContentResolver();
- // Bring up cell data and check that the factory stops looking.
- assertLength(1, mCm.getAllNetworks());
- mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- testFactory.expectAddRequestsWithScores(10, 50); // Unvalidated, then validated
- mCellNetworkAgent.connect(true);
- cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- testFactory.waitForNetworkRequests(2);
- assertFalse(testFactory.getMyStartRequested()); // Because the cell network outscores us.
+ // Turn on mobile data always on. The factory starts looking again.
+ testFactory.expectAddRequestsWithScores(0); // Always on requests comes up with score 0
+ setAlwaysOnNetworks(true);
+ testFactory.waitForNetworkRequests(2);
+ assertTrue(testFactory.getMyStartRequested());
- // Check that cell data stays up.
- waitForIdle();
- verifyActiveNetwork(TRANSPORT_WIFI);
- assertLength(2, mCm.getAllNetworks());
+ // Bring up cell data and check that the factory stops looking.
+ assertLength(1, mCm.getAllNetworks());
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ testFactory.expectAddRequestsWithScores(10, 50); // Unvalidated, then validated
+ mCellNetworkAgent.connect(true);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ testFactory.waitForNetworkRequests(2);
+ assertFalse(
+ testFactory.getMyStartRequested()); // Because the cell network outscores us.
- // Turn off mobile data always on and expect the request to disappear...
- testFactory.expectRemoveRequests(1);
- setAlwaysOnNetworks(false);
- testFactory.waitForNetworkRequests(1);
+ // Check that cell data stays up.
+ waitForIdle();
+ verifyActiveNetwork(TRANSPORT_WIFI);
+ assertLength(2, mCm.getAllNetworks());
- // ... and cell data to be torn down.
- cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
- assertLength(1, mCm.getAllNetworks());
+ // Turn off mobile data always on and expect the request to disappear...
+ testFactory.expectRemoveRequests(1);
+ setAlwaysOnNetworks(false);
+ testFactory.waitForNetworkRequests(1);
- testFactory.terminate();
- mCm.unregisterNetworkCallback(cellNetworkCallback);
- handlerThread.quit();
+ // ... and cell data to be torn down.
+ cellNetworkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ assertLength(1, mCm.getAllNetworks());
+ } finally {
+ testFactory.terminate();
+ mCm.unregisterNetworkCallback(cellNetworkCallback);
+ handlerThread.quit();
+ }
}
@Test
@@ -5047,6 +5187,7 @@
lp.setInterfaceName(VPN_IFNAME);
mMockVpn.establishForMyUid(lp);
+ assertUidRangesUpdatedForMyUid(true);
final Network[] cellAndVpn = new Network[] {
mCellNetworkAgent.getNetwork(), mMockVpn.getNetwork()};
@@ -5632,6 +5773,7 @@
// (and doing so is difficult without using reflection) but it's good to test that the code
// behaves approximately correctly.
mMockVpn.establishForMyUid(false, true, false);
+ assertUidRangesUpdatedForMyUid(true);
final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
mService.setUnderlyingNetworksForVpn(new Network[]{wifiNetwork});
callback.expectAvailableCallbacksUnvalidated(mMockVpn);
@@ -5789,6 +5931,7 @@
mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
+ assertUidRangesUpdatedForMyUid(true);
defaultCallback.assertNoCallback();
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -5814,6 +5957,7 @@
mMockVpn.establishForMyUid(true /* validated */, true /* hasInternet */,
false /* isStrictMode */);
+ assertUidRangesUpdatedForMyUid(true);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -5839,6 +5983,7 @@
// Bring up a VPN that has the INTERNET capability, initially unvalidated.
mMockVpn.establishForMyUid(false /* validated */, true /* hasInternet */,
false /* isStrictMode */);
+ assertUidRangesUpdatedForMyUid(true);
// Even though the VPN is unvalidated, it becomes the default network for our app.
callback.expectAvailableCallbacksUnvalidated(mMockVpn);
@@ -5890,6 +6035,7 @@
mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
+ assertUidRangesUpdatedForMyUid(true);
vpnNetworkCallback.expectAvailableCallbacks(mMockVpn.getNetwork(),
false /* suspended */, false /* validated */, false /* blocked */, TIMEOUT_MS);
@@ -5931,6 +6077,7 @@
mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
+ assertUidRangesUpdatedForMyUid(true);
vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
@@ -6098,6 +6245,7 @@
mMockVpn.establishForMyUid(true /* validated */, false /* hasInternet */,
false /* isStrictMode */);
+ assertUidRangesUpdatedForMyUid(true);
vpnNetworkCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
@@ -6156,6 +6304,7 @@
// Bring up a VPN
mMockVpn.establishForMyUid();
+ assertUidRangesUpdatedForMyUid(true);
callback.expectAvailableThenValidatedCallbacks(mMockVpn);
callback.assertNoCallback();
@@ -6176,11 +6325,15 @@
// Create a fake restricted profile whose parent is our user ID.
final int userId = UserHandle.getUserId(uid);
+ when(mUserManager.canHaveRestrictedProfile(userId)).thenReturn(true);
final int restrictedUserId = userId + 1;
final UserInfo info = new UserInfo(restrictedUserId, "user", UserInfo.FLAG_RESTRICTED);
info.restrictedProfileParentId = userId;
assertTrue(info.isRestricted());
when(mUserManager.getUserInfo(restrictedUserId)).thenReturn(info);
+ when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, restrictedUserId))
+ .thenReturn(UserHandle.getUid(restrictedUserId, VPN_UID));
+
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, restrictedUserId);
@@ -6220,6 +6373,54 @@
&& caps.getUids().contains(new UidRange(uid, uid))
&& caps.hasTransport(TRANSPORT_VPN)
&& !caps.hasTransport(TRANSPORT_WIFI));
+
+ // Test lockdown with restricted profiles.
+ mServiceContext.setPermission(
+ Manifest.permission.CONTROL_ALWAYS_ON_VPN, PERMISSION_GRANTED);
+ mServiceContext.setPermission(
+ Manifest.permission.CONTROL_VPN, PERMISSION_GRANTED);
+ mServiceContext.setPermission(
+ Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
+
+ // Connect wifi and check that UIDs in the main and restricted profiles have network access.
+ mMockVpn.disconnect();
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true /* validated */);
+ final int restrictedUid = UserHandle.getUid(restrictedUserId, 42 /* appId */);
+ assertNotNull(mCm.getActiveNetworkForUid(uid));
+ assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
+
+ // Enable always-on VPN lockdown. The main user loses network access because no VPN is up.
+ final ArrayList<String> allowList = new ArrayList<>();
+ mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+ waitForIdle();
+ assertNull(mCm.getActiveNetworkForUid(uid));
+ assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
+
+ // Start the restricted profile, and check that the UID within it loses network access.
+ when(mUserManager.getAliveUsers()).thenReturn(
+ Arrays.asList(new UserInfo[] {
+ new UserInfo(userId, "", 0),
+ info
+ }));
+ // TODO: check that VPN app within restricted profile still has access, etc.
+ handler.post(() -> mServiceContext.sendBroadcast(addedIntent));
+ waitForIdle();
+ assertNull(mCm.getActiveNetworkForUid(uid));
+ assertNull(mCm.getActiveNetworkForUid(restrictedUid));
+
+ // Stop the restricted profile, and check that the UID within it has network access again.
+ when(mUserManager.getAliveUsers()).thenReturn(
+ Arrays.asList(new UserInfo[] {
+ new UserInfo(userId, "", 0),
+ }));
+ handler.post(() -> mServiceContext.sendBroadcast(removedIntent));
+ waitForIdle();
+ assertNull(mCm.getActiveNetworkForUid(uid));
+ assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
+
+ mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+ waitForIdle();
}
@Test
@@ -6258,6 +6459,7 @@
// Connect VPN network. By default it is using current default network (Cell).
mMockVpn.establishForMyUid();
+ assertUidRangesUpdatedForMyUid(true);
// Ensure VPN is now the active network.
assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
@@ -6310,6 +6512,7 @@
// Connect VPN network.
mMockVpn.establishForMyUid();
+ assertUidRangesUpdatedForMyUid(true);
// Ensure VPN is now the active network.
assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
@@ -6684,6 +6887,7 @@
assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
mMockVpn.establishForMyUid();
+ assertUidRangesUpdatedForMyUid(true);
defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
vpnUidCallback.assertNoCallback(); // vpnUidCallback has NOT_VPN capability.
assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
@@ -6703,36 +6907,58 @@
mCm.unregisterNetworkCallback(vpnUidCallback);
}
+ /**
+ * Test mutable and requestable network capabilities such as
+ * {@link NetworkCapabilities#NET_CAPABILITY_TRUSTED} and
+ * {@link NetworkCapabilities#NET_CAPABILITY_NOT_VCN_MANAGED}. Verify that the
+ * {@code ConnectivityService} re-assign the networks accordingly.
+ */
@Test
- public final void testLoseTrusted() throws Exception {
- final NetworkRequest trustedRequest = new NetworkRequest.Builder()
- .addCapability(NET_CAPABILITY_TRUSTED)
- .build();
- final TestNetworkCallback trustedCallback = new TestNetworkCallback();
- mCm.requestNetwork(trustedRequest, trustedCallback);
+ public final void testLoseMutableAndRequestableCaps() throws Exception {
+ final int[] testCaps = new int [] {
+ NET_CAPABILITY_TRUSTED,
+ NET_CAPABILITY_NOT_VCN_MANAGED
+ };
+ for (final int testCap : testCaps) {
+ // Create requests with and without the testing capability.
+ final TestNetworkCallback callbackWithCap = new TestNetworkCallback();
+ final TestNetworkCallback callbackWithoutCap = new TestNetworkCallback();
+ mCm.requestNetwork(new NetworkRequest.Builder().addCapability(testCap).build(),
+ callbackWithCap);
+ mCm.requestNetwork(new NetworkRequest.Builder().removeCapability(testCap).build(),
+ callbackWithoutCap);
- mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- mCellNetworkAgent.connect(true);
- trustedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
- reset(mMockNetd);
+ // Setup networks with testing capability and verify the default network changes.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+ callbackWithCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ callbackWithoutCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
- mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- mWiFiNetworkAgent.connect(true);
- trustedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
- verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId));
- reset(mMockNetd);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+ callbackWithCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ callbackWithoutCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
- mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
- trustedCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
- verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
- reset(mMockNetd);
+ // Remove the testing capability on wifi, verify the callback and default network
+ // changes back to cellular.
+ mWiFiNetworkAgent.removeCapability(testCap);
+ callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent);
+ verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
- mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
- trustedCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
- verify(mMockNetd).networkClearDefault();
+ mCellNetworkAgent.removeCapability(testCap);
+ callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ callbackWithoutCap.assertNoCallback();
+ verify(mMockNetd).networkClearDefault();
- mCm.unregisterNetworkCallback(trustedCallback);
+ mCm.unregisterNetworkCallback(callbackWithCap);
+ mCm.unregisterNetworkCallback(callbackWithoutCap);
+ }
}
@Ignore // 40%+ flakiness : figure out why and re-enable.
@@ -7341,6 +7567,7 @@
LinkProperties testLinkProperties = new LinkProperties();
testLinkProperties.setHttpProxy(testProxyInfo);
mMockVpn.establishForMyUid(testLinkProperties);
+ assertUidRangesUpdatedForMyUid(true);
// Test that the VPN network returns a proxy, and the WiFi does not.
assertEquals(testProxyInfo, mService.getProxyForNetwork(mMockVpn.getNetwork()));
@@ -7378,6 +7605,7 @@
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
mMockVpn.establish(lp, VPN_UID, vpnRange);
+ assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
// A connected VPN should have interface rules set up. There are two expected invocations,
// one during the VPN initial connection, one during the VPN LinkProperties update.
@@ -7405,6 +7633,7 @@
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
+ assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
// Legacy VPN should not have interface rules set up
verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
@@ -7420,6 +7649,7 @@
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
mMockVpn.establish(lp, Process.SYSTEM_UID, vpnRange);
+ assertVpnUidRangesUpdated(true, vpnRange, Process.SYSTEM_UID);
// IPv6 unreachable route should not be misinterpreted as a default route
verify(mMockNetd, never()).firewallAddUidInterfaceRules(any(), any());
@@ -7434,6 +7664,7 @@
// The uid range needs to cover the test app so the network is visible to it.
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
mMockVpn.establish(lp, VPN_UID, vpnRange);
+ assertVpnUidRangesUpdated(true, vpnRange, VPN_UID);
// Connected VPN should have interface rules set up. There are two expected invocations,
// one during VPN uid update, one during VPN LinkProperties update
@@ -7484,7 +7715,9 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
// The uid range needs to cover the test app so the network is visible to it.
final UidRange vpnRange = UidRange.createForUser(VPN_USER);
- mMockVpn.establish(lp, VPN_UID, Collections.singleton(vpnRange));
+ final Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
+ mMockVpn.establish(lp, VPN_UID, vpnRanges);
+ assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
reset(mMockNetd);
InOrder inOrder = inOrder(mMockNetd);
@@ -7635,6 +7868,7 @@
throws Exception {
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
+ assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
mMockVpn.setVpnType(vpnType);
final VpnInfo vpnInfo = new VpnInfo();
@@ -7892,6 +8126,7 @@
Manifest.permission.ACCESS_FINE_LOCATION);
mMockVpn.establishForMyUid();
+ assertUidRangesUpdatedForMyUid(true);
// Wait for networks to connect and broadcasts to be sent before removing permissions.
waitForIdle();
@@ -8171,4 +8406,54 @@
assertTrue(isRequestIdInOrder);
}
}
+
+ private void assertUidRangesUpdatedForMyUid(boolean add) throws Exception {
+ final int uid = Process.myUid();
+ assertVpnUidRangesUpdated(add, uidRangesForUid(uid), uid);
+ }
+
+ private void assertVpnUidRangesUpdated(boolean add, Set<UidRange> vpnRanges, int exemptUid)
+ throws Exception {
+ InOrder inOrder = inOrder(mMockNetd);
+ ArgumentCaptor<int[]> exemptUidCaptor = ArgumentCaptor.forClass(int[].class);
+
+ inOrder.verify(mMockNetd, times(1)).socketDestroy(eq(toUidRangeStableParcels(vpnRanges)),
+ exemptUidCaptor.capture());
+ assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid);
+
+ if (add) {
+ inOrder.verify(mMockNetd, times(1)).networkAddUidRanges(eq(mMockVpn.getNetId()),
+ eq(toUidRangeStableParcels(vpnRanges)));
+ } else {
+ inOrder.verify(mMockNetd, times(1)).networkRemoveUidRanges(eq(mMockVpn.getNetId()),
+ eq(toUidRangeStableParcels(vpnRanges)));
+ }
+
+ inOrder.verify(mMockNetd, times(1)).socketDestroy(eq(toUidRangeStableParcels(vpnRanges)),
+ exemptUidCaptor.capture());
+ assertContainsExactly(exemptUidCaptor.getValue(), Process.VPN_UID, exemptUid);
+ }
+
+ @Test
+ public void testVpnUidRangesUpdate() throws Exception {
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName("tun0");
+ lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
+ lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
+ final UidRange vpnRange = UidRange.createForUser(VPN_USER);
+ Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
+ mMockVpn.establish(lp, VPN_UID, vpnRanges);
+ assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
+
+ reset(mMockNetd);
+ // Update to new range which is old range minus APP1, i.e. only APP2
+ final Set<UidRange> newRanges = new HashSet<>(Arrays.asList(
+ new UidRange(vpnRange.start, APP1_UID - 1),
+ new UidRange(APP1_UID + 1, vpnRange.stop)));
+ mMockVpn.setUids(newRanges);
+ waitForIdle();
+
+ assertVpnUidRangesUpdated(true, newRanges, VPN_UID);
+ assertVpnUidRangesUpdated(false, vpnRanges, VPN_UID);
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 3a07166..8c5d1d6 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -124,6 +124,22 @@
assertEquals("", output2);
}
+ private void logDefaultNetworkEvent(long timeMs, NetworkAgentInfo nai,
+ NetworkAgentInfo oldNai) {
+ final Network network = (nai != null) ? nai.network() : null;
+ final int score = (nai != null) ? nai.getCurrentScore() : 0;
+ final boolean validated = (nai != null) ? nai.lastValidated : false;
+ final LinkProperties lp = (nai != null) ? nai.linkProperties : null;
+ final NetworkCapabilities nc = (nai != null) ? nai.networkCapabilities : null;
+
+ final Network prevNetwork = (oldNai != null) ? oldNai.network() : null;
+ final int prevScore = (oldNai != null) ? oldNai.getCurrentScore() : 0;
+ final LinkProperties prevLp = (oldNai != null) ? oldNai.linkProperties : null;
+ final NetworkCapabilities prevNc = (oldNai != null) ? oldNai.networkCapabilities : null;
+
+ mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, network, score, validated,
+ lp, nc, prevNetwork, prevScore, prevLp, prevNc);
+ }
@Test
public void testDefaultNetworkEvents() throws Exception {
final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
@@ -147,7 +163,7 @@
for (NetworkAgentInfo[] pair : defaultNetworks) {
timeMs += durationMs;
durationMs += durationMs;
- mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, pair[1], pair[0]);
+ logDefaultNetworkEvent(timeMs, pair[1], pair[0]);
}
String want = String.join("\n",
@@ -331,8 +347,8 @@
final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
NetworkAgentInfo cellNai = makeNai(100, 50, false, true, cell);
NetworkAgentInfo wifiNai = makeNai(101, 60, true, false, wifi);
- mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs + 200, cellNai, null);
- mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs + 300, wifiNai, cellNai);
+ logDefaultNetworkEvent(timeMs + 200L, cellNai, null);
+ logDefaultNetworkEvent(timeMs + 300L, wifiNai, cellNai);
String want = String.join("\n",
"dropped_events: 0",
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 3648c4d..02a2aad 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -339,14 +339,8 @@
final Vpn vpn = createVpn(primaryUser.id);
final UidRange user = PRI_USER_RANGE;
- // Default state.
- assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1],
- user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
-
// Set always-on without lockdown.
assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null, mKeyStore));
- assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1],
- user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
// Set always-on with lockdown.
assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null, mKeyStore));
@@ -355,10 +349,6 @@
new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
}));
- assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2],
- user.start + PKG_UIDS[3]);
- assertUnblocked(vpn, user.start + PKG_UIDS[1]);
-
// Switch to another app.
assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
@@ -369,9 +359,6 @@
new UidRangeParcel(user.start, user.start + PKG_UIDS[3] - 1),
new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
}));
- assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1],
- user.start + PKG_UIDS[2]);
- assertUnblocked(vpn, user.start + PKG_UIDS[3]);
}
@Test
@@ -386,8 +373,6 @@
new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
}));
- assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]);
- assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
// Change allowed app list to PKGS[3].
assertTrue(vpn.setAlwaysOnPackage(
PKGS[1], true, Collections.singletonList(PKGS[3]), mKeyStore));
@@ -398,8 +383,6 @@
new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1),
new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
}));
- assertBlocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[2]);
- assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[3]);
// Change the VPN app.
assertTrue(vpn.setAlwaysOnPackage(
@@ -412,8 +395,6 @@
new UidRangeParcel(user.start, user.start + PKG_UIDS[0] - 1),
new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1)
}));
- assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
- assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]);
// Remove the list of allowed packages.
assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null, mKeyStore));
@@ -424,9 +405,6 @@
verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.stop),
}));
- assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2],
- user.start + PKG_UIDS[3]);
- assertUnblocked(vpn, user.start + PKG_UIDS[0]);
// Add the list of allowed packages.
assertTrue(vpn.setAlwaysOnPackage(
@@ -438,8 +416,6 @@
new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
}));
- assertBlocked(vpn, user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
- assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1]);
// Try allowing a package with a comma, should be rejected.
assertFalse(vpn.setAlwaysOnPackage(
@@ -460,45 +436,6 @@
}
@Test
- public void testLockdownAddingAProfile() throws Exception {
- final Vpn vpn = createVpn(primaryUser.id);
- setMockedUsers(primaryUser);
-
- // Make a copy of the restricted profile, as we're going to mark it deleted halfway through.
- final UserInfo tempProfile = new UserInfo(restrictedProfileA.id, restrictedProfileA.name,
- restrictedProfileA.flags);
- tempProfile.restrictedProfileParentId = primaryUser.id;
-
- final UidRange user = PRI_USER_RANGE;
- final UidRange profile = UidRange.createForUser(tempProfile.id);
-
- // Set lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
- verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
- new UidRangeParcel(user.start, user.start + PKG_UIDS[3] - 1),
- new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
- }));
- // Verify restricted user isn't affected at first.
- assertUnblocked(vpn, profile.start + PKG_UIDS[0]);
-
- // Add the restricted user.
- setMockedUsers(primaryUser, tempProfile);
- vpn.onUserAdded(tempProfile.id);
- verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
- new UidRangeParcel(profile.start, profile.start + PKG_UIDS[3] - 1),
- new UidRangeParcel(profile.start + PKG_UIDS[3] + 1, profile.stop)
- }));
-
- // Remove the restricted user.
- tempProfile.partial = true;
- vpn.onUserRemoved(tempProfile.id);
- verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
- new UidRangeParcel(profile.start, profile.start + PKG_UIDS[3] - 1),
- new UidRangeParcel(profile.start + PKG_UIDS[3] + 1, profile.stop)
- }));
- }
-
- @Test
public void testLockdownRuleRepeatability() throws Exception {
final Vpn vpn = createVpn(primaryUser.id);
final UidRangeParcel[] primaryUserRangeParcel = new UidRangeParcel[] {
@@ -1207,20 +1144,6 @@
return vpn;
}
- private static void assertBlocked(Vpn vpn, int... uids) {
- for (int uid : uids) {
- final boolean blocked = vpn.getLockdown() && vpn.isBlockingUid(uid);
- assertTrue("Uid " + uid + " should be blocked", blocked);
- }
- }
-
- private static void assertUnblocked(Vpn vpn, int... uids) {
- for (int uid : uids) {
- final boolean blocked = vpn.getLockdown() && vpn.isBlockingUid(uid);
- assertFalse("Uid " + uid + " should not be blocked", blocked);
- }
- }
-
/**
* Populate {@link #mUserManager} with a list of fake users.
*/