Merge "Add visibility of framework-connectivity.impl for wifi test" into sc-dev
diff --git a/framework/Android.bp b/framework/Android.bp
index 90b8d03..5f5ebf4 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -23,26 +23,6 @@
default_applicable_licenses: ["frameworks_base_license"],
}
-java_library {
- name: "framework-connectivity-protos",
- sdk_version: "module_current",
- min_sdk_version: "30",
- proto: {
- type: "nano",
- },
- srcs: [
- // TODO: consider moving relevant .proto files directly to the module directory
- ":framework-javastream-protos",
- ],
- apex_available: [
- "com.android.tethering",
- ],
- jarjar_rules: "jarjar-rules-proto.txt",
- visibility: [
- "//visibility:private",
- ],
-}
-
filegroup {
name: "framework-connectivity-internal-sources",
srcs: [
@@ -109,9 +89,6 @@
libs: [
"unsupportedappusage",
],
- static_libs: [
- "framework-connectivity-protos",
- ],
jarjar_rules: "jarjar-rules.txt",
permitted_packages: ["android.net"],
impl_library_visibility: [
@@ -138,8 +115,9 @@
],
}
-cc_defaults {
- name: "libframework-connectivity-defaults",
+cc_library_shared {
+ name: "libframework-connectivity-jni",
+ min_sdk_version: "30",
cflags: [
"-Wall",
"-Werror",
@@ -150,38 +128,19 @@
"-Wno-unguarded-availability",
"-Wthread-safety",
],
+ srcs: [
+ "jni/android_net_NetworkUtils.cpp",
+ "jni/onload.cpp",
+ ],
shared_libs: [
+ "libandroid",
"liblog",
"libnativehelper",
],
header_libs: [
"dnsproxyd_protocol_headers",
],
-}
-
-cc_library_static {
- name: "libconnectivityframeworkutils",
- defaults: ["libframework-connectivity-defaults"],
- srcs: [
- "jni/android_net_NetworkUtils.cpp",
- ],
- shared_libs: ["libandroid_net"],
- apex_available: [
- "//apex_available:platform",
- "com.android.tethering",
- ],
-}
-
-cc_library_shared {
- name: "libframework-connectivity-jni",
- min_sdk_version: "30",
- defaults: ["libframework-connectivity-defaults"],
- srcs: [
- "jni/android_net_NetworkUtils.cpp",
- "jni/onload.cpp",
- ],
- shared_libs: ["libandroid"],
- stl: "libc++_static",
+ stl: "none",
apex_available: [
"com.android.tethering",
],
diff --git a/framework/aidl-export/android/net/IpPrefix.aidl b/framework/aidl-export/android/net/IpPrefix.aidl
index 0d70f2a..3495efc 100644
--- a/framework/aidl-export/android/net/IpPrefix.aidl
+++ b/framework/aidl-export/android/net/IpPrefix.aidl
@@ -18,5 +18,5 @@
package android.net;
// @JavaOnlyStableParcelable only affects the parcelable when built as stable aidl (aidl_interface
-// build rule). IpPrefix is also used in cpp but only as non-stable aidl.
-@JavaOnlyStableParcelable parcelable IpPrefix cpp_header "binder/IpPrefix.h";
+// build rule).
+@JavaOnlyStableParcelable parcelable IpPrefix;
diff --git a/framework/api/current.txt b/framework/api/current.txt
index 7428c6e..9c77c85 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -298,7 +298,6 @@
method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier();
method public int getOwnerUid();
method public int getSignalStrength();
- method @NonNull public java.util.Set<java.lang.Integer> getSubIds();
method @Nullable public android.net.TransportInfo getTransportInfo();
method public boolean hasCapability(int);
method public boolean hasTransport(int);
@@ -410,7 +409,6 @@
method public android.net.NetworkRequest.Builder removeTransportType(int);
method @Deprecated public android.net.NetworkRequest.Builder setNetworkSpecifier(String);
method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
- method @NonNull public android.net.NetworkRequest.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>);
}
public class ParseException extends java.lang.RuntimeException {
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index 6c3b620..847bcbc 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -232,6 +232,7 @@
method @NonNull public android.net.Network register();
method public final void sendLinkProperties(@NonNull android.net.LinkProperties);
method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
+ method public final void sendNetworkScore(@NonNull android.net.NetworkScore);
method public final void sendNetworkScore(@IntRange(from=0, to=99) int);
method public final void sendQosCallbackError(int, int);
method public final void sendQosSessionAvailable(int, int, @NonNull android.net.QosSessionAttributes);
@@ -275,6 +276,7 @@
method @NonNull public int[] getAdministratorUids();
method @Nullable public static String getCapabilityCarrierName(int);
method @Nullable public String getSsid();
+ method @NonNull public java.util.Set<java.lang.Integer> getSubIds();
method @NonNull public int[] getTransportTypes();
method public boolean isPrivateDnsBroken();
method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
@@ -335,12 +337,15 @@
public static class NetworkRequest.Builder {
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int);
+ method @NonNull public android.net.NetworkRequest.Builder setSubIds(@NonNull java.util.Set<java.lang.Integer>);
}
public final class NetworkScore implements android.os.Parcelable {
method public int describeContents();
method public int getKeepConnectedReason();
method public int getLegacyInt();
+ method public boolean isExiting();
+ method public boolean isTransportPrimary();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkScore> CREATOR;
field public static final int KEEP_CONNECTED_FOR_HANDOVER = 1; // 0x1
@@ -350,8 +355,10 @@
public static final class NetworkScore.Builder {
ctor public NetworkScore.Builder();
method @NonNull public android.net.NetworkScore build();
+ method @NonNull public android.net.NetworkScore.Builder setExiting(boolean);
method @NonNull public android.net.NetworkScore.Builder setKeepConnectedReason(int);
method @NonNull public android.net.NetworkScore.Builder setLegacyInt(int);
+ method @NonNull public android.net.NetworkScore.Builder setTransportPrimary(boolean);
}
public final class OemNetworkPreferences implements android.os.Parcelable {
diff --git a/framework/jarjar-rules-proto.txt b/framework/jarjar-rules-proto.txt
deleted file mode 100644
index 37b4dec..0000000
--- a/framework/jarjar-rules-proto.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-keep android.net.NetworkCapabilitiesProto
-keep android.net.NetworkProto
-keep android.net.NetworkRequestProto
diff --git a/framework/jarjar-rules.txt b/framework/jarjar-rules.txt
index 7474c24..2e5848c 100644
--- a/framework/jarjar-rules.txt
+++ b/framework/jarjar-rules.txt
@@ -1,11 +1,2 @@
rule com.android.net.module.util.** android.net.connectivity.framework.util.@1
rule android.net.NetworkFactory* android.net.connectivity.framework.NetworkFactory@1
-
-# TODO (b/149403767): remove the annotations from net-utils-device-common instead of here
-zap android.annotation.**
-zap com.android.net.module.annotation.**
-zap com.android.internal.annotations.**
-
-rule android.net.NetworkCapabilitiesProto* android.net.connectivity.proto.NetworkCapabilitiesProto@1
-rule android.net.NetworkProto* android.net.connectivity.proto.NetworkProto@1
-rule android.net.NetworkRequestProto* android.net.connectivity.proto.NetworkRequestProto@1
diff --git a/framework/jni/android_net_NetworkUtils.cpp b/framework/jni/android_net_NetworkUtils.cpp
index e8bb42d..9bf910b 100644
--- a/framework/jni/android_net_NetworkUtils.cpp
+++ b/framework/jni/android_net_NetworkUtils.cpp
@@ -16,34 +16,19 @@
#define LOG_TAG "NetworkUtils"
-#include <vector>
-
#include <android/file_descriptor_jni.h>
#include <android/multinetwork.h>
-#include <arpa/inet.h>
#include <linux/filter.h>
-#include <linux/if_arp.h>
#include <linux/tcp.h>
-#include <net/if.h>
-#include <netinet/ether.h>
-#include <netinet/ip.h>
-#include <netinet/udp.h>
+#include <netinet/in.h>
+#include <string.h>
#include <DnsProxydProtocol.h> // NETID_USE_LOCAL_NAMESERVERS
-#include <cutils/properties.h>
-#include <nativehelper/JNIHelp.h>
#include <nativehelper/JNIPlatformHelp.h>
-#include <nativehelper/ScopedLocalRef.h>
#include <utils/Log.h>
-#include <utils/misc.h>
#include "jni.h"
-extern "C" {
-int ifc_enable(const char *ifname);
-int ifc_disable(const char *ifname);
-}
-
#define NETUTILS_PKG_NAME "android/net/NetworkUtils"
namespace android {
@@ -52,6 +37,9 @@
// FrameworkListener limits the size of commands to 4096 bytes.
constexpr int MAXCMDSIZE = 4096;
+static volatile jclass class_Network = 0;
+static volatile jmethodID method_fromNetworkHandle = 0;
+
static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) {
jclass clazz = env->FindClass(class_name);
LOG_ALWAYS_FATAL_IF(clazz == NULL, "Unable to find class %s", class_name);
@@ -138,11 +126,11 @@
// Only allow dname which could be simply formatted to UTF8.
// In native layer, res_mkquery would re-format the input char array to packet.
- std::vector<char> queryname(byteCountUTF8 + 1, 0);
+ char queryname[byteCountUTF8 + 1];
+ memset(queryname, 0, (byteCountUTF8 + 1) * sizeof(char));
- env->GetStringUTFRegion(dname, 0, javaCharsCount, queryname.data());
-
- int fd = android_res_nquery(netHandle, queryname.data(), ns_class, ns_type, flags);
+ env->GetStringUTFRegion(dname, 0, javaCharsCount, queryname);
+ int fd = android_res_nquery(netHandle, queryname, ns_class, ns_type, flags);
if (fd < 0) {
jniThrowErrnoException(env, "resNetworkQuery", -fd);
@@ -170,9 +158,9 @@
static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, jobject javaFd) {
int fd = AFileDescriptor_getFD(env, javaFd);
int rcode;
- std::vector<uint8_t> buf(MAXPACKETSIZE, 0);
+ uint8_t buf[MAXPACKETSIZE] = {0};
- int res = android_res_nresult(fd, &rcode, buf.data(), MAXPACKETSIZE);
+ int res = android_res_nresult(fd, &rcode, buf, MAXPACKETSIZE);
jniSetFileDescriptorOfFD(env, javaFd, -1);
if (res < 0) {
jniThrowErrnoException(env, "resNetworkResult", -res);
@@ -184,8 +172,7 @@
jniThrowErrnoException(env, "resNetworkResult", ENOMEM);
return nullptr;
} else {
- env->SetByteArrayRegion(answer, 0, res,
- reinterpret_cast<jbyte*>(buf.data()));
+ env->SetByteArrayRegion(answer, 0, res, reinterpret_cast<jbyte*>(buf));
}
jclass class_DnsResponse = env->FindClass("android/net/DnsResolver$DnsResponse");
@@ -207,11 +194,14 @@
return nullptr;
}
- static jclass class_Network = MakeGlobalRefOrDie(
- env, FindClassOrDie(env, "android/net/Network"));
- static jmethodID method = env->GetStaticMethodID(class_Network, "fromNetworkHandle",
- "(J)Landroid/net/Network;");
- return env->CallStaticObjectMethod(class_Network, method, static_cast<jlong>(dnsNetHandle));
+ if (method_fromNetworkHandle == 0) {
+ // This may be called multiple times concurrently but that is fine
+ class_Network = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/net/Network"));
+ method_fromNetworkHandle = env->GetStaticMethodID(class_Network, "fromNetworkHandle",
+ "(J)Landroid/net/Network;");
+ }
+ return env->CallStaticObjectMethod(class_Network, method_fromNetworkHandle,
+ static_cast<jlong>(dnsNetHandle));
}
static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) {
diff --git a/framework/src/android/net/NetworkAgent.java b/framework/src/android/net/NetworkAgent.java
index c57da53..3622c1c 100644
--- a/framework/src/android/net/NetworkAgent.java
+++ b/framework/src/android/net/NetworkAgent.java
@@ -956,7 +956,6 @@
* Must be called by the agent to update the score of this network.
*
* @param score the new score.
- * @hide TODO : unhide when impl is complete
*/
public final void sendNetworkScore(@NonNull NetworkScore score) {
Objects.requireNonNull(score);
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index 4f95ccc..a54696f 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -2351,9 +2351,15 @@
/**
* Gets the subscription ID set that associated to this network or request.
+ *
+ * <p>Instances of NetworkCapabilities will only have this field populated by the system if the
+ * receiver holds the NETWORK_FACTORY permission. In all other cases, it will be the empty set.
+ *
* @return
+ * @hide
*/
@NonNull
+ @SystemApi
public Set<Integer> getSubIds() {
return new ArraySet<>(mSubIds);
}
@@ -2718,10 +2724,17 @@
/**
* Set the subscription ID set.
*
+ * <p>SubIds are populated in NetworkCapability instances from the system only for callers
+ * that hold the NETWORK_FACTORY permission. Similarly, the system will reject any
+ * NetworkRequests filed with a non-empty set of subIds unless the caller holds the
+ * NETWORK_FACTORY permission.
+ *
* @param subIds a set that represent the subscription IDs. Empty if clean up.
* @return this builder.
+ * @hide
*/
@NonNull
+ @SystemApi
public Builder setSubIds(@NonNull final Set<Integer> subIds) {
mCaps.setSubIds(subIds);
return this;
diff --git a/framework/src/android/net/NetworkRequest.java b/framework/src/android/net/NetworkRequest.java
index 5313f08..78e1011 100644
--- a/framework/src/android/net/NetworkRequest.java
+++ b/framework/src/android/net/NetworkRequest.java
@@ -500,9 +500,14 @@
* A network will satisfy this request only if it matches one of the subIds in this set.
* An empty set matches all networks, including those without a subId.
*
+ * <p>Registering a NetworkRequest with a non-empty set of subIds requires the
+ * NETWORK_FACTORY permission.
+ *
* @param subIds A {@code Set} that represents subscription IDs.
+ * @hide
*/
@NonNull
+ @SystemApi
public Builder setSubIds(@NonNull Set<Integer> subIds) {
mNetworkCapabilities.setSubIds(subIds);
return this;
@@ -655,25 +660,6 @@
", " + networkCapabilities.toString() + " ]";
}
- private int typeToProtoEnum(Type t) {
- switch (t) {
- case NONE:
- return NetworkRequestProto.TYPE_NONE;
- case LISTEN:
- return NetworkRequestProto.TYPE_LISTEN;
- case TRACK_DEFAULT:
- return NetworkRequestProto.TYPE_TRACK_DEFAULT;
- case REQUEST:
- return NetworkRequestProto.TYPE_REQUEST;
- case BACKGROUND_REQUEST:
- return NetworkRequestProto.TYPE_BACKGROUND_REQUEST;
- case TRACK_SYSTEM_DEFAULT:
- return NetworkRequestProto.TYPE_TRACK_SYSTEM_DEFAULT;
- default:
- return NetworkRequestProto.TYPE_UNKNOWN;
- }
- }
-
public boolean equals(@Nullable Object obj) {
if (obj instanceof NetworkRequest == false) return false;
NetworkRequest that = (NetworkRequest)obj;
diff --git a/framework/src/android/net/NetworkScore.java b/framework/src/android/net/NetworkScore.java
index 9786b09..0dee225 100644
--- a/framework/src/android/net/NetworkScore.java
+++ b/framework/src/android/net/NetworkScore.java
@@ -52,11 +52,27 @@
public static final int KEEP_CONNECTED_FOR_HANDOVER = 1;
// Agent-managed policies
- // TODO : add them here, starting from 1
+ // This network should lose to a wifi that has ever been validated
+ // NOTE : temporarily this policy is managed by ConnectivityService, because of legacy. The
+ // legacy design has this bit global to the system and tacked on WiFi which means it will affect
+ // networks from carriers who don't want it and non-carrier networks, which is bad for users.
+ // The S design has this on mobile networks only, so this can be fixed eventually ; as CS
+ // doesn't know what carriers need this bit, the initial S implementation will continue to
+ // affect other carriers but will at least leave non-mobile networks alone. Eventually Telephony
+ // should set this on networks from carriers that require it.
/** @hide */
- public static final int MIN_AGENT_MANAGED_POLICY = 0;
+ public static final int POLICY_YIELD_TO_BAD_WIFI = 1;
+ // This network is primary for this transport.
/** @hide */
- public static final int MAX_AGENT_MANAGED_POLICY = -1;
+ public static final int POLICY_TRANSPORT_PRIMARY = 2;
+ // This network is exiting : it will likely disconnect in a few seconds.
+ /** @hide */
+ public static final int POLICY_EXITING = 3;
+
+ /** @hide */
+ public static final int MIN_AGENT_MANAGED_POLICY = POLICY_YIELD_TO_BAD_WIFI;
+ /** @hide */
+ public static final int MAX_AGENT_MANAGED_POLICY = POLICY_EXITING;
// Bitmask of all the policies applied to this score.
private final long mPolicies;
@@ -98,9 +114,63 @@
return 0 != (mPolicies & (1L << policy));
}
+ /**
+ * To the exclusive usage of FullScore
+ * @hide
+ */
+ public long getPolicies() {
+ return mPolicies;
+ }
+
+ /**
+ * Whether this network should yield to a previously validated wifi gone bad.
+ *
+ * If this policy is set, other things being equal, the device will prefer a previously
+ * validated WiFi even if this network is validated and the WiFi is not.
+ * If this policy is not set, the device prefers the validated network.
+ *
+ * @hide
+ */
+ // TODO : Unhide this for telephony and have telephony call it on the relevant carriers.
+ // In the mean time this is handled by Connectivity in a backward-compatible manner.
+ public boolean shouldYieldToBadWifi() {
+ return hasPolicy(POLICY_YIELD_TO_BAD_WIFI);
+ }
+
+ /**
+ * Whether this network is primary for this transport.
+ *
+ * When multiple networks of the same transport are active, the device prefers the ones that
+ * are primary. This is meant in particular for DS-DA devices with a user setting to choose the
+ * default SIM card, or for WiFi STA+STA and make-before-break cases.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isTransportPrimary() {
+ return hasPolicy(POLICY_TRANSPORT_PRIMARY);
+ }
+
+ /**
+ * Whether this network is exiting.
+ *
+ * If this policy is set, the device will expect this network to disconnect within seconds.
+ * It will try to migrate to some other network if any is available, policy permitting, to
+ * avoid service disruption.
+ * This is useful in particular when a good cellular network is available and WiFi is getting
+ * weak and risks disconnecting soon. The WiFi network should be marked as exiting so that
+ * the device will prefer the reliable mobile network over this soon-to-be-lost WiFi.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isExiting() {
+ return hasPolicy(POLICY_EXITING);
+ }
+
@Override
public String toString() {
- return "Score(" + mLegacyInt + ")";
+ return "Score(" + mLegacyInt + " ; Policies : " + mPolicies + ")";
}
@Override
@@ -137,6 +207,7 @@
private static final int INVALID_LEGACY_INT = Integer.MIN_VALUE;
private int mLegacyInt = INVALID_LEGACY_INT;
private int mKeepConnectedReason = KEEP_CONNECTED_NONE;
+ private int mPolicies = 0;
/**
* Sets the legacy int for this score.
@@ -152,6 +223,75 @@
return this;
}
+
+ /**
+ * Set for a network that should never be preferred to a wifi that has ever been validated
+ *
+ * If this policy is set, other things being equal, the device will prefer a previously
+ * validated WiFi even if this network is validated and the WiFi is not.
+ * If this policy is not set, the device prefers the validated network.
+ *
+ * @return this builder
+ * @hide
+ */
+ // TODO : Unhide this for telephony and have telephony call it on the relevant carriers.
+ // In the mean time this is handled by Connectivity in a backward-compatible manner.
+ @NonNull
+ public Builder setShouldYieldToBadWifi(final boolean val) {
+ if (val) {
+ mPolicies |= (1L << POLICY_YIELD_TO_BAD_WIFI);
+ } else {
+ mPolicies &= ~(1L << POLICY_YIELD_TO_BAD_WIFI);
+ }
+ return this;
+ }
+
+ /**
+ * Set for a network that is primary for this transport.
+ *
+ * When multiple networks of the same transport are active, the device prefers the ones that
+ * are primary. This is meant in particular for DS-DA devices with a user setting to choose
+ * the default SIM card, or for WiFi STA+STA and make-before-break cases.
+ *
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ public Builder setTransportPrimary(final boolean val) {
+ if (val) {
+ mPolicies |= (1L << POLICY_TRANSPORT_PRIMARY);
+ } else {
+ mPolicies &= ~(1L << POLICY_TRANSPORT_PRIMARY);
+ }
+ return this;
+ }
+
+ /**
+ * Set for a network that will likely disconnect in a few seconds.
+ *
+ * If this policy is set, the device will expect this network to disconnect within seconds.
+ * It will try to migrate to some other network if any is available, policy permitting, to
+ * avoid service disruption.
+ * This is useful in particular when a good cellular network is available and WiFi is
+ * getting weak and risks disconnecting soon. The WiFi network should be marked as exiting
+ * so that the device will prefer the reliable mobile network over this soon-to-be-lost
+ * WiFi.
+ *
+ * @return this builder
+ * @hide
+ */
+ @SystemApi
+ @NonNull
+ public Builder setExiting(final boolean val) {
+ if (val) {
+ mPolicies |= (1L << POLICY_EXITING);
+ } else {
+ mPolicies &= ~(1L << POLICY_EXITING);
+ }
+ return this;
+ }
+
/**
* Set the keep-connected reason.
*
@@ -169,7 +309,7 @@
*/
@NonNull
public NetworkScore build() {
- return new NetworkScore(mLegacyInt, POLICY_NONE, mKeepConnectedReason);
+ return new NetworkScore(mLegacyInt, mPolicies, mKeepConnectedReason);
}
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f3e6428..2390589 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1329,7 +1329,7 @@
mLingerMonitor = new LingerMonitor(mContext, mNotifier, dailyLimit, rateLimit);
mMultinetworkPolicyTracker = mDeps.makeMultinetworkPolicyTracker(
- mContext, mHandler, () -> rematchForAvoidBadWifiUpdate());
+ mContext, mHandler, () -> updateAvoidBadWifi());
mMultinetworkPolicyTracker.start();
mDnsManager = new DnsManager(mContext, mDnsResolver);
@@ -1914,6 +1914,10 @@
newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact());
}
newNc.setAdministratorUids(new int[0]);
+ if (!checkAnyPermissionOf(
+ callerPid, callerUid, android.Manifest.permission.NETWORK_FACTORY)) {
+ newNc.setSubIds(Collections.emptySet());
+ }
return newNc;
}
@@ -4052,7 +4056,7 @@
}
}
}
- decrementRequestCount(nri);
+ nri.decrementRequestCount();
mNetworkRequestInfoLogs.log("RELEASE " + nri);
if (null != nri.getActiveRequest()) {
@@ -4163,14 +4167,6 @@
? mSystemNetworkRequestCounter : mNetworkRequestCounter;
}
- private void incrementRequestCountOrThrow(NetworkRequestInfo nri) {
- getRequestCounter(nri).incrementCountOrThrow(nri.mUid);
- }
-
- private void decrementRequestCount(NetworkRequestInfo nri) {
- getRequestCounter(nri).decrementCount(nri.mUid);
- }
-
@Override
public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
enforceNetworkStackSettingsOrSetup();
@@ -4389,8 +4385,10 @@
return avoidBadWifi();
}
- // TODO : this function is now useless.
- private void rematchForAvoidBadWifiUpdate() {
+ private void updateAvoidBadWifi() {
+ for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
+ nai.updateScoreForNetworkAgentConfigUpdate();
+ }
rematchAllNetworksAndRequests();
}
@@ -5456,6 +5454,9 @@
@Nullable
final String mCallingAttributionTag;
+ // Counter keeping track of this NRI.
+ final PerUidCounter mPerUidCounter;
+
// Effective UID of this request. This is different from mUid when a privileged process
// files a request on behalf of another UID. This UID is used to determine blocked status,
// UID matching, and so on. mUid above is used for permission checks and to enforce the
@@ -5506,7 +5507,8 @@
mPid = getCallingPid();
mUid = mDeps.getCallingUid();
mAsUid = asUid;
- incrementRequestCountOrThrow(this);
+ mPerUidCounter = getRequestCounter(this);
+ mPerUidCounter.incrementCountOrThrow(mUid);
/**
* Location sensitive data not included in pending intent. Only included in
* {@link NetworkCallback}.
@@ -5538,15 +5540,11 @@
mUid = mDeps.getCallingUid();
mAsUid = asUid;
mPendingIntent = null;
- incrementRequestCountOrThrow(this);
+ mPerUidCounter = getRequestCounter(this);
+ mPerUidCounter.incrementCountOrThrow(mUid);
mCallbackFlags = callbackFlags;
mCallingAttributionTag = callingAttributionTag;
-
- try {
- mBinder.linkToDeath(this, 0);
- } catch (RemoteException e) {
- binderDied();
- }
+ linkDeathRecipient();
}
NetworkRequestInfo(@NonNull final NetworkRequestInfo nri,
@@ -5581,9 +5579,11 @@
mUid = nri.mUid;
mAsUid = nri.mAsUid;
mPendingIntent = nri.mPendingIntent;
- incrementRequestCountOrThrow(this);
+ mPerUidCounter = getRequestCounter(this);
+ mPerUidCounter.incrementCountOrThrow(mUid);
mCallbackFlags = nri.mCallbackFlags;
mCallingAttributionTag = nri.mCallingAttributionTag;
+ linkDeathRecipient();
}
NetworkRequestInfo(int asUid, @NonNull final NetworkRequest r) {
@@ -5612,8 +5612,22 @@
return Collections.unmodifiableList(tempRequests);
}
+ void decrementRequestCount() {
+ mPerUidCounter.decrementCount(mUid);
+ }
+
+ void linkDeathRecipient() {
+ if (null != mBinder) {
+ try {
+ mBinder.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ binderDied();
+ }
+ }
+ }
+
void unlinkDeathRecipient() {
- if (mBinder != null) {
+ if (null != mBinder) {
mBinder.unlinkToDeath(this, 0);
}
}
@@ -5659,6 +5673,10 @@
"Insufficient permissions to request a specific signal strength");
}
mAppOpsManager.checkPackage(callerUid, callerPackageName);
+
+ if (!nc.getSubIds().isEmpty()) {
+ enforceNetworkFactoryPermission();
+ }
}
private int[] getSignalStrengthThresholds(@NonNull final NetworkAgentInfo nai) {
@@ -7740,7 +7758,7 @@
NetworkAgentInfo bestNetwork = null;
NetworkRequest bestRequest = null;
for (final NetworkRequest req : nri.mRequests) {
- bestNetwork = mNetworkRanker.getBestNetwork(req, nais);
+ bestNetwork = mNetworkRanker.getBestNetwork(req, nais, nri.getSatisfier());
// Stop evaluating as the highest possible priority request is satisfied.
if (null != bestNetwork) {
bestRequest = req;
@@ -7993,7 +8011,6 @@
@NonNull final NetworkOffer offer, @NonNull final NetworkRanker networkRanker) {
final NetworkRequest activeRequest = nri.isBeingSatisfied() ? nri.getActiveRequest() : null;
final NetworkAgentInfo satisfier = null != activeRequest ? nri.getSatisfier() : null;
- final FullScore satisfierScore = null != satisfier ? satisfier.getScore() : null;
// Multi-layer requests have a currently active request, the one being satisfied.
// Since the system will try to bring up a better network than is currently satisfying
@@ -8032,7 +8049,7 @@
&& satisfier.factorySerialNumber == offer.providerId;
final boolean newNeeded = (currentlyServing
|| (activeRequest.canBeSatisfiedBy(offer.caps)
- && networkRanker.mightBeat(activeRequest, satisfierScore, offer)));
+ && networkRanker.mightBeat(activeRequest, satisfier, offer)));
if (newNeeded != oldNeeded) {
if (newNeeded) {
offer.onNetworkNeeded(activeRequest);
@@ -8868,7 +8885,7 @@
// Decrement the reference count for this NetworkRequestInfo. The reference count is
// incremented when the NetworkRequestInfo is created as part of
// enforceRequestCountLimit().
- decrementRequestCount(nri);
+ nri.decrementRequestCount();
return;
}
@@ -8934,7 +8951,7 @@
// Decrement the reference count for this NetworkRequestInfo. The reference count is
// incremented when the NetworkRequestInfo is created as part of
// enforceRequestCountLimit().
- decrementRequestCount(nri);
+ nri.decrementRequestCount();
iCb.unlinkToDeath(cbInfo, 0);
}
@@ -9510,14 +9527,8 @@
throw new IllegalArgumentException("Must explicitly specify a user handle ("
+ "UserHandle.CURRENT not supported)");
}
- final UserManager um;
- try {
- um = mContext.createContextAsUser(profile, 0 /* flags */)
- .getSystemService(UserManager.class);
- } catch (IllegalStateException e) {
- throw new IllegalArgumentException("Profile does not exist");
- }
- if (!um.isManagedProfile()) {
+ final UserManager um = mContext.getSystemService(UserManager.class);
+ if (!um.isManagedProfile(profile.getIdentifier())) {
throw new IllegalArgumentException("Profile must be a managed profile");
}
// Strictly speaking, mOemNetworkPreferences should only be touched on the
diff --git a/services/core/java/com/android/server/connectivity/FullScore.java b/services/core/java/com/android/server/connectivity/FullScore.java
index 375d005..a8a83fc 100644
--- a/services/core/java/com/android/server/connectivity/FullScore.java
+++ b/services/core/java/com/android/server/connectivity/FullScore.java
@@ -17,9 +17,13 @@
package com.android.server.connectivity;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkScore.KEEP_CONNECTED_NONE;
+import static android.net.NetworkScore.POLICY_EXITING;
+import static android.net.NetworkScore.POLICY_TRANSPORT_PRIMARY;
+import static android.net.NetworkScore.POLICY_YIELD_TO_BAD_WIFI;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -52,7 +56,8 @@
POLICY_IS_VALIDATED,
POLICY_IS_VPN,
POLICY_EVER_USER_SELECTED,
- POLICY_ACCEPT_UNVALIDATED
+ POLICY_ACCEPT_UNVALIDATED,
+ POLICY_IS_UNMETERED
})
public @interface Policy {
}
@@ -77,12 +82,27 @@
/** @hide */
public static final int POLICY_ACCEPT_UNVALIDATED = 60;
+ // This network is unmetered. {@see NetworkCapabilities.NET_CAPABILITY_NOT_METERED}.
+ /** @hide */
+ public static final int POLICY_IS_UNMETERED = 59;
+
+ // This network is invincible. This is useful for offers until there is an API to listen
+ // to requests.
+ /** @hide */
+ public static final int POLICY_IS_INVINCIBLE = 58;
+
// To help iterate when printing
@VisibleForTesting
- static final int MIN_CS_MANAGED_POLICY = POLICY_ACCEPT_UNVALIDATED;
+ static final int MIN_CS_MANAGED_POLICY = POLICY_IS_INVINCIBLE;
@VisibleForTesting
static final int MAX_CS_MANAGED_POLICY = POLICY_IS_VALIDATED;
+ // Mask for policies in NetworkScore. This should have all bits managed by NetworkScore set
+ // and all bits managed by FullScore unset. As bits are handled from 0 up in NetworkScore and
+ // from 63 down in FullScore, cut at the 32rd bit for simplicity, but change this if some day
+ // there are more than 32 bits handled on either side.
+ private static final int EXTERNAL_POLICIES_MASK = 0x0000FFFF;
+
@VisibleForTesting
static @NonNull String policyNameOf(final int policy) {
switch (policy) {
@@ -90,6 +110,11 @@
case POLICY_IS_VPN: return "IS_VPN";
case POLICY_EVER_USER_SELECTED: return "EVER_USER_SELECTED";
case POLICY_ACCEPT_UNVALIDATED: return "ACCEPT_UNVALIDATED";
+ case POLICY_IS_UNMETERED: return "IS_UNMETERED";
+ case POLICY_YIELD_TO_BAD_WIFI: return "YIELD_TO_BAD_WIFI";
+ case POLICY_TRANSPORT_PRIMARY: return "TRANSPORT_PRIMARY";
+ case POLICY_EXITING: return "EXITING";
+ case POLICY_IS_INVINCIBLE: return "INVINCIBLE";
}
throw new IllegalArgumentException("Unknown policy : " + policy);
}
@@ -112,15 +137,24 @@
* @param score the score supplied by the agent
* @param caps the NetworkCapabilities of the network
* @param config the NetworkAgentConfig of the network
- * @return an FullScore that is appropriate to use for ranking.
+ * @param yieldToBadWiFi whether this network yields to a previously validated wifi gone bad
+ * @return a FullScore that is appropriate to use for ranking.
*/
+ // TODO : this shouldn't manage bad wifi avoidance – instead this should be done by the
+ // telephony factory, so that it depends on the carrier. For now this is handled by
+ // connectivity for backward compatibility.
public static FullScore fromNetworkScore(@NonNull final NetworkScore score,
- @NonNull final NetworkCapabilities caps, @NonNull final NetworkAgentConfig config) {
- return withPolicies(score.getLegacyInt(), score.getKeepConnectedReason(),
+ @NonNull final NetworkCapabilities caps, @NonNull final NetworkAgentConfig config,
+ final boolean yieldToBadWiFi) {
+ return withPolicies(score.getLegacyInt(), score.getPolicies(),
+ score.getKeepConnectedReason(),
caps.hasCapability(NET_CAPABILITY_VALIDATED),
caps.hasTransport(TRANSPORT_VPN),
+ caps.hasCapability(NET_CAPABILITY_NOT_METERED),
config.explicitlySelected,
- config.acceptUnvalidated);
+ config.acceptUnvalidated,
+ yieldToBadWiFi,
+ false /* invincible */); // only prospective scores can be invincible
}
/**
@@ -142,12 +176,21 @@
final boolean mayValidate = caps.hasCapability(NET_CAPABILITY_INTERNET);
// VPN transports are known in advance.
final boolean vpn = caps.hasTransport(TRANSPORT_VPN);
+ // Prospective scores are always unmetered, because unmetered networks are stronger
+ // than metered networks, and it's not known in advance whether the network is metered.
+ final boolean unmetered = true;
// The network hasn't been chosen by the user (yet, at least).
final boolean everUserSelected = false;
// Don't assume the user will accept unvalidated connectivity.
final boolean acceptUnvalidated = false;
- return withPolicies(score.getLegacyInt(), KEEP_CONNECTED_NONE,
- mayValidate, vpn, everUserSelected, acceptUnvalidated);
+ // Don't assume clinging to bad wifi
+ final boolean yieldToBadWiFi = false;
+ // A prospective score is invincible if the legacy int in the filter is over the maximum
+ // score.
+ final boolean invincible = score.getLegacyInt() > NetworkRanker.LEGACY_INT_MAX;
+ return withPolicies(score.getLegacyInt(), score.getPolicies(), KEEP_CONNECTED_NONE,
+ mayValidate, vpn, unmetered, everUserSelected, acceptUnvalidated, yieldToBadWiFi,
+ invincible);
}
/**
@@ -157,26 +200,42 @@
* @param config the NetworkAgentConfig of the network
* @return a score with the policies from the arguments reset
*/
+ // TODO : this shouldn't manage bad wifi avoidance – instead this should be done by the
+ // telephony factory, so that it depends on the carrier. For now this is handled by
+ // connectivity for backward compatibility.
public FullScore mixInScore(@NonNull final NetworkCapabilities caps,
- @NonNull final NetworkAgentConfig config) {
- return withPolicies(mLegacyInt, mKeepConnectedReason,
+ @NonNull final NetworkAgentConfig config, final boolean yieldToBadWifi) {
+ return withPolicies(mLegacyInt, mPolicies, mKeepConnectedReason,
caps.hasCapability(NET_CAPABILITY_VALIDATED),
caps.hasTransport(TRANSPORT_VPN),
+ caps.hasCapability(NET_CAPABILITY_NOT_METERED),
config.explicitlySelected,
- config.acceptUnvalidated);
+ config.acceptUnvalidated,
+ yieldToBadWifi,
+ false /* invincible */); // only prospective scores can be invincible
}
+ // TODO : this shouldn't manage bad wifi avoidance – instead this should be done by the
+ // telephony factory, so that it depends on the carrier. For now this is handled by
+ // connectivity for backward compatibility.
private static FullScore withPolicies(@NonNull final int legacyInt,
+ final long externalPolicies,
@KeepConnectedReason final int keepConnectedReason,
final boolean isValidated,
final boolean isVpn,
+ final boolean isUnmetered,
final boolean everUserSelected,
- final boolean acceptUnvalidated) {
- return new FullScore(legacyInt,
- (isValidated ? 1L << POLICY_IS_VALIDATED : 0)
+ final boolean acceptUnvalidated,
+ final boolean yieldToBadWiFi,
+ final boolean invincible) {
+ return new FullScore(legacyInt, (externalPolicies & EXTERNAL_POLICIES_MASK)
+ | (isValidated ? 1L << POLICY_IS_VALIDATED : 0)
| (isVpn ? 1L << POLICY_IS_VPN : 0)
+ | (isUnmetered ? 1L << POLICY_IS_UNMETERED : 0)
| (everUserSelected ? 1L << POLICY_EVER_USER_SELECTED : 0)
- | (acceptUnvalidated ? 1L << POLICY_ACCEPT_UNVALIDATED : 0),
+ | (acceptUnvalidated ? 1L << POLICY_ACCEPT_UNVALIDATED : 0)
+ | (yieldToBadWiFi ? 1L << POLICY_YIELD_TO_BAD_WIFI : 0)
+ | (invincible ? 1L << POLICY_IS_INVINCIBLE : 0),
keepConnectedReason);
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index d16a2de..5d793fd 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -17,6 +17,7 @@
package com.android.server.connectivity;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.transportNamesOf;
import android.annotation.NonNull;
@@ -142,7 +143,7 @@
// the network is no longer considered "lingering". After the linger timer expires, if the network
// is satisfying one or more background NetworkRequests it is kept up in the background. If it is
// not, ConnectivityService disconnects the NetworkAgent's AsyncChannel.
-public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
+public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRanker.Scoreable {
@NonNull public NetworkInfo networkInfo;
// This Network object should always be used if possible, so as to encourage reuse of the
@@ -362,9 +363,9 @@
linkProperties = lp;
networkCapabilities = nc;
networkAgentConfig = config;
- setScore(score); // uses members networkCapabilities and networkAgentConfig
- clatd = new Nat464Xlat(this, netd, dnsResolver, deps);
mConnService = connService;
+ setScore(score); // uses members connService, networkCapabilities and networkAgentConfig
+ clatd = new Nat464Xlat(this, netd, dnsResolver, deps);
mContext = context;
mHandler = handler;
this.factorySerialNumber = factorySerialNumber;
@@ -706,7 +707,7 @@
@NonNull final NetworkCapabilities nc) {
final NetworkCapabilities oldNc = networkCapabilities;
networkCapabilities = nc;
- mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig);
+ mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig, yieldToBadWiFi());
final NetworkMonitorManager nm = mNetworkMonitor;
if (nm != null) {
nm.notifyNetworkCapabilitiesChanged(nc);
@@ -714,6 +715,11 @@
return oldNc;
}
+ private boolean yieldToBadWiFi() {
+ // Only cellular networks yield to bad wifi
+ return networkCapabilities.hasTransport(TRANSPORT_CELLULAR) && !mConnService.avoidBadWifi();
+ }
+
public ConnectivityService connService() {
return mConnService;
}
@@ -884,16 +890,15 @@
return isVPN();
}
- // Return true on devices configured to ignore score penalty for wifi networks
- // that become unvalidated (b/31075769).
- private boolean ignoreWifiUnvalidationPenalty() {
- boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
- networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
- boolean avoidBadWifi = mConnService.avoidBadWifi() || avoidUnvalidated;
- return isWifi && !avoidBadWifi && everValidated;
+ // Caller must not mutate. This method is called frequently and making a defensive copy
+ // would be too expensive. This is used by NetworkRanker.Scoreable, so it can be compared
+ // against other scoreables.
+ @Override public NetworkCapabilities getCaps() {
+ return networkCapabilities;
}
- public FullScore getScore() {
+ // NetworkRanker.Scoreable
+ @Override public FullScore getScore() {
return mScore;
}
@@ -913,7 +918,8 @@
* Mix-in the ConnectivityService-managed bits in the score.
*/
public void setScore(final NetworkScore score) {
- mScore = FullScore.fromNetworkScore(score, networkCapabilities, networkAgentConfig);
+ mScore = FullScore.fromNetworkScore(score, networkCapabilities, networkAgentConfig,
+ yieldToBadWiFi());
}
/**
@@ -922,7 +928,7 @@
* Call this after updating the network agent config.
*/
public void updateScoreForNetworkAgentConfigUpdate() {
- mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig);
+ mScore = mScore.mixInScore(networkCapabilities, networkAgentConfig, yieldToBadWiFi());
}
/**
@@ -1085,7 +1091,7 @@
return "NetworkAgentInfo{"
+ "network{" + network + "} handle{" + network.getNetworkHandle() + "} ni{"
+ networkInfo.toShortString() + "} "
- + " Score{" + getCurrentScore() + "} "
+ + mScore + " "
+ (isNascent() ? " nascent" : (isLingering() ? " lingering" : ""))
+ (everValidated ? " everValidated" : "")
+ (lastValidated ? " lastValidated" : "")
diff --git a/services/core/java/com/android/server/connectivity/NetworkOffer.java b/services/core/java/com/android/server/connectivity/NetworkOffer.java
index a0d3924..5336593 100644
--- a/services/core/java/com/android/server/connectivity/NetworkOffer.java
+++ b/services/core/java/com/android/server/connectivity/NetworkOffer.java
@@ -40,7 +40,7 @@
*
* @hide
*/
-public class NetworkOffer {
+public class NetworkOffer implements NetworkRanker.Scoreable {
@NonNull public final FullScore score;
@NonNull public final NetworkCapabilities caps;
@NonNull public final INetworkOfferCallback callback;
@@ -67,6 +67,20 @@
}
/**
+ * Get the score filter of this offer
+ */
+ @Override @NonNull public FullScore getScore() {
+ return score;
+ }
+
+ /**
+ * Get the capabilities filter of this offer
+ */
+ @Override @NonNull public NetworkCapabilities getCaps() {
+ return caps;
+ }
+
+ /**
* Tell the provider for this offer that the network is needed for a request.
* @param request the request for which the offer is needed
*/
diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/services/core/java/com/android/server/connectivity/NetworkRanker.java
index 0e4dda2..3aaff59 100644
--- a/services/core/java/com/android/server/connectivity/NetworkRanker.java
+++ b/services/core/java/com/android/server/connectivity/NetworkRanker.java
@@ -16,31 +16,233 @@
package com.android.server.connectivity;
+import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkScore.POLICY_EXITING;
+import static android.net.NetworkScore.POLICY_TRANSPORT_PRIMARY;
+import static android.net.NetworkScore.POLICY_YIELD_TO_BAD_WIFI;
+
+import static com.android.server.connectivity.FullScore.POLICY_ACCEPT_UNVALIDATED;
+import static com.android.server.connectivity.FullScore.POLICY_EVER_USER_SELECTED;
+import static com.android.server.connectivity.FullScore.POLICY_IS_INVINCIBLE;
+import static com.android.server.connectivity.FullScore.POLICY_IS_VALIDATED;
+import static com.android.server.connectivity.FullScore.POLICY_IS_VPN;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import com.android.net.module.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
+import java.util.function.Predicate;
/**
* A class that knows how to find the best network matching a request out of a list of networks.
*/
public class NetworkRanker {
+ // Historically the legacy ints have been 0~100 in principle (though the highest score in
+ // AOSP has always been 90). This is relied on by VPNs that send a legacy score of 101.
+ public static final int LEGACY_INT_MAX = 100;
+
+ /**
+ * A class that can be scored against other scoreables.
+ */
+ public interface Scoreable {
+ /** Get score of this scoreable */
+ FullScore getScore();
+ /** Get capabilities of this scoreable */
+ NetworkCapabilities getCaps();
+ }
+
+ private static final boolean USE_POLICY_RANKING = false;
+
public NetworkRanker() { }
+ // TODO : move to module utils CollectionUtils.
+ @NonNull private static <T> ArrayList<T> filter(@NonNull final Collection<T> source,
+ @NonNull final Predicate<T> test) {
+ final ArrayList<T> matches = new ArrayList<>();
+ for (final T e : source) {
+ if (test.test(e)) {
+ matches.add(e);
+ }
+ }
+ return matches;
+ }
+
/**
* Find the best network satisfying this request among the list of passed networks.
*/
- // Almost equivalent to Collections.max(nais), but allows returning null if no network
- // satisfies the request.
@Nullable
public NetworkAgentInfo getBestNetwork(@NonNull final NetworkRequest request,
+ @NonNull final Collection<NetworkAgentInfo> nais,
+ @Nullable final NetworkAgentInfo currentSatisfier) {
+ final ArrayList<NetworkAgentInfo> candidates = filter(nais, nai -> nai.satisfies(request));
+ if (candidates.size() == 1) return candidates.get(0); // Only one potential satisfier
+ if (candidates.size() <= 0) return null; // No network can satisfy this request
+ if (USE_POLICY_RANKING) {
+ return getBestNetworkByPolicy(candidates, currentSatisfier);
+ } else {
+ return getBestNetworkByLegacyInt(candidates);
+ }
+ }
+
+ // Transport preference order, if it comes down to that.
+ private static final int[] PREFERRED_TRANSPORTS_ORDER = { TRANSPORT_ETHERNET, TRANSPORT_WIFI,
+ TRANSPORT_BLUETOOTH, TRANSPORT_CELLULAR };
+
+ // Function used to partition a list into two working areas depending on whether they
+ // satisfy a predicate. All items satisfying the predicate will be put in |positive|, all
+ // items that don't will be put in |negative|.
+ // This is useful in this file because many of the ranking checks will retain only networks that
+ // satisfy a predicate if any of them do, but keep them all if all of them do. Having working
+ // areas is uncustomary in Java, but this function is called in a fairly intensive manner
+ // and doing allocation quite that often might affect performance quite badly.
+ private static <T> void partitionInto(@NonNull final List<T> source, @NonNull Predicate<T> test,
+ @NonNull final List<T> positive, @NonNull final List<T> negative) {
+ positive.clear();
+ negative.clear();
+ for (final T item : source) {
+ if (test.test(item)) {
+ positive.add(item);
+ } else {
+ negative.add(item);
+ }
+ }
+ }
+
+ @Nullable private <T extends Scoreable> T getBestNetworkByPolicy(
+ @NonNull List<T> candidates,
+ @Nullable final T currentSatisfier) {
+ // Used as working areas.
+ final ArrayList<T> accepted =
+ new ArrayList<>(candidates.size() /* initialCapacity */);
+ final ArrayList<T> rejected =
+ new ArrayList<>(candidates.size() /* initialCapacity */);
+
+ // The following tests will search for a network matching a given criterion. They all
+ // function the same way : if any network matches the criterion, drop from consideration
+ // all networks that don't. To achieve this, the tests below :
+ // 1. partition the list of remaining candidates into accepted and rejected networks.
+ // 2. if only one candidate remains, that's the winner : if accepted.size == 1 return [0]
+ // 3. if multiple remain, keep only the accepted networks and go on to the next criterion.
+ // Because the working areas will be wiped, a copy of the accepted networks needs to be
+ // made.
+ // 4. if none remain, the criterion did not help discriminate so keep them all. As an
+ // optimization, skip creating a new array and go on to the next criterion.
+
+ // If a network is invincible, use it.
+ partitionInto(candidates, nai -> nai.getScore().hasPolicy(POLICY_IS_INVINCIBLE),
+ accepted, rejected);
+ if (accepted.size() == 1) return accepted.get(0);
+ if (accepted.size() > 0 && rejected.size() > 0) candidates = new ArrayList<>(accepted);
+
+ // If there is a connected VPN, use it.
+ partitionInto(candidates, nai -> nai.getScore().hasPolicy(POLICY_IS_VPN),
+ accepted, rejected);
+ if (accepted.size() == 1) return accepted.get(0);
+ if (accepted.size() > 0 && rejected.size() > 0) candidates = new ArrayList<>(accepted);
+
+ // Selected & Accept-unvalidated policy : if any network has both of these, then don't
+ // choose one that doesn't.
+ partitionInto(candidates, nai -> nai.getScore().hasPolicy(POLICY_EVER_USER_SELECTED)
+ && nai.getScore().hasPolicy(POLICY_ACCEPT_UNVALIDATED),
+ accepted, rejected);
+ if (accepted.size() == 1) return accepted.get(0);
+ if (accepted.size() > 0 && rejected.size() > 0) candidates = new ArrayList<>(accepted);
+
+ // Yield to bad wifi policy : if any wifi has ever been validated, keep only networks
+ // that don't yield to such a wifi network.
+ final boolean anyWiFiEverValidated = CollectionUtils.any(candidates,
+ nai -> nai.getScore().hasPolicy(POLICY_EVER_USER_SELECTED)
+ && nai.getCaps().hasTransport(TRANSPORT_WIFI));
+ if (anyWiFiEverValidated) {
+ partitionInto(candidates, nai -> !nai.getScore().hasPolicy(POLICY_YIELD_TO_BAD_WIFI),
+ accepted, rejected);
+ if (accepted.size() == 1) return accepted.get(0);
+ if (accepted.size() > 0 && rejected.size() > 0) candidates = new ArrayList<>(accepted);
+ }
+
+ // If any network is validated (or should be accepted even if it's not validated), then
+ // don't choose one that isn't.
+ partitionInto(candidates, nai -> nai.getScore().hasPolicy(POLICY_IS_VALIDATED)
+ || nai.getScore().hasPolicy(POLICY_ACCEPT_UNVALIDATED),
+ accepted, rejected);
+ if (accepted.size() == 1) return accepted.get(0);
+ if (accepted.size() > 0 && rejected.size() > 0) candidates = new ArrayList<>(accepted);
+
+ // If any network is not exiting, don't choose one that is.
+ partitionInto(candidates, nai -> !nai.getScore().hasPolicy(POLICY_EXITING),
+ accepted, rejected);
+ if (accepted.size() == 1) return accepted.get(0);
+ if (accepted.size() > 0 && rejected.size() > 0) candidates = new ArrayList<>(accepted);
+
+ // TODO : If any network is unmetered, don't choose a metered network.
+ // This can't be implemented immediately because prospective networks are always
+ // considered unmetered because factories don't know if the network will be metered.
+ // Saying an unmetered network always beats a metered one would mean that when metered wifi
+ // is connected, the offer for telephony would beat WiFi but the actual metered network
+ // would lose, so we'd have an infinite loop where telephony would continually bring up
+ // a network that is immediately torn down.
+ // Fix this by getting the agent to tell connectivity whether the network they will
+ // bring up is metered. Cell knows that in advance, while WiFi has a good estimate and
+ // can revise it if the network later turns out to be metered.
+ // partitionInto(candidates, nai -> nai.getScore().hasPolicy(POLICY_IS_UNMETERED),
+ // accepted, rejected);
+ // if (accepted.size() == 1) return accepted.get(0);
+ // if (accepted.size() > 0 && rejected.size() > 0) candidates = new ArrayList<>(accepted);
+
+ // If any network is for the default subscription, don't choose a network for another
+ // subscription with the same transport.
+ partitionInto(candidates, nai -> nai.getScore().hasPolicy(POLICY_TRANSPORT_PRIMARY),
+ accepted, rejected);
+ for (final Scoreable defaultSubNai : accepted) {
+ // Remove all networks without the DEFAULT_SUBSCRIPTION policy and the same transports
+ // as a network that has it.
+ final int[] transports = defaultSubNai.getCaps().getTransportTypes();
+ candidates.removeIf(nai -> !nai.getScore().hasPolicy(POLICY_TRANSPORT_PRIMARY)
+ && Arrays.equals(transports, nai.getCaps().getTransportTypes()));
+ }
+ if (1 == candidates.size()) return candidates.get(0);
+ // It's guaranteed candidates.size() > 0 because there is at least one with DEFAULT_SUB
+ // policy and only those without it were removed.
+
+ // If some of the networks have a better transport than others, keep only the ones with
+ // the best transports.
+ for (final int transport : PREFERRED_TRANSPORTS_ORDER) {
+ partitionInto(candidates, nai -> nai.getCaps().hasTransport(transport),
+ accepted, rejected);
+ if (accepted.size() == 1) return accepted.get(0);
+ if (accepted.size() > 0 && rejected.size() > 0) {
+ candidates = new ArrayList<>(accepted);
+ break;
+ }
+ }
+
+ // At this point there are still multiple networks passing all the tests above. If any
+ // of them is the previous satisfier, keep it.
+ if (candidates.contains(currentSatisfier)) return currentSatisfier;
+
+ // If there are still multiple options at this point but none of them is any of the
+ // transports above, it doesn't matter which is returned. They are all the same.
+ return candidates.get(0);
+ }
+
+ // TODO : switch to the policy implementation and remove
+ // Almost equivalent to Collections.max(nais), but allows returning null if no network
+ // satisfies the request.
+ private NetworkAgentInfo getBestNetworkByLegacyInt(
@NonNull final Collection<NetworkAgentInfo> nais) {
NetworkAgentInfo bestNetwork = null;
int bestScore = Integer.MIN_VALUE;
for (final NetworkAgentInfo nai : nais) {
- if (!nai.satisfies(request)) continue;
if (nai.getCurrentScore() > bestScore) {
bestNetwork = nai;
bestScore = nai.getCurrentScore();
@@ -63,19 +265,37 @@
* beat a current champion.
*
* @param request The request to evaluate against.
- * @param championScore The currently best network for this request.
+ * @param champion The currently best network for this request.
* @param offer The offer.
* @return Whether the offer stands a chance to beat the champion.
*/
public boolean mightBeat(@NonNull final NetworkRequest request,
- @Nullable final FullScore championScore,
+ @Nullable final NetworkAgentInfo champion,
@NonNull final NetworkOffer offer) {
// If this network can't even satisfy the request then it can't beat anything, not
// even an absence of network. It can't satisfy it anyway.
if (!request.canBeSatisfiedBy(offer.caps)) return false;
// If there is no satisfying network, then this network can beat, because some network
// is always better than no network.
- if (null == championScore) return true;
+ if (null == champion) return true;
+ if (USE_POLICY_RANKING) {
+ // If there is no champion, the offer can always beat.
+ // Otherwise rank them.
+ final ArrayList<Scoreable> candidates = new ArrayList<>();
+ candidates.add(champion);
+ candidates.add(offer);
+ return offer == getBestNetworkByPolicy(candidates, champion);
+ } else {
+ return mightBeatByLegacyInt(request, champion.getScore(), offer);
+ }
+ }
+
+ /**
+ * Returns whether an offer might beat a champion according to the legacy int.
+ */
+ public boolean mightBeatByLegacyInt(@NonNull final NetworkRequest request,
+ @Nullable final FullScore championScore,
+ @NonNull final NetworkOffer offer) {
final int offerIntScore;
if (offer.caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
// If the offer might have Internet access, then it might validate.
diff --git a/tests/net/integration/AndroidManifest.xml b/tests/net/integration/AndroidManifest.xml
index f5a4234..db18500 100644
--- a/tests/net/integration/AndroidManifest.xml
+++ b/tests/net/integration/AndroidManifest.xml
@@ -37,6 +37,7 @@
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
<!-- Reading DeviceConfig flags -->
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/>
+ <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
<application android:debuggable="true">
<uses-library android:name="android.test.runner"/>
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index 6245e85..40d068d 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -52,7 +52,6 @@
import android.util.Range;
import com.android.net.module.util.ArrayTrackRecord;
-import com.android.server.connectivity.ConnectivityConstants;
import com.android.testutils.HandlerUtils;
import com.android.testutils.TestableNetworkCallback;
@@ -70,7 +69,7 @@
private final ConditionVariable mDisconnected = new ConditionVariable();
private final ConditionVariable mPreventReconnectReceived = new ConditionVariable();
private final AtomicBoolean mConnected = new AtomicBoolean(false);
- private int mScore;
+ private NetworkScore mScore;
private NetworkAgent mNetworkAgent;
private int mStartKeepaliveError = SocketKeepalive.ERROR_UNSUPPORTED;
private int mStopKeepaliveError = SocketKeepalive.NO_KEEPALIVE;
@@ -91,23 +90,23 @@
mNetworkCapabilities.addTransportType(transport);
switch (transport) {
case TRANSPORT_ETHERNET:
- mScore = 70;
+ mScore = new NetworkScore.Builder().setLegacyInt(70).build();
break;
case TRANSPORT_WIFI:
- mScore = 60;
+ mScore = new NetworkScore.Builder().setLegacyInt(60).build();
break;
case TRANSPORT_CELLULAR:
- mScore = 50;
+ mScore = new NetworkScore.Builder().setLegacyInt(50).build();
break;
case TRANSPORT_WIFI_AWARE:
- mScore = 20;
+ mScore = new NetworkScore.Builder().setLegacyInt(20).build();
break;
case TRANSPORT_VPN:
mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_VPN);
// VPNs deduce the SUSPENDED capability from their underlying networks and there
// is no public API to let VPN services set it.
mNetworkCapabilities.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
- mScore = ConnectivityConstants.VPN_DEFAULT_SCORE;
+ mScore = new NetworkScore.Builder().setLegacyInt(101).build();
break;
default:
throw new UnsupportedOperationException("unimplemented network type");
@@ -201,16 +200,22 @@
}
public void setScore(@NonNull final NetworkScore score) {
- mScore = score.getLegacyInt();
+ mScore = score;
mNetworkAgent.sendNetworkScore(score);
}
public void adjustScore(int change) {
- mScore += change;
+ final int newLegacyScore = mScore.getLegacyInt() + change;
+ final NetworkScore.Builder builder = new NetworkScore.Builder()
+ .setLegacyInt(newLegacyScore);
+ if (mNetworkCapabilities.hasTransport(TRANSPORT_WIFI) && newLegacyScore < 50) {
+ builder.setExiting(true);
+ }
+ mScore = builder.build();
mNetworkAgent.sendNetworkScore(mScore);
}
- public int getScore() {
+ public NetworkScore getScore() {
return mScore;
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c3cb5f8..f903a45 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.CHANGE_NETWORK_STATE;
import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
+import static android.Manifest.permission.NETWORK_FACTORY;
import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.content.Intent.ACTION_USER_ADDED;
@@ -290,7 +291,6 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.net.module.util.ArrayTrackRecord;
import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
-import com.android.server.connectivity.ConnectivityConstants;
import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.NetworkAgentInfo;
@@ -578,6 +578,7 @@
final UserManager umMock = createContextAsUser(userHandle, 0 /* flags */)
.getSystemService(UserManager.class);
doReturn(value).when(umMock).isManagedProfile();
+ doReturn(value).when(mUserManager).isManagedProfile(eq(userHandle.getIdentifier()));
}
@Override
@@ -3033,8 +3034,9 @@
}
NetworkCapabilities filter = new NetworkCapabilities();
+ filter.addTransportType(TRANSPORT_CELLULAR);
filter.addCapability(capability);
- // Add NOT_VCN_MANAGED capability into filter unconditionally since some request will add
+ // Add NOT_VCN_MANAGED capability into filter unconditionally since some requests will add
// NOT_VCN_MANAGED automatically but not for NetworkCapabilities,
// see {@code NetworkCapabilities#deduceNotVcnManagedCapability} for more details.
filter.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
@@ -3063,15 +3065,11 @@
// Now bring in a higher scored network.
TestNetworkAgentWrapper testAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- // Rather than create a validated network which complicates things by registering it's
- // own NetworkRequest during startup, just bump up the score to cancel out the
- // unvalidated penalty.
- testAgent.adjustScore(40);
-
- // When testAgent connects, because of its 50 score (50 for cell + 40 adjustment score
- // - 40 penalty for not being validated), it will beat the testFactory's offer, so
- // the request will be removed.
- testAgent.connect(false);
+ // When testAgent connects, because of its score (50 legacy int / cell transport)
+ // it will beat or equal the testFactory's offer, so the request will be removed.
+ // Note the agent as validated only if the capability is INTERNET, as it's the only case
+ // where it makes sense.
+ testAgent.connect(NET_CAPABILITY_INTERNET == capability /* validated */);
testAgent.addCapability(capability);
testFactory.expectRequestRemove();
testFactory.assertRequestCountEquals(0);
@@ -3085,17 +3083,18 @@
testFactory.assertRequestCountEquals(0);
assertFalse(testFactory.getMyStartRequested());
- // Make the test agent weak enough to have the exact same score as the
- // factory (50 for cell + 40 adjustment -40 validation penalty - 5 adjustment). Make sure
- // the factory doesn't see the request.
+ // If using legacy scores, make the test agent weak enough to have the exact same score as
+ // the factory (50 for cell - 5 adjustment). Make sure the factory doesn't see the request.
+ // If not using legacy score, this is a no-op and the "same score removes request" behavior
+ // has already been tested above.
testAgent.adjustScore(-5);
expectNoRequestChanged(testFactory);
assertFalse(testFactory.getMyStartRequested());
- // Make the test agent weak enough to see the two requests (the one that was just sent,
- // and either the default one or the one sent at the top of this test if the default
- // won't be seen).
- testAgent.adjustScore(-45);
+ // Make the test agent weak enough that the factory will see the two requests (the one that
+ // was just sent, and either the default one or the one sent at the top of this test if
+ // the default won't be seen).
+ testAgent.setScore(new NetworkScore.Builder().setLegacyInt(2).setExiting(true).build());
testFactory.expectRequestAdds(2);
testFactory.assertRequestCountEquals(2);
assertTrue(testFactory.getMyStartRequested());
@@ -3127,7 +3126,7 @@
assertTrue(testFactory.getMyStartRequested());
// Adjust the agent score up again. Expect the request to be withdrawn.
- testAgent.adjustScore(50);
+ testAgent.setScore(new NetworkScore.Builder().setLegacyInt(50).build());
testFactory.expectRequestRemove();
testFactory.assertRequestCountEquals(0);
assertFalse(testFactory.getMyStartRequested());
@@ -3168,22 +3167,35 @@
@Test
public void testRegisterIgnoringScore() throws Exception {
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- mWiFiNetworkAgent.adjustScore(30); // score = 60 (wifi) + 30 = 90
+ mWiFiNetworkAgent.setScore(new NetworkScore.Builder().setLegacyInt(90).build());
mWiFiNetworkAgent.connect(true /* validated */);
// Make sure the factory sees the default network
final NetworkCapabilities filter = new NetworkCapabilities();
+ filter.addTransportType(TRANSPORT_CELLULAR);
filter.addCapability(NET_CAPABILITY_INTERNET);
filter.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests");
handlerThread.start();
final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
mServiceContext, "testFactory", filter, mCsHandlerThread);
- testFactory.registerIgnoringScore();
- testFactory.expectRequestAdd();
+ testFactory.register();
- mWiFiNetworkAgent.adjustScore(20); // exceed the maximum score
- expectNoRequestChanged(testFactory); // still seeing the request
+ final MockNetworkFactory testFactoryAll = new MockNetworkFactory(handlerThread.getLooper(),
+ mServiceContext, "testFactoryAll", filter, mCsHandlerThread);
+ testFactoryAll.registerIgnoringScore();
+
+ // The regular test factory should not see the request, because WiFi is stronger than cell.
+ expectNoRequestChanged(testFactory);
+ // With ignoringScore though the request is seen.
+ testFactoryAll.expectRequestAdd();
+
+ // The legacy int will be ignored anyway, set the only other knob to true
+ mWiFiNetworkAgent.setScore(new NetworkScore.Builder().setLegacyInt(110)
+ .setTransportPrimary(true).build());
+
+ expectNoRequestChanged(testFactory); // still not seeing the request
+ expectNoRequestChanged(testFactoryAll); // still seeing the request
mWiFiNetworkAgent.disconnect();
}
@@ -6047,7 +6059,8 @@
// called again, it does. For example, connect Ethernet, but with a low score, such that it
// does not become the default network.
mEthernetNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
- mEthernetNetworkAgent.adjustScore(-40);
+ mEthernetNetworkAgent.setScore(
+ new NetworkScore.Builder().setLegacyInt(30).setExiting(true).build());
mEthernetNetworkAgent.connect(false);
waitForIdle();
verify(mStatsManager).notifyNetworkStatus(any(List.class),
@@ -6922,8 +6935,6 @@
callback.expectAvailableCallbacksUnvalidated(mMockVpn);
callback.assertNoCallback();
- assertTrue(mMockVpn.getAgent().getScore() > mEthernetNetworkAgent.getScore());
- assertEquals(ConnectivityConstants.VPN_DEFAULT_SCORE, mMockVpn.getAgent().getScore());
assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
NetworkCapabilities nc = mCm.getNetworkCapabilities(mMockVpn.getNetwork());
@@ -8225,12 +8236,12 @@
assertExtraInfoFromCmPresent(mWiFiNetworkAgent);
b1 = expectConnectivityAction(TYPE_WIFI, DetailedState.DISCONNECTED);
+ b2 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED);
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
systemDefaultCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
b1.expectBroadcast();
callback.expectCapabilitiesThat(mMockVpn, nc -> !nc.hasTransport(TRANSPORT_WIFI));
- b2 = expectConnectivityAction(TYPE_VPN, DetailedState.DISCONNECTED);
mMockVpn.expectStopVpnRunnerPrivileged();
callback.expectCallback(CallbackEntry.LOST, mMockVpn);
b2.expectBroadcast();
@@ -11645,14 +11656,14 @@
new LinkProperties(), oemPaidNc);
oemPaidAgent.connect(true);
- // The oemPaidAgent has score 50 (default for cell) so it beats what the oemPaidFactory can
+ // The oemPaidAgent has score 50/cell transport, so it beats what the oemPaidFactory can
// provide, therefore it loses the request.
oemPaidFactory.expectRequestRemove();
oemPaidFactory.assertRequestCountEquals(0);
expectNoRequestChanged(internetFactory);
internetFactory.assertRequestCountEquals(0);
- oemPaidAgent.adjustScore(-30);
+ oemPaidAgent.setScore(new NetworkScore.Builder().setLegacyInt(20).setExiting(true).build());
// Now the that the agent is weak, the oemPaidFactory can beat the existing network for the
// OEM_PAID request. The internet factory however can't beat a network that has OEM_PAID
// for the preference request, so it doesn't see the request.
@@ -11682,7 +11693,8 @@
// Now WiFi connects and it's unmetered, but it's weaker than cell.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
- mWiFiNetworkAgent.adjustScore(-30); // Not the best Internet network, but unmetered
+ mWiFiNetworkAgent.setScore(new NetworkScore.Builder().setLegacyInt(30).setExiting(true)
+ .build()); // Not the best Internet network, but unmetered
mWiFiNetworkAgent.connect(true);
// The OEM_PAID preference prefers an unmetered network to an OEM_PAID network, so
@@ -12464,4 +12476,68 @@
mCm.setProfileNetworkPreference(testHandle,
PROFILE_NETWORK_PREFERENCE_ENTERPRISE, null, null));
}
+
+ @Test
+ public void testSubIdsClearedWithoutNetworkFactoryPermission() throws Exception {
+ mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_DENIED);
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ nc.setSubIds(Collections.singleton(Process.myUid()));
+
+ final NetworkCapabilities result =
+ mService.networkCapabilitiesRestrictedForCallerPermissions(
+ nc, Process.myPid(), Process.myUid());
+ assertTrue(result.getSubIds().isEmpty());
+ }
+
+ @Test
+ public void testSubIdsExistWithNetworkFactoryPermission() throws Exception {
+ mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_GRANTED);
+
+ final Set<Integer> subIds = Collections.singleton(Process.myUid());
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ nc.setSubIds(subIds);
+
+ final NetworkCapabilities result =
+ mService.networkCapabilitiesRestrictedForCallerPermissions(
+ nc, Process.myPid(), Process.myUid());
+ assertEquals(subIds, result.getSubIds());
+ }
+
+ private NetworkRequest getRequestWithSubIds() {
+ return new NetworkRequest.Builder()
+ .setSubIds(Collections.singleton(Process.myUid()))
+ .build();
+ }
+
+ @Test
+ public void testNetworkRequestWithSubIdsWithNetworkFactoryPermission() throws Exception {
+ mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_GRANTED);
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE);
+ final NetworkCallback networkCallback1 = new NetworkCallback();
+ final NetworkCallback networkCallback2 = new NetworkCallback();
+
+ mCm.requestNetwork(getRequestWithSubIds(), networkCallback1);
+ mCm.requestNetwork(getRequestWithSubIds(), pendingIntent);
+ mCm.registerNetworkCallback(getRequestWithSubIds(), networkCallback2);
+
+ mCm.unregisterNetworkCallback(networkCallback1);
+ mCm.releaseNetworkRequest(pendingIntent);
+ mCm.unregisterNetworkCallback(networkCallback2);
+ }
+
+ @Test
+ public void testNetworkRequestWithSubIdsWithoutNetworkFactoryPermission() throws Exception {
+ mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_DENIED);
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ mContext, 0 /* requestCode */, new Intent("a"), FLAG_IMMUTABLE);
+
+ final Class<SecurityException> expected = SecurityException.class;
+ assertThrows(
+ expected, () -> mCm.requestNetwork(getRequestWithSubIds(), new NetworkCallback()));
+ assertThrows(expected, () -> mCm.requestNetwork(getRequestWithSubIds(), pendingIntent));
+ assertThrows(
+ expected,
+ () -> mCm.registerNetworkCallback(getRequestWithSubIds(), new NetworkCallback()));
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/FullScoreTest.kt b/tests/net/java/com/android/server/connectivity/FullScoreTest.kt
index 2864bc7..f0d7d86 100644
--- a/tests/net/java/com/android/server/connectivity/FullScoreTest.kt
+++ b/tests/net/java/com/android/server/connectivity/FullScoreTest.kt
@@ -56,7 +56,7 @@
if (vpn) addTransportType(NetworkCapabilities.TRANSPORT_VPN)
if (validated) addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
}.build()
- return mixInScore(nc, nac)
+ return mixInScore(nc, nac, false /* avoidBadWifi */)
}
@Test
diff --git a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
index 86c9116..1348c6a 100644
--- a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
+++ b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
@@ -43,7 +43,7 @@
val nais = scores.map { makeNai(true, it) }
val bestNetwork = nais[2] // The one with the top score
val someRequest = mock(NetworkRequest::class.java)
- assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais))
+ assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais, bestNetwork))
}
@Test
@@ -52,20 +52,20 @@
makeNai(false, 60), makeNai(true, 23), makeNai(false, 68))
val bestNetwork = nais[1] // Top score that's satisfying
val someRequest = mock(NetworkRequest::class.java)
- assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais))
+ assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais, nais[1]))
}
@Test
fun testNoMatch() {
val nais = listOf(makeNai(false, 20), makeNai(false, 50), makeNai(false, 90))
val someRequest = mock(NetworkRequest::class.java)
- assertNull(ranker.getBestNetwork(someRequest, nais))
+ assertNull(ranker.getBestNetwork(someRequest, nais, null))
}
@Test
fun testEmpty() {
val someRequest = mock(NetworkRequest::class.java)
- assertNull(ranker.getBestNetwork(someRequest, emptyList()))
+ assertNull(ranker.getBestNetwork(someRequest, emptyList(), null))
}
// Make sure the ranker is "stable" (as in stable sort), that is, it always returns the FIRST
@@ -75,10 +75,10 @@
val nais1 = listOf(makeNai(true, 30), makeNai(true, 30), makeNai(true, 30),
makeNai(true, 30), makeNai(true, 30), makeNai(true, 30))
val someRequest = mock(NetworkRequest::class.java)
- assertEquals(nais1[0], ranker.getBestNetwork(someRequest, nais1))
+ assertEquals(nais1[0], ranker.getBestNetwork(someRequest, nais1, nais1[0]))
val nais2 = listOf(makeNai(true, 30), makeNai(true, 50), makeNai(true, 20),
makeNai(true, 50), makeNai(true, 50), makeNai(true, 40))
- assertEquals(nais2[1], ranker.getBestNetwork(someRequest, nais2))
+ assertEquals(nais2[1], ranker.getBestNetwork(someRequest, nais2, nais2[1]))
}
}