Merge "Ignore the asynchronous result while stopping keepalive"
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 4a64128..6ba4a30 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3234,7 +3234,7 @@
*
* @hide
*/
- public void onPreCheck(Network network) {}
+ public void onPreCheck(@NonNull Network network) {}
/**
* Called when the framework connects and has declared a new network ready for use.
@@ -3247,8 +3247,9 @@
* @param blocked Whether access to the {@link Network} is blocked due to system policy.
* @hide
*/
- public void onAvailable(Network network, NetworkCapabilities networkCapabilities,
- LinkProperties linkProperties, boolean blocked) {
+ public void onAvailable(@NonNull Network network,
+ @NonNull NetworkCapabilities networkCapabilities,
+ @NonNull LinkProperties linkProperties, boolean blocked) {
// Internally only this method is called when a new network is available, and
// it calls the callback in the same way and order that older versions used
// to call so as not to change the behavior.
@@ -3272,7 +3273,7 @@
*
* @param network The {@link Network} of the satisfying network.
*/
- public void onAvailable(Network network) {}
+ public void onAvailable(@NonNull Network network) {}
/**
* Called when the network is about to be disconnected. Often paired with an
@@ -3288,7 +3289,7 @@
* network connected. Note that the network may suffer a
* hard loss at any time.
*/
- public void onLosing(Network network, int maxMsToLive) {}
+ public void onLosing(@NonNull Network network, int maxMsToLive) {}
/**
* Called when the framework has a hard loss of the network or when the
@@ -3296,7 +3297,7 @@
*
* @param network The {@link Network} lost.
*/
- public void onLost(Network network) {}
+ public void onLost(@NonNull Network network) {}
/**
* Called if no network is found in the timeout time specified in
@@ -3316,8 +3317,8 @@
* @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this
* network.
*/
- public void onCapabilitiesChanged(Network network,
- NetworkCapabilities networkCapabilities) {}
+ public void onCapabilitiesChanged(@NonNull Network network,
+ @NonNull NetworkCapabilities networkCapabilities) {}
/**
* Called when the network the framework connected to for this request
@@ -3326,7 +3327,8 @@
* @param network The {@link Network} whose link properties have changed.
* @param linkProperties The new {@link LinkProperties} for this network.
*/
- public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {}
+ public void onLinkPropertiesChanged(@NonNull Network network,
+ @NonNull LinkProperties linkProperties) {}
/**
* Called when the network the framework connected to for this request
@@ -3337,7 +3339,7 @@
* a tunnel, etc.
* @hide
*/
- public void onNetworkSuspended(Network network) {}
+ public void onNetworkSuspended(@NonNull Network network) {}
/**
* Called when the network the framework connected to for this request
@@ -3345,7 +3347,7 @@
* preceded by a matching {@link NetworkCallback#onNetworkSuspended} call.
* @hide
*/
- public void onNetworkResumed(Network network) {}
+ public void onNetworkResumed(@NonNull Network network) {}
/**
* Called when access to the specified network is blocked or unblocked.
diff --git a/core/java/android/net/DnsResolver.java b/core/java/android/net/DnsResolver.java
index 5980251..06c32c6 100644
--- a/core/java/android/net/DnsResolver.java
+++ b/core/java/android/net/DnsResolver.java
@@ -22,6 +22,10 @@
import static android.net.NetworkUtils.resNetworkSend;
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.SOCK_DGRAM;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
@@ -30,12 +34,18 @@
import android.os.CancellationSignal;
import android.os.Looper;
import android.system.ErrnoException;
+import android.system.Os;
import android.util.Log;
+import libcore.io.IoUtils;
+
import java.io.FileDescriptor;
+import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
@@ -52,6 +62,7 @@
private static final String TAG = "DnsResolver";
private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
private static final int MAXPACKET = 8 * 1024;
+ private static final int SLEEP_TIME_MS = 2;
@IntDef(prefix = { "CLASS_" }, value = {
CLASS_IN
@@ -188,9 +199,9 @@
* Send a raw DNS query.
* The answer will be provided asynchronously through the provided {@link AnswerCallback}.
*
- * @param network {@link Network} specifying which network for querying.
+ * @param network {@link Network} specifying which network to query on.
* {@code null} for query on default network.
- * @param query blob message
+ * @param query blob message to query
* @param flags flags as a combination of the FLAGS_* constants
* @param executor The {@link Executor} that the callback should be executed on.
* @param cancellationSignal used by the caller to signal if the query should be
@@ -205,26 +216,32 @@
if (cancellationSignal != null && cancellationSignal.isCanceled()) {
return;
}
+ final Object lock = new Object();
final FileDescriptor queryfd;
try {
queryfd = resNetworkSend((network != null
? network.netId : NETID_UNSET), query, query.length, flags);
} catch (ErrnoException e) {
- callback.onQueryException(e);
+ executor.execute(() -> {
+ callback.onQueryException(e);
+ });
return;
}
- maybeAddCancellationSignal(cancellationSignal, queryfd);
- registerFDListener(executor, queryfd, callback);
+ synchronized (lock) {
+ registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
+ if (cancellationSignal == null) return;
+ addCancellationSignal(cancellationSignal, queryfd, lock);
+ }
}
/**
* Send a DNS query with the specified name, class and query type.
* The answer will be provided asynchronously through the provided {@link AnswerCallback}.
*
- * @param network {@link Network} specifying which network for querying.
+ * @param network {@link Network} specifying which network to query on.
* {@code null} for query on default network.
- * @param domain domain name for querying
+ * @param domain domain name to query
* @param nsClass dns class as one of the CLASS_* constants
* @param nsType dns resource record (RR) type as one of the TYPE_* constants
* @param flags flags as a combination of the FLAGS_* constants
@@ -242,40 +259,187 @@
if (cancellationSignal != null && cancellationSignal.isCanceled()) {
return;
}
+ final Object lock = new Object();
final FileDescriptor queryfd;
try {
queryfd = resNetworkQuery((network != null
? network.netId : NETID_UNSET), domain, nsClass, nsType, flags);
} catch (ErrnoException e) {
- callback.onQueryException(e);
+ executor.execute(() -> {
+ callback.onQueryException(e);
+ });
return;
}
+ synchronized (lock) {
+ registerFDListener(executor, queryfd, callback, cancellationSignal, lock);
+ if (cancellationSignal == null) return;
+ addCancellationSignal(cancellationSignal, queryfd, lock);
+ }
+ }
- maybeAddCancellationSignal(cancellationSignal, queryfd);
- registerFDListener(executor, queryfd, callback);
+ private class InetAddressAnswerAccumulator extends InetAddressAnswerCallback {
+ private final List<InetAddress> mAllAnswers;
+ private ParseException mParseException;
+ private ErrnoException mErrnoException;
+ private final InetAddressAnswerCallback mUserCallback;
+ private final int mTargetAnswerCount;
+ private int mReceivedAnswerCount = 0;
+
+ InetAddressAnswerAccumulator(int size, @NonNull InetAddressAnswerCallback callback) {
+ mTargetAnswerCount = size;
+ mAllAnswers = new ArrayList<>();
+ mUserCallback = callback;
+ }
+
+ private boolean maybeReportException() {
+ if (mErrnoException != null) {
+ mUserCallback.onQueryException(mErrnoException);
+ return true;
+ }
+ if (mParseException != null) {
+ mUserCallback.onParseException(mParseException);
+ return true;
+ }
+ return false;
+ }
+
+ private void maybeReportAnswer() {
+ if (++mReceivedAnswerCount != mTargetAnswerCount) return;
+ if (mAllAnswers.isEmpty() && maybeReportException()) return;
+ // TODO: Do RFC6724 sort.
+ mUserCallback.onAnswer(mAllAnswers);
+ }
+
+ @Override
+ public void onAnswer(@NonNull List<InetAddress> answer) {
+ mAllAnswers.addAll(answer);
+ maybeReportAnswer();
+ }
+
+ @Override
+ public void onParseException(@NonNull ParseException e) {
+ mParseException = e;
+ maybeReportAnswer();
+ }
+
+ @Override
+ public void onQueryException(@NonNull ErrnoException e) {
+ mErrnoException = e;
+ maybeReportAnswer();
+ }
+ }
+
+ /**
+ * Send a DNS query with the specified name, get back a set of InetAddresses asynchronously.
+ * The answer will be provided asynchronously through the provided
+ * {@link InetAddressAnswerCallback}.
+ *
+ * @param network {@link Network} specifying which network to query on.
+ * {@code null} for query on default network.
+ * @param domain domain name to query
+ * @param flags flags as a combination of the FLAGS_* constants
+ * @param executor The {@link Executor} that the callback should be executed on.
+ * @param cancellationSignal used by the caller to signal if the query should be
+ * cancelled. May be {@code null}.
+ * @param callback an {@link InetAddressAnswerCallback} which will be called to notify the
+ * caller of the result of dns query.
+ */
+ public void query(@Nullable Network network, @NonNull String domain, @QueryFlag int flags,
+ @NonNull @CallbackExecutor Executor executor,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull InetAddressAnswerCallback callback) {
+ if (cancellationSignal != null && cancellationSignal.isCanceled()) {
+ return;
+ }
+ final Object lock = new Object();
+ final boolean queryIpv6 = haveIpv6(network);
+ final boolean queryIpv4 = haveIpv4(network);
+
+ final FileDescriptor v4fd;
+ final FileDescriptor v6fd;
+
+ int queryCount = 0;
+
+ if (queryIpv6) {
+ try {
+ v6fd = resNetworkQuery((network != null
+ ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_AAAA, flags);
+ } catch (ErrnoException e) {
+ executor.execute(() -> {
+ callback.onQueryException(e);
+ });
+ return;
+ }
+ queryCount++;
+ } else v6fd = null;
+
+ // TODO: Use device flag to control the sleep time.
+ // Avoiding gateways drop packets if queries are sent too close together
+ try {
+ Thread.sleep(SLEEP_TIME_MS);
+ } catch (InterruptedException ex) { }
+
+ if (queryIpv4) {
+ try {
+ v4fd = resNetworkQuery((network != null
+ ? network.netId : NETID_UNSET), domain, CLASS_IN, TYPE_A, flags);
+ } catch (ErrnoException e) {
+ if (queryIpv6) resNetworkCancel(v6fd); // Closes fd, marks it invalid.
+ executor.execute(() -> {
+ callback.onQueryException(e);
+ });
+ return;
+ }
+ queryCount++;
+ } else v4fd = null;
+
+ final InetAddressAnswerAccumulator accumulator =
+ new InetAddressAnswerAccumulator(queryCount, callback);
+
+ synchronized (lock) {
+ if (queryIpv6) {
+ registerFDListener(executor, v6fd, accumulator, cancellationSignal, lock);
+ }
+ if (queryIpv4) {
+ registerFDListener(executor, v4fd, accumulator, cancellationSignal, lock);
+ }
+ if (cancellationSignal == null) return;
+ cancellationSignal.setOnCancelListener(() -> {
+ synchronized (lock) {
+ if (queryIpv4) cancelQuery(v4fd);
+ if (queryIpv6) cancelQuery(v6fd);
+ }
+ });
+ }
}
private <T> void registerFDListener(@NonNull Executor executor,
- @NonNull FileDescriptor queryfd, @NonNull AnswerCallback<T> answerCallback) {
+ @NonNull FileDescriptor queryfd, @NonNull AnswerCallback<T> answerCallback,
+ @Nullable CancellationSignal cancellationSignal, @NonNull Object lock) {
Looper.getMainLooper().getQueue().addOnFileDescriptorEventListener(
queryfd,
FD_EVENTS,
(fd, events) -> {
executor.execute(() -> {
- byte[] answerbuf = null;
- try {
- answerbuf = resNetworkResult(fd);
- } catch (ErrnoException e) {
- Log.e(TAG, "resNetworkResult:" + e.toString());
- answerCallback.onQueryException(e);
- return;
- }
+ synchronized (lock) {
+ if (cancellationSignal != null && cancellationSignal.isCanceled()) {
+ return;
+ }
+ byte[] answerbuf = null;
+ try {
+ answerbuf = resNetworkResult(fd); // Closes fd, marks it invalid.
+ } catch (ErrnoException e) {
+ Log.e(TAG, "resNetworkResult:" + e.toString());
+ answerCallback.onQueryException(e);
+ return;
+ }
- try {
- answerCallback.onAnswer(
- answerCallback.parser.parse(answerbuf));
- } catch (ParseException e) {
- answerCallback.onParseException(e);
+ try {
+ answerCallback.onAnswer(
+ answerCallback.parser.parse(answerbuf));
+ } catch (ParseException e) {
+ answerCallback.onParseException(e);
+ }
}
});
// Unregister this fd listener
@@ -283,15 +447,51 @@
});
}
- private void maybeAddCancellationSignal(@Nullable CancellationSignal cancellationSignal,
- @NonNull FileDescriptor queryfd) {
- if (cancellationSignal == null) return;
- cancellationSignal.setOnCancelListener(
- () -> {
- Looper.getMainLooper().getQueue()
- .removeOnFileDescriptorEventListener(queryfd);
- resNetworkCancel(queryfd);
- });
+ private void cancelQuery(@NonNull FileDescriptor queryfd) {
+ if (!queryfd.valid()) return;
+ Looper.getMainLooper().getQueue().removeOnFileDescriptorEventListener(queryfd);
+ resNetworkCancel(queryfd); // Closes fd, marks it invalid.
+ }
+
+ private void addCancellationSignal(@NonNull CancellationSignal cancellationSignal,
+ @NonNull FileDescriptor queryfd, @NonNull Object lock) {
+ cancellationSignal.setOnCancelListener(() -> {
+ synchronized (lock) {
+ cancelQuery(queryfd);
+ }
+ });
+ }
+
+ // These two functions match the behaviour of have_ipv4 and have_ipv6 in the native resolver.
+ private boolean haveIpv4(@Nullable Network network) {
+ final SocketAddress addrIpv4 =
+ new InetSocketAddress(InetAddresses.parseNumericAddress("8.8.8.8"), 0);
+ return checkConnectivity(network, AF_INET, addrIpv4);
+ }
+
+ private boolean haveIpv6(@Nullable Network network) {
+ final SocketAddress addrIpv6 =
+ new InetSocketAddress(InetAddresses.parseNumericAddress("2000::"), 0);
+ return checkConnectivity(network, AF_INET6, addrIpv6);
+ }
+
+ private boolean checkConnectivity(@Nullable Network network,
+ int domain, @NonNull SocketAddress addr) {
+ final FileDescriptor socket;
+ try {
+ socket = Os.socket(domain, SOCK_DGRAM, IPPROTO_UDP);
+ } catch (ErrnoException e) {
+ return false;
+ }
+ try {
+ if (network != null) network.bindSocket(socket);
+ Os.connect(socket, addr);
+ } catch (IOException | ErrnoException e) {
+ return false;
+ } finally {
+ IoUtils.closeQuietly(socket);
+ }
+ return true;
}
private static class DnsAddressAnswer extends DnsPacket {
diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java
index b4f3a28..416157c 100644
--- a/core/java/android/net/IpPrefix.java
+++ b/core/java/android/net/IpPrefix.java
@@ -16,6 +16,7 @@
package android.net;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -71,7 +72,7 @@
*
* @hide
*/
- public IpPrefix(@NonNull byte[] address, int prefixLength) {
+ public IpPrefix(@NonNull byte[] address, @IntRange(from = 0, to = 128) int prefixLength) {
this.address = address.clone();
this.prefixLength = prefixLength;
checkAndMaskAddressAndPrefixLength();
@@ -88,7 +89,7 @@
*/
@SystemApi
@TestApi
- public IpPrefix(@NonNull InetAddress address, int prefixLength) {
+ public IpPrefix(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength) {
// We don't reuse the (byte[], int) constructor because it calls clone() on the byte array,
// which is unnecessary because getAddress() already returns a clone.
this.address = address.getAddress();
@@ -150,13 +151,13 @@
*
* @return the address in the form of a byte array.
*/
- public InetAddress getAddress() {
+ public @NonNull InetAddress getAddress() {
try {
return InetAddress.getByAddress(address);
} catch (UnknownHostException e) {
// Cannot happen. InetAddress.getByAddress can only throw an exception if the byte
// array is the wrong length, but we check that in the constructor.
- return null;
+ throw new IllegalArgumentException("Address is invalid");
}
}
@@ -166,7 +167,7 @@
*
* @return the address in the form of a byte array.
*/
- public byte[] getRawAddress() {
+ public @NonNull byte[] getRawAddress() {
return address.clone();
}
@@ -175,6 +176,7 @@
*
* @return the prefix length.
*/
+ @IntRange(from = 0, to = 128)
public int getPrefixLength() {
return prefixLength;
}
@@ -183,10 +185,10 @@
* Determines whether the prefix contains the specified address.
*
* @param address An {@link InetAddress} to test.
- * @return {@code true} if the prefix covers the given address.
+ * @return {@code true} if the prefix covers the given address. {@code false} otherwise.
*/
- public boolean contains(InetAddress address) {
- byte[] addrBytes = (address == null) ? null : address.getAddress();
+ public boolean contains(@NonNull InetAddress address) {
+ byte[] addrBytes = address.getAddress();
if (addrBytes == null || addrBytes.length != this.address.length) {
return false;
}
@@ -201,7 +203,7 @@
* @param otherPrefix the prefix to test
* @hide
*/
- public boolean containsPrefix(IpPrefix otherPrefix) {
+ public boolean containsPrefix(@NonNull IpPrefix otherPrefix) {
if (otherPrefix.getPrefixLength() < prefixLength) return false;
final byte[] otherAddress = otherPrefix.getRawAddress();
NetworkUtils.maskRawAddress(otherAddress, prefixLength);
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index 78b4665..f17adea 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -25,6 +25,7 @@
import static android.system.OsConstants.RT_SCOPE_SITE;
import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -170,7 +171,7 @@
* Constructs a new {@code LinkAddress} from an {@code InetAddress} and prefix length, with
* the specified flags and scope. Flags and scope are not checked for validity.
* @param address The IP address.
- * @param prefixLength The prefix length.
+ * @param prefixLength The prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6).
* @param flags A bitmask of {@code IFA_F_*} values representing properties of the address.
* @param scope An integer defining the scope in which the address is unique (e.g.,
* {@link OsConstants#RT_SCOPE_LINK} or {@link OsConstants#RT_SCOPE_SITE}).
@@ -178,7 +179,8 @@
*/
@SystemApi
@TestApi
- public LinkAddress(InetAddress address, int prefixLength, int flags, int scope) {
+ public LinkAddress(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength,
+ int flags, int scope) {
init(address, prefixLength, flags, scope);
}
@@ -186,12 +188,13 @@
* Constructs a new {@code LinkAddress} from an {@code InetAddress} and a prefix length.
* The flags are set to zero and the scope is determined from the address.
* @param address The IP address.
- * @param prefixLength The prefix length.
+ * @param prefixLength The prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6).
* @hide
*/
@SystemApi
@TestApi
- public LinkAddress(@NonNull InetAddress address, int prefixLength) {
+ public LinkAddress(@NonNull InetAddress address,
+ @IntRange(from = 0, to = 128) int prefixLength) {
this(address, prefixLength, 0, 0);
this.scope = scopeForUnicastAddress(address);
}
@@ -202,7 +205,7 @@
* @param interfaceAddress The interface address.
* @hide
*/
- public LinkAddress(InterfaceAddress interfaceAddress) {
+ public LinkAddress(@NonNull InterfaceAddress interfaceAddress) {
this(interfaceAddress.getAddress(),
interfaceAddress.getNetworkPrefixLength());
}
@@ -306,6 +309,7 @@
/**
* Returns the prefix length of this {@code LinkAddress}.
*/
+ @IntRange(from = 0, to = 128)
public int getPrefixLength() {
return prefixLength;
}
@@ -316,6 +320,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @IntRange(from = 0, to = 128)
public int getNetworkPrefixLength() {
return getPrefixLength();
}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 03d6d48..ad67763 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -316,9 +316,6 @@
@SystemApi
@TestApi
public boolean removeLinkAddress(@NonNull LinkAddress toRemove) {
- if (toRemove == null) {
- return false;
- }
int i = findLinkAddressIndex(toRemove);
if (i >= 0) {
mLinkAddresses.remove(i);
@@ -391,10 +388,7 @@
@TestApi
@SystemApi
public boolean removeDnsServer(@NonNull InetAddress dnsServer) {
- if (dnsServer != null) {
- return mDnses.remove(dnsServer);
- }
- return false;
+ return mDnses.remove(dnsServer);
}
/**
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 3a41a07..acafa13 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.UnsupportedAppUsage;
import android.net.NetworkCapabilities.NetCapability;
@@ -343,10 +344,14 @@
* current value. A value of {@code SIGNAL_STRENGTH_UNSPECIFIED} means no value when
* received and has no effect when requesting a callback.
*
+ * <p>This method requires the caller to hold the
+ * {@link android.Manifest.permission#NETWORK_SIGNAL_STRENGTH_WAKEUP} permission
+ *
* @param signalStrength the bearer-specific signal strength.
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP)
public @NonNull Builder setSignalStrength(int signalStrength) {
mNetworkCapabilities.setSignalStrength(signalStrength);
return this;
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 24d9b8e..fdd904a 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -16,6 +16,8 @@
package android.net;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -24,6 +26,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -51,20 +55,32 @@
* (IPv4 or IPv6).
*/
public final class RouteInfo implements Parcelable {
+ /** @hide */
+ @IntDef(value = {
+ RTN_UNICAST,
+ RTN_UNREACHABLE,
+ RTN_THROW,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RouteType {}
+
/**
* The IP destination address for this route.
*/
+ @NonNull
private final IpPrefix mDestination;
/**
* The gateway address for this route.
*/
@UnsupportedAppUsage
+ @Nullable
private final InetAddress mGateway;
/**
* The interface for this route.
*/
+ @Nullable
private final String mInterface;
@@ -108,13 +124,14 @@
* @param destination the destination prefix
* @param gateway the IP address to route packets through
* @param iface the interface name to send packets on
+ * @param type the type of this route
*
* @hide
*/
@SystemApi
@TestApi
public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway,
- @Nullable String iface, int type) {
+ @Nullable String iface, @RouteType int type) {
switch (type) {
case RTN_UNICAST:
case RTN_UNREACHABLE:
@@ -173,10 +190,24 @@
}
/**
- * @hide
+ * Constructs a {@code RouteInfo} object.
+ *
+ * If destination is null, then gateway must be specified and the
+ * constructed route is either the IPv4 default route <code>0.0.0.0</code>
+ * if the gateway is an instance of {@link Inet4Address}, or the IPv6 default
+ * route <code>::/0</code> if gateway is an instance of {@link Inet6Address}.
+ * <p>
+ * Destination and gateway may not both be null.
+ *
+ * @param destination the destination address and prefix in an {@link IpPrefix}
+ * @param gateway the {@link InetAddress} to route packets through
+ * @param iface the interface name to send packets on
+ *
+ * @hide
*/
@UnsupportedAppUsage
- public RouteInfo(IpPrefix destination, InetAddress gateway, String iface) {
+ public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway,
+ @Nullable String iface) {
this(destination, gateway, iface, RTN_UNICAST);
}
@@ -184,7 +215,8 @@
* @hide
*/
@UnsupportedAppUsage
- public RouteInfo(LinkAddress destination, InetAddress gateway, String iface) {
+ public RouteInfo(@Nullable LinkAddress destination, @Nullable InetAddress gateway,
+ @Nullable String iface) {
this(destination == null ? null :
new IpPrefix(destination.getAddress(), destination.getPrefixLength()),
gateway, iface);
@@ -205,7 +237,7 @@
*
* @hide
*/
- public RouteInfo(IpPrefix destination, InetAddress gateway) {
+ public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway) {
this(destination, gateway, null);
}
@@ -215,7 +247,7 @@
* TODO: Remove this.
*/
@UnsupportedAppUsage
- public RouteInfo(LinkAddress destination, InetAddress gateway) {
+ public RouteInfo(@Nullable LinkAddress destination, @Nullable InetAddress gateway) {
this(destination, gateway, null);
}
@@ -227,7 +259,7 @@
* @hide
*/
@UnsupportedAppUsage
- public RouteInfo(InetAddress gateway) {
+ public RouteInfo(@NonNull InetAddress gateway) {
this((IpPrefix) null, gateway, null);
}
@@ -239,35 +271,36 @@
*
* @hide
*/
- public RouteInfo(IpPrefix destination) {
+ public RouteInfo(@NonNull IpPrefix destination) {
this(destination, null, null);
}
/**
* @hide
*/
- public RouteInfo(LinkAddress destination) {
+ public RouteInfo(@NonNull LinkAddress destination) {
this(destination, null, null);
}
/**
* @hide
*/
- public RouteInfo(IpPrefix destination, int type) {
+ public RouteInfo(@NonNull IpPrefix destination, @RouteType int type) {
this(destination, null, null, type);
}
/**
* @hide
*/
- public static RouteInfo makeHostRoute(InetAddress host, String iface) {
+ public static RouteInfo makeHostRoute(@NonNull InetAddress host, @Nullable String iface) {
return makeHostRoute(host, null, iface);
}
/**
* @hide
*/
- public static RouteInfo makeHostRoute(InetAddress host, InetAddress gateway, String iface) {
+ public static RouteInfo makeHostRoute(@Nullable InetAddress host, @Nullable InetAddress gateway,
+ @Nullable String iface) {
if (host == null) return null;
if (host instanceof Inet4Address) {
@@ -290,6 +323,7 @@
*
* @return {@link IpPrefix} specifying the destination. This is never {@code null}.
*/
+ @NonNull
public IpPrefix getDestination() {
return mDestination;
}
@@ -298,6 +332,7 @@
* TODO: Convert callers to use IpPrefix and then remove.
* @hide
*/
+ @NonNull
public LinkAddress getDestinationLinkAddress() {
return new LinkAddress(mDestination.getAddress(), mDestination.getPrefixLength());
}
@@ -308,6 +343,7 @@
* @return {@link InetAddress} specifying the gateway or next hop. This may be
* {@code null} for a directly-connected route."
*/
+ @Nullable
public InetAddress getGateway() {
return mGateway;
}
@@ -317,6 +353,7 @@
*
* @return The name of the interface used for this route.
*/
+ @Nullable
public String getInterface() {
return mInterface;
}
@@ -330,6 +367,7 @@
*/
@TestApi
@SystemApi
+ @RouteType
public int getType() {
return mType;
}
@@ -401,6 +439,7 @@
* @hide
*/
@UnsupportedAppUsage
+ @Nullable
public static RouteInfo selectBestRoute(Collection<RouteInfo> routes, InetAddress dest) {
if ((routes == null) || (dest == null)) return null;
diff --git a/core/java/android/net/apf/ApfCapabilities.java b/core/java/android/net/apf/ApfCapabilities.java
index 17a03c7..4dd2ace 100644
--- a/core/java/android/net/apf/ApfCapabilities.java
+++ b/core/java/android/net/apf/ApfCapabilities.java
@@ -19,14 +19,17 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.content.Context;
+import android.content.res.Resources;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.R;
/**
- * APF program support capabilities.
+ * APF program support capabilities. APF stands for Android Packet Filtering and it is a flexible
+ * way to drop unwanted network packets to save power.
+ *
+ * See documentation at hardware/google/apf/apf.h
*
* This class is immutable.
* @hide
@@ -104,10 +107,11 @@
}
/**
- * Returns true if the APF interpreter advertises support for the data buffer access opcodes
- * LDDW and STDW.
+ * Determines whether the APF interpreter advertises support for the data buffer access opcodes
+ * LDDW (LoaD Data Word) and STDW (STore Data Word). Full LDDW (LoaD Data Word) and
+ * STDW (STore Data Word) support is present from APFv4 on.
*
- * Full LDDW and STDW support is present from APFv4 on.
+ * @return {@code true} if the IWifiStaIface#readApfPacketFilterData is supported.
*/
public boolean hasDataAccess() {
return apfVersionSupported >= 4;
@@ -116,14 +120,14 @@
/**
* @return Whether the APF Filter in the device should filter out IEEE 802.3 Frames.
*/
- public static boolean getApfDrop8023Frames(@NonNull Context context) {
- return context.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
+ public static boolean getApfDrop8023Frames() {
+ return Resources.getSystem().getBoolean(R.bool.config_apfDrop802_3Frames);
}
/**
* @return An array of blacklisted EtherType, packets with EtherTypes within it will be dropped.
*/
- public static @NonNull int[] getApfEthTypeBlackList(@NonNull Context context) {
- return context.getResources().getIntArray(R.array.config_apfEthTypeBlackList);
+ public static @NonNull int[] getApfEtherTypeBlackList() {
+ return Resources.getSystem().getIntArray(R.array.config_apfEthTypeBlackList);
}
}
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index d7a981e..82acf6f 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -470,6 +470,7 @@
std::vector<uint8_t> buf(MAXPACKETSIZE, 0);
int res = resNetworkResult(fd, &rcode, buf.data(), MAXPACKETSIZE);
+ jniSetFileDescriptorOfFD(env, javaFd, -1);
if (res < 0) {
throwErrnoException(env, "resNetworkResult", -res);
return nullptr;
@@ -490,6 +491,7 @@
static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobject javaFd) {
int fd = jniGetFDFromFileDescriptor(env, javaFd);
resNetworkCancel(fd);
+ jniSetFileDescriptorOfFD(env, javaFd, -1);
}
static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index bca2df4..31d8113 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -63,6 +63,7 @@
import android.net.ConnectivityManager;
import android.net.ICaptivePortal;
import android.net.IConnectivityManager;
+import android.net.IDnsResolver;
import android.net.IIpConnectivityMetrics;
import android.net.INetd;
import android.net.INetdEventCallback;
@@ -294,6 +295,8 @@
private INetworkManagementService mNMS;
@VisibleForTesting
+ protected IDnsResolver mDnsResolver;
+ @VisibleForTesting
protected INetd mNetd;
private INetworkStatsService mStatsService;
private INetworkPolicyManager mPolicyManager;
@@ -525,6 +528,11 @@
return sMagicDecoderRing.get(what, Integer.toString(what));
}
+ private static IDnsResolver getDnsResolver() {
+ return IDnsResolver.Stub
+ .asInterface(ServiceManager.getService("dnsresolver"));
+ }
+
/** Handler thread used for both of the handlers below. */
@VisibleForTesting
protected final HandlerThread mHandlerThread;
@@ -810,13 +818,14 @@
public ConnectivityService(Context context, INetworkManagementService netManager,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
- this(context, netManager, statsService, policyManager, new IpConnectivityLog());
+ this(context, netManager, statsService, policyManager,
+ getDnsResolver(), new IpConnectivityLog());
}
@VisibleForTesting
protected ConnectivityService(Context context, INetworkManagementService netManager,
INetworkStatsService statsService, INetworkPolicyManager policyManager,
- IpConnectivityLog logger) {
+ IDnsResolver dnsresolver, IpConnectivityLog logger) {
if (DBG) log("ConnectivityService starting up");
mSystemProperties = getSystemProperties();
@@ -853,6 +862,7 @@
mPolicyManagerInternal = checkNotNull(
LocalServices.getService(NetworkPolicyManagerInternal.class),
"missing NetworkPolicyManagerInternal");
+ mDnsResolver = checkNotNull(dnsresolver, "missing IDnsResolver");
mProxyTracker = makeProxyTracker();
mNetd = NetdService.getInstance();
@@ -1006,7 +1016,7 @@
mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler);
- mDnsManager = new DnsManager(mContext, mNMS, mSystemProperties);
+ mDnsManager = new DnsManager(mContext, mDnsResolver, mSystemProperties);
registerPrivateDnsSettingsCallbacks();
}
@@ -1882,6 +1892,15 @@
return false;
}
+ private boolean checkAnyPermissionOf(int pid, int uid, String... permissions) {
+ for (String permission : permissions) {
+ if (mContext.checkPermission(permission, pid, uid) == PERMISSION_GRANTED) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private void enforceAnyPermissionOf(String... permissions) {
if (!checkAnyPermissionOf(permissions)) {
throw new SecurityException("Requires one of the following permissions: "
@@ -1956,6 +1975,12 @@
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
+ private boolean checkNetworkSignalStrengthWakeupPermission(int pid, int uid) {
+ return checkAnyPermissionOf(pid, uid,
+ android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ }
+
private void enforceConnectivityRestrictedNetworksPermission() {
try {
mContext.enforceCallingOrSelfPermission(
@@ -3021,9 +3046,9 @@
// NetworkFactories, so network traffic isn't interrupted for an unnecessarily
// long time.
try {
- mNMS.removeNetwork(nai.network.netId);
- } catch (Exception e) {
- loge("Exception removing network: " + e);
+ mNetd.networkDestroy(nai.network.netId);
+ } catch (RemoteException | ServiceSpecificException e) {
+ loge("Exception destroying network: " + e);
}
mDnsManager.removeNetwork(nai.network);
}
@@ -3728,16 +3753,6 @@
break;
}
case EVENT_SYSTEM_READY: {
- for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
- // Might have been called already in handleRegisterNetworkAgent since
- // mSystemReady is set before sending EVENT_SYSTEM_READY, but calling
- // this several times is fine.
- try {
- nai.networkMonitor().notifySystemReady();
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- }
mMultipathPolicyTracker.start();
break;
}
@@ -4299,7 +4314,7 @@
/**
* @return VPN information for accounting, or null if we can't retrieve all required
- * information, e.g primary underlying iface.
+ * information, e.g underlying ifaces.
*/
@Nullable
private VpnInfo createVpnInfo(Vpn vpn) {
@@ -4311,17 +4326,24 @@
// see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
// the underlyingNetworks list.
if (underlyingNetworks == null) {
- NetworkAgentInfo defaultNetwork = getDefaultNetwork();
- if (defaultNetwork != null && defaultNetwork.linkProperties != null) {
- info.primaryUnderlyingIface = getDefaultNetwork().linkProperties.getInterfaceName();
- }
- } else if (underlyingNetworks.length > 0) {
- LinkProperties linkProperties = getLinkProperties(underlyingNetworks[0]);
- if (linkProperties != null) {
- info.primaryUnderlyingIface = linkProperties.getInterfaceName();
+ NetworkAgentInfo defaultNai = getDefaultNetwork();
+ if (defaultNai != null && defaultNai.linkProperties != null) {
+ underlyingNetworks = new Network[] { defaultNai.network };
}
}
- return info.primaryUnderlyingIface == null ? null : info;
+ if (underlyingNetworks != null && underlyingNetworks.length > 0) {
+ List<String> interfaces = new ArrayList<>();
+ for (Network network : underlyingNetworks) {
+ LinkProperties lp = getLinkProperties(network);
+ if (lp != null) {
+ interfaces.add(lp.getInterfaceName());
+ }
+ }
+ if (!interfaces.isEmpty()) {
+ info.underlyingIfaces = interfaces.toArray(new String[interfaces.size()]);
+ }
+ }
+ return info.underlyingIfaces == null ? null : info;
}
/**
@@ -4952,13 +4974,19 @@
}
}
- // This checks that the passed capabilities either do not request a specific SSID, or the
- // calling app has permission to do so.
+ // This checks that the passed capabilities either do not request a specific SSID/SignalStrength
+ // , or the calling app has permission to do so.
private void ensureSufficientPermissionsForRequest(NetworkCapabilities nc,
int callerPid, int callerUid) {
if (null != nc.getSSID() && !checkSettingsPermission(callerPid, callerUid)) {
throw new SecurityException("Insufficient permissions to request a specific SSID");
}
+
+ if (nc.hasSignalStrength()
+ && !checkNetworkSignalStrengthWakeupPermission(callerPid, callerUid)) {
+ throw new SecurityException(
+ "Insufficient permissions to request a specific signal strength");
+ }
}
private ArrayList<Integer> getSignalStrengthThresholds(NetworkAgentInfo nai) {
@@ -5372,10 +5400,10 @@
final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
new Network(reserveNetId()), new NetworkInfo(networkInfo), lp, nc, currentScore,
- mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd, mNMS,
- factorySerialNumber);
+ mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd, mDnsResolver,
+ mNMS, factorySerialNumber);
// Make sure the network capabilities reflect what the agent info says.
- nai.networkCapabilities = mixInCapabilities(nai, nc);
+ nai.setNetworkCapabilities(mixInCapabilities(nai, nc));
final String extraInfo = networkInfo.getExtraInfo();
final String name = TextUtils.isEmpty(extraInfo)
? nai.networkCapabilities.getSSID() : extraInfo;
@@ -5406,15 +5434,6 @@
synchronized (mNetworkForNetId) {
mNetworkForNetId.put(nai.network.netId, nai);
}
- synchronized (this) {
- if (mSystemReady) {
- try {
- networkMonitor.notifySystemReady();
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- }
- }
try {
networkMonitor.start();
@@ -5468,12 +5487,12 @@
// Start or stop DNS64 detection and 464xlat according to network state.
networkAgent.clatd.update();
notifyIfacesChangedForNetworkStats();
+ try {
+ networkAgent.networkMonitor().notifyLinkPropertiesChanged(newLp);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
if (networkAgent.everConnected) {
- try {
- networkAgent.networkMonitor().notifyLinkPropertiesChanged();
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
}
}
@@ -5701,7 +5720,7 @@
final NetworkCapabilities prevNc;
synchronized (nai) {
prevNc = nai.networkCapabilities;
- nai.networkCapabilities = newNc;
+ nai.setNetworkCapabilities(newNc);
}
updateUids(nai, prevNc, newNc);
@@ -5716,11 +5735,6 @@
// If the requestable capabilities have changed or the score changed, we can't have been
// called by rematchNetworkAndRequests, so it's safe to start a rematch.
rematchAllNetworksAndRequests(nai, oldScore);
- try {
- nai.networkMonitor().notifyNetworkCapabilitiesChanged();
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
@@ -5979,11 +5993,6 @@
}
if (capabilitiesChanged) {
- try {
- nai.networkMonitor().notifyNetworkCapabilitiesChanged();
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
@@ -6392,7 +6401,8 @@
if (networkAgent.networkMisc.acceptPartialConnectivity) {
networkAgent.networkMonitor().setAcceptPartialConnectivity();
}
- networkAgent.networkMonitor().notifyNetworkConnected();
+ networkAgent.networkMonitor().notifyNetworkConnected(
+ networkAgent.linkProperties, networkAgent.networkCapabilities);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index d8bb635..1913635 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -30,13 +30,15 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.net.IDnsResolver;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkUtils;
import android.net.Uri;
import android.net.shared.PrivateDnsConfig;
import android.os.Binder;
-import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
@@ -229,7 +231,7 @@
private final Context mContext;
private final ContentResolver mContentResolver;
- private final INetworkManagementService mNMS;
+ private final IDnsResolver mDnsResolver;
private final MockableSystemProperties mSystemProperties;
// TODO: Replace these Maps with SparseArrays.
private final Map<Integer, PrivateDnsConfig> mPrivateDnsMap;
@@ -243,10 +245,10 @@
private String mPrivateDnsMode;
private String mPrivateDnsSpecifier;
- public DnsManager(Context ctx, INetworkManagementService nms, MockableSystemProperties sp) {
+ public DnsManager(Context ctx, IDnsResolver dnsResolver, MockableSystemProperties sp) {
mContext = ctx;
mContentResolver = mContext.getContentResolver();
- mNMS = nms;
+ mDnsResolver = dnsResolver;
mSystemProperties = sp;
mPrivateDnsMap = new HashMap<>();
mPrivateDnsValidationMap = new HashMap<>();
@@ -260,6 +262,12 @@
}
public void removeNetwork(Network network) {
+ try {
+ mDnsResolver.clearResolverConfiguration(network.netId);
+ } catch (RemoteException | ServiceSpecificException e) {
+ Slog.e(TAG, "Error clearing DNS configuration: " + e);
+ return;
+ }
mPrivateDnsMap.remove(network.netId);
mPrivateDnsValidationMap.remove(network.netId);
}
@@ -344,10 +352,12 @@
Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %s, %s, %s)",
netId, Arrays.toString(assignedServers), Arrays.toString(domainStrs),
Arrays.toString(params), tlsHostname, Arrays.toString(tlsServers)));
+ final String[] tlsFingerprints = new String[0];
try {
- mNMS.setDnsConfigurationForNetwork(
- netId, assignedServers, domainStrs, params, tlsHostname, tlsServers);
- } catch (Exception e) {
+ mDnsResolver.setResolverConfiguration(
+ netId, assignedServers, domainStrs, params,
+ tlsHostname, tlsServers, tlsFingerprints);
+ } catch (RemoteException | ServiceSpecificException e) {
Slog.e(TAG, "Error setting DNS configuration: " + e);
return;
}
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 262ba7a..66bd27c 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -17,6 +17,7 @@
package com.android.server.connectivity;
import android.net.ConnectivityManager;
+import android.net.IDnsResolver;
import android.net.INetd;
import android.net.InetAddresses;
import android.net.InterfaceConfiguration;
@@ -65,6 +66,7 @@
NetworkInfo.State.SUSPENDED,
};
+ private final IDnsResolver mDnsResolver;
private final INetd mNetd;
private final INetworkManagementService mNMService;
@@ -84,7 +86,9 @@
private Inet6Address mIPv6Address;
private State mState = State.IDLE;
- public Nat464Xlat(NetworkAgentInfo nai, INetd netd, INetworkManagementService nmService) {
+ public Nat464Xlat(NetworkAgentInfo nai, INetd netd, IDnsResolver dnsResolver,
+ INetworkManagementService nmService) {
+ mDnsResolver = dnsResolver;
mNetd = netd;
mNMService = nmService;
mNetwork = nai;
@@ -269,7 +273,7 @@
private void startPrefixDiscovery() {
try {
- mNetd.resolverStartPrefix64Discovery(getNetId());
+ mDnsResolver.startPrefix64Discovery(getNetId());
mState = State.DISCOVERING;
} catch (RemoteException | ServiceSpecificException e) {
Slog.e(TAG, "Error starting prefix discovery on netId " + getNetId() + ": " + e);
@@ -278,7 +282,7 @@
private void stopPrefixDiscovery() {
try {
- mNetd.resolverStopPrefix64Discovery(getNetId());
+ mDnsResolver.stopPrefix64Discovery(getNetId());
} catch (RemoteException | ServiceSpecificException e) {
Slog.e(TAG, "Error stopping prefix discovery on netId " + getNetId() + ": " + e);
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 8f2825c..cfa9131 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 android.content.Context;
+import android.net.IDnsResolver;
import android.net.INetd;
import android.net.INetworkMonitor;
import android.net.LinkProperties;
@@ -29,6 +30,7 @@
import android.os.Handler;
import android.os.INetworkManagementService;
import android.os.Messenger;
+import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import android.util.SparseArray;
@@ -120,7 +122,8 @@
// This Network object is always valid.
public final Network network;
public LinkProperties linkProperties;
- // This should only be modified via ConnectivityService.updateCapabilities().
+ // This should only be modified by ConnectivityService, via setNetworkCapabilities().
+ // TODO: make this private with a getter.
public NetworkCapabilities networkCapabilities;
public final NetworkMisc networkMisc;
// Indicates if netd has been told to create this Network. From this point on the appropriate
@@ -255,7 +258,7 @@
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
NetworkMisc misc, ConnectivityService connService, INetd netd,
- INetworkManagementService nms, int factorySerialNumber) {
+ IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber) {
this.messenger = messenger;
asyncChannel = ac;
network = net;
@@ -263,7 +266,7 @@
linkProperties = lp;
networkCapabilities = nc;
currentScore = score;
- clatd = new Nat464Xlat(this, netd, nms);
+ clatd = new Nat464Xlat(this, netd, dnsResolver, nms);
mConnService = connService;
mContext = context;
mHandler = handler;
@@ -278,6 +281,25 @@
mNetworkMonitor = networkMonitor;
}
+ /**
+ * Set the NetworkCapabilities on this NetworkAgentInfo. Also attempts to notify NetworkMonitor
+ * of the new capabilities, if NetworkMonitor has been created.
+ *
+ * <p>If {@link NetworkMonitor#notifyNetworkCapabilitiesChanged(NetworkCapabilities)} fails,
+ * the exception is logged but not reported to callers.
+ */
+ public void setNetworkCapabilities(NetworkCapabilities nc) {
+ networkCapabilities = nc;
+ final INetworkMonitor nm = mNetworkMonitor;
+ if (nm != null) {
+ try {
+ nm.notifyNetworkCapabilitiesChanged(nc);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error notifying NetworkMonitor of updated NetworkCapabilities", e);
+ }
+ }
+ }
+
public ConnectivityService connService() {
return mConnService;
}
diff --git a/tests/net/java/android/net/IpPrefixTest.java b/tests/net/java/android/net/IpPrefixTest.java
index 3cc0e36..abf019a 100644
--- a/tests/net/java/android/net/IpPrefixTest.java
+++ b/tests/net/java/android/net/IpPrefixTest.java
@@ -231,7 +231,6 @@
assertFalse(p.contains(Address("2001:db8:f00::ace:d00e")));
assertFalse(p.contains(Address("2001:db8:f00::bad:d00d")));
assertFalse(p.contains(Address("2001:4868:4860::8888")));
- assertFalse(p.contains((InetAddress)null));
assertFalse(p.contains(Address("8.8.8.8")));
p = new IpPrefix("192.0.2.0/23");
diff --git a/tests/net/java/android/net/NetworkStatsTest.java b/tests/net/java/android/net/NetworkStatsTest.java
index b5b0384..c16a0f4 100644
--- a/tests/net/java/android/net/NetworkStatsTest.java
+++ b/tests/net/java/android/net/NetworkStatsTest.java
@@ -569,7 +569,7 @@
.addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 0L, 0L, 0L, 0L, 0L);
- assertTrue(delta.toString(), delta.migrateTun(tunUid, tunIface, underlyingIface));
+ delta.migrateTun(tunUid, tunIface, new String[] {underlyingIface});
assertEquals(20, delta.size());
// tunIface and TEST_IFACE entries are not changed.
@@ -650,7 +650,7 @@
.addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
DEFAULT_NETWORK_NO, 75500L, 37L, 130000L, 70L, 0L);
- assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface));
+ delta.migrateTun(tunUid, tunIface, new String[]{underlyingIface});
assertEquals(9, delta.size());
// tunIface entries should not be changed.
@@ -813,6 +813,37 @@
}
@Test
+ public void testFilterDebugEntries() {
+ NetworkStats.Entry entry1 = new NetworkStats.Entry(
+ "test1", 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+ NetworkStats.Entry entry2 = new NetworkStats.Entry(
+ "test2", 10101, SET_DBG_VPN_IN, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+ NetworkStats.Entry entry3 = new NetworkStats.Entry(
+ "test2", 10101, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+ NetworkStats.Entry entry4 = new NetworkStats.Entry(
+ "test2", 10101, SET_DBG_VPN_OUT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+
+ NetworkStats stats = new NetworkStats(TEST_START, 4)
+ .addValues(entry1)
+ .addValues(entry2)
+ .addValues(entry3)
+ .addValues(entry4);
+
+ stats.filterDebugEntries();
+
+ assertEquals(2, stats.size());
+ assertEquals(entry1, stats.getValues(0, null));
+ assertEquals(entry3, stats.getValues(1, null));
+ }
+
+ @Test
public void testApply464xlatAdjustments() {
final String v4Iface = "v4-wlan0";
final String baseIface = "wlan0";
diff --git a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
index 76cccc9..1a3ea60 100644
--- a/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
+++ b/tests/net/java/android/net/ipmemorystore/ParcelableTests.java
@@ -44,6 +44,8 @@
assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable())));
builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4"));
+ // lease will expire in two hours
+ builder.setAssignedV4AddressExpiry(System.currentTimeMillis() + 7_200_000);
// groupHint stays null this time around
builder.setDnsAddresses(Collections.emptyList());
builder.setMtu(18);
@@ -51,6 +53,7 @@
assertEquals(in, new NetworkAttributes(parcelingRoundTrip(in.toParcelable())));
builder.setAssignedV4Address((Inet4Address) Inet4Address.getByName("6.7.8.9"));
+ builder.setAssignedV4AddressExpiry(System.currentTimeMillis() + 3_600_000);
builder.setGroupHint("groupHint");
builder.setDnsAddresses(Arrays.asList(
InetAddress.getByName("ACA1:652B:0911:DE8F:1200:115E:913B:AA2A"),
@@ -66,7 +69,7 @@
// Verify that this test does not miss any new field added later.
// If any field is added to NetworkAttributes it must be tested here for parceling
// roundtrip.
- assertEquals(4, Arrays.stream(NetworkAttributes.class.getDeclaredFields())
+ assertEquals(5, Arrays.stream(NetworkAttributes.class.getDeclaredFields())
.filter(f -> !Modifier.isStatic(f.getModifiers())).count());
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 6f48da3..c2fc0b3 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -104,6 +104,7 @@
import android.net.ConnectivityManager.PacketKeepaliveCallback;
import android.net.ConnectivityManager.TooManyRequestsException;
import android.net.ConnectivityThread;
+import android.net.IDnsResolver;
import android.net.INetd;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
@@ -240,6 +241,7 @@
private static final String CLAT_PREFIX = "v4-";
private static final String MOBILE_IFNAME = "test_rmnet_data0";
private static final String WIFI_IFNAME = "test_wlan0";
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
private MockContext mServiceContext;
private WrappedConnectivityService mService;
@@ -256,6 +258,7 @@
@Mock INetworkManagementService mNetworkManagementService;
@Mock INetworkStatsService mStatsService;
@Mock INetworkPolicyManager mNpm;
+ @Mock IDnsResolver mMockDnsResolver;
@Mock INetd mMockNetd;
@Mock NetworkStackClient mNetworkStack;
@@ -496,7 +499,7 @@
};
try {
- doAnswer(validateAnswer).when(mNetworkMonitor).notifyNetworkConnected();
+ doAnswer(validateAnswer).when(mNetworkMonitor).notifyNetworkConnected(any(), any());
doAnswer(validateAnswer).when(mNetworkMonitor).forceReevaluation(anyInt());
} catch (RemoteException e) {
fail(e.getMessage());
@@ -1053,8 +1056,8 @@
public WrappedConnectivityService(Context context, INetworkManagementService netManager,
INetworkStatsService statsService, INetworkPolicyManager policyManager,
- IpConnectivityLog log, INetd netd) {
- super(context, netManager, statsService, policyManager, log);
+ IpConnectivityLog log, INetd netd, IDnsResolver dnsResolver) {
+ super(context, netManager, statsService, policyManager, dnsResolver, log);
mNetd = netd;
mLingerDelayMs = TEST_LINGER_DELAY_MS;
}
@@ -1218,7 +1221,8 @@
mStatsService,
mNpm,
mock(IpConnectivityLog.class),
- mMockNetd);
+ mMockNetd,
+ mMockDnsResolver);
final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
ArgumentCaptor.forClass(INetworkPolicyListener.class);
@@ -3043,6 +3047,47 @@
}
@Test
+ public void testInvalidSignalStrength() {
+ NetworkRequest r = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addTransportType(TRANSPORT_WIFI)
+ .setSignalStrength(-75)
+ .build();
+ // Registering a NetworkCallback with signal strength but w/o NETWORK_SIGNAL_STRENGTH_WAKEUP
+ // permission should get SecurityException.
+ try {
+ mCm.registerNetworkCallback(r, new NetworkCallback());
+ fail("Expected SecurityException filing a callback with signal strength");
+ } catch (SecurityException expected) {
+ // expected
+ }
+
+ try {
+ mCm.registerNetworkCallback(r, PendingIntent.getService(
+ mServiceContext, 0, new Intent(), 0));
+ fail("Expected SecurityException filing a callback with signal strength");
+ } catch (SecurityException expected) {
+ // expected
+ }
+
+ // Requesting a Network with signal strength should get IllegalArgumentException.
+ try {
+ mCm.requestNetwork(r, new NetworkCallback());
+ fail("Expected IllegalArgumentException filing a request with signal strength");
+ } catch (IllegalArgumentException expected) {
+ // expected
+ }
+
+ try {
+ mCm.requestNetwork(r, PendingIntent.getService(
+ mServiceContext, 0, new Intent(), 0));
+ fail("Expected IllegalArgumentException filing a request with signal strength");
+ } catch (IllegalArgumentException expected) {
+ // expected
+ }
+ }
+
+ @Test
public void testRegisterDefaultNetworkCallback() throws Exception {
final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultNetworkCallback);
@@ -4777,14 +4822,14 @@
ArgumentCaptor<String[]> tlsServers = ArgumentCaptor.forClass(String[].class);
// Clear any interactions that occur as a result of CS starting up.
- reset(mNetworkManagementService);
+ reset(mMockDnsResolver);
- final String[] EMPTY_STRING_ARRAY = new String[0];
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
waitForIdle();
- verify(mNetworkManagementService, never()).setDnsConfigurationForNetwork(
- anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), eq(EMPTY_STRING_ARRAY));
- verifyNoMoreInteractions(mNetworkManagementService);
+ verify(mMockDnsResolver, never()).setResolverConfiguration(
+ anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""),
+ eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY));
+ verifyNoMoreInteractions(mMockDnsResolver);
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
@@ -4801,28 +4846,29 @@
mCellNetworkAgent.connect(false);
waitForIdle();
// CS tells netd about the empty DNS config for this network.
- verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
- anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), eq(EMPTY_STRING_ARRAY));
- reset(mNetworkManagementService);
+ verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(
+ anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""),
+ eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY));
+ reset(mMockDnsResolver);
cellLp.addDnsServer(InetAddress.getByName("2001:db8::1"));
mCellNetworkAgent.sendLinkProperties(cellLp);
waitForIdle();
- verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
+ verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(
anyInt(), mStringArrayCaptor.capture(), any(), any(),
- eq(""), tlsServers.capture());
+ eq(""), tlsServers.capture(), eq(EMPTY_STRING_ARRAY));
assertEquals(1, mStringArrayCaptor.getValue().length);
assertTrue(ArrayUtils.contains(mStringArrayCaptor.getValue(), "2001:db8::1"));
// Opportunistic mode.
assertTrue(ArrayUtils.contains(tlsServers.getValue(), "2001:db8::1"));
- reset(mNetworkManagementService);
+ reset(mMockDnsResolver);
cellLp.addDnsServer(InetAddress.getByName("192.0.2.1"));
mCellNetworkAgent.sendLinkProperties(cellLp);
waitForIdle();
- verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
+ verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(
anyInt(), mStringArrayCaptor.capture(), any(), any(),
- eq(""), tlsServers.capture());
+ eq(""), tlsServers.capture(), eq(EMPTY_STRING_ARRAY));
assertEquals(2, mStringArrayCaptor.getValue().length);
assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
new String[]{"2001:db8::1", "192.0.2.1"}));
@@ -4830,7 +4876,7 @@
assertEquals(2, tlsServers.getValue().length);
assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
new String[]{"2001:db8::1", "192.0.2.1"}));
- reset(mNetworkManagementService);
+ reset(mMockDnsResolver);
final String TLS_SPECIFIER = "tls.example.com";
final String TLS_SERVER6 = "2001:db8:53::53";
@@ -4840,22 +4886,21 @@
new PrivateDnsConfig(TLS_SPECIFIER, TLS_IPS).toParcel());
waitForIdle();
- verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
+ verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(
anyInt(), mStringArrayCaptor.capture(), any(), any(),
- eq(TLS_SPECIFIER), eq(TLS_SERVERS));
+ eq(TLS_SPECIFIER), eq(TLS_SERVERS), eq(EMPTY_STRING_ARRAY));
assertEquals(2, mStringArrayCaptor.getValue().length);
assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
new String[]{"2001:db8::1", "192.0.2.1"}));
- reset(mNetworkManagementService);
+ reset(mMockDnsResolver);
}
@Test
public void testPrivateDnsSettingsChange() throws Exception {
- final String[] EMPTY_STRING_ARRAY = new String[0];
ArgumentCaptor<String[]> tlsServers = ArgumentCaptor.forClass(String[].class);
// Clear any interactions that occur as a result of CS starting up.
- reset(mNetworkManagementService);
+ reset(mMockDnsResolver);
// The default on Android is opportunistic mode ("Automatic").
setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
@@ -4868,9 +4913,10 @@
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
waitForIdle();
// CS tells netd about the empty DNS config for this network.
- verify(mNetworkManagementService, never()).setDnsConfigurationForNetwork(
- anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), eq(EMPTY_STRING_ARRAY));
- verifyNoMoreInteractions(mNetworkManagementService);
+ verify(mMockDnsResolver, never()).setResolverConfiguration(
+ anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""),
+ eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY));
+ verifyNoMoreInteractions(mMockDnsResolver);
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
@@ -4889,9 +4935,9 @@
mCellNetworkAgent.sendLinkProperties(cellLp);
mCellNetworkAgent.connect(false);
waitForIdle();
- verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
+ verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(
anyInt(), mStringArrayCaptor.capture(), any(), any(),
- eq(""), tlsServers.capture());
+ eq(""), tlsServers.capture(), eq(EMPTY_STRING_ARRAY));
assertEquals(2, mStringArrayCaptor.getValue().length);
assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
new String[]{"2001:db8::1", "192.0.2.1"}));
@@ -4899,7 +4945,7 @@
assertEquals(2, tlsServers.getValue().length);
assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
new String[]{"2001:db8::1", "192.0.2.1"}));
- reset(mNetworkManagementService);
+ reset(mMockDnsResolver);
cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES,
mCellNetworkAgent);
@@ -4911,26 +4957,26 @@
assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
- verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+ verify(mMockDnsResolver, times(1)).setResolverConfiguration(
anyInt(), mStringArrayCaptor.capture(), any(), any(),
- eq(""), eq(EMPTY_STRING_ARRAY));
+ eq(""), eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY));
assertEquals(2, mStringArrayCaptor.getValue().length);
assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
new String[]{"2001:db8::1", "192.0.2.1"}));
- reset(mNetworkManagementService);
+ reset(mMockDnsResolver);
cellNetworkCallback.assertNoCallback();
setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
- verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
+ verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(
anyInt(), mStringArrayCaptor.capture(), any(), any(),
- eq(""), tlsServers.capture());
+ eq(""), tlsServers.capture(), eq(EMPTY_STRING_ARRAY));
assertEquals(2, mStringArrayCaptor.getValue().length);
assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
new String[]{"2001:db8::1", "192.0.2.1"}));
assertEquals(2, tlsServers.getValue().length);
assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
new String[]{"2001:db8::1", "192.0.2.1"}));
- reset(mNetworkManagementService);
+ reset(mMockDnsResolver);
cellNetworkCallback.assertNoCallback();
setPrivateDnsSettings(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, "strict.example.com");
@@ -5761,6 +5807,7 @@
cellLp.addRoute(new RouteInfo((IpPrefix) null, myIpv6.getAddress(), MOBILE_IFNAME));
cellLp.addRoute(new RouteInfo(myIpv6, null, MOBILE_IFNAME));
reset(mNetworkManagementService);
+ reset(mMockDnsResolver);
when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
.thenReturn(getClatInterfaceConfig(myIpv4));
@@ -5768,7 +5815,7 @@
mCellNetworkAgent.sendLinkProperties(cellLp);
mCellNetworkAgent.connect(true);
networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- verify(mMockNetd, times(1)).resolverStartPrefix64Discovery(cellNetId);
+ verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
// Switching default network updates TCP buffer sizes.
verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES);
@@ -5778,17 +5825,22 @@
cellLp.addLinkAddress(myIpv4);
mCellNetworkAgent.sendLinkProperties(cellLp);
networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
- verify(mMockNetd, times(1)).resolverStopPrefix64Discovery(cellNetId);
+ verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId);
+ verify(mMockDnsResolver, atLeastOnce()).setResolverConfiguration(
+ eq(cellNetId), eq(EMPTY_STRING_ARRAY), any(), any(),
+ eq(""), eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY));
verifyNoMoreInteractions(mMockNetd);
+ verifyNoMoreInteractions(mMockDnsResolver);
reset(mMockNetd);
+ reset(mMockDnsResolver);
// Remove IPv4 address. Expect prefix discovery to be started again.
cellLp.removeLinkAddress(myIpv4);
cellLp.removeRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
mCellNetworkAgent.sendLinkProperties(cellLp);
networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
- verify(mMockNetd, times(1)).resolverStartPrefix64Discovery(cellNetId);
+ verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
// When NAT64 prefix discovery succeeds, LinkProperties are updated and clatd is started.
Nat464Xlat clat = mService.getNat464Xlat(mCellNetworkAgent);
@@ -5818,6 +5870,12 @@
assertNotEquals(stackedLpsAfterChange, Collections.EMPTY_LIST);
assertEquals(makeClatLinkProperties(myIpv4), stackedLpsAfterChange.get(0));
+ verify(mMockDnsResolver, times(1)).setResolverConfiguration(
+ eq(cellNetId), mStringArrayCaptor.capture(), any(), any(),
+ eq(""), eq(EMPTY_STRING_ARRAY), eq(EMPTY_STRING_ARRAY));
+ assertEquals(1, mStringArrayCaptor.getValue().length);
+ assertTrue(ArrayUtils.contains(mStringArrayCaptor.getValue(), "8.8.8.8"));
+
// Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked
// linkproperties are cleaned up.
cellLp.addLinkAddress(myIpv4);
@@ -5825,7 +5883,7 @@
mCellNetworkAgent.sendLinkProperties(cellLp);
networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
- verify(mMockNetd, times(1)).resolverStopPrefix64Discovery(cellNetId);
+ verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId);
// As soon as stop is called, the linkproperties lose the stacked interface.
networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
@@ -5840,7 +5898,9 @@
networkCallback.assertNoCallback();
verifyNoMoreInteractions(mMockNetd);
+ verifyNoMoreInteractions(mMockDnsResolver);
reset(mMockNetd);
+ reset(mMockDnsResolver);
// Stopping prefix discovery causes netd to tell us that the NAT64 prefix is gone.
mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, false /* added */,
@@ -5854,7 +5914,7 @@
cellLp.removeDnsServer(InetAddress.getByName("8.8.8.8"));
mCellNetworkAgent.sendLinkProperties(cellLp);
networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
- verify(mMockNetd, times(1)).resolverStartPrefix64Discovery(cellNetId);
+ verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
kNat64PrefixString, 96);
networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
@@ -5937,6 +5997,7 @@
// Disconnect cell
reset(mNetworkManagementService);
+ reset(mMockNetd);
mCellNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
// LOST callback is triggered earlier than removing idle timer. Broadcast should also be
@@ -5944,8 +6005,9 @@
// unexpectedly before network being removed.
waitForIdle();
verify(mNetworkManagementService, times(0)).removeIdleTimer(eq(MOBILE_IFNAME));
- verify(mNetworkManagementService, times(1)).removeNetwork(
- eq(mCellNetworkAgent.getNetwork().netId));
+ verify(mMockNetd, times(1)).networkDestroy(eq(mCellNetworkAgent.getNetwork().netId));
+ verify(mMockDnsResolver, times(1))
+ .clearResolverConfiguration(eq(mCellNetworkAgent.getNetwork().netId));
// Disconnect wifi
ConditionVariable cv = waitForConnectivityBroadcasts(1);
diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
index 15ba43d..8fa0ab9 100644
--- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
@@ -29,13 +29,13 @@
import static org.mockito.Mockito.when;
import android.content.Context;
+import android.net.IDnsResolver;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.RouteInfo;
import android.net.shared.PrivateDnsConfig;
-import android.os.INetworkManagementService;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
@@ -73,7 +73,7 @@
MockContentResolver mContentResolver;
@Mock Context mCtx;
- @Mock INetworkManagementService mNMService;
+ @Mock IDnsResolver mMockDnsResolver;
@Mock MockableSystemProperties mSystemProperties;
@Before
@@ -83,7 +83,7 @@
mContentResolver.addProvider(Settings.AUTHORITY,
new FakeSettingsProvider());
when(mCtx.getContentResolver()).thenReturn(mContentResolver);
- mDnsManager = new DnsManager(mCtx, mNMService, mSystemProperties);
+ mDnsManager = new DnsManager(mCtx, mMockDnsResolver, mSystemProperties);
// Clear the private DNS settings
Settings.Global.putString(mContentResolver, PRIVATE_DNS_DEFAULT_MODE, "");
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 6de4aa1..142769f 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -32,6 +32,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.net.ConnectivityManager;
+import android.net.IDnsResolver;
import android.net.INetd;
import android.net.Network;
import android.net.NetworkCapabilities;
@@ -69,6 +70,7 @@
LingerMonitor mMonitor;
@Mock ConnectivityService mConnService;
+ @Mock IDnsResolver mDnsResolver;
@Mock INetd mNetd;
@Mock INetworkManagementService mNMS;
@Mock Context mCtx;
@@ -353,7 +355,7 @@
caps.addCapability(0);
caps.addTransportType(transport);
NetworkAgentInfo nai = new NetworkAgentInfo(null, null, new Network(netId), info, null,
- caps, 50, mCtx, null, mMisc, mConnService, mNetd, mNMS,
+ caps, 50, mCtx, null, mMisc, mConnService, mNetd, mDnsResolver, mNMS,
NetworkFactory.SerialNumber.NONE);
nai.everValidated = true;
return nai;
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index cc09fb7..b709af1 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -27,6 +27,7 @@
import static org.mockito.Mockito.when;
import android.net.ConnectivityManager;
+import android.net.IDnsResolver;
import android.net.INetd;
import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
@@ -63,6 +64,7 @@
@Mock ConnectivityService mConnectivity;
@Mock NetworkMisc mMisc;
+ @Mock IDnsResolver mDnsResolver;
@Mock INetd mNetd;
@Mock INetworkManagementService mNms;
@Mock InterfaceConfiguration mConfig;
@@ -72,7 +74,7 @@
Handler mHandler;
Nat464Xlat makeNat464Xlat() {
- return new Nat464Xlat(mNai, mNetd, mNms) {
+ return new Nat464Xlat(mNai, mNetd, mDnsResolver, mNms) {
@Override protected int getNetId() {
return NETID;
}
@@ -205,7 +207,7 @@
verify(mNms).unregisterObserver(eq(nat));
assertTrue(c.getValue().getStackedLinks().isEmpty());
assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
- verify(mNetd).resolverStopPrefix64Discovery(eq(NETID));
+ verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
assertIdle(nat);
// Stacked interface removed notification arrives and is ignored.
@@ -331,7 +333,7 @@
verify(mNetd).clatdStop(eq(BASE_IFACE));
verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
verify(mNms).unregisterObserver(eq(nat));
- verify(mNetd).resolverStopPrefix64Discovery(eq(NETID));
+ verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
assertTrue(c.getValue().getStackedLinks().isEmpty());
assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
assertIdle(nat);
@@ -358,7 +360,7 @@
verify(mNetd).clatdStop(eq(BASE_IFACE));
verify(mNms).unregisterObserver(eq(nat));
- verify(mNetd).resolverStopPrefix64Discovery(eq(NETID));
+ verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
assertIdle(nat);
// In-flight interface up notification arrives: no-op
@@ -390,7 +392,7 @@
verify(mNetd).clatdStop(eq(BASE_IFACE));
verify(mNms).unregisterObserver(eq(nat));
- verify(mNetd).resolverStopPrefix64Discovery(eq(NETID));
+ verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
assertIdle(nat);
verifyNoMoreInteractions(mNetd, mNms, mConnectivity);
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index bce526d..e35c34a 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -57,6 +57,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -216,11 +217,16 @@
expectNetworkStatsUidDetail(buildEmptyStats());
expectSystemReady();
+ assertNull(mService.getTunAdjustedStats());
mService.systemReady();
+ // Verify that system ready fetches realtime stats and initializes tun adjusted stats.
+ verify(mNetManager).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL);
+ assertNotNull("failed to initialize TUN adjusted stats", mService.getTunAdjustedStats());
+ assertEquals(0, mService.getTunAdjustedStats().size());
+
mSession = mService.openSession();
assertNotNull("openSession() failed", mSession);
-
// catch INetworkManagementEventObserver during systemReady()
ArgumentCaptor<INetworkManagementEventObserver> networkObserver =
ArgumentCaptor.forClass(INetworkManagementEventObserver.class);
@@ -733,11 +739,13 @@
NetworkStats stats = mService.getDetailedUidStats(ifaceFilter);
- verify(mNetManager, times(1)).getNetworkStatsUidDetail(eq(UID_ALL), argThat(ifaces ->
- ifaces != null && ifaces.length == 2
- && ArrayUtils.contains(ifaces, TEST_IFACE)
- && ArrayUtils.contains(ifaces, stackedIface)));
-
+ // mNetManager#getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL) has following invocations:
+ // 1) NetworkStatsService#systemReady from #setUp.
+ // 2) mService#forceUpdateIfaces in the test above.
+ // 3) Finally, mService#getDetailedUidStats.
+ verify(mNetManager, times(3)).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL);
+ assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), TEST_IFACE));
+ assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), stackedIface));
assertEquals(2, stats.size());
assertEquals(uidStats, stats.getValues(0, null));
assertEquals(tetheredStats1, stats.getValues(1, null));
@@ -927,7 +935,7 @@
// WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
expectDefaultSettings();
NetworkState[] networkStates = new NetworkState[] {buildWifiState(), buildVpnState()};
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(TEST_IFACE)};
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
@@ -947,8 +955,10 @@
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
.addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 1L)
.addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 500L, 50L, 500L, 50L, 1L)
- .addValues(
- TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1650L, 150L, 1650L, 150L, 2L));
+ // VPN received 1650 bytes over WiFi in background (SET_DEFAULT).
+ .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1650L, 150L, 0L, 0L, 1L)
+ // VPN sent 1650 bytes over WiFi in foreground (SET_FOREGROUND).
+ .addValues(TEST_IFACE, UID_VPN, SET_FOREGROUND, TAG_NONE, 0L, 0L, 1650L, 150L, 1L));
forcePollAndWaitForIdle();
@@ -962,7 +972,7 @@
// WiFi network is connected and VPN is using WiFi (which has TEST_IFACE).
expectDefaultSettings();
NetworkState[] networkStates = new NetworkState[] {buildWifiState(), buildVpnState()};
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(TEST_IFACE)};
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
@@ -993,6 +1003,132 @@
}
@Test
+ public void vpnWithTwoUnderlyingIfaces_packetDuplication() throws Exception {
+ // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
+ // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
+ // Additionally, VPN is duplicating traffic across both WiFi and Cell.
+ expectDefaultSettings();
+ NetworkState[] networkStates =
+ new NetworkState[] {
+ buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState()
+ };
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
+ expectNetworkStatsUidDetail(buildEmptyStats());
+ expectBandwidthControlCheck();
+
+ mService.forceUpdateIfaces(
+ new Network[] {WIFI_NETWORK, VPN_NETWORK},
+ vpnInfos,
+ networkStates,
+ getActiveIface(networkStates));
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were sent/received by UID_RED and UID_BLUE over VPN.
+ // VPN sent/received 4400 bytes (400 packets) over both WiFi and Cell (8800 bytes in total).
+ // Of 8800 bytes over WiFi/Cell, expect:
+ // - 500 bytes rx/tx each over WiFi/Cell attributed to both UID_RED and UID_BLUE.
+ // - 1200 bytes rx/tx each over WiFi/Cell for VPN_UID.
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 4)
+ .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 2L)
+ .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 2L)
+ .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 2200L, 200L, 2200L, 200L, 2L)
+ .addValues(
+ TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 2200L, 200L, 2200L, 200L, 2L));
+
+ forcePollAndWaitForIdle();
+
+ assertUidTotal(sTemplateWifi, UID_RED, 500L, 50L, 500L, 50L, 1);
+ assertUidTotal(sTemplateWifi, UID_BLUE, 500L, 50L, 500L, 50L, 1);
+ assertUidTotal(sTemplateWifi, UID_VPN, 1200L, 100L, 1200L, 100L, 2);
+
+ assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 500L, 50L, 500L, 50L, 1);
+ assertUidTotal(buildTemplateMobileWildcard(), UID_BLUE, 500L, 50L, 500L, 50L, 1);
+ assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 1200L, 100L, 1200L, 100L, 2);
+ }
+
+ @Test
+ public void vpnWithTwoUnderlyingIfaces_splitTraffic() throws Exception {
+ // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
+ // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
+ // Additionally, VPN is arbitrarily splitting traffic across WiFi and Cell.
+ expectDefaultSettings();
+ NetworkState[] networkStates =
+ new NetworkState[] {
+ buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState()
+ };
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
+ expectNetworkStatsUidDetail(buildEmptyStats());
+ expectBandwidthControlCheck();
+
+ mService.forceUpdateIfaces(
+ new Network[] {WIFI_NETWORK, VPN_NETWORK},
+ vpnInfos,
+ networkStates,
+ getActiveIface(networkStates));
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
+ // VPN sent/received 660 bytes (60 packets) over WiFi and 440 bytes (40 packets) over Cell.
+ // For UID_RED, expect 600 bytes attributed over WiFi and 400 bytes over Cell for both
+ // rx/tx.
+ // For UID_VPN, expect 60 bytes attributed over WiFi and 40 bytes over Cell for both rx/tx.
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
+ .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 2L)
+ .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 660L, 60L, 660L, 60L, 1L)
+ .addValues(TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 440L, 40L, 440L, 40L, 1L));
+
+ forcePollAndWaitForIdle();
+
+ assertUidTotal(sTemplateWifi, UID_RED, 600L, 60L, 600L, 60L, 1);
+ assertUidTotal(sTemplateWifi, UID_VPN, 60L, 0L, 60L, 0L, 1);
+
+ assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 400L, 40L, 400L, 40L, 1);
+ assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 40L, 0L, 40L, 0L, 1);
+ }
+
+ @Test
+ public void vpnWithTwoUnderlyingIfaces_splitTrafficWithCompression() throws Exception {
+ // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and
+ // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set.
+ // Additionally, VPN is arbitrarily splitting compressed traffic across WiFi and Cell.
+ expectDefaultSettings();
+ NetworkState[] networkStates =
+ new NetworkState[] {
+ buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState()
+ };
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})};
+ expectNetworkStatsUidDetail(buildEmptyStats());
+ expectBandwidthControlCheck();
+
+ mService.forceUpdateIfaces(
+ new Network[] {WIFI_NETWORK, VPN_NETWORK},
+ vpnInfos,
+ networkStates,
+ getActiveIface(networkStates));
+ // create some traffic (assume 10 bytes of MTU for VPN interface:
+ // 1000 bytes (100 packets) were sent/received by UID_RED over VPN.
+ // VPN sent/received 600 bytes (60 packets) over WiFi and 200 bytes (20 packets) over Cell.
+ // For UID_RED, expect 600 bytes attributed over WiFi and 200 bytes over Cell for both
+ // rx/tx.
+ // UID_VPN gets nothing attributed to it (avoiding negative stats).
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 4)
+ .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 1L)
+ .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 600L, 60L, 600L, 60L, 0L)
+ .addValues(TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 200L, 20L, 200L, 20L, 0L));
+
+ forcePollAndWaitForIdle();
+
+ assertUidTotal(sTemplateWifi, UID_RED, 600L, 60L, 600L, 60L, 0);
+ assertUidTotal(sTemplateWifi, UID_VPN, 0L, 0L, 0L, 0L, 0);
+
+ assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 200L, 20L, 200L, 20L, 0);
+ assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 0L, 0L, 0L, 0L, 0);
+ }
+
+ @Test
public void vpnWithIncorrectUnderlyingIface() throws Exception {
// WiFi and Cell networks are connected and VPN is using Cell (which has TEST_IFACE2),
// but has declared only WiFi (TEST_IFACE) in its underlying network set.
@@ -1001,7 +1137,7 @@
new NetworkState[] {
buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState()
};
- VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(TEST_IFACE)};
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
expectNetworkStatsUidDetail(buildEmptyStats());
expectBandwidthControlCheck();
@@ -1030,6 +1166,134 @@
}
@Test
+ public void recordSnapshot_migratesTunTrafficAndUpdatesTunAdjustedStats() throws Exception {
+ assertEquals(0, mService.getTunAdjustedStats().size());
+ // VPN using WiFi (TEST_IFACE).
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ expectBandwidthControlCheck();
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were downloaded by UID_RED over VPN.
+ // VPN received 1100 bytes (100 packets) over WiFi.
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
+ .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 0L, 0L, 0L));
+
+ // this should lead to NSS#recordSnapshotLocked
+ mService.forceUpdateIfaces(
+ new Network[0], vpnInfos, new NetworkState[0], null /* activeIface */);
+
+ // Verify TUN adjusted stats have traffic migrated correctly.
+ // Of 1100 bytes VPN received over WiFi, expect 1000 bytes attributed to UID_RED and 100
+ // bytes attributed to UID_VPN.
+ NetworkStats tunAdjStats = mService.getTunAdjustedStats();
+ assertValues(
+ tunAdjStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0);
+ assertValues(
+ tunAdjStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 100L, 0L, 0L, 0L, 0);
+ }
+
+ @Test
+ public void getDetailedUidStats_migratesTunTrafficAndUpdatesTunAdjustedStats()
+ throws Exception {
+ assertEquals(0, mService.getTunAdjustedStats().size());
+ // VPN using WiFi (TEST_IFACE).
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ expectBandwidthControlCheck();
+ mService.forceUpdateIfaces(
+ new Network[0], vpnInfos, new NetworkState[0], null /* activeIface */);
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were downloaded by UID_RED over VPN.
+ // VPN received 1100 bytes (100 packets) over WiFi.
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
+ .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 0L, 0L, 0L));
+
+ mService.getDetailedUidStats(INTERFACES_ALL);
+
+ // Verify internally maintained TUN adjusted stats
+ NetworkStats tunAdjStats = mService.getTunAdjustedStats();
+ // Verify stats for TEST_IFACE (WiFi):
+ // Of 1100 bytes VPN received over WiFi, expect 1000 bytes attributed to UID_RED and 100
+ // bytes attributed to UID_VPN.
+ assertValues(
+ tunAdjStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0);
+ assertValues(
+ tunAdjStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 100L, 0L, 0L, 0L, 0);
+ // Verify stats for TUN_IFACE; only UID_RED should have usage on it.
+ assertValues(
+ tunAdjStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0);
+ assertValues(
+ tunAdjStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 0L, 0L, 0L, 0L, 0);
+
+ // lets assume that since last time, VPN received another 1100 bytes (same assumptions as
+ // before i.e. UID_RED downloaded another 1000 bytes).
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ // Note - NetworkStatsFactory returns counters that are monotonically increasing.
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
+ .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 2000L, 200L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 2200L, 200L, 0L, 0L, 0L));
+
+ mService.getDetailedUidStats(INTERFACES_ALL);
+
+ tunAdjStats = mService.getTunAdjustedStats();
+ // verify TEST_IFACE stats:
+ assertValues(
+ tunAdjStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 2000L, 200L, 0L, 0L, 0);
+ assertValues(
+ tunAdjStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 200L, 0L, 0L, 0L, 0);
+ // verify TUN_IFACE stats:
+ assertValues(
+ tunAdjStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 2000L, 200L, 0L, 0L, 0);
+ assertValues(
+ tunAdjStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 0L, 0L, 0L, 0L, 0);
+ }
+
+ @Test
+ public void getDetailedUidStats_returnsCorrectStatsWithVpnRunning() throws Exception {
+ // VPN using WiFi (TEST_IFACE).
+ VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})};
+ expectBandwidthControlCheck();
+ mService.forceUpdateIfaces(
+ new Network[0], vpnInfos, new NetworkState[0], null /* activeIface */);
+ // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption
+ // overhead per packet):
+ // 1000 bytes (100 packets) were downloaded by UID_RED over VPN.
+ // VPN received 1100 bytes (100 packets) over WiFi.
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2)
+ .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 0L, 0L, 0L));
+
+ // Query realtime stats for TEST_IFACE.
+ NetworkStats queriedStats =
+ mService.getDetailedUidStats(new String[] {TEST_IFACE});
+
+ assertEquals(HOUR_IN_MILLIS, queriedStats.getElapsedRealtime());
+ // verify that returned stats are only for TEST_IFACE and VPN traffic is migrated correctly.
+ assertEquals(new String[] {TEST_IFACE}, queriedStats.getUniqueIfaces());
+ assertValues(
+ queriedStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0);
+ assertValues(
+ queriedStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
+ DEFAULT_NETWORK_ALL, 100L, 0L, 0L, 0L, 0);
+ }
+
+ @Test
public void testRegisterUsageCallback() throws Exception {
// pretend that wifi network comes online; service should ask about full
// network state, and poll any existing interfaces before updating.
@@ -1382,11 +1646,11 @@
return new NetworkState(info, prop, new NetworkCapabilities(), VPN_NETWORK, null, null);
}
- private static VpnInfo createVpnInfo(String underlyingIface) {
+ private static VpnInfo createVpnInfo(String[] underlyingIfaces) {
VpnInfo info = new VpnInfo();
info.ownerUid = UID_VPN;
info.vpnIface = TUN_IFACE;
- info.primaryUnderlyingIface = underlyingIface;
+ info.underlyingIfaces = underlyingIfaces;
return info;
}
diff --git a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java b/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
index dc20185..fb84611 100644
--- a/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
+++ b/tests/net/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
@@ -57,6 +57,7 @@
final NetworkAttributes na =
new NetworkAttributes(
(Inet4Address) Inet4Address.getByAddress(new byte[] {1, 2, 3, 4}),
+ System.currentTimeMillis() + 7_200_000,
"some hint",
Arrays.asList(Inet4Address.getByAddress(new byte[] {5, 6, 7, 8}),
Inet4Address.getByAddress(new byte[] {9, 0, 1, 2})),