diff options
14 files changed, 612 insertions, 194 deletions
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index 2dacf8f460bb..52a2354840c9 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -17,6 +17,7 @@ package android.net; import android.content.Context; +import android.net.ConnectivityManager.PacketKeepalive; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -26,7 +27,6 @@ import android.util.Log; import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; -import android.net.ConnectivityManager.PacketKeepalive; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicBoolean; @@ -101,20 +101,6 @@ public abstract class NetworkAgent extends Handler { public static final int EVENT_NETWORK_SCORE_CHANGED = BASE + 4; /** - * Sent by the NetworkAgent to ConnectivityService to add new UID ranges - * to be forced into this Network. For VPNs only. - * obj = UidRange[] to forward - */ - public static final int EVENT_UID_RANGES_ADDED = BASE + 5; - - /** - * Sent by the NetworkAgent to ConnectivityService to remove UID ranges - * from being forced into this Network. For VPNs only. - * obj = UidRange[] to stop forwarding - */ - public static final int EVENT_UID_RANGES_REMOVED = BASE + 6; - - /** * Sent by ConnectivityService to the NetworkAgent to inform the agent of the * networks status - whether we could use the network or could not, due to * either a bad network configuration (no internet link) or captive portal. @@ -390,22 +376,6 @@ public abstract class NetworkAgent extends Handler { } /** - * Called by the VPN code when it wants to add ranges of UIDs to be routed - * through the VPN network. - */ - public void addUidRanges(UidRange[] ranges) { - queueOrSendMessage(EVENT_UID_RANGES_ADDED, ranges); - } - - /** - * Called by the VPN code when it wants to remove ranges of UIDs from being routed - * through the VPN network. - */ - public void removeUidRanges(UidRange[] ranges) { - queueOrSendMessage(EVENT_UID_RANGES_REMOVED, ranges); - } - - /** * Called by the bearer to indicate this network was manually selected by the user. * This should be called before the NetworkInfo is marked CONNECTED so that this * Network can be given special treatment at that time. If {@code acceptUnvalidated} is diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 214ff64c6315..1a4765bcf631 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.net.ConnectivityManager.NetworkCallback; import android.os.Parcel; import android.os.Parcelable; +import android.util.ArraySet; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; @@ -29,6 +30,7 @@ import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; +import java.util.Set; import java.util.StringJoiner; /** @@ -47,6 +49,7 @@ import java.util.StringJoiner; */ public final class NetworkCapabilities implements Parcelable { private static final String TAG = "NetworkCapabilities"; + private static final int INVALID_UID = -1; /** * @hide @@ -64,6 +67,8 @@ public final class NetworkCapabilities implements Parcelable { mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps; mNetworkSpecifier = nc.mNetworkSpecifier; mSignalStrength = nc.mSignalStrength; + mUids = nc.mUids; + mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid; } } @@ -77,6 +82,8 @@ public final class NetworkCapabilities implements Parcelable { mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED; mNetworkSpecifier = null; mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED; + mUids = null; + mEstablishingVpnAppUid = INVALID_UID; } /** @@ -619,6 +626,29 @@ public final class NetworkCapabilities implements Parcelable { } /** + * UID of the app that manages this network, or INVALID_UID if none/unknown. + * + * This field keeps track of the UID of the app that created this network and is in charge + * of managing it. In the practice, it is used to store the UID of VPN apps so it is named + * accordingly, but it may be renamed if other mechanisms are offered for third party apps + * to create networks. + * + * Because this field is only used in the services side (and to avoid apps being able to + * set this to whatever they want), this field is not parcelled and will not be conserved + * across the IPC boundary. + * @hide + */ + private int mEstablishingVpnAppUid = INVALID_UID; + + /** + * Set the UID of the managing app. + * @hide + */ + public void setEstablishingVpnAppUid(final int uid) { + mEstablishingVpnAppUid = uid; + } + + /** * Value indicating that link bandwidth is unspecified. * @hide */ @@ -837,6 +867,174 @@ public final class NetworkCapabilities implements Parcelable { } /** + * List of UIDs this network applies to. No restriction if null. + * <p> + * This is typically (and at this time, only) used by VPN. This network is only available to + * the UIDs in this list, and it is their default network. Apps in this list that wish to + * bypass the VPN can do so iff the VPN app allows them to or if they are privileged. If this + * member is null, then the network is not restricted by app UID. If it's an empty list, then + * it means nobody can use it. + * As a special exception, the app managing this network (as identified by its UID stored in + * mEstablishingVpnAppUid) can always see this network. This is embodied by a special check in + * satisfiedByUids. That still does not mean the network necessarily <strong>applies</strong> + * to the app that manages it as determined by #appliesToUid. + * <p> + * Please note that in principle a single app can be associated with multiple UIDs because + * each app will have a different UID when it's run as a different (macro-)user. A single + * macro user can only have a single active VPN app at any given time however. + * <p> + * Also please be aware this class does not try to enforce any normalization on this. Callers + * can only alter the UIDs by setting them wholesale : this class does not provide any utility + * to add or remove individual UIDs or ranges. If callers have any normalization needs on + * their own (like requiring sortedness or no overlap) they need to enforce it + * themselves. Some of the internal methods also assume this is normalized as in no adjacent + * or overlapping ranges are present. + * + * @hide + */ + private Set<UidRange> mUids = null; + + /** + * Convenience method to set the UIDs this network applies to to a single UID. + * @hide + */ + public NetworkCapabilities setSingleUid(int uid) { + final ArraySet<UidRange> identity = new ArraySet<>(1); + identity.add(new UidRange(uid, uid)); + setUids(identity); + return this; + } + + /** + * Set the list of UIDs this network applies to. + * This makes a copy of the set so that callers can't modify it after the call. + * @hide + */ + public NetworkCapabilities setUids(Set<UidRange> uids) { + if (null == uids) { + mUids = null; + } else { + mUids = new ArraySet<>(uids); + } + return this; + } + + /** + * Get the list of UIDs this network applies to. + * This returns a copy of the set so that callers can't modify the original object. + * @hide + */ + public Set<UidRange> getUids() { + return null == mUids ? null : new ArraySet<>(mUids); + } + + /** + * Test whether this network applies to this UID. + * @hide + */ + public boolean appliesToUid(int uid) { + if (null == mUids) return true; + for (UidRange range : mUids) { + if (range.contains(uid)) { + return true; + } + } + return false; + } + + /** + * Tests if the set of UIDs that this network applies to is the same of the passed set of UIDs. + * <p> + * This test only checks whether equal range objects are in both sets. It will + * return false if the ranges are not exactly the same, even if the covered UIDs + * are for an equivalent result. + * <p> + * Note that this method is not very optimized, which is fine as long as it's not used very + * often. + * <p> + * nc is assumed nonnull. + * + * @hide + */ + @VisibleForTesting + public boolean equalsUids(NetworkCapabilities nc) { + Set<UidRange> comparedUids = nc.mUids; + if (null == comparedUids) return null == mUids; + if (null == mUids) return false; + // Make a copy so it can be mutated to check that all ranges in mUids + // also are in uids. + final Set<UidRange> uids = new ArraySet<>(mUids); + for (UidRange range : comparedUids) { + if (!uids.contains(range)) { + return false; + } + uids.remove(range); + } + return uids.isEmpty(); + } + + /** + * Test whether the passed NetworkCapabilities satisfies the UIDs this capabilities require. + * + * This method is called on the NetworkCapabilities embedded in a request with the + * capabilities of an available network. It checks whether all the UIDs from this listen + * (representing the UIDs that must have access to the network) are satisfied by the UIDs + * in the passed nc (representing the UIDs that this network is available to). + * <p> + * As a special exception, the UID that created the passed network (as represented by its + * mEstablishingVpnAppUid field) always satisfies a NetworkRequest requiring it (of LISTEN + * or REQUEST types alike), even if the network does not apply to it. That is so a VPN app + * can see its own network when it listens for it. + * <p> + * nc is assumed nonnull. Else, NPE. + * @see #appliesToUid + * @hide + */ + public boolean satisfiedByUids(NetworkCapabilities nc) { + if (null == nc.mUids) return true; // The network satisfies everything. + if (null == mUids) return false; // Not everything allowed but requires everything + for (UidRange requiredRange : mUids) { + if (requiredRange.contains(nc.mEstablishingVpnAppUid)) return true; + if (!nc.appliesToUidRange(requiredRange)) { + return false; + } + } + return true; + } + + /** + * Returns whether this network applies to the passed ranges. + * This assumes that to apply, the passed range has to be entirely contained + * within one of the ranges this network applies to. If the ranges are not normalized, + * this method may return false even though all required UIDs are covered because no + * single range contained them all. + * @hide + */ + @VisibleForTesting + public boolean appliesToUidRange(UidRange requiredRange) { + if (null == mUids) return true; + for (UidRange uidRange : mUids) { + if (uidRange.containsRange(requiredRange)) { + return true; + } + } + return false; + } + + /** + * Combine the UIDs this network currently applies to with the UIDs the passed + * NetworkCapabilities apply to. + * nc is assumed nonnull. + */ + private void combineUids(NetworkCapabilities nc) { + if (null == nc.mUids || null == mUids) { + mUids = null; + return; + } + mUids.addAll(nc.mUids); + } + + /** * Combine a set of Capabilities to this one. Useful for coming up with the complete set * @hide */ @@ -846,6 +1044,7 @@ public final class NetworkCapabilities implements Parcelable { combineLinkBandwidths(nc); combineSpecifiers(nc); combineSignalStrength(nc); + combineUids(nc); } /** @@ -858,12 +1057,13 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ private boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc, boolean onlyImmutable) { - return (nc != null && - satisfiedByNetCapabilities(nc, onlyImmutable) && - satisfiedByTransportTypes(nc) && - (onlyImmutable || satisfiedByLinkBandwidths(nc)) && - satisfiedBySpecifier(nc) && - (onlyImmutable || satisfiedBySignalStrength(nc))); + return (nc != null + && satisfiedByNetCapabilities(nc, onlyImmutable) + && satisfiedByTransportTypes(nc) + && (onlyImmutable || satisfiedByLinkBandwidths(nc)) + && satisfiedBySpecifier(nc) + && (onlyImmutable || satisfiedBySignalStrength(nc)) + && (onlyImmutable || satisfiedByUids(nc))); } /** @@ -944,24 +1144,26 @@ public final class NetworkCapabilities implements Parcelable { @Override public boolean equals(Object obj) { if (obj == null || (obj instanceof NetworkCapabilities == false)) return false; - NetworkCapabilities that = (NetworkCapabilities)obj; - return (equalsNetCapabilities(that) && - equalsTransportTypes(that) && - equalsLinkBandwidths(that) && - equalsSignalStrength(that) && - equalsSpecifier(that)); + NetworkCapabilities that = (NetworkCapabilities) obj; + return (equalsNetCapabilities(that) + && equalsTransportTypes(that) + && equalsLinkBandwidths(that) + && equalsSignalStrength(that) + && equalsSpecifier(that) + && equalsUids(that)); } @Override public int hashCode() { - return ((int)(mNetworkCapabilities & 0xFFFFFFFF) + - ((int)(mNetworkCapabilities >> 32) * 3) + - ((int)(mTransportTypes & 0xFFFFFFFF) * 5) + - ((int)(mTransportTypes >> 32) * 7) + - (mLinkUpBandwidthKbps * 11) + - (mLinkDownBandwidthKbps * 13) + - Objects.hashCode(mNetworkSpecifier) * 17 + - (mSignalStrength * 19)); + return ((int) (mNetworkCapabilities & 0xFFFFFFFF) + + ((int) (mNetworkCapabilities >> 32) * 3) + + ((int) (mTransportTypes & 0xFFFFFFFF) * 5) + + ((int) (mTransportTypes >> 32) * 7) + + (mLinkUpBandwidthKbps * 11) + + (mLinkDownBandwidthKbps * 13) + + Objects.hashCode(mNetworkSpecifier) * 17 + + (mSignalStrength * 19) + + Objects.hashCode(mUids) * 23); } @Override @@ -976,6 +1178,7 @@ public final class NetworkCapabilities implements Parcelable { dest.writeInt(mLinkDownBandwidthKbps); dest.writeParcelable((Parcelable) mNetworkSpecifier, flags); dest.writeInt(mSignalStrength); + dest.writeArraySet(new ArraySet<>(mUids)); } public static final Creator<NetworkCapabilities> CREATOR = @@ -990,6 +1193,8 @@ public final class NetworkCapabilities implements Parcelable { netCap.mLinkDownBandwidthKbps = in.readInt(); netCap.mNetworkSpecifier = in.readParcelable(null); netCap.mSignalStrength = in.readInt(); + netCap.mUids = (ArraySet<UidRange>) in.readArraySet( + null /* ClassLoader, null for default */); return netCap; } @Override @@ -1022,7 +1227,12 @@ public final class NetworkCapabilities implements Parcelable { String signalStrength = (hasSignalStrength() ? " SignalStrength: " + mSignalStrength : ""); - return "[" + transports + capabilities + upBand + dnBand + specifier + signalStrength + "]"; + String uids = (null != mUids ? " Uids: <" + mUids + ">" : ""); + + String establishingAppUid = " EstablishingAppUid: " + mEstablishingVpnAppUid; + + return "[" + transports + capabilities + upBand + dnBand + specifier + signalStrength + + uids + establishingAppUid + "]"; } /** @hide */ diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 5030dce7cbf9..8a9f59b9550f 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -30,6 +30,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; 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_VPN; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.TRANSPORT_VPN; @@ -106,6 +107,7 @@ import android.security.Credentials; import android.security.KeyStore; import android.telephony.TelephonyManager; import android.text.TextUtils; +import android.util.ArraySet; import android.util.LocalLog; import android.util.LocalLog.ReadOnlyLocalLog; import android.util.Log; @@ -176,6 +178,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; @@ -733,12 +736,12 @@ public class ConnectivityService extends IConnectivityManager.Stub mSystemProperties = getSystemProperties(); mMetricsLog = logger; - mDefaultRequest = createInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST); + mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST); NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest, new Binder()); mNetworkRequests.put(mDefaultRequest, defaultNRI); mNetworkRequestInfoLogs.log("REGISTER " + defaultNRI); - mDefaultMobileDataRequest = createInternetRequestForTransport( + mDefaultMobileDataRequest = createDefaultInternetRequestForTransport( NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST); mHandlerThread = new HandlerThread("ConnectivityServiceThread"); @@ -903,7 +906,7 @@ public class ConnectivityService extends IConnectivityManager.Stub deps); } - private NetworkRequest createInternetRequestForTransport( + private NetworkRequest createDefaultInternetRequestForTransport( int transportType, NetworkRequest.Type type) { NetworkCapabilities netCap = new NetworkCapabilities(); netCap.addCapability(NET_CAPABILITY_INTERNET); @@ -1281,7 +1284,11 @@ public class ConnectivityService extends IConnectivityManager.Stub for (Network network : networks) { nai = getNetworkAgentInfoForNetwork(network); nc = getNetworkCapabilitiesInternal(nai); + // nc is a copy of the capabilities in nai, so it's fine to mutate it + // TODO : don't remove the UIDs when communicating with processes + // that have the NETWORK_SETTINGS permission. if (nc != null) { + nc.setSingleUid(userId); result.put(network, nc); } } @@ -2079,24 +2086,6 @@ public class ConnectivityService extends IConnectivityManager.Stub if (score != null) updateNetworkScore(nai, score.intValue()); break; } - case NetworkAgent.EVENT_UID_RANGES_ADDED: { - try { - mNetd.addVpnUidRanges(nai.network.netId, (UidRange[])msg.obj); - } catch (Exception e) { - // Never crash! - loge("Exception in addVpnUidRanges: " + e); - } - break; - } - case NetworkAgent.EVENT_UID_RANGES_REMOVED: { - try { - mNetd.removeVpnUidRanges(nai.network.netId, (UidRange[])msg.obj); - } catch (Exception e) { - // Never crash! - loge("Exception in removeVpnUidRanges: " + e); - } - break; - } case NetworkAgent.EVENT_SET_EXPLICITLY_SELECTED: { if (nai.everConnected && !nai.networkMisc.explicitlySelected) { loge("ERROR: already-connected network explicitly selected."); @@ -4235,6 +4224,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // the system default network. if (type == NetworkRequest.Type.TRACK_DEFAULT) { networkCapabilities = new NetworkCapabilities(mDefaultRequest.networkCapabilities); + networkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN); enforceAccessPermission(); } else { networkCapabilities = new NetworkCapabilities(networkCapabilities); @@ -4245,6 +4235,13 @@ public class ConnectivityService extends IConnectivityManager.Stub enforceMeteredApnPolicy(networkCapabilities); } ensureRequestableCapabilities(networkCapabilities); + // Set the UID range for this request to the single UID of the requester. + // This will overwrite any allowed UIDs in the requested capabilities. Though there + // are no visible methods to set the UIDs, an app could use reflection to try and get + // networks for other apps so it's essential that the UIDs are overwritten. + // TODO : don't forcefully set the UID when communicating with processes + // that have the NETWORK_SETTINGS permission. + networkCapabilities.setSingleUid(Binder.getCallingUid()); if (timeoutMs < 0) { throw new IllegalArgumentException("Bad timeout specified"); @@ -4318,6 +4315,9 @@ public class ConnectivityService extends IConnectivityManager.Stub enforceMeteredApnPolicy(networkCapabilities); ensureRequestableCapabilities(networkCapabilities); ensureValidNetworkSpecifier(networkCapabilities); + // TODO : don't forcefully set the UID when communicating with processes + // that have the NETWORK_SETTINGS permission. + networkCapabilities.setSingleUid(Binder.getCallingUid()); NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.REQUEST); @@ -4371,6 +4371,9 @@ public class ConnectivityService extends IConnectivityManager.Stub } NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities); + // TODO : don't forcefully set the UIDs when communicating with processes + // that have the NETWORK_SETTINGS permission. + nc.setSingleUid(Binder.getCallingUid()); if (!ConnectivityManager.checkChangePermission(mContext)) { // 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 @@ -4399,8 +4402,12 @@ public class ConnectivityService extends IConnectivityManager.Stub } ensureValidNetworkSpecifier(networkCapabilities); - NetworkRequest networkRequest = new NetworkRequest( - new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId(), + final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities); + // TODO : don't forcefully set the UIDs when communicating with processes + // that have the NETWORK_SETTINGS permission. + nc.setSingleUid(Binder.getCallingUid()); + + NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(), NetworkRequest.Type.LISTEN); NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation); if (VDBG) log("pendingListenForNetwork for " + nri); @@ -4543,6 +4550,7 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkInfo networkInfo = na.networkInfo; na.networkInfo = null; updateNetworkInfo(na, networkInfo); + updateUids(na, null, na.networkCapabilities); } private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties oldLp) { @@ -4791,6 +4799,8 @@ public class ConnectivityService extends IConnectivityManager.Stub nai.networkCapabilities = newNc; } + updateUids(nai, prevNc, newNc); + if (nai.getCurrentScore() == oldScore && newNc.equalRequestableCapabilities(prevNc)) { // If the requestable capabilities haven't changed, and the score hasn't changed, then // the change we're processing can't affect any requests, it can only affect the listens @@ -4827,6 +4837,34 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc, + NetworkCapabilities newNc) { + Set<UidRange> prevRanges = null == prevNc ? null : prevNc.getUids(); + Set<UidRange> newRanges = null == newNc ? null : newNc.getUids(); + if (null == prevRanges) prevRanges = new ArraySet<>(); + if (null == newRanges) newRanges = new ArraySet<>(); + final Set<UidRange> prevRangesCopy = new ArraySet<>(prevRanges); + + prevRanges.removeAll(newRanges); + newRanges.removeAll(prevRangesCopy); + + try { + if (!newRanges.isEmpty()) { + final UidRange[] addedRangesArray = new UidRange[newRanges.size()]; + newRanges.toArray(addedRangesArray); + mNetd.addVpnUidRanges(nai.network.netId, addedRangesArray); + } + if (!prevRanges.isEmpty()) { + final UidRange[] removedRangesArray = new UidRange[prevRanges.size()]; + prevRanges.toArray(removedRangesArray); + mNetd.removeVpnUidRanges(nai.network.netId, removedRangesArray); + } + } catch (Exception e) { + // Never crash! + loge("Exception in updateUids: " + e); + } + } + public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) { if (mNetworkForNetId.get(nai.network.netId) != nai) { // Ignore updates for disconnected networks @@ -4918,7 +4956,12 @@ public class ConnectivityService extends IConnectivityManager.Stub break; } case ConnectivityManager.CALLBACK_CAP_CHANGED: { - putParcelable(bundle, new NetworkCapabilities(networkAgent.networkCapabilities)); + final NetworkCapabilities nc = + new NetworkCapabilities(networkAgent.networkCapabilities); + // TODO : don't remove the UIDs when communicating with processes + // that have the NETWORK_SETTINGS permission. + nc.setSingleUid(nri.mUid); + putParcelable(bundle, nc); break; } case ConnectivityManager.CALLBACK_IP_CHANGED: { @@ -5442,6 +5485,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } } + updateUids(networkAgent, networkAgent.networkCapabilities, null); } } else if ((oldInfo != null && oldInfo.getState() == NetworkInfo.State.SUSPENDED) || state == NetworkInfo.State.SUSPENDED) { diff --git a/services/core/java/com/android/server/connectivity/ConnectivityConstants.java b/services/core/java/com/android/server/connectivity/ConnectivityConstants.java new file mode 100644 index 000000000000..24865bcd9a09 --- /dev/null +++ b/services/core/java/com/android/server/connectivity/ConnectivityConstants.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.connectivity; + +/** + * A class encapsulating various constants used by Connectivity. + * @hide + */ +public class ConnectivityConstants { + // IPC constants + public static final String ACTION_NETWORK_CONDITIONS_MEASURED = + "android.net.conn.NETWORK_CONDITIONS_MEASURED"; + public static final String EXTRA_CONNECTIVITY_TYPE = "extra_connectivity_type"; + public static final String EXTRA_NETWORK_TYPE = "extra_network_type"; + public static final String EXTRA_RESPONSE_RECEIVED = "extra_response_received"; + public static final String EXTRA_IS_CAPTIVE_PORTAL = "extra_is_captive_portal"; + public static final String EXTRA_CELL_ID = "extra_cellid"; + public static final String EXTRA_SSID = "extra_ssid"; + public static final String EXTRA_BSSID = "extra_bssid"; + /** real time since boot */ + public static final String EXTRA_REQUEST_TIMESTAMP_MS = "extra_request_timestamp_ms"; + public static final String EXTRA_RESPONSE_TIMESTAMP_MS = "extra_response_timestamp_ms"; + + public static final String PERMISSION_ACCESS_NETWORK_CONDITIONS = + "android.permission.ACCESS_NETWORK_CONDITIONS"; + + // Penalty applied to scores of Networks that have not been validated. + public static final int UNVALIDATED_SCORE_PENALTY = 40; + + // Score for explicitly connected network. + // + // This ensures that a) the explicitly selected network is never trumped by anything else, and + // b) the explicitly selected network is never torn down. + public static final int MAXIMUM_NETWORK_SCORE = 100; + // VPNs typically have priority over other networks. Give them a score that will + // let them win every single time. + public static final int VPN_DEFAULT_SCORE = 101; +} diff --git a/services/core/java/com/android/server/connectivity/KeepalivePacketData.java b/services/core/java/com/android/server/connectivity/KeepalivePacketData.java index 2ccfdd1f3136..f6b73b7d6949 100644 --- a/services/core/java/com/android/server/connectivity/KeepalivePacketData.java +++ b/services/core/java/com/android/server/connectivity/KeepalivePacketData.java @@ -16,6 +16,9 @@ package com.android.server.connectivity; +import static android.net.util.NetworkConstants.IPV4_HEADER_MIN_LEN; +import static android.net.util.NetworkConstants.UDP_HEADER_LEN; + import android.system.OsConstants; import android.net.ConnectivityManager; import android.net.NetworkUtils; @@ -57,9 +60,6 @@ public class KeepalivePacketData { /** Packet data. A raw byte string of packet data, not including the link-layer header. */ public final byte[] data; - private static final int IPV4_HEADER_LENGTH = 20; - private static final int UDP_HEADER_LENGTH = 8; - protected KeepalivePacketData(InetAddress srcAddress, int srcPort, InetAddress dstAddress, int dstPort, byte[] data) throws InvalidPacketException { this.srcAddress = srcAddress; @@ -111,7 +111,7 @@ public class KeepalivePacketData { throw new InvalidPacketException(ERROR_INVALID_PORT); } - int length = IPV4_HEADER_LENGTH + UDP_HEADER_LENGTH + 1; + int length = IPV4_HEADER_MIN_LEN + UDP_HEADER_LEN + 1; ByteBuffer buf = ByteBuffer.allocate(length); buf.order(ByteOrder.BIG_ENDIAN); buf.putShort((short) 0x4500); // IP version and TOS @@ -130,7 +130,7 @@ public class KeepalivePacketData { buf.putShort((short) 0); // UDP checksum buf.put((byte) 0xff); // NAT-T keepalive buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0)); - buf.putShort(udpChecksumOffset, IpUtils.udpChecksum(buf, 0, IPV4_HEADER_LENGTH)); + buf.putShort(udpChecksumOffset, IpUtils.udpChecksum(buf, 0, IPV4_HEADER_MIN_LEN)); return new KeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array()); } diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index a4d7242086bf..85b70ca0ffcd 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -223,14 +223,6 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // This represents the last score received from the NetworkAgent. private int currentScore; - // Penalty applied to scores of Networks that have not been validated. - private static final int UNVALIDATED_SCORE_PENALTY = 40; - - // Score for explicitly connected network. - // - // This ensures that a) the explicitly selected network is never trumped by anything else, and - // b) the explicitly selected network is never torn down. - private static final int MAXIMUM_NETWORK_SCORE = 100; // The list of NetworkRequests being satisfied by this Network. private final SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>(); @@ -428,12 +420,12 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // down an explicitly selected network before the user gets a chance to prefer it when // a higher-scoring network (e.g., Ethernet) is available. if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) { - return MAXIMUM_NETWORK_SCORE; + return ConnectivityConstants.MAXIMUM_NETWORK_SCORE; } int score = currentScore; if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty()) { - score -= UNVALIDATED_SCORE_PENALTY; + score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY; } if (score < 0) score = 0; return score; diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java index 85d1d1ef1d75..c471f0caa3cc 100644 --- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java +++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java @@ -24,6 +24,7 @@ import android.net.Network; import android.net.NetworkUtils; import android.net.RouteInfo; import android.net.TrafficStats; +import android.net.util.NetworkConstants; import android.os.SystemClock; import android.system.ErrnoException; import android.system.Os; @@ -421,8 +422,6 @@ public class NetworkDiagnostics { private class IcmpCheck extends SimpleSocketCheck implements Runnable { private static final int TIMEOUT_SEND = 100; private static final int TIMEOUT_RECV = 300; - private static final int ICMPV4_ECHO_REQUEST = 8; - private static final int ICMPV6_ECHO_REQUEST = 128; private static final int PACKET_BUFSIZE = 512; private final int mProtocol; private final int mIcmpType; @@ -432,11 +431,11 @@ public class NetworkDiagnostics { if (mAddressFamily == AF_INET6) { mProtocol = IPPROTO_ICMPV6; - mIcmpType = ICMPV6_ECHO_REQUEST; + mIcmpType = NetworkConstants.ICMPV6_ECHO_REQUEST_TYPE; mMeasurement.description = "ICMPv6"; } else { mProtocol = IPPROTO_ICMP; - mIcmpType = ICMPV4_ECHO_REQUEST; + mIcmpType = NetworkConstants.ICMPV4_ECHO_REQUEST_TYPE; mMeasurement.description = "ICMPv4"; } @@ -504,7 +503,6 @@ public class NetworkDiagnostics { private class DnsUdpCheck extends SimpleSocketCheck implements Runnable { private static final int TIMEOUT_SEND = 100; private static final int TIMEOUT_RECV = 500; - private static final int DNS_SERVER_PORT = 53; private static final int RR_TYPE_A = 1; private static final int RR_TYPE_AAAA = 28; private static final int PACKET_BUFSIZE = 512; @@ -546,7 +544,8 @@ public class NetworkDiagnostics { } try { - setupSocket(SOCK_DGRAM, IPPROTO_UDP, TIMEOUT_SEND, TIMEOUT_RECV, DNS_SERVER_PORT); + setupSocket(SOCK_DGRAM, IPPROTO_UDP, TIMEOUT_SEND, TIMEOUT_RECV, + NetworkConstants.DNS_SERVER_PORT); } catch (ErrnoException | IOException e) { mMeasurement.recordFailure(e.toString()); return; diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java index ed268581b50c..8a2e71c1fba8 100644 --- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java @@ -122,22 +122,6 @@ public class NetworkMonitor extends StateMachine { } } - public static final String ACTION_NETWORK_CONDITIONS_MEASURED = - "android.net.conn.NETWORK_CONDITIONS_MEASURED"; - public static final String EXTRA_CONNECTIVITY_TYPE = "extra_connectivity_type"; - public static final String EXTRA_NETWORK_TYPE = "extra_network_type"; - public static final String EXTRA_RESPONSE_RECEIVED = "extra_response_received"; - public static final String EXTRA_IS_CAPTIVE_PORTAL = "extra_is_captive_portal"; - public static final String EXTRA_CELL_ID = "extra_cellid"; - public static final String EXTRA_SSID = "extra_ssid"; - public static final String EXTRA_BSSID = "extra_bssid"; - /** real time since boot */ - public static final String EXTRA_REQUEST_TIMESTAMP_MS = "extra_request_timestamp_ms"; - public static final String EXTRA_RESPONSE_TIMESTAMP_MS = "extra_response_timestamp_ms"; - - private static final String PERMISSION_ACCESS_NETWORK_CONDITIONS = - "android.permission.ACCESS_NETWORK_CONDITIONS"; - // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED. // The network should be used as a default internet connection. It was found to be: // 1. a functioning network providing internet access, or @@ -1136,7 +1120,8 @@ public class NetworkMonitor extends StateMachine { return; } - Intent latencyBroadcast = new Intent(ACTION_NETWORK_CONDITIONS_MEASURED); + Intent latencyBroadcast = + new Intent(ConnectivityConstants.ACTION_NETWORK_CONDITIONS_MEASURED); switch (mNetworkAgentInfo.networkInfo.getType()) { case ConnectivityManager.TYPE_WIFI: WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo(); @@ -1148,15 +1133,18 @@ public class NetworkMonitor extends StateMachine { // not change it here as it would become impossible to tell whether the SSID is // simply being surrounded by quotes due to the API, or whether those quotes // are actually part of the SSID. - latencyBroadcast.putExtra(EXTRA_SSID, currentWifiInfo.getSSID()); - latencyBroadcast.putExtra(EXTRA_BSSID, currentWifiInfo.getBSSID()); + latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_SSID, + currentWifiInfo.getSSID()); + latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_BSSID, + currentWifiInfo.getBSSID()); } else { if (VDBG) logw("network info is TYPE_WIFI but no ConnectionInfo found"); return; } break; case ConnectivityManager.TYPE_MOBILE: - latencyBroadcast.putExtra(EXTRA_NETWORK_TYPE, mTelephonyManager.getNetworkType()); + latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_NETWORK_TYPE, + mTelephonyManager.getNetworkType()); List<CellInfo> info = mTelephonyManager.getAllCellInfo(); if (info == null) return; int numRegisteredCellInfo = 0; @@ -1170,16 +1158,16 @@ public class NetworkMonitor extends StateMachine { } if (cellInfo instanceof CellInfoCdma) { CellIdentityCdma cellId = ((CellInfoCdma) cellInfo).getCellIdentity(); - latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId); + latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId); } else if (cellInfo instanceof CellInfoGsm) { CellIdentityGsm cellId = ((CellInfoGsm) cellInfo).getCellIdentity(); - latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId); + latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId); } else if (cellInfo instanceof CellInfoLte) { CellIdentityLte cellId = ((CellInfoLte) cellInfo).getCellIdentity(); - latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId); + latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId); } else if (cellInfo instanceof CellInfoWcdma) { CellIdentityWcdma cellId = ((CellInfoWcdma) cellInfo).getCellIdentity(); - latencyBroadcast.putExtra(EXTRA_CELL_ID, cellId); + latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CELL_ID, cellId); } else { if (VDBG) logw("Registered cellinfo is unrecognized"); return; @@ -1190,16 +1178,21 @@ public class NetworkMonitor extends StateMachine { default: return; } - latencyBroadcast.putExtra(EXTRA_CONNECTIVITY_TYPE, mNetworkAgentInfo.networkInfo.getType()); - latencyBroadcast.putExtra(EXTRA_RESPONSE_RECEIVED, responseReceived); - latencyBroadcast.putExtra(EXTRA_REQUEST_TIMESTAMP_MS, requestTimestampMs); + latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_CONNECTIVITY_TYPE, + mNetworkAgentInfo.networkInfo.getType()); + latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_RESPONSE_RECEIVED, + responseReceived); + latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_REQUEST_TIMESTAMP_MS, + requestTimestampMs); if (responseReceived) { - latencyBroadcast.putExtra(EXTRA_IS_CAPTIVE_PORTAL, isCaptivePortal); - latencyBroadcast.putExtra(EXTRA_RESPONSE_TIMESTAMP_MS, responseTimestampMs); + latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_IS_CAPTIVE_PORTAL, + isCaptivePortal); + latencyBroadcast.putExtra(ConnectivityConstants.EXTRA_RESPONSE_TIMESTAMP_MS, + responseTimestampMs); } mContext.sendBroadcastAsUser(latencyBroadcast, UserHandle.CURRENT, - PERMISSION_ACCESS_NETWORK_CONDITIONS); + ConnectivityConstants.PERMISSION_ACCESS_NETWORK_CONDITIONS); } private void logNetworkEvent(int evtype) { diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java index d56fb1ab09f9..3a27fcb352aa 100644 --- a/services/core/java/com/android/server/connectivity/PacManager.java +++ b/services/core/java/com/android/server/connectivity/PacManager.java @@ -54,12 +54,12 @@ import java.net.URLConnection; * @hide */ public class PacManager { - public static final String PAC_PACKAGE = "com.android.pacprocessor"; - public static final String PAC_SERVICE = "com.android.pacprocessor.PacService"; - public static final String PAC_SERVICE_NAME = "com.android.net.IProxyService"; + private static final String PAC_PACKAGE = "com.android.pacprocessor"; + private static final String PAC_SERVICE = "com.android.pacprocessor.PacService"; + private static final String PAC_SERVICE_NAME = "com.android.net.IProxyService"; - public static final String PROXY_PACKAGE = "com.android.proxyhandler"; - public static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService"; + private static final String PROXY_PACKAGE = "com.android.proxyhandler"; + private static final String PROXY_SERVICE = "com.android.proxyhandler.ProxyService"; private static final String TAG = "PacManager"; @@ -71,8 +71,6 @@ public class PacManager { private static final int DELAY_LONG = 4; private static final long MAX_PAC_SIZE = 20 * 1000 * 1000; - /** Keep these values up-to-date with ProxyService.java */ - public static final String KEY_PROXY = "keyProxy"; private String mCurrentPac; @GuardedBy("mProxyLock") private volatile Uri mPacUrl = Uri.EMPTY; diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index aa174e3ad715..bb46d5e23696 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -164,19 +164,6 @@ public class Vpn { private boolean mLockdown = false; /** - * List of UIDs that are set to use this VPN by default. Normally, every UID in the user is - * added to this set but that can be changed by adding allowed or disallowed applications. It - * is non-null iff the VPN is connected. - * - * Unless the VPN has set allowBypass=true, these UIDs are forced into the VPN. - * - * @see VpnService.Builder#addAllowedApplication(String) - * @see VpnService.Builder#addDisallowedApplication(String) - */ - @GuardedBy("this") - private Set<UidRange> mVpnUsers = null; - - /** * List of UIDs for which networking should be blocked until VPN is ready, during brief periods * when VPN is not running. For example, during system startup or after a crash. * @see mLockdown @@ -688,7 +675,7 @@ public class Vpn { agentDisconnect(); jniReset(mInterface); mInterface = null; - mVpnUsers = null; + mNetworkCapabilities.setUids(null); } // Revoke the connection or stop LegacyVpnRunner. @@ -857,10 +844,14 @@ public class Vpn { NetworkMisc networkMisc = new NetworkMisc(); networkMisc.allowBypass = mConfig.allowBypass && !mLockdown; + mNetworkCapabilities.setEstablishingVpnAppUid(Binder.getCallingUid()); + mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserHandle, + mConfig.allowedApplications, mConfig.disallowedApplications)); long token = Binder.clearCallingIdentity(); try { mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE /* logtag */, - mNetworkInfo, mNetworkCapabilities, lp, 0 /* score */, networkMisc) { + mNetworkInfo, mNetworkCapabilities, lp, + ConnectivityConstants.VPN_DEFAULT_SCORE, networkMisc) { @Override public void unwanted() { // We are user controlled, not driven by NetworkRequest. @@ -869,11 +860,6 @@ public class Vpn { } finally { Binder.restoreCallingIdentity(token); } - - mVpnUsers = createUserAndRestrictedProfilesRanges(mUserHandle, - mConfig.allowedApplications, mConfig.disallowedApplications); - mNetworkAgent.addUidRanges(mVpnUsers.toArray(new UidRange[mVpnUsers.size()])); - mNetworkInfo.setIsAvailable(true); updateState(DetailedState.CONNECTED, "agentConnect"); } @@ -953,7 +939,7 @@ public class Vpn { Connection oldConnection = mConnection; NetworkAgent oldNetworkAgent = mNetworkAgent; mNetworkAgent = null; - Set<UidRange> oldUsers = mVpnUsers; + Set<UidRange> oldUsers = mNetworkCapabilities.getUids(); // Configure the interface. Abort if any of these steps fails. ParcelFileDescriptor tun = ParcelFileDescriptor.adoptFd(jniCreate(config.mtu)); @@ -1011,7 +997,7 @@ public class Vpn { // restore old state mConfig = oldConfig; mConnection = oldConnection; - mVpnUsers = oldUsers; + mNetworkCapabilities.setUids(oldUsers); mNetworkAgent = oldNetworkAgent; mInterface = oldInterface; throw e; @@ -1131,10 +1117,12 @@ public class Vpn { // Returns the subset of the full list of active UID ranges the VPN applies to (mVpnUsers) that // apply to userHandle. - private List<UidRange> uidRangesForUser(int userHandle) { + static private List<UidRange> uidRangesForUser(int userHandle, Set<UidRange> existingRanges) { + // UidRange#createForUser returns the entire range of UIDs available to a macro-user. + // This is something like 0-99999 ; {@see UserHandle#PER_USER_RANGE} final UidRange userRange = UidRange.createForUser(userHandle); final List<UidRange> ranges = new ArrayList<UidRange>(); - for (UidRange range : mVpnUsers) { + for (UidRange range : existingRanges) { if (userRange.containsRange(range)) { ranges.add(range); } @@ -1142,30 +1130,18 @@ public class Vpn { return ranges; } - private void removeVpnUserLocked(int userHandle) { - if (mVpnUsers == null) { - throw new IllegalStateException("VPN is not active"); - } - final List<UidRange> ranges = uidRangesForUser(userHandle); - if (mNetworkAgent != null) { - mNetworkAgent.removeUidRanges(ranges.toArray(new UidRange[ranges.size()])); - } - mVpnUsers.removeAll(ranges); - } - public void onUserAdded(int userHandle) { // If the user is restricted tie them to the parent user's VPN UserInfo user = UserManager.get(mContext).getUserInfo(userHandle); if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) { synchronized(Vpn.this) { - if (mVpnUsers != null) { + final Set<UidRange> existingRanges = mNetworkCapabilities.getUids(); + if (existingRanges != null) { try { - addUserToRanges(mVpnUsers, userHandle, mConfig.allowedApplications, + addUserToRanges(existingRanges, userHandle, mConfig.allowedApplications, mConfig.disallowedApplications); - if (mNetworkAgent != null) { - final List<UidRange> ranges = uidRangesForUser(userHandle); - mNetworkAgent.addUidRanges(ranges.toArray(new UidRange[ranges.size()])); - } + mNetworkCapabilities.setUids(existingRanges); + updateCapabilities(); } catch (Exception e) { Log.wtf(TAG, "Failed to add restricted user to owner", e); } @@ -1180,9 +1156,14 @@ public class Vpn { UserInfo user = UserManager.get(mContext).getUserInfo(userHandle); if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) { synchronized(Vpn.this) { - if (mVpnUsers != null) { + final Set<UidRange> existingRanges = mNetworkCapabilities.getUids(); + if (existingRanges != null) { try { - removeVpnUserLocked(userHandle); + final List<UidRange> removedRanges = + uidRangesForUser(userHandle, existingRanges); + existingRanges.removeAll(removedRanges); + mNetworkCapabilities.setUids(existingRanges); + updateCapabilities(); } catch (Exception e) { Log.wtf(TAG, "Failed to remove restricted user to owner", e); } @@ -1226,15 +1207,6 @@ public class Vpn { private void setVpnForcedLocked(boolean enforce) { final List<String> exemptedPackages = isNullOrLegacyVpn(mPackage) ? null : Collections.singletonList(mPackage); - setVpnForcedWithExemptionsLocked(enforce, exemptedPackages); - } - - /** - * @see #setVpnForcedLocked - */ - @GuardedBy("this") - private void setVpnForcedWithExemptionsLocked(boolean enforce, - @Nullable List<String> exemptedPackages) { final Set<UidRange> removedRanges = new ArraySet<>(mBlockedUsers); Set<UidRange> addedRanges = Collections.emptySet(); @@ -1314,7 +1286,7 @@ public class Vpn { synchronized (Vpn.this) { if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) { mStatusIntent = null; - mVpnUsers = null; + mNetworkCapabilities.setUids(null); mConfig = null; mInterface = null; if (mConnection != null) { @@ -1433,12 +1405,7 @@ public class Vpn { if (!isRunningLocked()) { return false; } - for (UidRange uidRange : mVpnUsers) { - if (uidRange.contains(uid)) { - return true; - } - } - return false; + return mNetworkCapabilities.appliesToUid(uid); } /** diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java index acbc10b9dc43..09bce7f4feb6 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java @@ -28,6 +28,8 @@ import android.net.ConnectivityManager; import android.telephony.TelephonyManager; import android.net.util.SharedLog; +import com.android.internal.annotations.VisibleForTesting; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -49,6 +51,7 @@ import java.util.StringJoiner; public class TetheringConfiguration { private static final String TAG = TetheringConfiguration.class.getSimpleName(); + @VisibleForTesting public static final int DUN_NOT_REQUIRED = 0; public static final int DUN_REQUIRED = 1; public static final int DUN_UNSPECIFIED = 2; diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java index 5a3a8be923af..984c9f817d75 100644 --- a/services/net/java/android/net/util/NetworkConstants.java +++ b/services/net/java/android/net/util/NetworkConstants.java @@ -121,6 +121,14 @@ public final class NetworkConstants { public static final int ICMP_ECHO_DATA_OFFSET = 8; /** + * ICMPv4 constants. + * + * See also: + * - https://tools.ietf.org/html/rfc792 + */ + public static final int ICMPV4_ECHO_REQUEST_TYPE = 8; + + /** * ICMPv6 constants. * * See also: @@ -139,6 +147,8 @@ public final class NetworkConstants { public static final int ICMPV6_ND_OPTION_TLLA = 2; public static final int ICMPV6_ND_OPTION_MTU = 5; + public static final int ICMPV6_ECHO_REQUEST_TYPE = 128; + /** * UDP constants. * @@ -157,6 +167,14 @@ public final class NetworkConstants { public static final int DHCP4_CLIENT_PORT = 68; /** + * DNS constants. + * + * See also: + * - https://tools.ietf.org/html/rfc1035 + */ + public static final int DNS_SERVER_PORT = 53; + + /** * Utility functions. */ public static byte asByte(int i) { return (byte) i; } diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java index cd2d0985c793..2e1519b8717b 100644 --- a/tests/net/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java @@ -33,12 +33,15 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import android.os.Parcel; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; +import android.util.ArraySet; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Set; @RunWith(AndroidJUnit4.class) @SmallTest @@ -180,4 +183,84 @@ public class NetworkCapabilitiesTest { assertEquals(20, NetworkCapabilities .maxBandwidth(10, 20)); } + + @Test + public void testSetUids() { + final NetworkCapabilities netCap = new NetworkCapabilities(); + final Set<UidRange> uids = new ArraySet<>(); + uids.add(new UidRange(50, 100)); + uids.add(new UidRange(3000, 4000)); + netCap.setUids(uids); + assertTrue(netCap.appliesToUid(50)); + assertTrue(netCap.appliesToUid(80)); + assertTrue(netCap.appliesToUid(100)); + assertTrue(netCap.appliesToUid(3000)); + assertTrue(netCap.appliesToUid(3001)); + assertFalse(netCap.appliesToUid(10)); + assertFalse(netCap.appliesToUid(25)); + assertFalse(netCap.appliesToUid(49)); + assertFalse(netCap.appliesToUid(101)); + assertFalse(netCap.appliesToUid(2000)); + assertFalse(netCap.appliesToUid(100000)); + + assertTrue(netCap.appliesToUidRange(new UidRange(50, 100))); + assertTrue(netCap.appliesToUidRange(new UidRange(70, 72))); + assertTrue(netCap.appliesToUidRange(new UidRange(3500, 3912))); + assertFalse(netCap.appliesToUidRange(new UidRange(1, 100))); + assertFalse(netCap.appliesToUidRange(new UidRange(49, 100))); + assertFalse(netCap.appliesToUidRange(new UidRange(1, 10))); + assertFalse(netCap.appliesToUidRange(new UidRange(60, 101))); + assertFalse(netCap.appliesToUidRange(new UidRange(60, 3400))); + + NetworkCapabilities netCap2 = new NetworkCapabilities(); + assertFalse(netCap2.satisfiedByUids(netCap)); + assertFalse(netCap2.equalsUids(netCap)); + netCap2.setUids(uids); + assertTrue(netCap2.satisfiedByUids(netCap)); + assertTrue(netCap.equalsUids(netCap2)); + assertTrue(netCap2.equalsUids(netCap)); + + uids.add(new UidRange(600, 700)); + netCap2.setUids(uids); + assertFalse(netCap2.satisfiedByUids(netCap)); + assertFalse(netCap.appliesToUid(650)); + assertTrue(netCap2.appliesToUid(650)); + netCap.combineCapabilities(netCap2); + assertTrue(netCap2.satisfiedByUids(netCap)); + assertTrue(netCap.appliesToUid(650)); + assertFalse(netCap.appliesToUid(500)); + + assertFalse(new NetworkCapabilities().satisfiedByUids(netCap)); + netCap.combineCapabilities(new NetworkCapabilities()); + assertTrue(netCap.appliesToUid(500)); + assertTrue(netCap.appliesToUidRange(new UidRange(1, 100000))); + assertFalse(netCap2.appliesToUid(500)); + assertFalse(netCap2.appliesToUidRange(new UidRange(1, 100000))); + assertTrue(new NetworkCapabilities().satisfiedByUids(netCap)); + } + + @Test + public void testParcelNetworkCapabilities() { + final Set<UidRange> uids = new ArraySet<>(); + uids.add(new UidRange(50, 100)); + uids.add(new UidRange(3000, 4000)); + final NetworkCapabilities netCap = new NetworkCapabilities() + .addCapability(NET_CAPABILITY_INTERNET) + .setUids(uids) + .addCapability(NET_CAPABILITY_EIMS) + .addCapability(NET_CAPABILITY_NOT_METERED); + assertEqualsThroughMarshalling(netCap); + } + + private void assertEqualsThroughMarshalling(NetworkCapabilities netCap) { + Parcel p = Parcel.obtain(); + netCap.writeToParcel(p, /* flags */ 0); + p.setDataPosition(0); + byte[] marshalledData = p.marshall(); + + p = Parcel.obtain(); + p.unmarshall(marshalledData, 0, marshalledData.length); + p.setDataPosition(0); + assertEquals(NetworkCapabilities.CREATOR.createFromParcel(p), netCap); + } } diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 70cacb3af009..6e643a3dae12 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -44,6 +44,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P; import static android.net.NetworkCapabilities.NET_CAPABILITY_XCAP; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; +import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE; @@ -101,6 +102,7 @@ import android.net.NetworkSpecifier; import android.net.NetworkUtils; import android.net.RouteInfo; import android.net.StringNetworkSpecifier; +import android.net.UidRange; import android.net.metrics.IpConnectivityLog; import android.net.util.MultinetworkPolicyTracker; import android.os.ConditionVariable; @@ -126,11 +128,13 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.WakeupMessage; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.FakeSettingsProvider; +import com.android.server.connectivity.ConnectivityConstants; import com.android.server.connectivity.DefaultNetworkMetrics; import com.android.server.connectivity.IpConnectivityMetrics; import com.android.server.connectivity.MockableSystemProperties; import com.android.server.connectivity.NetworkAgentInfo; import com.android.server.connectivity.NetworkMonitor; +import com.android.server.connectivity.Vpn; import com.android.server.net.NetworkPinner; import com.android.server.net.NetworkPolicyManagerInternal; @@ -360,7 +364,7 @@ public class ConnectivityServiceTest { MockNetworkAgent(int transport, LinkProperties linkProperties) { final int type = transportToLegacyType(transport); - final String typeName = ConnectivityManager.getNetworkTypeName(type); + final String typeName = ConnectivityManager.getNetworkTypeName(transport); mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock"); mNetworkCapabilities = new NetworkCapabilities(); mNetworkCapabilities.addTransportType(transport); @@ -377,6 +381,9 @@ public class ConnectivityServiceTest { case TRANSPORT_WIFI_AWARE: mScore = 20; break; + case TRANSPORT_VPN: + mScore = ConnectivityConstants.VPN_DEFAULT_SCORE; + break; default: throw new UnsupportedOperationException("unimplemented network type"); } @@ -438,6 +445,11 @@ public class ConnectivityServiceTest { mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); } + public void setUids(Set<UidRange> uids) { + mNetworkCapabilities.setUids(uids); + mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); + } + public void setSignalStrength(int signalStrength) { mNetworkCapabilities.setSignalStrength(signalStrength); mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities); @@ -1463,6 +1475,11 @@ public class ConnectivityServiceTest { return nc; } + void expectCapabilitiesLike(Predicate<NetworkCapabilities> fn, MockNetworkAgent agent) { + CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent); + assertTrue(fn.test((NetworkCapabilities) cbi.arg)); + } + void assertNoCallback() { waitForIdle(); CallbackInfo c = mCallbacks.peek(); @@ -3625,4 +3642,76 @@ public class ConnectivityServiceTest { return; } } + + @Test + public void testVpnNetworkActive() { + final int uid = Process.myUid(); + + final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback(); + final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback(); + final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback(); + final NetworkRequest genericRequest = new NetworkRequest.Builder().build(); + final NetworkRequest wifiRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_WIFI).build(); + final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder() + .addTransportType(TRANSPORT_VPN).build(); + mCm.registerNetworkCallback(genericRequest, genericNetworkCallback); + mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback); + mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback); + + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + mWiFiNetworkAgent.connect(false); + + genericNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + vpnNetworkCallback.assertNoCallback(); + + // TODO : check callbacks agree with the return value of mCm.getActiveNetwork(). + // Right now this is not possible because establish() is not adequately instrumented + // in this test. + + final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN); + final ArraySet<UidRange> ranges = new ArraySet<>(); + ranges.add(new UidRange(uid, uid)); + vpnNetworkAgent.setUids(ranges); + vpnNetworkAgent.connect(false); + + genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent); + wifiNetworkCallback.assertNoCallback(); + vpnNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent); + + genericNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, vpnNetworkAgent); + vpnNetworkCallback.expectCapabilitiesLike( + nc -> nc.appliesToUid(uid) && !nc.appliesToUid(uid + 1), vpnNetworkAgent); + + ranges.clear(); + vpnNetworkAgent.setUids(ranges); + + genericNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent); + wifiNetworkCallback.assertNoCallback(); + vpnNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent); + + ranges.add(new UidRange(uid, uid)); + vpnNetworkAgent.setUids(ranges); + + genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent); + wifiNetworkCallback.assertNoCallback(); + vpnNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent); + + mWiFiNetworkAgent.disconnect(); + + genericNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); + wifiNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); + vpnNetworkCallback.assertNoCallback(); + + vpnNetworkAgent.disconnect(); + + genericNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent); + wifiNetworkCallback.assertNoCallback(); + vpnNetworkCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent); + + mCm.unregisterNetworkCallback(genericNetworkCallback); + mCm.unregisterNetworkCallback(wifiNetworkCallback); + mCm.unregisterNetworkCallback(vpnNetworkCallback); + } } |