Merge "Check if NetworkSpecifier is an instance of MatchAllNetworkSpecifier"
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 6fecee6..7197831 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -31,6 +31,7 @@
import android.net.NetworkState;
import android.net.ProxyInfo;
import android.net.UidRange;
+import android.net.VpnInfo;
import android.net.QosSocketInfo;
import android.os.Bundle;
import android.os.IBinder;
@@ -43,7 +44,6 @@
import com.android.connectivity.aidl.INetworkAgent;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
/**
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index f98a1f8..fbca7f1 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -420,7 +420,7 @@
throw new SocketException("Only AF_INET/AF_INET6 sockets supported");
}
- final int err = NetworkUtils.bindSocketToNetwork(fd.getInt$(), netId);
+ final int err = NetworkUtils.bindSocketToNetwork(fd, netId);
if (err != 0) {
// bindSocketToNetwork returns negative errno.
throw new ErrnoException("Binding socket to network " + netId, -err)
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index b5962c5..8be4af7 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -81,11 +81,11 @@
public native static boolean bindProcessToNetworkForHostResolution(int netId);
/**
- * Explicitly binds {@code socketfd} to the network designated by {@code netId}. This
+ * Explicitly binds {@code fd} to the network designated by {@code netId}. This
* overrides any binding via {@link #bindProcessToNetwork}.
* @return 0 on success or negative errno on failure.
*/
- public native static int bindSocketToNetwork(int socketfd, int netId);
+ public static native int bindSocketToNetwork(FileDescriptor fd, int netId);
/**
* Protect {@code fd} from VPN connections. After protecting, data sent through
@@ -93,9 +93,7 @@
* forwarded through the VPN.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static boolean protectFromVpn(FileDescriptor fd) {
- return protectFromVpn(fd.getInt$());
- }
+ public static native boolean protectFromVpn(FileDescriptor fd);
/**
* Protect {@code socketfd} from VPN connections. After protecting, data sent through
diff --git a/core/java/android/net/QosFilter.java b/core/java/android/net/QosFilter.java
index 0705468..ab55002 100644
--- a/core/java/android/net/QosFilter.java
+++ b/core/java/android/net/QosFilter.java
@@ -19,6 +19,8 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
+import java.net.InetAddress;
+
/**
* Provides the related filtering logic to the {@link NetworkAgent} to match {@link QosSession}s
* to their related {@link QosCallback}.
@@ -58,5 +60,16 @@
*/
@QosCallbackException.ExceptionType
public abstract int validate();
+
+ /**
+ * Determines whether or not the parameters is a match for the filter.
+ *
+ * @param address the local address
+ * @param startPort the start of the port range
+ * @param endPort the end of the port range
+ * @return whether the parameters match the local address of the filter
+ */
+ public abstract boolean matchesLocalAddress(@NonNull InetAddress address,
+ int startPort, int endPort);
}
diff --git a/core/java/android/net/QosSocketFilter.java b/core/java/android/net/QosSocketFilter.java
index f51a088..2080e68 100644
--- a/core/java/android/net/QosSocketFilter.java
+++ b/core/java/android/net/QosSocketFilter.java
@@ -26,7 +26,10 @@
import android.system.Os;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.FileDescriptor;
+import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
@@ -125,4 +128,39 @@
public Network getNetwork() {
return mQosSocketInfo.getNetwork();
}
+
+ /**
+ * @inheritDoc
+ */
+ @Override
+ public boolean matchesLocalAddress(@NonNull final InetAddress address, final int startPort,
+ final int endPort) {
+ if (mQosSocketInfo.getLocalSocketAddress() == null) {
+ return false;
+ }
+
+ return matchesLocalAddress(mQosSocketInfo.getLocalSocketAddress(), address, startPort,
+ endPort);
+ }
+
+ /**
+ * Called from {@link QosSocketFilter#matchesLocalAddress(InetAddress, int, int)} with the
+ * filterSocketAddress coming from {@link QosSocketInfo#getLocalSocketAddress()}.
+ * <p>
+ * This method exists for testing purposes since {@link QosSocketInfo} couldn't be mocked
+ * due to being final.
+ *
+ * @param filterSocketAddress the socket address of the filter
+ * @param address the address to compare the filterSocketAddressWith
+ * @param startPort the start of the port range to check
+ * @param endPort the end of the port range to check
+ */
+ @VisibleForTesting
+ public static boolean matchesLocalAddress(@NonNull final InetSocketAddress filterSocketAddress,
+ @NonNull final InetAddress address,
+ final int startPort, final int endPort) {
+ return startPort <= filterSocketAddress.getPort()
+ && endPort >= filterSocketAddress.getPort()
+ && filterSocketAddress.getAddress().equals(address);
+ }
}
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 2155246..e2af87e 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -18,6 +18,7 @@
#include <vector>
+#include <android/file_descriptor_jni.h>
#include <arpa/inet.h>
#include <linux/filter.h>
#include <linux/if_arp.h>
@@ -83,7 +84,7 @@
filter_code,
};
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
@@ -93,7 +94,7 @@
static void android_net_utils_detachBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
{
int optval_ignored = 0;
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &optval_ignored, sizeof(optval_ignored)) !=
0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
@@ -117,10 +118,9 @@
return (jboolean) !setNetworkForResolv(netId);
}
-static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jint socket,
- jint netId)
-{
- return setNetworkForSocket(netId, socket);
+static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jobject javaFd,
+ jint netId) {
+ return setNetworkForSocket(netId, AFileDescriptor_getFD(env, javaFd));
}
static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint socket)
@@ -128,6 +128,10 @@
return (jboolean) !protectFromVpn(socket);
}
+static jboolean android_net_utils_protectFromVpnWithFd(JNIEnv *env, jobject thiz, jobject javaFd) {
+ return android_net_utils_protectFromVpn(env, thiz, AFileDescriptor_getFD(env, javaFd));
+}
+
static jboolean android_net_utils_queryUserAccess(JNIEnv *env, jobject thiz, jint uid, jint netId)
{
return (jboolean) !queryUserAccess(uid, netId);
@@ -178,7 +182,7 @@
}
static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, jobject javaFd) {
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
int rcode;
std::vector<uint8_t> buf(MAXPACKETSIZE, 0);
@@ -205,7 +209,7 @@
}
static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobject javaFd) {
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
resNetworkCancel(fd);
jniSetFileDescriptorOfFD(env, javaFd, -1);
}
@@ -231,7 +235,7 @@
return NULL;
}
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
struct tcp_repair_window trw = {};
socklen_t size = sizeof(trw);
@@ -271,8 +275,9 @@
{ "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork },
{ "getBoundNetworkForProcess", "()I", (void*) android_net_utils_getBoundNetworkForProcess },
{ "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
- { "bindSocketToNetwork", "(II)I", (void*) android_net_utils_bindSocketToNetwork },
- { "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
+ { "bindSocketToNetwork", "(Ljava/io/FileDescriptor;I)I", (void*) android_net_utils_bindSocketToNetwork },
+ { "protectFromVpn", "(I)Z", (void*) android_net_utils_protectFromVpn },
+ { "protectFromVpn", "(Ljava/io/FileDescriptor;)Z", (void*) android_net_utils_protectFromVpnWithFd },
{ "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
{ "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter },
{ "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter },
diff --git a/framework/Android.bp b/framework/Android.bp
new file mode 100644
index 0000000..8db8d76
--- /dev/null
+++ b/framework/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2020 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.
+//
+
+// TODO: use a java_library in the bootclasspath instead
+filegroup {
+ name: "framework-connectivity-sources",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.aidl",
+ ],
+ path: "src",
+ visibility: [
+ "//frameworks/base",
+ "//packages/modules/Connectivity:__subpackages__",
+ ],
+}
\ No newline at end of file
diff --git a/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl b/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl
new file mode 100644
index 0000000..64b5567
--- /dev/null
+++ b/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2020, 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 perNmissions and
+ * limitations under the License.
+ */
+package com.android.connectivity.aidl;
+
+import android.net.NattKeepalivePacketData;
+import android.net.QosFilterParcelable;
+import android.net.TcpKeepalivePacketData;
+
+import com.android.connectivity.aidl.INetworkAgentRegistry;
+
+/**
+ * Interface to notify NetworkAgent of connectivity events.
+ * @hide
+ */
+oneway interface INetworkAgent {
+ void onRegistered(in INetworkAgentRegistry registry);
+ void onDisconnected();
+ void onBandwidthUpdateRequested();
+ void onValidationStatusChanged(int validationStatus,
+ in @nullable String captivePortalUrl);
+ void onSaveAcceptUnvalidated(boolean acceptUnvalidated);
+ void onStartNattSocketKeepalive(int slot, int intervalDurationMs,
+ in NattKeepalivePacketData packetData);
+ void onStartTcpSocketKeepalive(int slot, int intervalDurationMs,
+ in TcpKeepalivePacketData packetData);
+ void onStopSocketKeepalive(int slot);
+ void onSignalStrengthThresholdsUpdated(in int[] thresholds);
+ void onPreventAutomaticReconnect();
+ void onAddNattKeepalivePacketFilter(int slot,
+ in NattKeepalivePacketData packetData);
+ void onAddTcpKeepalivePacketFilter(int slot,
+ in TcpKeepalivePacketData packetData);
+ void onRemoveKeepalivePacketFilter(int slot);
+ void onQosFilterCallbackRegistered(int qosCallbackId, in QosFilterParcelable filterParcel);
+ void onQosCallbackUnregistered(int qosCallbackId);
+}
diff --git a/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl b/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
new file mode 100644
index 0000000..f0193db
--- /dev/null
+++ b/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
@@ -0,0 +1,41 @@
+/**
+ * Copyright (c) 2020, 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 perNmissions and
+ * limitations under the License.
+ */
+package com.android.connectivity.aidl;
+
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.QosSession;
+import android.telephony.data.EpsBearerQosSessionAttributes;
+
+/**
+ * Interface for NetworkAgents to send network network properties.
+ * @hide
+ */
+oneway interface INetworkAgentRegistry {
+ void sendNetworkCapabilities(in NetworkCapabilities nc);
+ void sendLinkProperties(in LinkProperties lp);
+ // TODO: consider replacing this by "markConnected()" and removing
+ void sendNetworkInfo(in NetworkInfo info);
+ void sendScore(int score);
+ void sendExplicitlySelected(boolean explicitlySelected, boolean acceptPartial);
+ void sendSocketKeepaliveEvent(int slot, int reason);
+ void sendUnderlyingNetworks(in @nullable List<Network> networks);
+ void sendEpsQosSessionAvailable(int callbackId, in QosSession session, in EpsBearerQosSessionAttributes attributes);
+ void sendQosSessionLost(int qosCallbackId, in QosSession session);
+ void sendQosCallbackError(int qosCallbackId, int exceptionType);
+}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 45ce868..410ec1d 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -133,6 +133,7 @@
import android.net.UidRange;
import android.net.UidRangeParcel;
import android.net.Uri;
+import android.net.VpnInfo;
import android.net.VpnManager;
import android.net.VpnService;
import android.net.metrics.INetdEventListener;
@@ -184,7 +185,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.AsyncChannel;
@@ -1458,9 +1458,8 @@
return;
}
final String action = blocked ? "BLOCKED" : "UNBLOCKED";
- final NetworkRequest satisfiedRequest = nri.getSatisfiedRequest();
- final int requestId = satisfiedRequest != null
- ? satisfiedRequest.requestId : nri.mRequests.get(0).requestId;
+ final int requestId = nri.getActiveRequest() != null
+ ? nri.getActiveRequest().requestId : nri.mRequests.get(0).requestId;
mNetworkInfoBlockingLogs.log(String.format(
"%s %d(%d) on netId %d", action, nri.mUid, requestId, net.getNetId()));
}
@@ -2730,7 +2729,7 @@
@VisibleForTesting
NetworkRequestInfo[] requestsSortedById() {
NetworkRequestInfo[] requests = new NetworkRequestInfo[0];
- requests = mNetworkRequests.values().toArray(requests);
+ requests = getNrisFromGlobalRequests().toArray(requests);
// Sort the array based off the NRI containing the min requestId in its requests.
Arrays.sort(requests,
Comparator.comparingInt(nri -> Collections.min(nri.mRequests,
@@ -3435,10 +3434,10 @@
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest request = nai.requestAt(i);
final NetworkRequestInfo nri = mNetworkRequests.get(request);
- final NetworkAgentInfo currentNetwork = nri.mSatisfier;
+ final NetworkAgentInfo currentNetwork = nri.getSatisfier();
if (currentNetwork != null
&& currentNetwork.network.getNetId() == nai.network.getNetId()) {
- nri.mSatisfier = null;
+ nri.setSatisfier(null, null);
sendUpdatedScoreToFactories(request, null);
}
}
@@ -3516,42 +3515,63 @@
return null;
}
- private void handleRegisterNetworkRequestWithIntent(Message msg) {
+ private void handleRegisterNetworkRequestWithIntent(@NonNull final Message msg) {
final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj);
-
- NetworkRequestInfo existingRequest = findExistingNetworkRequestInfo(nri.mPendingIntent);
+ // handleRegisterNetworkRequestWithIntent() doesn't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleRegisterNetworkRequestWithIntent");
+ final NetworkRequestInfo existingRequest =
+ findExistingNetworkRequestInfo(nri.mPendingIntent);
if (existingRequest != null) { // remove the existing request.
- if (DBG) log("Replacing " + existingRequest.request + " with "
- + nri.request + " because their intents matched.");
- handleReleaseNetworkRequest(existingRequest.request, getCallingUid(),
+ if (DBG) {
+ log("Replacing " + existingRequest.mRequests.get(0) + " with "
+ + nri.mRequests.get(0) + " because their intents matched.");
+ }
+ handleReleaseNetworkRequest(existingRequest.mRequests.get(0), getCallingUid(),
/* callOnUnavailable */ false);
}
handleRegisterNetworkRequest(nri);
}
- private void handleRegisterNetworkRequest(NetworkRequestInfo nri) {
+ private void handleRegisterNetworkRequest(@NonNull final NetworkRequestInfo nri) {
ensureRunningOnConnectivityServiceThread();
- mNetworkRequests.put(nri.request, nri);
mNetworkRequestInfoLogs.log("REGISTER " + nri);
- if (nri.request.isListen()) {
- for (NetworkAgentInfo network : mNetworkAgentInfos) {
- if (nri.request.networkCapabilities.hasSignalStrength() &&
- network.satisfiesImmutableCapabilitiesOf(nri.request)) {
- updateSignalStrengthThresholds(network, "REGISTER", nri.request);
+ for (final NetworkRequest req : nri.mRequests) {
+ mNetworkRequests.put(req, nri);
+ if (req.isListen()) {
+ for (final NetworkAgentInfo network : mNetworkAgentInfos) {
+ if (req.networkCapabilities.hasSignalStrength()
+ && network.satisfiesImmutableCapabilitiesOf(req)) {
+ updateSignalStrengthThresholds(network, "REGISTER", req);
+ }
}
}
}
rematchAllNetworksAndRequests();
- if (nri.request.isRequest() && nri.mSatisfier == null) {
- sendUpdatedScoreToFactories(nri.request, null);
+ // If an active request exists, return as its score has already been sent if needed.
+ if (null != nri.getActiveRequest()) {
+ return;
+ }
+
+ // As this request was not satisfied on rematch and thus never had any scores sent to the
+ // factories, send null now for each request of type REQUEST.
+ for (final NetworkRequest req : nri.mRequests) {
+ if (!req.isRequest()) {
+ continue;
+ }
+ sendUpdatedScoreToFactories(req, null);
}
}
- private void handleReleaseNetworkRequestWithIntent(PendingIntent pendingIntent,
- int callingUid) {
- NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent);
+ private void handleReleaseNetworkRequestWithIntent(@NonNull final PendingIntent pendingIntent,
+ final int callingUid) {
+ final NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent);
if (nri != null) {
- handleReleaseNetworkRequest(nri.request, callingUid, /* callOnUnavailable */ false);
+ // handleReleaseNetworkRequestWithIntent() paths don't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleReleaseNetworkRequestWithIntent");
+ handleReleaseNetworkRequest(
+ nri.mRequests.get(0),
+ callingUid,
+ /* callOnUnavailable */ false);
}
}
@@ -3605,6 +3625,11 @@
return false;
}
for (final NetworkRequest req : nri.mRequests) {
+ // This multilayer listen request is satisfied therefore no further requests need to be
+ // evaluated deeming this network not a potential satisfier.
+ if (req.isListen() && nri.getActiveRequest() == req) {
+ return false;
+ }
// As non-multilayer listen requests have already returned, the below would only happen
// for a multilayer request therefore continue to the next request if available.
if (req.isListen()) {
@@ -3625,7 +3650,7 @@
// 2. Unvalidated WiFi will not be reaped when validated cellular
// is currently satisfying the request. This is desirable when
// WiFi ends up validating and out scoring cellular.
- || nri.mSatisfier.getCurrentScore()
+ || nri.getSatisfier().getCurrentScore()
< candidate.getCurrentScoreAsValidated();
return isNetworkNeeded;
}
@@ -3650,30 +3675,45 @@
return nri;
}
- private void handleTimedOutNetworkRequest(final NetworkRequestInfo nri) {
- ensureRunningOnConnectivityServiceThread();
- if (mNetworkRequests.get(nri.request) == null) {
- return;
+ private void ensureNotMultilayerRequest(@NonNull final NetworkRequestInfo nri,
+ final String callingMethod) {
+ if (nri.isMultilayerRequest()) {
+ throw new IllegalStateException(
+ callingMethod + " does not support multilayer requests.");
}
- if (nri.mSatisfier != null) {
- return;
- }
- if (VDBG || (DBG && nri.request.isRequest())) {
- log("releasing " + nri.request + " (timeout)");
- }
- handleRemoveNetworkRequest(nri);
- callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_UNAVAIL, 0);
}
- private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid,
- boolean callOnUnavailable) {
+ private void handleTimedOutNetworkRequest(@NonNull final NetworkRequestInfo nri) {
+ ensureRunningOnConnectivityServiceThread();
+ // handleTimedOutNetworkRequest() is part of the requestNetwork() flow which works off of a
+ // single NetworkRequest and thus does not apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleTimedOutNetworkRequest");
+ if (mNetworkRequests.get(nri.mRequests.get(0)) == null) {
+ return;
+ }
+ if (nri.getSatisfier() != null) {
+ return;
+ }
+ if (VDBG || (DBG && nri.mRequests.get(0).isRequest())) {
+ log("releasing " + nri.mRequests.get(0) + " (timeout)");
+ }
+ handleRemoveNetworkRequest(nri);
+ callCallbackForRequest(
+ nri, null, ConnectivityManager.CALLBACK_UNAVAIL, 0);
+ }
+
+ private void handleReleaseNetworkRequest(@NonNull final NetworkRequest request,
+ final int callingUid,
+ final boolean callOnUnavailable) {
final NetworkRequestInfo nri =
getNriForAppRequest(request, callingUid, "release NetworkRequest");
if (nri == null) {
return;
}
- if (VDBG || (DBG && nri.request.isRequest())) {
- log("releasing " + nri.request + " (release request)");
+ // handleReleaseNetworkRequest() paths don't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleReleaseNetworkRequest");
+ if (VDBG || (DBG && request.isRequest())) {
+ log("releasing " + request + " (release request)");
}
handleRemoveNetworkRequest(nri);
if (callOnUnavailable) {
@@ -3681,42 +3721,88 @@
}
}
- private void handleRemoveNetworkRequest(final NetworkRequestInfo nri) {
+ private void handleRemoveNetworkRequest(@NonNull final NetworkRequestInfo nri) {
ensureRunningOnConnectivityServiceThread();
nri.unlinkDeathRecipient();
- mNetworkRequests.remove(nri.request);
-
+ for (final NetworkRequest req : nri.mRequests) {
+ mNetworkRequests.remove(req);
+ if (req.isListen()) {
+ removeListenRequestFromNetworks(req);
+ }
+ }
mNetworkRequestCounter.decrementCount(nri.mUid);
-
mNetworkRequestInfoLogs.log("RELEASE " + nri);
- if (nri.request.isRequest()) {
- boolean wasKept = false;
- final NetworkAgentInfo nai = nri.mSatisfier;
- if (nai != null) {
- boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
- nai.removeRequest(nri.request.requestId);
- if (VDBG || DDBG) {
- log(" Removing from current network " + nai.toShortString()
- + ", leaving " + nai.numNetworkRequests() + " requests.");
- }
- // If there are still lingered requests on this network, don't tear it down,
- // but resume lingering instead.
- final long now = SystemClock.elapsedRealtime();
- if (updateLingerState(nai, now)) {
- notifyNetworkLosing(nai, now);
- }
- if (unneeded(nai, UnneededFor.TEARDOWN)) {
- if (DBG) log("no live requests for " + nai.toShortString() + "; disconnecting");
- teardownUnneededNetwork(nai);
- } else {
- wasKept = true;
- }
- nri.mSatisfier = null;
- if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) {
- // Went from foreground to background.
- updateCapabilitiesForNetwork(nai);
- }
+
+ if (null != nri.getActiveRequest()) {
+ if (nri.getActiveRequest().isRequest()) {
+ removeSatisfiedNetworkRequestFromNetwork(nri);
+ } else {
+ nri.setSatisfier(null, null);
+ }
+ }
+
+ cancelNpiRequests(nri);
+ }
+
+ private void cancelNpiRequests(@NonNull final NetworkRequestInfo nri) {
+ for (final NetworkRequest req : nri.mRequests) {
+ cancelNpiRequest(req);
+ }
+ }
+
+ private void cancelNpiRequest(@NonNull final NetworkRequest req) {
+ if (req.isRequest()) {
+ for (final NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
+ npi.cancelRequest(req);
+ }
+ }
+ }
+
+ private void removeListenRequestFromNetworks(@NonNull final NetworkRequest req) {
+ // listens don't have a singular affected Network. Check all networks to see
+ // if this listen request applies and remove it.
+ for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
+ nai.removeRequest(req.requestId);
+ if (req.networkCapabilities.hasSignalStrength()
+ && nai.satisfiesImmutableCapabilitiesOf(req)) {
+ updateSignalStrengthThresholds(nai, "RELEASE", req);
+ }
+ }
+ }
+
+ /**
+ * Remove a NetworkRequestInfo's satisfied request from its 'satisfier' (NetworkAgentInfo) and
+ * manage the necessary upkeep (linger, teardown networks, etc.) when doing so.
+ * @param nri the NetworkRequestInfo to disassociate from its current NetworkAgentInfo
+ */
+ private void removeSatisfiedNetworkRequestFromNetwork(@NonNull final NetworkRequestInfo nri) {
+ boolean wasKept = false;
+ final NetworkAgentInfo nai = nri.getSatisfier();
+ if (nai != null) {
+ final int requestLegacyType = nri.getActiveRequest().legacyType;
+ final boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
+ nai.removeRequest(nri.getActiveRequest().requestId);
+ if (VDBG || DDBG) {
+ log(" Removing from current network " + nai.toShortString()
+ + ", leaving " + nai.numNetworkRequests() + " requests.");
+ }
+ // If there are still lingered requests on this network, don't tear it down,
+ // but resume lingering instead.
+ final long now = SystemClock.elapsedRealtime();
+ if (updateLingerState(nai, now)) {
+ notifyNetworkLosing(nai, now);
+ }
+ if (unneeded(nai, UnneededFor.TEARDOWN)) {
+ if (DBG) log("no live requests for " + nai.toShortString() + "; disconnecting");
+ teardownUnneededNetwork(nai);
+ } else {
+ wasKept = true;
+ }
+ nri.setSatisfier(null, null);
+ if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) {
+ // Went from foreground to background.
+ updateCapabilitiesForNetwork(nai);
}
// Maintain the illusion. When this request arrived, we might have pretended
@@ -3724,15 +3810,15 @@
// connected. Now that this request has gone away, we might have to pretend
// that the network disconnected. LegacyTypeTracker will generate that
// phantom disconnect for this type.
- if (nri.request.legacyType != TYPE_NONE && nai != null) {
+ if (requestLegacyType != TYPE_NONE) {
boolean doRemove = true;
if (wasKept) {
// check if any of the remaining requests for this network are for the
// same legacy type - if so, don't remove the nai
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest otherRequest = nai.requestAt(i);
- if (otherRequest.legacyType == nri.request.legacyType &&
- otherRequest.isRequest()) {
+ if (otherRequest.legacyType == requestLegacyType
+ && otherRequest.isRequest()) {
if (DBG) log(" still have other legacy request - leaving");
doRemove = false;
}
@@ -3740,21 +3826,7 @@
}
if (doRemove) {
- mLegacyTypeTracker.remove(nri.request.legacyType, nai, false);
- }
- }
-
- for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
- npi.cancelRequest(nri.request);
- }
- } else {
- // listens don't have a singular affectedNetwork. Check all networks to see
- // if this listen request applies and remove it.
- for (NetworkAgentInfo nai : mNetworkAgentInfos) {
- nai.removeRequest(nri.request.requestId);
- if (nri.request.networkCapabilities.hasSignalStrength() &&
- nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
- updateSignalStrengthThresholds(nai, "RELEASE", nri.request);
+ mLegacyTypeTracker.remove(requestLegacyType, nai, false);
}
}
}
@@ -4832,16 +4904,14 @@
if (interfaces.isEmpty()) return null;
- VpnInfo info = new VpnInfo();
- info.ownerUid = nai.networkCapabilities.getOwnerUid();
- info.vpnIface = nai.linkProperties.getInterfaceName();
// Must be non-null or NetworkStatsService will crash.
// Cannot happen in production code because Vpn only registers the NetworkAgent after the
// tun or ipsec interface is created.
- if (info.vpnIface == null) return null;
- info.underlyingIfaces = interfaces.toArray(new String[0]);
+ if (nai.linkProperties.getInterfaceName() == null) return null;
- return info;
+ return new VpnInfo(nai.networkCapabilities.getOwnerUid(),
+ nai.linkProperties.getInterfaceName(),
+ interfaces.toArray(new String[0]));
}
/**
@@ -5417,18 +5487,38 @@
/**
* Tracks info about the requester.
- * Also used to notice when the calling process dies so we can self-expire
+ * Also used to notice when the calling process dies so as to self-expire
*/
@VisibleForTesting
protected class NetworkRequestInfo implements IBinder.DeathRecipient {
final List<NetworkRequest> mRequests;
- final NetworkRequest request;
+
+ // mSatisfier and mActiveRequest rely on one another therefore set them together.
+ void setSatisfier(
+ @Nullable final NetworkAgentInfo satisfier,
+ @Nullable final NetworkRequest activeRequest) {
+ mSatisfier = satisfier;
+ mActiveRequest = activeRequest;
+ }
// The network currently satisfying this request, or null if none. Must only be touched
// on the handler thread. This only makes sense for network requests and not for listens,
// as defined by NetworkRequest#isRequest(). For listens, this is always null.
@Nullable
- NetworkAgentInfo mSatisfier;
+ private NetworkAgentInfo mSatisfier;
+ NetworkAgentInfo getSatisfier() {
+ return mSatisfier;
+ }
+
+ // The request in mRequests assigned to a network agent. This is null if none of the
+ // requests in mRequests can be satisfied. This member has the constraint of only being
+ // accessible on the handler thread.
+ @Nullable
+ private NetworkRequest mActiveRequest;
+ NetworkRequest getActiveRequest() {
+ return mActiveRequest;
+ }
+
final PendingIntent mPendingIntent;
boolean mPendingIntentSent;
private final IBinder mBinder;
@@ -5437,7 +5527,6 @@
final Messenger messenger;
NetworkRequestInfo(NetworkRequest r, PendingIntent pi) {
- request = r;
mRequests = initializeRequests(r);
ensureAllNetworkRequestsHaveType(mRequests);
mPendingIntent = pi;
@@ -5451,7 +5540,6 @@
NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder) {
super();
messenger = m;
- request = r;
mRequests = initializeRequests(r);
ensureAllNetworkRequestsHaveType(mRequests);
mBinder = binder;
@@ -5481,20 +5569,6 @@
return Collections.unmodifiableList(tempRequests);
}
- private NetworkRequest getSatisfiedRequest() {
- if (mSatisfier == null) {
- return null;
- }
-
- for (NetworkRequest req : mRequests) {
- if (mSatisfier.isSatisfyingRequest(req.requestId)) {
- return req;
- }
- }
-
- return null;
- }
-
void unlinkDeathRecipient() {
if (mBinder != null) {
mBinder.unlinkToDeath(this, 0);
@@ -5541,6 +5615,10 @@
private int[] getSignalStrengthThresholds(@NonNull final NetworkAgentInfo nai) {
final SortedSet<Integer> thresholds = new TreeSet<>();
synchronized (nai) {
+ // mNetworkRequests may contain the same value multiple times in case of
+ // multilayer requests. It won't matter in this case because the thresholds
+ // will then be the same and be deduplicated as they enter the `thresholds` set.
+ // TODO : have mNetworkRequests be a Set<NetworkRequestInfo> or the like.
for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
for (final NetworkRequest req : nri.mRequests) {
if (req.networkCapabilities.hasSignalStrength()
@@ -5918,13 +5996,19 @@
}
@Override
- public void declareNetworkRequestUnfulfillable(NetworkRequest request) {
+ public void declareNetworkRequestUnfulfillable(@NonNull final NetworkRequest request) {
if (request.hasTransport(TRANSPORT_TEST)) {
enforceNetworkFactoryOrTestNetworksPermission();
} else {
enforceNetworkFactoryPermission();
}
- mHandler.post(() -> handleReleaseNetworkRequest(request, mDeps.getCallingUid(), true));
+ final NetworkRequestInfo nri = mNetworkRequests.get(request);
+ if (nri != null) {
+ // declareNetworkRequestUnfulfillable() paths don't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "declareNetworkRequestUnfulfillable");
+ mHandler.post(() -> handleReleaseNetworkRequest(
+ nri.mRequests.get(0), mDeps.getCallingUid(), true));
+ }
}
// NOTE: Accessed on multiple threads, must be synchronized on itself.
@@ -6849,6 +6933,39 @@
}
}
+ private void sendUpdatedScoreToFactories(
+ @NonNull final NetworkReassignment.RequestReassignment event) {
+ // If a request of type REQUEST is now being satisfied by a new network.
+ if (null != event.mNewNetworkRequest && event.mNewNetworkRequest.isRequest()) {
+ sendUpdatedScoreToFactories(event.mNewNetworkRequest, event.mNewNetwork);
+ }
+
+ // If a previously satisfied request of type REQUEST is no longer being satisfied.
+ if (null != event.mOldNetworkRequest && event.mOldNetworkRequest.isRequest()
+ && event.mOldNetworkRequest != event.mNewNetworkRequest) {
+ sendUpdatedScoreToFactories(event.mOldNetworkRequest, null);
+ }
+
+ cancelMultilayerLowerPriorityNpiRequests(event.mNetworkRequestInfo);
+ }
+
+ /**
+ * Cancel with all NPIs the given NRI's multilayer requests that are a lower priority than
+ * its currently satisfied active request.
+ * @param nri the NRI to cancel lower priority requests for.
+ */
+ private void cancelMultilayerLowerPriorityNpiRequests(
+ @NonNull final NetworkRequestInfo nri) {
+ if (!nri.isMultilayerRequest() || null == nri.mActiveRequest) {
+ return;
+ }
+
+ final int indexOfNewRequest = nri.mRequests.indexOf(nri.mActiveRequest);
+ for (int i = indexOfNewRequest + 1; i < nri.mRequests.size(); i++) {
+ cancelNpiRequest(nri.mRequests.get(i));
+ }
+ }
+
private void sendUpdatedScoreToFactories(@NonNull NetworkRequest networkRequest,
@Nullable NetworkAgentInfo nai) {
final int score;
@@ -6869,21 +6986,35 @@
}
/** Sends all current NetworkRequests to the specified factory. */
- private void sendAllRequestsToProvider(NetworkProviderInfo npi) {
+ private void sendAllRequestsToProvider(@NonNull final NetworkProviderInfo npi) {
ensureRunningOnConnectivityServiceThread();
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- if (nri.request.isListen()) continue;
- NetworkAgentInfo nai = nri.mSatisfier;
- final int score;
- final int serial;
- if (nai != null) {
- score = nai.getCurrentScore();
- serial = nai.factorySerialNumber;
- } else {
- score = 0;
- serial = NetworkProvider.ID_NONE;
+ for (final NetworkRequestInfo nri : getNrisFromGlobalRequests()) {
+ for (final NetworkRequest req : nri.mRequests) {
+ if (req.isListen() && nri.getActiveRequest() == req) {
+ break;
+ }
+ if (req.isListen()) {
+ continue;
+ }
+ // Only set the nai for the request it is satisfying.
+ final NetworkAgentInfo nai =
+ nri.getActiveRequest() == req ? nri.getSatisfier() : null;
+ final int score;
+ final int serial;
+ if (null != nai) {
+ score = nai.getCurrentScore();
+ serial = nai.factorySerialNumber;
+ } else {
+ score = 0;
+ serial = NetworkProvider.ID_NONE;
+ }
+ npi.requestNetwork(req, score, serial);
+ // For multilayer requests, don't send lower priority requests if a higher priority
+ // request is already satisfied.
+ if (null != nai) {
+ break;
+ }
}
- npi.requestNetwork(nri.request, score, serial);
}
}
@@ -6892,7 +7023,12 @@
if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE && !nri.mPendingIntentSent) {
Intent intent = new Intent();
intent.putExtra(ConnectivityManager.EXTRA_NETWORK, networkAgent.network);
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, nri.request);
+ // If apps could file multi-layer requests with PendingIntents, they'd need to know
+ // which of the layer is satisfied alongside with some ID for the request. Hence, if
+ // such an API is ever implemented, there is no doubt the right request to send in
+ // EXTRA_NETWORK_REQUEST is mActiveRequest, and whatever ID would be added would need to
+ // be sent as a separate extra.
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, nri.getActiveRequest());
nri.mPendingIntentSent = true;
sendIntent(nri.mPendingIntent, intent);
}
@@ -6922,8 +7058,9 @@
releasePendingNetworkRequestWithDelay(pendingIntent);
}
- private void callCallbackForRequest(NetworkRequestInfo nri,
- NetworkAgentInfo networkAgent, int notificationType, int arg1) {
+ private void callCallbackForRequest(@NonNull final NetworkRequestInfo nri,
+ @NonNull final NetworkAgentInfo networkAgent, final int notificationType,
+ final int arg1) {
if (nri.messenger == null) {
// Default request has no msgr. Also prevents callbacks from being invoked for
// NetworkRequestInfos registered with ConnectivityDiagnostics requests. Those callbacks
@@ -6931,8 +7068,14 @@
return;
}
Bundle bundle = new Bundle();
+ // In the case of multi-layer NRIs, the first request is not necessarily the one that
+ // is satisfied. This is vexing, but the ConnectivityManager code that receives this
+ // callback is only using the request as a token to identify the callback, so it doesn't
+ // matter too much at this point as long as the callback can be found.
+ // TODO b/177608132: make sure callbacks are indexed by NRIs and not NetworkRequest objects.
// TODO: check if defensive copies of data is needed.
- putParcelable(bundle, new NetworkRequest(nri.request));
+ final NetworkRequest nrForCallback = new NetworkRequest(nri.mRequests.get(0));
+ putParcelable(bundle, nrForCallback);
Message msg = Message.obtain();
if (notificationType != ConnectivityManager.CALLBACK_UNAVAIL) {
putParcelable(bundle, networkAgent.network);
@@ -6945,7 +7088,7 @@
putParcelable(
bundle,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- nc, nri.mUid, nri.request.getRequestorPackageName()));
+ nc, nri.mUid, nrForCallback.getRequestorPackageName()));
putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
networkAgent.linkProperties, nri.mPid, nri.mUid));
// For this notification, arg1 contains the blocked status.
@@ -6964,7 +7107,7 @@
putParcelable(
bundle,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- netCap, nri.mUid, nri.request.getRequestorPackageName()));
+ netCap, nri.mUid, nrForCallback.getRequestorPackageName()));
break;
}
case ConnectivityManager.CALLBACK_IP_CHANGED: {
@@ -6983,12 +7126,12 @@
try {
if (VDBG) {
String notification = ConnectivityManager.getCallbackName(notificationType);
- log("sending notification " + notification + " for " + nri.request);
+ log("sending notification " + notification + " for " + nrForCallback);
}
nri.messenger.send(msg);
} catch (RemoteException e) {
// may occur naturally in the race of binder death.
- loge("RemoteException caught trying to send a callback msg for " + nri.request);
+ loge("RemoteException caught trying to send a callback msg for " + nrForCallback);
}
}
@@ -7064,19 +7207,25 @@
}
private void processNewlyLostListenRequests(@NonNull final NetworkAgentInfo nai) {
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- NetworkRequest nr = nri.request;
+ for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+ if (nri.isMultilayerRequest()) {
+ continue;
+ }
+ final NetworkRequest nr = nri.mRequests.get(0);
if (!nr.isListen()) continue;
if (nai.isSatisfyingRequest(nr.requestId) && !nai.satisfies(nr)) {
- nai.removeRequest(nri.request.requestId);
+ nai.removeRequest(nr.requestId);
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_LOST, 0);
}
}
}
private void processNewlySatisfiedListenRequests(@NonNull final NetworkAgentInfo nai) {
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- NetworkRequest nr = nri.request;
+ for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+ if (nri.isMultilayerRequest()) {
+ continue;
+ }
+ final NetworkRequest nr = nri.mRequests.get(0);
if (!nr.isListen()) continue;
if (nai.satisfies(nr) && !nai.isSatisfyingRequest(nr.requestId)) {
nai.addRequest(nr);
@@ -7088,19 +7237,25 @@
// An accumulator class to gather the list of changes that result from a rematch.
private static class NetworkReassignment {
static class RequestReassignment {
- @NonNull public final NetworkRequestInfo mRequest;
+ @NonNull public final NetworkRequestInfo mNetworkRequestInfo;
+ @NonNull public final NetworkRequest mOldNetworkRequest;
+ @NonNull public final NetworkRequest mNewNetworkRequest;
@Nullable public final NetworkAgentInfo mOldNetwork;
@Nullable public final NetworkAgentInfo mNewNetwork;
- RequestReassignment(@NonNull final NetworkRequestInfo request,
+ RequestReassignment(@NonNull final NetworkRequestInfo networkRequestInfo,
+ @NonNull final NetworkRequest oldNetworkRequest,
+ @NonNull final NetworkRequest newNetworkRequest,
@Nullable final NetworkAgentInfo oldNetwork,
@Nullable final NetworkAgentInfo newNetwork) {
- mRequest = request;
+ mNetworkRequestInfo = networkRequestInfo;
+ mOldNetworkRequest = oldNetworkRequest;
+ mNewNetworkRequest = newNetworkRequest;
mOldNetwork = oldNetwork;
mNewNetwork = newNetwork;
}
public String toString() {
- return mRequest.mRequests.get(0).requestId + " : "
+ return mNetworkRequestInfo.mRequests.get(0).requestId + " : "
+ (null != mOldNetwork ? mOldNetwork.network.getNetId() : "null")
+ " → " + (null != mNewNetwork ? mNewNetwork.network.getNetId() : "null");
}
@@ -7118,7 +7273,7 @@
// sure this stays true, but without imposing this expensive check on all
// reassignments on all user devices.
for (final RequestReassignment existing : mReassignments) {
- if (existing.mRequest.equals(reassignment.mRequest)) {
+ if (existing.mNetworkRequestInfo.equals(reassignment.mNetworkRequestInfo)) {
throw new IllegalStateException("Trying to reassign ["
+ reassignment + "] but already have ["
+ existing + "]");
@@ -7133,7 +7288,7 @@
@Nullable
private RequestReassignment getReassignment(@NonNull final NetworkRequestInfo nri) {
for (final RequestReassignment event : getRequestReassignments()) {
- if (nri == event.mRequest) return event;
+ if (nri == event.mNetworkRequestInfo) return event;
}
return null;
}
@@ -7160,6 +7315,8 @@
}
private void updateSatisfiersForRematchRequest(@NonNull final NetworkRequestInfo nri,
+ @NonNull final NetworkRequest previousRequest,
+ @NonNull final NetworkRequest newRequest,
@Nullable final NetworkAgentInfo previousSatisfier,
@Nullable final NetworkAgentInfo newSatisfier,
final long now) {
@@ -7169,58 +7326,98 @@
if (VDBG || DDBG) {
log(" accepting network in place of " + previousSatisfier.toShortString());
}
- previousSatisfier.removeRequest(nri.request.requestId);
- previousSatisfier.lingerRequest(nri.request.requestId, now, mLingerDelayMs);
+ previousSatisfier.removeRequest(previousRequest.requestId);
+ previousSatisfier.lingerRequest(previousRequest.requestId, now, mLingerDelayMs);
} else {
if (VDBG || DDBG) log(" accepting network in place of null");
}
- newSatisfier.unlingerRequest(nri.request.requestId);
- if (!newSatisfier.addRequest(nri.request)) {
+ newSatisfier.unlingerRequest(newRequest.requestId);
+ if (!newSatisfier.addRequest(newRequest)) {
Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
- + nri.request);
+ + newRequest);
}
} else {
if (DBG) {
log("Network " + previousSatisfier.toShortString() + " stopped satisfying"
- + " request " + nri.request.requestId);
+ + " request " + previousRequest.requestId);
}
- previousSatisfier.removeRequest(nri.request.requestId);
+ previousSatisfier.removeRequest(previousRequest.requestId);
}
- nri.mSatisfier = newSatisfier;
+ nri.setSatisfier(newSatisfier, newRequest);
}
+ /**
+ * This function is triggered when something can affect what network should satisfy what
+ * request, and it computes the network reassignment from the passed collection of requests to
+ * network match to the one that the system should now have. That data is encoded in an
+ * object that is a list of changes, each of them having an NRI, and old satisfier, and a new
+ * satisfier.
+ *
+ * After the reassignment is computed, it is applied to the state objects.
+ *
+ * @param networkRequests the nri objects to evaluate for possible network reassignment
+ * @return NetworkReassignment listing of proposed network assignment changes
+ */
@NonNull
- private NetworkReassignment computeNetworkReassignment() {
- ensureRunningOnConnectivityServiceThread();
+ private NetworkReassignment computeNetworkReassignment(
+ @NonNull final Collection<NetworkRequestInfo> networkRequests) {
final NetworkReassignment changes = new NetworkReassignment();
// Gather the list of all relevant agents and sort them by score.
final ArrayList<NetworkAgentInfo> nais = new ArrayList<>();
for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
- if (!nai.everConnected) continue;
+ if (!nai.everConnected) {
+ continue;
+ }
nais.add(nai);
}
- for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
- if (nri.request.isListen()) continue;
- final NetworkAgentInfo bestNetwork = mNetworkRanker.getBestNetwork(nri.request, nais);
+ for (final NetworkRequestInfo nri : networkRequests) {
+ // Non-multilayer listen requests can be ignored.
+ if (!nri.isMultilayerRequest() && nri.mRequests.get(0).isListen()) {
+ continue;
+ }
+ NetworkAgentInfo bestNetwork = null;
+ NetworkRequest bestRequest = null;
+ for (final NetworkRequest req : nri.mRequests) {
+ bestNetwork = mNetworkRanker.getBestNetwork(req, nais);
+ // Stop evaluating as the highest possible priority request is satisfied.
+ if (null != bestNetwork) {
+ bestRequest = req;
+ break;
+ }
+ }
if (bestNetwork != nri.mSatisfier) {
// bestNetwork may be null if no network can satisfy this request.
changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
- nri, nri.mSatisfier, bestNetwork));
+ nri, nri.mActiveRequest, bestRequest, nri.getSatisfier(), bestNetwork));
}
}
return changes;
}
+ private Set<NetworkRequestInfo> getNrisFromGlobalRequests() {
+ return new HashSet<>(mNetworkRequests.values());
+ }
+
/**
- * Attempt to rematch all Networks with NetworkRequests. This may result in Networks
+ * Attempt to rematch all Networks with all NetworkRequests. This may result in Networks
* being disconnected.
*/
private void rematchAllNetworksAndRequests() {
+ rematchNetworksAndRequests(getNrisFromGlobalRequests());
+ }
+
+ /**
+ * Attempt to rematch all Networks with given NetworkRequests. This may result in Networks
+ * being disconnected.
+ */
+ private void rematchNetworksAndRequests(
+ @NonNull final Set<NetworkRequestInfo> networkRequests) {
+ ensureRunningOnConnectivityServiceThread();
// TODO: This may be slow, and should be optimized.
final long now = SystemClock.elapsedRealtime();
- final NetworkReassignment changes = computeNetworkReassignment();
+ final NetworkReassignment changes = computeNetworkReassignment(networkRequests);
if (VDBG || DDBG) {
log(changes.debugString());
} else if (DBG) {
@@ -7245,8 +7442,10 @@
// the linger status.
for (final NetworkReassignment.RequestReassignment event :
changes.getRequestReassignments()) {
- updateSatisfiersForRematchRequest(event.mRequest, event.mOldNetwork,
- event.mNewNetwork, now);
+ updateSatisfiersForRematchRequest(event.mNetworkRequestInfo,
+ event.mOldNetworkRequest, event.mNewNetworkRequest,
+ event.mOldNetwork, event.mNewNetwork,
+ now);
}
final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork();
@@ -7298,12 +7497,12 @@
// trying to connect if they know they cannot match it.
// TODO - this could get expensive if there are a lot of outstanding requests for this
// network. Think of a way to reduce this. Push netid->request mapping to each factory?
- sendUpdatedScoreToFactories(event.mRequest.request, event.mNewNetwork);
+ sendUpdatedScoreToFactories(event);
if (null != event.mNewNetwork) {
- notifyNetworkAvailable(event.mNewNetwork, event.mRequest);
+ notifyNetworkAvailable(event.mNewNetwork, event.mNetworkRequestInfo);
} else {
- callCallbackForRequest(event.mRequest, event.mOldNetwork,
+ callCallbackForRequest(event.mNetworkRequestInfo, event.mOldNetwork,
ConnectivityManager.CALLBACK_LOST, 0);
}
}
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index 16c4865..083c8c8 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -38,6 +38,7 @@
import android.os.ConditionVariable
import android.os.IBinder
import android.os.INetworkManagementService
+import android.os.UserHandle
import android.testing.TestableContext
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -55,10 +56,13 @@
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.AdditionalAnswers
import org.mockito.Mock
import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
import org.mockito.MockitoAnnotations
@@ -143,7 +147,10 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- doNothing().`when`(context).sendStickyBroadcastAsUser(any(), any(), any())
+ val asUserCtx = mock(Context::class.java, AdditionalAnswers.delegatesTo<Context>(context))
+ doReturn(UserHandle.ALL).`when`(asUserCtx).user
+ doReturn(asUserCtx).`when`(context).createContextAsUser(eq(UserHandle.ALL), anyInt())
+ doNothing().`when`(context).sendStickyBroadcast(any(), any())
networkStackClient = TestNetworkStackClient(realContext)
networkStackClient.init()
diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt
index 9ba56e4..91fcbc0 100644
--- a/tests/net/java/android/net/NetworkTemplateTest.kt
+++ b/tests/net/java/android/net/NetworkTemplateTest.kt
@@ -67,6 +67,7 @@
val caps = NetworkCapabilities().apply {
setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false)
setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true)
+ setSSID(ssid)
}
return NetworkState(info, lp, caps, mock(Network::class.java), subscriberId, ssid)
}
diff --git a/tests/net/java/android/net/QosSocketFilterTest.java b/tests/net/java/android/net/QosSocketFilterTest.java
new file mode 100644
index 0000000..ad58960
--- /dev/null
+++ b/tests/net/java/android/net/QosSocketFilterTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 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 android.net;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+
+@RunWith(AndroidJUnit4.class)
+@androidx.test.filters.SmallTest
+public class QosSocketFilterTest {
+
+ @Test
+ public void testPortExactMatch() {
+ final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
+ final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4");
+ assertTrue(QosSocketFilter.matchesLocalAddress(
+ new InetSocketAddress(addressA, 10), addressB, 10, 10));
+
+ }
+
+ @Test
+ public void testPortLessThanStart() {
+ final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
+ final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4");
+ assertFalse(QosSocketFilter.matchesLocalAddress(
+ new InetSocketAddress(addressA, 8), addressB, 10, 10));
+ }
+
+ @Test
+ public void testPortGreaterThanEnd() {
+ final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
+ final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4");
+ assertFalse(QosSocketFilter.matchesLocalAddress(
+ new InetSocketAddress(addressA, 18), addressB, 10, 10));
+ }
+
+ @Test
+ public void testPortBetweenStartAndEnd() {
+ final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
+ final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4");
+ assertTrue(QosSocketFilter.matchesLocalAddress(
+ new InetSocketAddress(addressA, 10), addressB, 8, 18));
+ }
+
+ @Test
+ public void testAddressesDontMatch() {
+ final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
+ final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.5");
+ assertFalse(QosSocketFilter.matchesLocalAddress(
+ new InetSocketAddress(addressA, 10), addressB, 10, 10));
+ }
+}
+
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index f893e9e..ca73689 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -201,6 +201,7 @@
import android.net.UidRange;
import android.net.UidRangeParcel;
import android.net.Uri;
+import android.net.VpnInfo;
import android.net.VpnManager;
import android.net.metrics.IpConnectivityLog;
import android.net.shared.NetworkMonitorUtils;
@@ -245,7 +246,6 @@
import com.android.internal.app.IBatteryStats;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.WakeupMessage;
@@ -8323,8 +8323,7 @@
assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
mMockVpn.setVpnType(vpnType);
- final VpnInfo vpnInfo = new VpnInfo();
- vpnInfo.ownerUid = vpnOwnerUid;
+ final VpnInfo vpnInfo = new VpnInfo(vpnOwnerUid, null, null);
mMockVpn.setVpnInfo(vpnInfo);
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
index 3aafe0b..1b33930 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
@@ -33,8 +33,7 @@
import static org.junit.Assert.assertEquals;
import android.net.NetworkStats;
-
-import com.android.internal.net.VpnInfo;
+import android.net.VpnInfo;
/** Superclass with utilities for NetworkStats(Service|Factory)Test */
abstract class NetworkStatsBaseTest {
@@ -113,10 +112,6 @@
}
static VpnInfo createVpnInfo(String vpnIface, String[] underlyingIfaces) {
- VpnInfo info = new VpnInfo();
- info.ownerUid = UID_VPN;
- info.vpnIface = vpnIface;
- info.underlyingIfaces = underlyingIfaces;
- return info;
+ return new VpnInfo(UID_VPN, vpnIface, underlyingIfaces);
}
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
index e4996d9..76647a6 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -36,13 +36,13 @@
import android.content.res.Resources;
import android.net.NetworkStats;
import android.net.TrafficStats;
+import android.net.VpnInfo;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.tests.net.R;
-import com.android.internal.net.VpnInfo;
import libcore.io.IoUtils;
import libcore.io.Streams;
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index c783629..b4e37de 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -21,7 +21,6 @@
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.net.ConnectivityManager.TYPE_WIMAX;
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
@@ -44,6 +43,7 @@
import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateMobileWithRatType;
+import static android.net.NetworkTemplate.buildTemplateWifi;
import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.net.TrafficStats.UID_REMOVED;
@@ -86,6 +86,7 @@
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
+import android.net.VpnInfo;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.ConditionVariable;
import android.os.Handler;
@@ -104,7 +105,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
@@ -146,7 +146,7 @@
private static final String IMSI_2 = "310260";
private static final String TEST_SSID = "AndroidAP";
- private static NetworkTemplate sTemplateWifi = buildTemplateWifiWildcard();
+ private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_SSID);
private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1);
private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2);
@@ -291,7 +291,6 @@
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
-
// modify some number on wifi, and trigger poll event
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
@@ -567,61 +566,6 @@
}
@Test
- public void testUid3gWimaxCombinedByTemplate() throws Exception {
- // pretend that network comes online
- expectDefaultSettings();
- NetworkState[] states = new NetworkState[] {buildMobile3gState(IMSI_1)};
- expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(buildEmptyStats());
-
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
-
- // create some traffic
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectDefaultSettings();
- expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
- mService.incrementOperationCount(UID_RED, 0xF00D, 5);
-
- forcePollAndWaitForIdle();
-
- // verify service recorded history
- assertUidTotal(sTemplateImsi1, UID_RED, 1024L, 8L, 1024L, 8L, 5);
-
-
- // now switch over to wimax network
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectDefaultSettings();
- states = new NetworkState[] {buildWimaxState(TEST_IFACE2)};
- expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
-
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
- forcePollAndWaitForIdle();
-
-
- // create traffic on second network
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectDefaultSettings();
- expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
- .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
- .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
- mService.incrementOperationCount(UID_RED, 0xFAAD, 5);
-
- forcePollAndWaitForIdle();
-
- // verify that ALL_MOBILE template combines both
- assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 1280L, 10L, 10);
- }
-
- @Test
public void testMobileStatsByRatType() throws Exception {
final NetworkTemplate template3g =
buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS);
@@ -1503,6 +1447,7 @@
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !isMetered);
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ capabilities.setSSID(TEST_SSID);
return new NetworkState(info, prop, capabilities, WIFI_NETWORK, null, TEST_SSID);
}
@@ -1524,17 +1469,6 @@
return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, subscriberId, null);
}
- private static NetworkState buildWimaxState(@NonNull String iface) {
- final NetworkInfo info = new NetworkInfo(TYPE_WIMAX, 0, null, null);
- info.setDetailedState(DetailedState.CONNECTED, null, null);
- final LinkProperties prop = new LinkProperties();
- prop.setInterfaceName(iface);
- final NetworkCapabilities capabilities = new NetworkCapabilities();
- capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
- capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
- return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, null, null);
- }
-
private NetworkStats buildEmptyStats() {
return new NetworkStats(getElapsedRealtime(), 0);
}