Merge "Consider NetworkOffer is unneeded if it cannot satisfy the request"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 7c638a2..0ce43cc 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -19,6 +19,11 @@
"name": "TetheringIntegrationTests"
}
],
+ "postsubmit": [
+ {
+ "name": "ConnectivityCoverageTests"
+ }
+ ],
"mainline-presubmit": [
{
"name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
@@ -37,11 +42,9 @@
},
{
"name": "TetheringCoverageTests[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]"
- }
- ],
- "imports": [
+ },
{
- "path": "packages/modules/NetworkStack"
+ "name": "ConnectivityCoverageTests[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]"
}
],
"imports": [
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index bd7ebda..88c885a 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -67,6 +67,20 @@
name: "com.android.tethering-bootclasspath-fragment",
contents: ["framework-tethering"],
apex_available: ["com.android.tethering"],
+
+ // The bootclasspath_fragments that provide APIs on which this depends.
+ fragments: [
+ {
+ apex: "com.android.art",
+ module: "art-bootclasspath-fragment",
+ },
+ ],
+
+ // Additional stubs libraries that this fragment's contents use which are
+ // not provided by another bootclasspath_fragment.
+ additional_stubs: [
+ "android-non-updatable",
+ ],
}
override_apex {
diff --git a/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java b/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
index 33f1c29..a33af61 100644
--- a/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
+++ b/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
@@ -33,6 +33,8 @@
import com.android.networkstack.tethering.Tether4Value;
import com.android.networkstack.tethering.TetherStatsValue;
+import java.util.function.BiConsumer;
+
/**
* Bpf coordinator class for API shims.
*/
@@ -161,6 +163,12 @@
}
@Override
+ public void tetherOffloadRuleForEach(boolean downstream,
+ @NonNull BiConsumer<Tether4Key, Tether4Value> action) {
+ /* no op */
+ }
+
+ @Override
public boolean attachProgram(String iface, boolean downstream) {
/* no op */
return true;
diff --git a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
index 74ddcbc..611c828 100644
--- a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
+++ b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
@@ -47,6 +47,7 @@
import java.io.FileDescriptor;
import java.io.IOException;
+import java.util.function.BiConsumer;
/**
* Bpf coordinator class for API shims.
@@ -380,10 +381,7 @@
try {
if (downstream) {
- if (!mBpfDownstream4Map.deleteEntry(key)) {
- mLog.e("Could not delete entry (key: " + key + ")");
- return false;
- }
+ if (!mBpfDownstream4Map.deleteEntry(key)) return false; // Rule did not exist
// Decrease the rule count while a deleting rule is not using a given upstream
// interface anymore.
@@ -401,19 +399,32 @@
mRule4CountOnUpstream.put(upstreamIfindex, count);
}
} else {
- mBpfUpstream4Map.deleteEntry(key);
+ if (!mBpfUpstream4Map.deleteEntry(key)) return false; // Rule did not exist
}
} catch (ErrnoException e) {
- // Silent if the rule did not exist.
- if (e.errno != OsConstants.ENOENT) {
- mLog.e("Could not delete entry: ", e);
- return false;
- }
+ mLog.e("Could not delete entry (key: " + key + ")", e);
+ return false;
}
return true;
}
@Override
+ public void tetherOffloadRuleForEach(boolean downstream,
+ @NonNull BiConsumer<Tether4Key, Tether4Value> action) {
+ if (!isInitialized()) return;
+
+ try {
+ if (downstream) {
+ mBpfDownstream4Map.forEach(action);
+ } else {
+ mBpfUpstream4Map.forEach(action);
+ }
+ } catch (ErrnoException e) {
+ mLog.e("Could not iterate map: ", e);
+ }
+ }
+
+ @Override
public boolean attachProgram(String iface, boolean downstream) {
if (!isInitialized()) return false;
diff --git a/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java b/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
index 8a7a49c..08ab9ca 100644
--- a/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
+++ b/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
@@ -28,6 +28,8 @@
import com.android.networkstack.tethering.Tether4Value;
import com.android.networkstack.tethering.TetherStatsValue;
+import java.util.function.BiConsumer;
+
/**
* Bpf coordinator class for API shims.
*/
@@ -145,10 +147,25 @@
/**
* Deletes a tethering IPv4 offload rule from the appropriate BPF map.
+ *
+ * @param downstream true if downstream, false if upstream.
+ * @param key the key to delete.
+ * @return true iff the map was modified, false if the key did not exist or there was an error.
*/
public abstract boolean tetherOffloadRuleRemove(boolean downstream, @NonNull Tether4Key key);
/**
+ * Iterate through the map and handle each key -> value retrieved base on the given BiConsumer.
+ *
+ * @param downstream true if downstream, false if upstream.
+ * @param action represents the action for each key -> value. The entry deletion is not
+ * allowed and use #tetherOffloadRuleRemove instead.
+ */
+ @Nullable
+ public abstract void tetherOffloadRuleForEach(boolean downstream,
+ @NonNull BiConsumer<Tether4Key, Tether4Value> action);
+
+ /**
* Whether there is currently any IPv4 rule on the specified upstream.
*/
public abstract boolean isAnyIpv4RuleOnUpstream(int ifIndex);
diff --git a/Tethering/bpf_progs/offload.c b/Tethering/bpf_progs/offload.c
index 6ff370c..336d27a 100644
--- a/Tethering/bpf_progs/offload.c
+++ b/Tethering/bpf_progs/offload.c
@@ -569,6 +569,16 @@
// For a rawip tx interface it will simply be a bunch of zeroes and later stripped.
*eth = v->macHeader;
+ // Decrement the IPv4 TTL, we already know it's greater than 1.
+ // u8 TTL field is followed by u8 protocol to make a u16 for ipv4 header checksum update.
+ // Since we're keeping the ipv4 checksum valid (which means the checksum of the entire
+ // ipv4 header remains 0), the overall checksum of the entire packet does not change.
+ const int sz2 = sizeof(__be16);
+ const __be16 old_ttl_proto = *(__be16 *)&ip->ttl;
+ const __be16 new_ttl_proto = old_ttl_proto - htons(0x0100);
+ bpf_l3_csum_replace(skb, ETH_IP4_OFFSET(check), old_ttl_proto, new_ttl_proto, sz2);
+ bpf_skb_store_bytes(skb, ETH_IP4_OFFSET(ttl), &new_ttl_proto, sz2, 0);
+
const int l4_offs_csum = is_tcp ? ETH_IP4_TCP_OFFSET(check) : ETH_IP4_UDP_OFFSET(check);
const int sz4 = sizeof(__be32);
// UDP 0 is special and stored as FFFF (this flag also causes a csum of 0 to be unmodified)
@@ -586,7 +596,6 @@
bpf_l3_csum_replace(skb, ETH_IP4_OFFSET(check), old_saddr, new_saddr, sz4);
bpf_skb_store_bytes(skb, ETH_IP4_OFFSET(saddr), &new_saddr, sz4, 0);
- const int sz2 = sizeof(__be16);
// The offsets for TCP and UDP ports: source (u16 @ L4 offset 0) & dest (u16 @ L4 offset 2) are
// actually the same, so the compiler should just optimize them both down to a constant.
bpf_l4_csum_replace(skb, l4_offs_csum, k.srcPort, v->srcPort, sz2 | l4_flags);
@@ -597,8 +606,6 @@
bpf_skb_store_bytes(skb, is_tcp ? ETH_IP4_TCP_OFFSET(dest) : ETH_IP4_UDP_OFFSET(dest),
&v->dstPort, sz2, 0);
- // TEMP HACK: lack of TTL decrement
-
// This requires the bpf_ktime_get_boot_ns() helper which was added in 5.8,
// and backported to all Android Common Kernel 4.14+ trees.
if (updatetime) v->last_used = bpf_ktime_get_boot_ns();
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index 3428c1d..822bdf6 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -596,6 +596,7 @@
// into calls to InterfaceController, shared with startIPv4().
mInterfaceCtrl.clearIPv4Address();
mPrivateAddressCoordinator.releaseDownstream(this);
+ mBpfCoordinator.tetherOffloadClientClear(this);
mIpv4Address = null;
mStaticIpv4ServerAddr = null;
mStaticIpv4ClientAddr = null;
@@ -949,7 +950,6 @@
if (e.isValid()) {
mBpfCoordinator.tetherOffloadClientAdd(this, clientInfo);
} else {
- // TODO: Delete all related offload rules which are using this client.
mBpfCoordinator.tetherOffloadClientRemove(this, clientInfo);
}
}
@@ -1283,6 +1283,16 @@
super.exit();
}
+ // Note that IPv4 offload rules cleanup is implemented in BpfCoordinator while upstream
+ // state is null or changed because IPv4 and IPv6 tethering have different code flow
+ // and behaviour. While upstream is switching from offload supported interface to
+ // offload non-supportted interface, event CMD_TETHER_CONNECTION_CHANGED calls
+ // #cleanupUpstreamInterface but #cleanupUpstream because new UpstreamIfaceSet is not null.
+ // This case won't happen in IPv6 tethering because IPv6 tethering upstream state is
+ // reported by IPv6TetheringCoordinator. #cleanupUpstream is also called by unwirding
+ // adding NAT failure. In that case, the IPv4 offload rules are removed by #stopIPv4
+ // in the state machine. Once there is any case out whish is not covered by previous cases,
+ // probably consider clearing rules in #cleanupUpstream as well.
private void cleanupUpstream() {
if (mUpstreamIfaceSet == null) return;
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 4a05c9f..2c1fd29 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -34,7 +34,6 @@
import android.app.usage.NetworkStatsManager;
import android.net.INetd;
-import android.net.LinkProperties;
import android.net.MacAddress;
import android.net.NetworkStats;
import android.net.NetworkStats.Entry;
@@ -42,7 +41,9 @@
import android.net.ip.ConntrackMonitor;
import android.net.ip.ConntrackMonitor.ConntrackEventConsumer;
import android.net.ip.IpServer;
+import android.net.netlink.ConntrackMessage;
import android.net.netlink.NetlinkConstants;
+import android.net.netlink.NetlinkSocket;
import android.net.netstats.provider.NetworkStatsProvider;
import android.net.util.InterfaceParams;
import android.net.util.SharedLog;
@@ -50,7 +51,9 @@
import android.os.Handler;
import android.os.SystemClock;
import android.system.ErrnoException;
+import android.system.OsConstants;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
@@ -69,6 +72,7 @@
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@@ -120,6 +124,13 @@
}
@VisibleForTesting
+ static final int POLLING_CONNTRACK_TIMEOUT_MS = 60_000;
+ @VisibleForTesting
+ static final int NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED = 432000;
+ @VisibleForTesting
+ static final int NF_CONNTRACK_UDP_TIMEOUT_STREAM = 180;
+
+ @VisibleForTesting
enum StatsType {
STATS_PER_IFACE,
STATS_PER_UID,
@@ -228,12 +239,22 @@
// BpfCoordinatorTest needs predictable iteration order.
private final Set<Integer> mDeviceMapSet = new LinkedHashSet<>();
+ // Tracks the last IPv4 upstream index. Support single upstream only.
+ // TODO: Support multi-upstream interfaces.
+ private int mLastIPv4UpstreamIfindex = 0;
+
// Runnable that used by scheduling next polling of stats.
- private final Runnable mScheduledPollingTask = () -> {
+ private final Runnable mScheduledPollingStats = () -> {
updateForwardedStats();
maybeSchedulePollingStats();
};
+ // Runnable that used by scheduling next polling of conntrack timeout.
+ private final Runnable mScheduledPollingConntrackTimeout = () -> {
+ maybeRefreshConntrackTimeout();
+ maybeSchedulePollingConntrackTimeout();
+ };
+
// TODO: add BpfMap<TetherDownstream64Key, TetherDownstream64Value> retrieving function.
@VisibleForTesting
public abstract static class Dependencies {
@@ -263,13 +284,19 @@
}
/**
+ * Represents an estimate of elapsed time since boot in nanoseconds.
+ */
+ public long elapsedRealtimeNanos() {
+ return SystemClock.elapsedRealtimeNanos();
+ }
+
+ /**
* Check OS Build at least S.
*
* TODO: move to BpfCoordinatorShim once the test doesn't need the mocked OS build for
* testing different code flows concurrently.
*/
public boolean isAtLeastS() {
- // TODO: consider using ShimUtils.isAtLeastS.
return SdkLevel.isAtLeastS();
}
@@ -407,6 +434,7 @@
mPollingStarted = true;
maybeSchedulePollingStats();
+ maybeSchedulePollingConntrackTimeout();
mLog.i("Polling started");
}
@@ -422,9 +450,13 @@
public void stopPolling() {
if (!mPollingStarted) return;
- // Stop scheduled polling tasks and poll the latest stats from BPF maps.
- if (mHandler.hasCallbacks(mScheduledPollingTask)) {
- mHandler.removeCallbacks(mScheduledPollingTask);
+ // Stop scheduled polling conntrack timeout.
+ if (mHandler.hasCallbacks(mScheduledPollingConntrackTimeout)) {
+ mHandler.removeCallbacks(mScheduledPollingConntrackTimeout);
+ }
+ // Stop scheduled polling stats and poll the latest stats from BPF maps.
+ if (mHandler.hasCallbacks(mScheduledPollingStats)) {
+ mHandler.removeCallbacks(mScheduledPollingStats);
}
updateForwardedStats();
mPollingStarted = false;
@@ -576,6 +608,7 @@
/**
* Clear all forwarding rules for a given downstream.
* Note that this can be only called on handler thread.
+ * TODO: rename to tetherOffloadRuleClear6 because of IPv6 only.
*/
public void tetherOffloadRuleClear(@NonNull final IpServer ipServer) {
if (!isUsingBpf()) return;
@@ -647,6 +680,7 @@
/**
* Add downstream client.
+ * Note that this can be only called on handler thread.
*/
public void tetherOffloadClientAdd(@NonNull final IpServer ipServer,
@NonNull final ClientInfo client) {
@@ -661,54 +695,180 @@
}
/**
- * Remove downstream client.
+ * Remove a downstream client and its rules if any.
+ * Note that this can be only called on handler thread.
*/
public void tetherOffloadClientRemove(@NonNull final IpServer ipServer,
@NonNull final ClientInfo client) {
if (!isUsingBpf()) return;
+ // No clients on the downstream, return early.
HashMap<Inet4Address, ClientInfo> clients = mTetherClients.get(ipServer);
if (clients == null) return;
- // If no rule is removed, return early. Avoid unnecessary work on a non-existent rule
- // which may have never been added or removed already.
+ // No client is removed, return early.
if (clients.remove(client.clientAddress) == null) return;
- // Remove the downstream entry if it has no more rule.
+ // Remove the client's rules. Removing the client implies that its rules are not used
+ // anymore.
+ tetherOffloadRuleClear(client);
+
+ // Remove the downstream entry if it has no more client.
if (clients.isEmpty()) {
mTetherClients.remove(ipServer);
}
}
/**
- * Call when UpstreamNetworkState may be changed.
- * If upstream has ipv4 for tethering, update this new UpstreamNetworkState to map. The
- * upstream interface index and its address mapping is prepared for building IPv4
- * offload rule.
- *
- * TODO: Delete the unused upstream interface mapping.
- * TODO: Support ether ip upstream interface.
+ * Clear all downstream clients and their rules if any.
+ * Note that this can be only called on handler thread.
*/
- public void addUpstreamIfindexToMap(LinkProperties lp) {
- if (!mPollingStarted) return;
+ public void tetherOffloadClientClear(@NonNull final IpServer ipServer) {
+ if (!isUsingBpf()) return;
+
+ final HashMap<Inet4Address, ClientInfo> clients = mTetherClients.get(ipServer);
+ if (clients == null) return;
+
+ // Need to build a client list because the client map may be changed in the iteration.
+ for (final ClientInfo c : new ArrayList<ClientInfo>(clients.values())) {
+ tetherOffloadClientRemove(ipServer, c);
+ }
+ }
+
+ /**
+ * Clear all forwarding IPv4 rules for a given client.
+ * Note that this can be only called on handler thread.
+ */
+ private void tetherOffloadRuleClear(@NonNull final ClientInfo clientInfo) {
+ // TODO: consider removing the rules in #tetherOffloadRuleForEach once BpfMap#forEach
+ // can guarantee that deleting some pass-in rules in the BPF map iteration can still
+ // walk through every entry.
+ final Inet4Address clientAddr = clientInfo.clientAddress;
+ final Set<Integer> upstreamIndiceSet = new ArraySet<Integer>();
+ final Set<Tether4Key> deleteUpstreamRuleKeys = new ArraySet<Tether4Key>();
+ final Set<Tether4Key> deleteDownstreamRuleKeys = new ArraySet<Tether4Key>();
+
+ // Find the rules which are related with the given client.
+ mBpfCoordinatorShim.tetherOffloadRuleForEach(UPSTREAM, (k, v) -> {
+ if (Arrays.equals(k.src4, clientAddr.getAddress())) {
+ deleteUpstreamRuleKeys.add(k);
+ }
+ });
+ mBpfCoordinatorShim.tetherOffloadRuleForEach(DOWNSTREAM, (k, v) -> {
+ if (Arrays.equals(v.dst46, toIpv4MappedAddressBytes(clientAddr))) {
+ deleteDownstreamRuleKeys.add(k);
+ upstreamIndiceSet.add((int) k.iif);
+ }
+ });
+
+ // The rules should be paired on upstream and downstream map because they are added by
+ // conntrack events which have bidirectional information.
+ // TODO: Consider figuring out a way to fix. Probably delete all rules to fallback.
+ if (deleteUpstreamRuleKeys.size() != deleteDownstreamRuleKeys.size()) {
+ Log.wtf(TAG, "The deleting rule numbers are different on upstream4 and downstream4 ("
+ + "upstream: " + deleteUpstreamRuleKeys.size() + ", "
+ + "downstream: " + deleteDownstreamRuleKeys.size() + ").");
+ return;
+ }
+
+ // Delete the rules which are related with the given client.
+ for (final Tether4Key k : deleteUpstreamRuleKeys) {
+ mBpfCoordinatorShim.tetherOffloadRuleRemove(UPSTREAM, k);
+ }
+ for (final Tether4Key k : deleteDownstreamRuleKeys) {
+ mBpfCoordinatorShim.tetherOffloadRuleRemove(DOWNSTREAM, k);
+ }
+
+ // Cleanup each upstream interface by a set which avoids duplicated work on the same
+ // upstream interface. Cleaning up the same interface twice (or more) here may raise
+ // an exception because all related information were removed in the first deletion.
+ for (final int upstreamIndex : upstreamIndiceSet) {
+ maybeClearLimit(upstreamIndex);
+ }
+ }
+
+ /**
+ * Clear all forwarding IPv4 rules for a given downstream. Needed because the client may still
+ * connect on the downstream but the existing rules are not required anymore. Ex: upstream
+ * changed.
+ */
+ private void tetherOffloadRule4Clear(@NonNull final IpServer ipServer) {
+ if (!isUsingBpf()) return;
+
+ final HashMap<Inet4Address, ClientInfo> clients = mTetherClients.get(ipServer);
+ if (clients == null) return;
+
+ // The value should be unique as its key because currently the key was using from its
+ // client address of ClientInfo. See #tetherOffloadClientAdd.
+ for (final ClientInfo client : clients.values()) {
+ tetherOffloadRuleClear(client);
+ }
+ }
+
+ private boolean isValidUpstreamIpv4Address(@NonNull final InetAddress addr) {
+ if (!(addr instanceof Inet4Address)) return false;
+ Inet4Address v4 = (Inet4Address) addr;
+ if (v4.isAnyLocalAddress() || v4.isLinkLocalAddress()
+ || v4.isLoopbackAddress() || v4.isMulticastAddress()) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Call when UpstreamNetworkState may be changed.
+ * If upstream has ipv4 for tethering, update this new UpstreamNetworkState
+ * to BpfCoordinator for building upstream interface index mapping. Otherwise,
+ * clear the all existing rules if any.
+ *
+ * Note that this can be only called on handler thread.
+ */
+ public void updateUpstreamNetworkState(UpstreamNetworkState ns) {
+ if (!isUsingBpf()) return;
+
+ int upstreamIndex = 0;
// This will not work on a network that is using 464xlat because hasIpv4Address will not be
// true.
// TODO: need to consider 464xlat.
- if (lp == null || !lp.hasIpv4Address()) return;
+ if (ns != null && ns.linkProperties != null && ns.linkProperties.hasIpv4Address()) {
+ // TODO: support ether ip upstream interface.
+ final InterfaceParams params = mDeps.getInterfaceParams(
+ ns.linkProperties.getInterfaceName());
+ if (params != null && !params.hasMacAddress /* raw ip upstream only */) {
+ upstreamIndex = params.index;
+ }
+ }
+ if (mLastIPv4UpstreamIfindex == upstreamIndex) return;
- // Support raw ip upstream interface only.
- final InterfaceParams params = mDeps.getInterfaceParams(lp.getInterfaceName());
- if (params == null || params.hasMacAddress) return;
+ // Clear existing rules if upstream interface is changed. The existing rules should be
+ // cleared before upstream index mapping is cleared. It can avoid that ipServer or
+ // conntrack event may use the non-existing upstream interfeace index to build a removing
+ // key while removeing the rules. Can't notify each IpServer to clear the rules as
+ // IPv6TetheringCoordinator#updateUpstreamNetworkState because the IpServer may not
+ // handle the upstream changing notification before changing upstream index mapping.
+ if (mLastIPv4UpstreamIfindex != 0) {
+ // Clear all forwarding IPv4 rules for all downstreams.
+ for (final IpServer ipserver : mTetherClients.keySet()) {
+ tetherOffloadRule4Clear(ipserver);
+ }
+ }
- Collection<InetAddress> addresses = lp.getAddresses();
- for (InetAddress addr: addresses) {
- if (addr instanceof Inet4Address) {
- Inet4Address i4addr = (Inet4Address) addr;
- if (!i4addr.isAnyLocalAddress() && !i4addr.isLinkLocalAddress()
- && !i4addr.isLoopbackAddress() && !i4addr.isMulticastAddress()) {
- mIpv4UpstreamIndices.put(i4addr, params.index);
- }
+ // Don't update mLastIPv4UpstreamIfindex before clearing existing rules if any. Need that
+ // to tell if it is required to clean the out-of-date rules.
+ mLastIPv4UpstreamIfindex = upstreamIndex;
+
+ // If link properties are valid, build the upstream information mapping. Otherwise, clear
+ // the upstream interface index mapping, to ensure that any conntrack events that arrive
+ // after the upstream is lost do not incorrectly add rules pointing at the upstream.
+ if (upstreamIndex == 0) {
+ mIpv4UpstreamIndices.clear();
+ return;
+ }
+ Collection<InetAddress> addresses = ns.linkProperties.getAddresses();
+ for (final InetAddress addr: addresses) {
+ if (isValidUpstreamIpv4Address(addr)) {
+ mIpv4UpstreamIndices.put((Inet4Address) addr, upstreamIndex);
}
}
}
@@ -793,6 +953,24 @@
dumpDevmap(pw);
pw.decreaseIndent();
+ pw.println("Client Information:");
+ pw.increaseIndent();
+ if (mTetherClients.isEmpty()) {
+ pw.println("<empty>");
+ } else {
+ pw.println(mTetherClients.toString());
+ }
+ pw.decreaseIndent();
+
+ pw.println("IPv4 Upstream Indices:");
+ pw.increaseIndent();
+ if (mIpv4UpstreamIndices.isEmpty()) {
+ pw.println("<empty>");
+ } else {
+ pw.println(mIpv4UpstreamIndices.toString());
+ }
+ pw.decreaseIndent();
+
pw.println();
pw.println("Forwarding counters:");
pw.increaseIndent();
@@ -891,11 +1069,13 @@
throw new AssertionError("IP address array not valid IPv4 address!");
}
- final long ageMs = (now - value.lastUsed) / 1_000_000;
- return String.format("[%s] %d(%s) %s:%d -> %d(%s) %s:%d -> %s:%d [%s] %dms",
- key.dstMac, key.iif, getIfName(key.iif), src4, key.srcPort,
+ final String protoStr = (key.l4proto == OsConstants.IPPROTO_TCP) ? "tcp" : "udp";
+ final String ageStr = (value.lastUsed == 0) ? "-"
+ : String.format("%dms", (now - value.lastUsed) / 1_000_000);
+ return String.format("%s [%s] %d(%s) %s:%d -> %d(%s) %s:%d -> %s:%d [%s] %s",
+ protoStr, key.dstMac, key.iif, getIfName(key.iif), src4, key.srcPort,
value.oif, getIfName(value.oif),
- public4, publicPort, dst4, value.dstPort, value.ethDstMac, ageMs);
+ public4, publicPort, dst4, value.dstPort, value.ethDstMac, ageStr);
}
private void dumpIpv4ForwardingRuleMap(long now, boolean downstream,
@@ -916,12 +1096,14 @@
try (BpfMap<Tether4Key, Tether4Value> upstreamMap = mDeps.getBpfUpstream4Map();
BpfMap<Tether4Key, Tether4Value> downstreamMap = mDeps.getBpfDownstream4Map()) {
- pw.println("IPv4 Upstream: [inDstMac] iif(iface) src -> nat -> dst [outDstMac] age");
+ pw.println("IPv4 Upstream: proto [inDstMac] iif(iface) src -> nat -> "
+ + "dst [outDstMac] age");
pw.increaseIndent();
dumpIpv4ForwardingRuleMap(now, UPSTREAM, upstreamMap, pw);
pw.decreaseIndent();
- pw.println("IPv4 Downstream: [inDstMac] iif(iface) src -> nat -> dst [outDstMac] age");
+ pw.println("IPv4 Downstream: proto [inDstMac] iif(iface) src -> nat -> "
+ + "dst [outDstMac] age");
pw.increaseIndent();
dumpIpv4ForwardingRuleMap(now, DOWNSTREAM, downstreamMap, pw);
pw.decreaseIndent();
@@ -971,14 +1153,14 @@
return;
}
if (map.isEmpty()) {
- pw.println("No interface index");
+ pw.println("<empty>");
return;
}
pw.println("ifindex (iface) -> ifindex (iface)");
pw.increaseIndent();
map.forEach((k, v) -> {
// Only get upstream interface name. Just do the best to make the index readable.
- // TODO: get downstream interface name because the index is either upstrema or
+ // TODO: get downstream interface name because the index is either upstream or
// downstream interface in dev map.
pw.println(String.format("%d (%s) -> %d (%s)", k.ifIndex, getIfName(k.ifIndex),
v.ifIndex, getIfName(v.ifIndex)));
@@ -1248,12 +1430,99 @@
return null;
}
- // Support raw ip only.
- // TODO: add ether ip support.
+ @NonNull
+ private byte[] toIpv4MappedAddressBytes(Inet4Address ia4) {
+ final byte[] addr4 = ia4.getAddress();
+ final byte[] addr6 = new byte[16];
+ addr6[10] = (byte) 0xff;
+ addr6[11] = (byte) 0xff;
+ addr6[12] = addr4[0];
+ addr6[13] = addr4[1];
+ addr6[14] = addr4[2];
+ addr6[15] = addr4[3];
+ return addr6;
+ }
+
+ @Nullable
+ private Inet4Address ipv4MappedAddressBytesToIpv4Address(final byte[] addr46) {
+ if (addr46.length != 16) return null;
+ if (addr46[0] != 0 || addr46[1] != 0 || addr46[2] != 0 || addr46[3] != 0
+ || addr46[4] != 0 || addr46[5] != 0 || addr46[6] != 0 || addr46[7] != 0
+ || addr46[8] != 0 && addr46[9] != 0 || (addr46[10] & 0xff) != 0xff
+ || (addr46[11] & 0xff) != 0xff) {
+ return null;
+ }
+
+ final byte[] addr4 = new byte[4];
+ addr4[0] = addr46[12];
+ addr4[1] = addr46[13];
+ addr4[2] = addr46[14];
+ addr4[3] = addr46[15];
+
+ return parseIPv4Address(addr4);
+ }
+
// TODO: parse CTA_PROTOINFO of conntrack event in ConntrackMonitor. For TCP, only add rules
// while TCP status is established.
@VisibleForTesting
class BpfConntrackEventConsumer implements ConntrackEventConsumer {
+ // The upstream4 and downstream4 rules are built as the following tables. Only raw ip
+ // upstream interface is supported. Note that the field "lastUsed" is only updated by
+ // BPF program which records the last used time for a given rule.
+ // TODO: support ether ip upstream interface.
+ //
+ // NAT network topology:
+ //
+ // public network (rawip) private network
+ // | UE |
+ // +------------+ V +------------+------------+ V +------------+
+ // | Sever +---------+ Upstream | Downstream +---------+ Client |
+ // +------------+ +------------+------------+ +------------+
+ //
+ // upstream4 key and value:
+ //
+ // +------+------------------------------------------------+
+ // | | TetherUpstream4Key |
+ // +------+------+------+------+------+------+------+------+
+ // |field |iif |dstMac|l4prot|src4 |dst4 |srcPor|dstPor|
+ // | | | |o | | |t |t |
+ // +------+------+------+------+------+------+------+------+
+ // |value |downst|downst|tcp/ |client|server|client|server|
+ // | |ream |ream |udp | | | | |
+ // +------+------+------+------+------+------+------+------+
+ //
+ // +------+---------------------------------------------------------------------+
+ // | | TetherUpstream4Value |
+ // +------+------+------+------+------+------+------+------+------+------+------+
+ // |field |oif |ethDst|ethSrc|ethPro|pmtu |src46 |dst46 |srcPor|dstPor|lastUs|
+ // | | |mac |mac |to | | | |t |t |ed |
+ // +------+------+------+------+------+------+------+------+------+------+------+
+ // |value |upstre|-- |-- |ETH_P_|1500 |upstre|server|upstre|server|-- |
+ // | |am | | |IP | |am | |am | | |
+ // +------+------+------+------+------+------+------+------+------+------+------+
+ //
+ // downstream4 key and value:
+ //
+ // +------+------------------------------------------------+
+ // | | TetherDownstream4Key |
+ // +------+------+------+------+------+------+------+------+
+ // |field |iif |dstMac|l4prot|src4 |dst4 |srcPor|dstPor|
+ // | | | |o | | |t |t |
+ // +------+------+------+------+------+------+------+------+
+ // |value |upstre|-- |tcp/ |server|upstre|server|upstre|
+ // | |am | |udp | |am | |am |
+ // +------+------+------+------+------+------+------+------+
+ //
+ // +------+---------------------------------------------------------------------+
+ // | | TetherDownstream4Value |
+ // +------+------+------+------+------+------+------+------+------+------+------+
+ // |field |oif |ethDst|ethSrc|ethPro|pmtu |src46 |dst46 |srcPor|dstPor|lastUs|
+ // | | |mac |mac |to | | | |t |t |ed |
+ // +------+------+------+------+------+------+------+------+------+------+------+
+ // |value |downst|client|downst|ETH_P_|1500 |server|client|server|client|-- |
+ // | |ream | |ream |IP | | | | | | |
+ // +------+------+------+------+------+------+------+------+------+------+------+
+ //
@NonNull
private Tether4Key makeTetherUpstream4Key(
@NonNull ConntrackEvent e, @NonNull ClientInfo c) {
@@ -1292,19 +1561,6 @@
0 /* lastUsed, filled by bpf prog only */);
}
- @NonNull
- private byte[] toIpv4MappedAddressBytes(Inet4Address ia4) {
- final byte[] addr4 = ia4.getAddress();
- final byte[] addr6 = new byte[16];
- addr6[10] = (byte) 0xff;
- addr6[11] = (byte) 0xff;
- addr6[12] = addr4[0];
- addr6[13] = addr4[1];
- addr6[14] = addr4[2];
- addr6[15] = addr4[3];
- return addr6;
- }
-
public void accept(ConntrackEvent e) {
final ClientInfo tetherClient = getClientInfo(e.tupleOrig.srcIp);
if (tetherClient == null) return;
@@ -1318,8 +1574,23 @@
if (e.msgType == (NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8
| NetlinkConstants.IPCTNL_MSG_CT_DELETE)) {
- mBpfCoordinatorShim.tetherOffloadRuleRemove(UPSTREAM, upstream4Key);
- mBpfCoordinatorShim.tetherOffloadRuleRemove(DOWNSTREAM, downstream4Key);
+ final boolean deletedUpstream = mBpfCoordinatorShim.tetherOffloadRuleRemove(
+ UPSTREAM, upstream4Key);
+ final boolean deletedDownstream = mBpfCoordinatorShim.tetherOffloadRuleRemove(
+ DOWNSTREAM, downstream4Key);
+
+ if (!deletedUpstream && !deletedDownstream) {
+ // The rules may have been already removed by losing client or losing upstream.
+ return;
+ }
+
+ if (deletedUpstream != deletedDownstream) {
+ Log.wtf(TAG, "The bidirectional rules should be removed concurrently ("
+ + "upstream: " + deletedUpstream
+ + ", downstream: " + deletedDownstream + ")");
+ return;
+ }
+
maybeClearLimit(upstreamIndex);
return;
}
@@ -1585,14 +1856,89 @@
return Math.max(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS, configInterval);
}
+ @Nullable
+ private Inet4Address parseIPv4Address(byte[] addrBytes) {
+ try {
+ final InetAddress ia = Inet4Address.getByAddress(addrBytes);
+ if (ia instanceof Inet4Address) return (Inet4Address) ia;
+ } catch (UnknownHostException | IllegalArgumentException e) {
+ mLog.e("Failed to parse IPv4 address: " + e);
+ }
+ return null;
+ }
+
+ // Update CTA_TUPLE_ORIG timeout for a given conntrack entry. Note that there will also be
+ // coming a conntrack event to notify updated timeout.
+ private void updateConntrackTimeout(byte proto, Inet4Address src4, short srcPort,
+ Inet4Address dst4, short dstPort) {
+ if (src4 == null || dst4 == null) return;
+
+ // TODO: consider acquiring the timeout setting from nf_conntrack_* variables.
+ // - proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established
+ // - proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream
+ // See kernel document nf_conntrack-sysctl.txt.
+ final int timeoutSec = (proto == OsConstants.IPPROTO_TCP)
+ ? NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED
+ : NF_CONNTRACK_UDP_TIMEOUT_STREAM;
+ final byte[] msg = ConntrackMessage.newIPv4TimeoutUpdateRequest(
+ proto, src4, (int) srcPort, dst4, (int) dstPort, timeoutSec);
+ try {
+ NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_NETFILTER, msg);
+ } catch (ErrnoException e) {
+ mLog.e("Error updating conntrack entry ("
+ + "proto: " + proto + ", "
+ + "src4: " + src4 + ", "
+ + "srcPort: " + Short.toUnsignedInt(srcPort) + ", "
+ + "dst4: " + dst4 + ", "
+ + "dstPort: " + Short.toUnsignedInt(dstPort) + "), "
+ + "msg: " + NetlinkConstants.hexify(msg) + ", "
+ + "e: " + e);
+ }
+ }
+
+ private void maybeRefreshConntrackTimeout() {
+ final long now = mDeps.elapsedRealtimeNanos();
+
+ // Reverse the source and destination {address, port} from downstream value because
+ // #updateConntrackTimeout refresh the timeout of netlink attribute CTA_TUPLE_ORIG
+ // which is opposite direction for downstream map value.
+ mBpfCoordinatorShim.tetherOffloadRuleForEach(DOWNSTREAM, (k, v) -> {
+ if ((now - v.lastUsed) / 1_000_000 < POLLING_CONNTRACK_TIMEOUT_MS) {
+ updateConntrackTimeout((byte) k.l4proto,
+ ipv4MappedAddressBytesToIpv4Address(v.dst46), (short) v.dstPort,
+ ipv4MappedAddressBytesToIpv4Address(v.src46), (short) v.srcPort);
+ }
+ });
+
+ // TODO: Consider ignoring TCP traffic on upstream and monitor on downstream only
+ // because TCP is a bidirectional traffic. Probably don't need to extend timeout by
+ // both directions for TCP.
+ mBpfCoordinatorShim.tetherOffloadRuleForEach(UPSTREAM, (k, v) -> {
+ if ((now - v.lastUsed) / 1_000_000 < POLLING_CONNTRACK_TIMEOUT_MS) {
+ updateConntrackTimeout((byte) k.l4proto, parseIPv4Address(k.src4),
+ (short) k.srcPort, parseIPv4Address(k.dst4), (short) k.dstPort);
+ }
+ });
+ }
+
private void maybeSchedulePollingStats() {
if (!mPollingStarted) return;
- if (mHandler.hasCallbacks(mScheduledPollingTask)) {
- mHandler.removeCallbacks(mScheduledPollingTask);
+ if (mHandler.hasCallbacks(mScheduledPollingStats)) {
+ mHandler.removeCallbacks(mScheduledPollingStats);
}
- mHandler.postDelayed(mScheduledPollingTask, getPollingInterval());
+ mHandler.postDelayed(mScheduledPollingStats, getPollingInterval());
+ }
+
+ private void maybeSchedulePollingConntrackTimeout() {
+ if (!mPollingStarted) return;
+
+ if (mHandler.hasCallbacks(mScheduledPollingConntrackTimeout)) {
+ mHandler.removeCallbacks(mScheduledPollingConntrackTimeout);
+ }
+
+ mHandler.postDelayed(mScheduledPollingConntrackTimeout, POLLING_CONNTRACK_TIMEOUT_MS);
}
// Return forwarding rule map. This is used for testing only.
diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadController.java b/Tethering/src/com/android/networkstack/tethering/OffloadController.java
index 44e3916..beb1821 100644
--- a/Tethering/src/com/android/networkstack/tethering/OffloadController.java
+++ b/Tethering/src/com/android/networkstack/tethering/OffloadController.java
@@ -26,6 +26,8 @@
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_1;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_NONE;
import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
@@ -114,11 +116,42 @@
private ConcurrentHashMap<String, ForwardedStats> mForwardedStats =
new ConcurrentHashMap<>(16, 0.75F, 1);
+ private static class InterfaceQuota {
+ public final long warningBytes;
+ public final long limitBytes;
+
+ public static InterfaceQuota MAX_VALUE = new InterfaceQuota(Long.MAX_VALUE, Long.MAX_VALUE);
+
+ InterfaceQuota(long warningBytes, long limitBytes) {
+ this.warningBytes = warningBytes;
+ this.limitBytes = limitBytes;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof InterfaceQuota)) return false;
+ InterfaceQuota that = (InterfaceQuota) o;
+ return warningBytes == that.warningBytes
+ && limitBytes == that.limitBytes;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) (warningBytes * 3 + limitBytes * 5);
+ }
+
+ @Override
+ public String toString() {
+ return "InterfaceQuota{" + "warning=" + warningBytes + ", limit=" + limitBytes + '}';
+ }
+ }
+
// Maps upstream interface names to interface quotas.
// Always contains the latest value received from the framework for each interface, regardless
// of whether offload is currently running (or is even supported) on that interface. Only
// includes upstream interfaces that have a quota set.
- private HashMap<String, Long> mInterfaceQuotas = new HashMap<>();
+ private HashMap<String, InterfaceQuota> mInterfaceQuotas = new HashMap<>();
// Tracking remaining alert quota. Unlike limit quota is subject to interface, the alert
// quota is interface independent and global for tether offload. Note that this is only
@@ -250,6 +283,18 @@
}
@Override
+ public void onWarningReached() {
+ if (!started()) return;
+ mLog.log("onWarningReached");
+
+ updateStatsForCurrentUpstream();
+ if (mStatsProvider != null) {
+ mStatsProvider.pushTetherStats();
+ mStatsProvider.notifyWarningReached();
+ }
+ }
+
+ @Override
public void onNatTimeoutUpdate(int proto,
String srcAddr, int srcPort,
String dstAddr, int dstPort) {
@@ -263,7 +308,8 @@
mLog.i("tethering offload control not supported");
stop();
} else {
- mLog.log("tethering offload started");
+ mLog.log("tethering offload started, version: "
+ + OffloadHardwareInterface.halVerToString(mControlHalVersion));
mNatUpdateCallbacksReceived = 0;
mNatUpdateNetlinkErrors = 0;
maybeSchedulePollingStats();
@@ -322,24 +368,35 @@
@Override
public void onSetLimit(String iface, long quotaBytes) {
+ onSetWarningAndLimit(iface, QUOTA_UNLIMITED, quotaBytes);
+ }
+
+ @Override
+ public void onSetWarningAndLimit(@NonNull String iface,
+ long warningBytes, long limitBytes) {
// Listen for all iface is necessary since upstream might be changed after limit
// is set.
mHandler.post(() -> {
- final Long curIfaceQuota = mInterfaceQuotas.get(iface);
+ final InterfaceQuota curIfaceQuota = mInterfaceQuotas.get(iface);
+ final InterfaceQuota newIfaceQuota = new InterfaceQuota(
+ warningBytes == QUOTA_UNLIMITED ? Long.MAX_VALUE : warningBytes,
+ limitBytes == QUOTA_UNLIMITED ? Long.MAX_VALUE : limitBytes);
// If the quota is set to unlimited, the value set to HAL is Long.MAX_VALUE,
// which is ~8.4 x 10^6 TiB, no one can actually reach it. Thus, it is not
// useful to set it multiple times.
// Otherwise, the quota needs to be updated to tell HAL to re-count from now even
// if the quota is the same as the existing one.
- if (null == curIfaceQuota && QUOTA_UNLIMITED == quotaBytes) return;
+ if (null == curIfaceQuota && InterfaceQuota.MAX_VALUE.equals(newIfaceQuota)) {
+ return;
+ }
- if (quotaBytes == QUOTA_UNLIMITED) {
+ if (InterfaceQuota.MAX_VALUE.equals(newIfaceQuota)) {
mInterfaceQuotas.remove(iface);
} else {
- mInterfaceQuotas.put(iface, quotaBytes);
+ mInterfaceQuotas.put(iface, newIfaceQuota);
}
- maybeUpdateDataLimit(iface);
+ maybeUpdateDataWarningAndLimit(iface);
});
}
@@ -374,7 +431,11 @@
@Override
public void onSetAlert(long quotaBytes) {
- // TODO: Ask offload HAL to notify alert without stopping traffic.
+ // Ignore set alert calls from HAL V1.1 since the hardware supports set warning now.
+ // Thus, the software polling mechanism is not needed.
+ if (!useStatsPolling()) {
+ return;
+ }
// Post it to handler thread since it access remaining quota bytes.
mHandler.post(() -> {
updateAlertQuota(quotaBytes);
@@ -459,24 +520,32 @@
private boolean isPollingStatsNeeded() {
return started() && mRemainingAlertQuota > 0
+ && useStatsPolling()
&& !TextUtils.isEmpty(currentUpstreamInterface())
&& mDeps.getTetherConfig() != null
&& mDeps.getTetherConfig().getOffloadPollInterval()
>= DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
}
- private boolean maybeUpdateDataLimit(String iface) {
- // setDataLimit may only be called while offload is occurring on this upstream.
+ private boolean useStatsPolling() {
+ return mControlHalVersion == OFFLOAD_HAL_VERSION_1_0;
+ }
+
+ private boolean maybeUpdateDataWarningAndLimit(String iface) {
+ // setDataLimit or setDataWarningAndLimit may only be called while offload is occurring
+ // on this upstream.
if (!started() || !TextUtils.equals(iface, currentUpstreamInterface())) {
return true;
}
- Long limit = mInterfaceQuotas.get(iface);
- if (limit == null) {
- limit = Long.MAX_VALUE;
+ final InterfaceQuota quota = mInterfaceQuotas.getOrDefault(iface, InterfaceQuota.MAX_VALUE);
+ final boolean ret;
+ if (mControlHalVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ ret = mHwInterface.setDataWarningAndLimit(iface, quota.warningBytes, quota.limitBytes);
+ } else {
+ ret = mHwInterface.setDataLimit(iface, quota.limitBytes);
}
-
- return mHwInterface.setDataLimit(iface, limit);
+ return ret;
}
private void updateStatsForCurrentUpstream() {
@@ -630,7 +699,7 @@
maybeUpdateStats(prevUpstream);
// Data limits can only be set once offload is running on the upstream.
- success = maybeUpdateDataLimit(iface);
+ success = maybeUpdateDataWarningAndLimit(iface);
if (!success) {
// If we failed to set a data limit, don't use this upstream, because we don't want to
// blow through the data limit that we were told to apply.
diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
index 7685847..e3ac660 100644
--- a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
+++ b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
@@ -24,10 +24,10 @@
import android.annotation.NonNull;
import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
-import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
+import android.hardware.tetheroffload.control.V1_1.ITetheringOffloadCallback;
import android.net.netlink.NetlinkSocket;
import android.net.netlink.StructNfGenMsg;
import android.net.netlink.StructNlMsgHdr;
@@ -39,6 +39,7 @@
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
+import android.util.Log;
import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
@@ -140,6 +141,8 @@
public void onSupportAvailable() {}
/** Offload stopped because of usage limit reached. */
public void onStoppedLimitReached() {}
+ /** Indicate that data warning quota is reached. */
+ public void onWarningReached() {}
/** Indicate to update NAT timeout. */
public void onNatTimeoutUpdate(int proto,
@@ -381,7 +384,8 @@
(controlCb == null) ? "null"
: "0x" + Integer.toHexString(System.identityHashCode(controlCb)));
- mTetheringOffloadCallback = new TetheringOffloadCallback(mHandler, mControlCallback, mLog);
+ mTetheringOffloadCallback = new TetheringOffloadCallback(
+ mHandler, mControlCallback, mLog, mOffloadControlVersion);
final CbResults results = new CbResults();
try {
mOffloadControl.initOffload(
@@ -480,6 +484,33 @@
return results.mSuccess;
}
+ /** Set data warning and limit value to offload management process. */
+ public boolean setDataWarningAndLimit(String iface, long warning, long limit) {
+ if (mOffloadControlVersion < OFFLOAD_HAL_VERSION_1_1) {
+ throw new IllegalArgumentException(
+ "setDataWarningAndLimit is not supported below HAL V1.1");
+ }
+ final String logmsg =
+ String.format("setDataWarningAndLimit(%s, %d, %d)", iface, warning, limit);
+
+ final CbResults results = new CbResults();
+ try {
+ ((android.hardware.tetheroffload.control.V1_1.IOffloadControl) mOffloadControl)
+ .setDataWarningAndLimit(
+ iface, warning, limit,
+ (boolean success, String errMsg) -> {
+ results.mSuccess = success;
+ results.mErrMsg = errMsg;
+ });
+ } catch (RemoteException e) {
+ record(logmsg, e);
+ return false;
+ }
+
+ record(logmsg, results);
+ return results.mSuccess;
+ }
+
/** Set upstream parameters to offload management process. */
public boolean setUpstreamParameters(
String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) {
@@ -565,35 +596,64 @@
public final Handler handler;
public final ControlCallback controlCb;
public final SharedLog log;
+ private final int mOffloadControlVersion;
- TetheringOffloadCallback(Handler h, ControlCallback cb, SharedLog sharedLog) {
+ TetheringOffloadCallback(
+ Handler h, ControlCallback cb, SharedLog sharedLog, int offloadControlVersion) {
handler = h;
controlCb = cb;
log = sharedLog;
+ this.mOffloadControlVersion = offloadControlVersion;
+ }
+
+ private void handleOnEvent(int event) {
+ switch (event) {
+ case OffloadCallbackEvent.OFFLOAD_STARTED:
+ controlCb.onStarted();
+ break;
+ case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR:
+ controlCb.onStoppedError();
+ break;
+ case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED:
+ controlCb.onStoppedUnsupported();
+ break;
+ case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE:
+ controlCb.onSupportAvailable();
+ break;
+ case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED:
+ controlCb.onStoppedLimitReached();
+ break;
+ case android.hardware.tetheroffload.control
+ .V1_1.OffloadCallbackEvent.OFFLOAD_WARNING_REACHED:
+ controlCb.onWarningReached();
+ break;
+ default:
+ log.e("Unsupported OffloadCallbackEvent: " + event);
+ }
}
@Override
public void onEvent(int event) {
+ // The implementation should never call onEvent()) if the event is already reported
+ // through newer callback.
+ if (mOffloadControlVersion > OFFLOAD_HAL_VERSION_1_0) {
+ Log.wtf(TAG, "onEvent(" + event + ") fired on HAL "
+ + halVerToString(mOffloadControlVersion));
+ }
handler.post(() -> {
- switch (event) {
- case OffloadCallbackEvent.OFFLOAD_STARTED:
- controlCb.onStarted();
- break;
- case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR:
- controlCb.onStoppedError();
- break;
- case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED:
- controlCb.onStoppedUnsupported();
- break;
- case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE:
- controlCb.onSupportAvailable();
- break;
- case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED:
- controlCb.onStoppedLimitReached();
- break;
- default:
- log.e("Unsupported OffloadCallbackEvent: " + event);
- }
+ handleOnEvent(event);
+ });
+ }
+
+ @Override
+ public void onEvent_1_1(int event) {
+ if (mOffloadControlVersion < OFFLOAD_HAL_VERSION_1_1) {
+ Log.wtf(TAG, "onEvent_1_1(" + event + ") fired on HAL "
+ + halVerToString(mOffloadControlVersion));
+ return;
+ }
+ handler.post(() -> {
+ handleOnEvent(event);
});
}
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index b52ec86..079bf9c 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -1039,7 +1039,7 @@
final boolean rndisEnabled = intent.getBooleanExtra(USB_FUNCTION_RNDIS, false);
final boolean ncmEnabled = intent.getBooleanExtra(USB_FUNCTION_NCM, false);
- mLog.log(String.format("USB bcast connected:%s configured:%s rndis:%s ncm:%s",
+ mLog.i(String.format("USB bcast connected:%s configured:%s rndis:%s ncm:%s",
usbConnected, usbConfigured, rndisEnabled, ncmEnabled));
// There are three types of ACTION_USB_STATE:
@@ -1416,7 +1416,7 @@
// If TETHERING_USB is forced to use ncm function, TETHERING_NCM would no longer be
// available.
- if (mConfig.isUsingNcm()) return TETHER_ERROR_SERVICE_UNAVAIL;
+ if (mConfig.isUsingNcm() && enable) return TETHER_ERROR_SERVICE_UNAVAIL;
UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_NCM : UsbManager.FUNCTION_NONE);
@@ -1720,13 +1720,7 @@
protected void handleNewUpstreamNetworkState(UpstreamNetworkState ns) {
mIPv6TetheringCoordinator.updateUpstreamNetworkState(ns);
mOffload.updateUpstreamNetworkState(ns);
-
- // TODO: Delete all related offload rules which are using this upstream.
- if (ns != null) {
- // Add upstream index to the map. The upstream interface index is required while
- // the conntrack event builds the offload rules.
- mBpfCoordinator.addUpstreamIfindexToMap(ns.linkProperties);
- }
+ mBpfCoordinator.updateUpstreamNetworkState(ns);
}
private void handleInterfaceServingStateActive(int mode, IpServer who) {
@@ -2336,6 +2330,9 @@
pw.println("Tethering:");
pw.increaseIndent();
+ pw.println("Callbacks registered: "
+ + mTetheringEventCallbacks.getRegisteredCallbackCount());
+
pw.println("Configuration:");
pw.increaseIndent();
final TetheringConfiguration cfg = mConfig;
@@ -2558,7 +2555,7 @@
return;
}
- mLog.log("adding IpServer for: " + iface);
+ mLog.i("adding IpServer for: " + iface);
final TetherState tetherState = new TetherState(
new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mBpfCoordinator,
makeControlCallback(), mConfig.enableLegacyDhcpServer,
@@ -2573,7 +2570,7 @@
if (tetherState == null) return;
tetherState.ipServer.stop();
- mLog.log("removing IpServer for: " + iface);
+ mLog.i("removing IpServer for: " + iface);
mTetherStates.remove(iface);
}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index 31fcea4..d2f44d3 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -176,7 +176,9 @@
// us an interface name. Careful consideration needs to be given to
// implications for Settings and for provisioning checks.
tetherableWifiRegexs = getResourceStringArray(res, R.array.config_tether_wifi_regexs);
- tetherableWigigRegexs = getResourceStringArray(res, R.array.config_tether_wigig_regexs);
+ // TODO: Remove entire wigig code once tethering module no longer support R devices.
+ tetherableWigigRegexs = SdkLevel.isAtLeastS()
+ ? new String[0] : getResourceStringArray(res, R.array.config_tether_wigig_regexs);
tetherableWifiP2pRegexs = getResourceStringArray(
res, R.array.config_tether_wifi_p2p_regexs);
tetherableBluetoothRegexs = getResourceStringArray(
diff --git a/Tethering/tests/Android.bp b/Tethering/tests/Android.bp
index 8f31c57..72ca666 100644
--- a/Tethering/tests/Android.bp
+++ b/Tethering/tests/Android.bp
@@ -22,7 +22,7 @@
name: "TetheringTestsJarJarRules",
srcs: ["jarjar-rules.txt"],
visibility: [
- "//frameworks/base/packages/Tethering/tests:__subpackages__",
+ "//packages/modules/Connectivity/tests:__subpackages__",
"//packages/modules/Connectivity/Tethering/tests:__subpackages__",
]
}
diff --git a/Tethering/tests/integration/Android.bp b/Tethering/tests/integration/Android.bp
index e807613..b93a969 100644
--- a/Tethering/tests/integration/Android.bp
+++ b/Tethering/tests/integration/Android.bp
@@ -78,9 +78,27 @@
compile_multilib: "both",
}
+android_library {
+ name: "TetheringCoverageTestsLib",
+ min_sdk_version: "30",
+ static_libs: [
+ "NetdStaticLibTestsLib",
+ "NetworkStaticLibTestsLib",
+ "NetworkStackTestsLib",
+ "TetheringTestsLatestSdkLib",
+ "TetheringIntegrationTestsLatestSdkLib",
+ ],
+ jarjar_rules: ":TetheringTestsJarJarRules",
+ manifest: "AndroidManifest_coverage.xml",
+ visibility: [
+ "//packages/modules/Connectivity/tests:__subpackages__"
+ ],
+}
+
// Special version of the tethering tests that includes all tests necessary for code coverage
// purposes. This is currently the union of TetheringTests, TetheringIntegrationTests and
// NetworkStackTests.
+// TODO: remove in favor of ConnectivityCoverageTests, which includes below tests and more
android_test {
name: "TetheringCoverageTests",
platform_apis: true,
@@ -91,11 +109,7 @@
defaults: ["libnetworkstackutilsjni_deps"],
static_libs: [
"modules-utils-native-coverage-listener",
- "NetdStaticLibTestsLib",
- "NetworkStaticLibTestsLib",
- "NetworkStackTestsLib",
- "TetheringTestsLatestSdkLib",
- "TetheringIntegrationTestsLatestSdkLib",
+ "TetheringCoverageTestsLib",
],
jni_libs: [
// For mockito extended
diff --git a/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java b/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
index 07aab63..ef254ff 100644
--- a/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
+++ b/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java
@@ -22,7 +22,6 @@
import static android.Manifest.permission.TETHER_PRIVILEGED;
import static android.Manifest.permission.WRITE_SETTINGS;
import static android.net.TetheringManager.TETHERING_WIFI;
-import static android.net.cts.util.CtsTetheringUtils.isWifiTetheringSupported;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
@@ -102,8 +101,7 @@
TestNetworkTracker tnt = null;
try {
- tetherEventCallback.assumeTetheringSupported();
- assumeTrue(isWifiTetheringSupported(mContext, tetherEventCallback));
+ tetherEventCallback.assumeWifiTetheringSupported(mContext);
tetherEventCallback.expectNoTetheringActive();
final TetheringInterface tetheredIface =
diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp
index 192a540..f6e29cd 100644
--- a/Tethering/tests/unit/Android.bp
+++ b/Tethering/tests/unit/Android.bp
@@ -75,7 +75,6 @@
"libstaticjvmtiagent",
"libtetherutilsjni",
],
- jarjar_rules: ":TetheringTestsJarJarRules",
}
// Library containing the unit tests. This is used by the coverage test target to pull in the
@@ -100,4 +99,5 @@
],
defaults: ["TetheringTestsDefaults"],
compile_multilib: "both",
+ jarjar_rules: ":TetheringTestsJarJarRules",
}
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index ce69cb3..378a21c 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -584,6 +584,7 @@
inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
inOrder.verify(mNetd).interfaceSetCfg(argThat(cfg -> IFACE_NAME.equals(cfg.ifName)));
inOrder.verify(mAddressCoordinator).releaseDownstream(any());
+ inOrder.verify(mBpfCoordinator).tetherOffloadClientClear(mIpServer);
inOrder.verify(mBpfCoordinator).stopMonitoring(mIpServer);
inOrder.verify(mCallback).updateInterfaceState(
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
index cc912f4..914e0d4 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -36,9 +36,13 @@
import static android.system.OsConstants.ETH_P_IPV6;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.NETLINK_NETFILTER;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker;
+import static com.android.networkstack.tethering.BpfCoordinator.NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED;
+import static com.android.networkstack.tethering.BpfCoordinator.NF_CONNTRACK_UDP_TIMEOUT_STREAM;
+import static com.android.networkstack.tethering.BpfCoordinator.POLLING_CONNTRACK_TIMEOUT_MS;
import static com.android.networkstack.tethering.BpfCoordinator.StatsType;
import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_IFACE;
import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_UID;
@@ -70,13 +74,17 @@
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.MacAddress;
+import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.NetworkStats;
import android.net.TetherOffloadRuleParcel;
import android.net.TetherStatsParcel;
import android.net.ip.ConntrackMonitor;
import android.net.ip.ConntrackMonitor.ConntrackEventConsumer;
import android.net.ip.IpServer;
+import android.net.netlink.ConntrackMessage;
import android.net.netlink.NetlinkConstants;
+import android.net.netlink.NetlinkSocket;
import android.net.util.InterfaceParams;
import android.net.util.SharedLog;
import android.os.Build;
@@ -127,6 +135,8 @@
@Rule
public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
+ private static final int TEST_NET_ID = 24;
+
private static final int UPSTREAM_IFINDEX = 1001;
private static final int DOWNSTREAM_IFINDEX = 1002;
@@ -217,6 +227,7 @@
// it has to access the non-static function of BPF coordinator.
private BpfConntrackEventConsumer mConsumer;
+ private long mElapsedRealtimeNanos = 0;
private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
ArgumentCaptor.forClass(ArrayList.class);
private final TestLooper mTestLooper = new TestLooper();
@@ -256,6 +267,10 @@
return mConntrackMonitor;
}
+ public long elapsedRealtimeNanos() {
+ return mElapsedRealtimeNanos;
+ }
+
@Nullable
public BpfMap<Tether4Key, Tether4Value> getBpfDownstream4Map() {
return mBpfDownstream4Map;
@@ -1340,6 +1355,11 @@
}
@NonNull
+ private Tether4Key makeDownstream4Key() {
+ return makeDownstream4Key(IPPROTO_TCP);
+ }
+
+ @NonNull
private ConntrackEvent makeTestConntrackEvent(short msgType, int proto) {
if (msgType != IPCTNL_MSG_CT_NEW && msgType != IPCTNL_MSG_CT_DELETE) {
fail("Not support message type " + msgType);
@@ -1365,7 +1385,10 @@
final LinkProperties lp = new LinkProperties();
lp.setInterfaceName(UPSTREAM_IFACE);
lp.addLinkAddress(new LinkAddress(PUBLIC_ADDR, 32 /* prefix length */));
- coordinator.addUpstreamIfindexToMap(lp);
+ final NetworkCapabilities capabilities = new NetworkCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ coordinator.updateUpstreamNetworkState(new UpstreamNetworkState(lp, capabilities,
+ new Network(TEST_NET_ID)));
}
private void setDownstreamAndClientInformationTo(final BpfCoordinator coordinator) {
@@ -1379,8 +1402,11 @@
// was started.
coordinator.startPolling();
- // Needed because tetherOffloadRuleRemove of api31.BpfCoordinatorShimImpl only decreases
- // the count while the entry is deleted. In the other words, deleteEntry returns true.
+ // Needed because two reasons: (1) BpfConntrackEventConsumer#accept only performs cleanup
+ // when both upstream and downstream rules are removed. (2) tetherOffloadRuleRemove of
+ // api31.BpfCoordinatorShimImpl only decreases the count while the entry is deleted.
+ // In the other words, deleteEntry returns true.
+ doReturn(true).when(mBpfUpstream4Map).deleteEntry(any());
doReturn(true).when(mBpfDownstream4Map).deleteEntry(any());
// Needed because BpfCoordinator#addUpstreamIfindexToMap queries interface parameter for
@@ -1494,4 +1520,104 @@
mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, IPPROTO_UDP));
verify(mBpfDevMap, never()).updateEntry(any(), any());
}
+
+ private void setElapsedRealtimeNanos(long nanoSec) {
+ mElapsedRealtimeNanos = nanoSec;
+ }
+
+ private void checkRefreshConntrackTimeout(final TestBpfMap<Tether4Key, Tether4Value> bpfMap,
+ final Tether4Key tcpKey, final Tether4Value tcpValue, final Tether4Key udpKey,
+ final Tether4Value udpValue) throws Exception {
+ // Both system elapsed time since boot and the rule last used time are used to measure
+ // the rule expiration. In this test, all test rules are fixed the last used time to 0.
+ // Set the different testing elapsed time to make the rule to be valid or expired.
+ //
+ // Timeline:
+ // 0 60 (seconds)
+ // +---+---+---+---+--...--+---+---+---+---+---+- ..
+ // | POLLING_CONNTRACK_TIMEOUT_MS |
+ // +---+---+---+---+--...--+---+---+---+---+---+- ..
+ // |<- valid diff ->|
+ // |<- expired diff ->|
+ // ^ ^ ^
+ // last used time elapsed time (valid) elapsed time (expired)
+ final long validTime = (POLLING_CONNTRACK_TIMEOUT_MS - 1) * 1_000_000L;
+ final long expiredTime = (POLLING_CONNTRACK_TIMEOUT_MS + 1) * 1_000_000L;
+
+ // Static mocking for NetlinkSocket.
+ MockitoSession mockSession = ExtendedMockito.mockitoSession()
+ .mockStatic(NetlinkSocket.class)
+ .startMocking();
+ try {
+ final BpfCoordinator coordinator = makeBpfCoordinator();
+ coordinator.startPolling();
+ bpfMap.insertEntry(tcpKey, tcpValue);
+ bpfMap.insertEntry(udpKey, udpValue);
+
+ // [1] Don't refresh contrack timeout.
+ setElapsedRealtimeNanos(expiredTime);
+ mTestLooper.moveTimeForward(POLLING_CONNTRACK_TIMEOUT_MS);
+ waitForIdle();
+ ExtendedMockito.verifyNoMoreInteractions(staticMockMarker(NetlinkSocket.class));
+ ExtendedMockito.clearInvocations(staticMockMarker(NetlinkSocket.class));
+
+ // [2] Refresh contrack timeout.
+ setElapsedRealtimeNanos(validTime);
+ mTestLooper.moveTimeForward(POLLING_CONNTRACK_TIMEOUT_MS);
+ waitForIdle();
+ final byte[] expectedNetlinkTcp = ConntrackMessage.newIPv4TimeoutUpdateRequest(
+ IPPROTO_TCP, PRIVATE_ADDR, (int) PRIVATE_PORT, REMOTE_ADDR,
+ (int) REMOTE_PORT, NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED);
+ final byte[] expectedNetlinkUdp = ConntrackMessage.newIPv4TimeoutUpdateRequest(
+ IPPROTO_UDP, PRIVATE_ADDR, (int) PRIVATE_PORT, REMOTE_ADDR,
+ (int) REMOTE_PORT, NF_CONNTRACK_UDP_TIMEOUT_STREAM);
+ ExtendedMockito.verify(() -> NetlinkSocket.sendOneShotKernelMessage(
+ eq(NETLINK_NETFILTER), eq(expectedNetlinkTcp)));
+ ExtendedMockito.verify(() -> NetlinkSocket.sendOneShotKernelMessage(
+ eq(NETLINK_NETFILTER), eq(expectedNetlinkUdp)));
+ ExtendedMockito.verifyNoMoreInteractions(staticMockMarker(NetlinkSocket.class));
+ ExtendedMockito.clearInvocations(staticMockMarker(NetlinkSocket.class));
+
+ // [3] Don't refresh contrack timeout if polling stopped.
+ coordinator.stopPolling();
+ mTestLooper.moveTimeForward(POLLING_CONNTRACK_TIMEOUT_MS);
+ waitForIdle();
+ ExtendedMockito.verifyNoMoreInteractions(staticMockMarker(NetlinkSocket.class));
+ ExtendedMockito.clearInvocations(staticMockMarker(NetlinkSocket.class));
+ } finally {
+ mockSession.finishMocking();
+ }
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testRefreshConntrackTimeout_Upstream4Map() throws Exception {
+ // TODO: Replace the dependencies BPF map with a non-mocked TestBpfMap object.
+ final TestBpfMap<Tether4Key, Tether4Value> bpfUpstream4Map =
+ new TestBpfMap<>(Tether4Key.class, Tether4Value.class);
+ doReturn(bpfUpstream4Map).when(mDeps).getBpfUpstream4Map();
+
+ final Tether4Key tcpKey = makeUpstream4Key(IPPROTO_TCP);
+ final Tether4Key udpKey = makeUpstream4Key(IPPROTO_UDP);
+ final Tether4Value tcpValue = makeUpstream4Value();
+ final Tether4Value udpValue = makeUpstream4Value();
+
+ checkRefreshConntrackTimeout(bpfUpstream4Map, tcpKey, tcpValue, udpKey, udpValue);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testRefreshConntrackTimeout_Downstream4Map() throws Exception {
+ // TODO: Replace the dependencies BPF map with a non-mocked TestBpfMap object.
+ final TestBpfMap<Tether4Key, Tether4Value> bpfDownstream4Map =
+ new TestBpfMap<>(Tether4Key.class, Tether4Value.class);
+ doReturn(bpfDownstream4Map).when(mDeps).getBpfDownstream4Map();
+
+ final Tether4Key tcpKey = makeDownstream4Key(IPPROTO_TCP);
+ final Tether4Key udpKey = makeDownstream4Key(IPPROTO_UDP);
+ final Tether4Value tcpValue = makeDownstream4Value();
+ final Tether4Value udpValue = makeDownstream4Value();
+
+ checkRefreshConntrackTimeout(bpfDownstream4Map, tcpKey, tcpValue, udpKey, udpValue);
+ }
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
index 88f2054..fc34585 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
@@ -26,6 +26,7 @@
import static android.net.RouteInfo.RTN_UNICAST;
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_IFACE;
import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_UID;
import static com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats;
@@ -58,7 +59,6 @@
import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.net.ITetheringStatsProvider;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -67,6 +67,7 @@
import android.net.RouteInfo;
import android.net.netstats.provider.NetworkStatsProvider;
import android.net.util.SharedLog;
+import android.os.Build;
import android.os.Handler;
import android.os.test.TestLooper;
import android.provider.Settings;
@@ -77,10 +78,13 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.TestableNetworkStatsProviderCbBinder;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -96,6 +100,9 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class OffloadControllerTest {
+ @Rule
+ public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
+
private static final String RNDIS0 = "test_rndis0";
private static final String RMNET0 = "test_rmnet_data0";
private static final String WLAN0 = "test_wlan0";
@@ -150,6 +157,7 @@
when(mHardware.setUpstreamParameters(anyString(), any(), any(), any())).thenReturn(true);
when(mHardware.getForwardedStats(any())).thenReturn(new ForwardedStats());
when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
+ when(mHardware.setDataWarningAndLimit(anyString(), anyLong(), anyLong())).thenReturn(true);
}
private void enableOffload() {
@@ -503,77 +511,176 @@
expectedUidStatsDiff);
}
+ /**
+ * Test OffloadController with different combinations of HAL and framework versions can set
+ * data warning and/or limit correctly.
+ */
@Test
- public void testSetInterfaceQuota() throws Exception {
+ public void testSetDataWarningAndLimit() throws Exception {
+ // Verify the OffloadController is called by R framework, where the framework doesn't send
+ // warning.
+ // R only uses HAL 1.0.
+ checkSetDataWarningAndLimit(false, OFFLOAD_HAL_VERSION_1_0);
+ // Verify the OffloadController is called by S+ framework, where the framework sends
+ // warning along with limit.
+ checkSetDataWarningAndLimit(true, OFFLOAD_HAL_VERSION_1_0);
+ checkSetDataWarningAndLimit(true, OFFLOAD_HAL_VERSION_1_1);
+ }
+
+ private void checkSetDataWarningAndLimit(boolean isProviderSetWarning, int controlVersion)
+ throws Exception {
enableOffload();
final OffloadController offload =
- startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
+ startOffloadController(controlVersion, true /*expectStart*/);
final String ethernetIface = "eth1";
final String mobileIface = "rmnet_data0";
final long ethernetLimit = 12345;
+ final long mobileWarning = 123456;
final long mobileLimit = 12345678;
final LinkProperties lp = new LinkProperties();
lp.setInterfaceName(ethernetIface);
- offload.setUpstreamLinkProperties(lp);
final InOrder inOrder = inOrder(mHardware);
- when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true);
+ when(mHardware.setUpstreamParameters(
+ any(), any(), any(), any())).thenReturn(true);
when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
+ when(mHardware.setDataWarningAndLimit(anyString(), anyLong(), anyLong())).thenReturn(true);
+ offload.setUpstreamLinkProperties(lp);
+ // Applying an interface sends the initial quota to the hardware.
+ if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ inOrder.verify(mHardware).setDataWarningAndLimit(ethernetIface, Long.MAX_VALUE,
+ Long.MAX_VALUE);
+ } else {
+ inOrder.verify(mHardware).setDataLimit(ethernetIface, Long.MAX_VALUE);
+ }
+ inOrder.verifyNoMoreInteractions();
+
+ // Verify that set to unlimited again won't cause duplicated calls to the hardware.
+ if (isProviderSetWarning) {
+ mTetherStatsProvider.onSetWarningAndLimit(ethernetIface,
+ NetworkStatsProvider.QUOTA_UNLIMITED, NetworkStatsProvider.QUOTA_UNLIMITED);
+ } else {
+ mTetherStatsProvider.onSetLimit(ethernetIface, NetworkStatsProvider.QUOTA_UNLIMITED);
+ }
+ waitForIdle();
+ inOrder.verifyNoMoreInteractions();
// Applying an interface quota to the current upstream immediately sends it to the hardware.
- mTetherStatsProvider.onSetLimit(ethernetIface, ethernetLimit);
+ if (isProviderSetWarning) {
+ mTetherStatsProvider.onSetWarningAndLimit(ethernetIface,
+ NetworkStatsProvider.QUOTA_UNLIMITED, ethernetLimit);
+ } else {
+ mTetherStatsProvider.onSetLimit(ethernetIface, ethernetLimit);
+ }
waitForIdle();
- inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit);
+ if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ inOrder.verify(mHardware).setDataWarningAndLimit(ethernetIface, Long.MAX_VALUE,
+ ethernetLimit);
+ } else {
+ inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit);
+ }
inOrder.verifyNoMoreInteractions();
// Applying an interface quota to another upstream does not take any immediate action.
- mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
+ if (isProviderSetWarning) {
+ mTetherStatsProvider.onSetWarningAndLimit(mobileIface, mobileWarning, mobileLimit);
+ } else {
+ mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
+ }
waitForIdle();
- inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
+ if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ inOrder.verify(mHardware, never()).setDataWarningAndLimit(anyString(), anyLong(),
+ anyLong());
+ } else {
+ inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
+ }
// Switching to that upstream causes the quota to be applied if the parameters were applied
// correctly.
lp.setInterfaceName(mobileIface);
offload.setUpstreamLinkProperties(lp);
waitForIdle();
- inOrder.verify(mHardware).setDataLimit(mobileIface, mobileLimit);
+ if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ inOrder.verify(mHardware).setDataWarningAndLimit(mobileIface,
+ isProviderSetWarning ? mobileWarning : Long.MAX_VALUE,
+ mobileLimit);
+ } else {
+ inOrder.verify(mHardware).setDataLimit(mobileIface, mobileLimit);
+ }
- // Setting a limit of ITetheringStatsProvider.QUOTA_UNLIMITED causes the limit to be set
+ // Setting a limit of NetworkStatsProvider.QUOTA_UNLIMITED causes the limit to be set
// to Long.MAX_VALUE.
- mTetherStatsProvider.onSetLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
+ if (isProviderSetWarning) {
+ mTetherStatsProvider.onSetWarningAndLimit(mobileIface,
+ NetworkStatsProvider.QUOTA_UNLIMITED, NetworkStatsProvider.QUOTA_UNLIMITED);
+ } else {
+ mTetherStatsProvider.onSetLimit(mobileIface, NetworkStatsProvider.QUOTA_UNLIMITED);
+ }
waitForIdle();
- inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE);
+ if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ inOrder.verify(mHardware).setDataWarningAndLimit(mobileIface, Long.MAX_VALUE,
+ Long.MAX_VALUE);
+ } else {
+ inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE);
+ }
- // If setting upstream parameters fails, then the data limit is not set.
+ // If setting upstream parameters fails, then the data warning and limit is not set.
when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false);
lp.setInterfaceName(ethernetIface);
offload.setUpstreamLinkProperties(lp);
- mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
+ if (isProviderSetWarning) {
+ mTetherStatsProvider.onSetWarningAndLimit(mobileIface, mobileWarning, mobileLimit);
+ } else {
+ mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
+ }
waitForIdle();
inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
+ inOrder.verify(mHardware, never()).setDataWarningAndLimit(anyString(), anyLong(),
+ anyLong());
- // If setting the data limit fails while changing upstreams, offload is stopped.
+ // If setting the data warning and/or limit fails while changing upstreams, offload is
+ // stopped.
when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true);
when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false);
+ when(mHardware.setDataWarningAndLimit(anyString(), anyLong(), anyLong())).thenReturn(false);
lp.setInterfaceName(mobileIface);
offload.setUpstreamLinkProperties(lp);
- mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
+ if (isProviderSetWarning) {
+ mTetherStatsProvider.onSetWarningAndLimit(mobileIface, mobileWarning, mobileLimit);
+ } else {
+ mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
+ }
waitForIdle();
inOrder.verify(mHardware).getForwardedStats(ethernetIface);
inOrder.verify(mHardware).stopOffloadControl();
}
@Test
- public void testDataLimitCallback() throws Exception {
+ public void testDataWarningAndLimitCallback_LimitReached() throws Exception {
enableOffload();
- final OffloadController offload =
- startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
- OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
+ final OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
callback.onStoppedLimitReached();
mTetherStatsProviderCb.expectNotifyStatsUpdated();
+
+ if (isAtLeastS()) {
+ mTetherStatsProviderCb.expectNotifyWarningOrLimitReached();
+ } else {
+ mTetherStatsProviderCb.expectLegacyNotifyLimitReached();
+ }
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.R) // HAL 1.1 is only supported from S
+ public void testDataWarningAndLimitCallback_WarningReached() throws Exception {
+ startOffloadController(OFFLOAD_HAL_VERSION_1_1, true /*expectStart*/);
+ final OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
+ callback.onWarningReached();
+ mTetherStatsProviderCb.expectNotifyStatsUpdated();
+ mTetherStatsProviderCb.expectNotifyWarningOrLimitReached();
}
@Test
@@ -761,9 +868,7 @@
// Initialize with fake eth upstream.
final String ethernetIface = "eth1";
InOrder inOrder = inOrder(mHardware);
- final LinkProperties lp = new LinkProperties();
- lp.setInterfaceName(ethernetIface);
- offload.setUpstreamLinkProperties(lp);
+ offload.setUpstreamLinkProperties(makeEthernetLinkProperties());
// Previous upstream was null, so no stats are fetched.
inOrder.verify(mHardware, never()).getForwardedStats(any());
@@ -796,4 +901,33 @@
mTetherStatsProviderCb.assertNoCallback();
verify(mHardware, never()).getForwardedStats(any());
}
+
+ private static LinkProperties makeEthernetLinkProperties() {
+ final String ethernetIface = "eth1";
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(ethernetIface);
+ return lp;
+ }
+
+ private void checkSoftwarePollingUsed(int controlVersion) throws Exception {
+ enableOffload();
+ setOffloadPollInterval(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
+ OffloadController offload =
+ startOffloadController(controlVersion, true /*expectStart*/);
+ offload.setUpstreamLinkProperties(makeEthernetLinkProperties());
+ mTetherStatsProvider.onSetAlert(0);
+ waitForIdle();
+ if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ mTetherStatsProviderCb.assertNoCallback();
+ } else {
+ mTetherStatsProviderCb.expectNotifyAlertReached();
+ }
+ verify(mHardware, never()).getForwardedStats(any());
+ }
+
+ @Test
+ public void testSoftwarePollingUsed() throws Exception {
+ checkSoftwarePollingUsed(OFFLOAD_HAL_VERSION_1_0);
+ checkSoftwarePollingUsed(OFFLOAD_HAL_VERSION_1_1);
+ }
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
index f4194e5..a8b3b92 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
@@ -22,22 +22,27 @@
import static android.system.OsConstants.SOCK_STREAM;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_1;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
-import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
-import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
+import android.hardware.tetheroffload.control.V1_1.ITetheringOffloadCallback;
+import android.hardware.tetheroffload.control.V1_1.OffloadCallbackEvent;
import android.net.netlink.StructNfGenMsg;
import android.net.netlink.StructNlMsgHdr;
import android.net.util.SharedLog;
@@ -56,6 +61,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -76,7 +82,7 @@
private OffloadHardwareInterface.ControlCallback mControlCallback;
@Mock private IOffloadConfig mIOffloadConfig;
- @Mock private IOffloadControl mIOffloadControl;
+ private IOffloadControl mIOffloadControl;
@Mock private NativeHandle mNativeHandle;
// Random values to test Netlink message.
@@ -84,8 +90,10 @@
private static final short TEST_FLAGS = 263;
class MyDependencies extends OffloadHardwareInterface.Dependencies {
- MyDependencies(SharedLog log) {
+ private final int mMockControlVersion;
+ MyDependencies(SharedLog log, final int mockControlVersion) {
super(log);
+ mMockControlVersion = mockControlVersion;
}
@Override
@@ -95,7 +103,19 @@
@Override
public Pair<IOffloadControl, Integer> getOffloadControl() {
- return new Pair<IOffloadControl, Integer>(mIOffloadControl, OFFLOAD_HAL_VERSION_1_0);
+ switch (mMockControlVersion) {
+ case OFFLOAD_HAL_VERSION_1_0:
+ mIOffloadControl = mock(IOffloadControl.class);
+ break;
+ case OFFLOAD_HAL_VERSION_1_1:
+ mIOffloadControl =
+ mock(android.hardware.tetheroffload.control.V1_1.IOffloadControl.class);
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid offload control version "
+ + mMockControlVersion);
+ }
+ return new Pair<IOffloadControl, Integer>(mIOffloadControl, mMockControlVersion);
}
@Override
@@ -107,14 +127,13 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- final SharedLog log = new SharedLog("test");
- mOffloadHw = new OffloadHardwareInterface(new Handler(mTestLooper.getLooper()), log,
- new MyDependencies(log));
mControlCallback = spy(new OffloadHardwareInterface.ControlCallback());
}
- // TODO: Pass version to test version specific operations.
- private void startOffloadHardwareInterface() throws Exception {
+ private void startOffloadHardwareInterface(int controlVersion) throws Exception {
+ final SharedLog log = new SharedLog("test");
+ mOffloadHw = new OffloadHardwareInterface(new Handler(mTestLooper.getLooper()), log,
+ new MyDependencies(log, controlVersion));
mOffloadHw.initOffloadConfig();
mOffloadHw.initOffloadControl(mControlCallback);
final ArgumentCaptor<ITetheringOffloadCallback> mOffloadCallbackCaptor =
@@ -125,7 +144,7 @@
@Test
public void testGetForwardedStats() throws Exception {
- startOffloadHardwareInterface();
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
final OffloadHardwareInterface.ForwardedStats stats = mOffloadHw.getForwardedStats(RMNET0);
verify(mIOffloadControl).getForwardedStats(eq(RMNET0), any());
assertNotNull(stats);
@@ -133,7 +152,7 @@
@Test
public void testSetLocalPrefixes() throws Exception {
- startOffloadHardwareInterface();
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
final ArrayList<String> localPrefixes = new ArrayList<>();
localPrefixes.add("127.0.0.0/8");
localPrefixes.add("fe80::/64");
@@ -143,15 +162,32 @@
@Test
public void testSetDataLimit() throws Exception {
- startOffloadHardwareInterface();
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
final long limit = 12345;
mOffloadHw.setDataLimit(RMNET0, limit);
verify(mIOffloadControl).setDataLimit(eq(RMNET0), eq(limit), any());
}
@Test
+ public void testSetDataWarningAndLimit() throws Exception {
+ // Verify V1.0 control HAL would reject the function call with exception.
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
+ final long warning = 12345;
+ final long limit = 67890;
+ assertThrows(IllegalArgumentException.class,
+ () -> mOffloadHw.setDataWarningAndLimit(RMNET0, warning, limit));
+ reset(mIOffloadControl);
+
+ // Verify V1.1 control HAL could receive this function call.
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_1);
+ mOffloadHw.setDataWarningAndLimit(RMNET0, warning, limit);
+ verify((android.hardware.tetheroffload.control.V1_1.IOffloadControl) mIOffloadControl)
+ .setDataWarningAndLimit(eq(RMNET0), eq(warning), eq(limit), any());
+ }
+
+ @Test
public void testSetUpstreamParameters() throws Exception {
- startOffloadHardwareInterface();
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
final String v4addr = "192.168.10.1";
final String v4gateway = "192.168.10.255";
final ArrayList<String> v6gws = new ArrayList<>(0);
@@ -170,7 +206,7 @@
@Test
public void testUpdateDownstreamPrefix() throws Exception {
- startOffloadHardwareInterface();
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
final String ifName = "wlan1";
final String prefix = "192.168.43.0/24";
mOffloadHw.addDownstreamPrefix(ifName, prefix);
@@ -182,7 +218,7 @@
@Test
public void testTetheringOffloadCallback() throws Exception {
- startOffloadHardwareInterface();
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STARTED);
mTestLooper.dispatchAll();
@@ -221,10 +257,26 @@
eq(uint16(udpParams.src.port)),
eq(udpParams.dst.addr),
eq(uint16(udpParams.dst.port)));
+ reset(mControlCallback);
+
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_1);
+
+ // Verify the interface will process the events that comes from V1.1 HAL.
+ mTetheringOffloadCallback.onEvent_1_1(OffloadCallbackEvent.OFFLOAD_STARTED);
+ mTestLooper.dispatchAll();
+ final InOrder inOrder = inOrder(mControlCallback);
+ inOrder.verify(mControlCallback).onStarted();
+ inOrder.verifyNoMoreInteractions();
+
+ mTetheringOffloadCallback.onEvent_1_1(OffloadCallbackEvent.OFFLOAD_WARNING_REACHED);
+ mTestLooper.dispatchAll();
+ inOrder.verify(mControlCallback).onWarningReached();
+ inOrder.verifyNoMoreInteractions();
}
@Test
public void testSendIpv4NfGenMsg() throws Exception {
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
FileDescriptor writeSocket = new FileDescriptor();
FileDescriptor readSocket = new FileDescriptor();
try {
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java
index 6090213..e692015 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java
@@ -20,6 +20,8 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static com.android.networkstack.apishim.common.ShimUtils.isAtLeastS;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
@@ -36,6 +38,7 @@
import android.os.UserHandle;
import android.util.ArrayMap;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Map;
@@ -67,10 +70,10 @@
public static final boolean BROADCAST_FIRST = false;
public static final boolean CALLBACKS_FIRST = true;
- final Map<NetworkCallback, NetworkRequestInfo> mAllCallbacks = new ArrayMap<>();
+ final Map<NetworkCallback, Handler> mAllCallbacks = new ArrayMap<>();
// This contains the callbacks tracking the system default network, whether it's registered
// with registerSystemDefaultNetworkCallback (S+) or with a custom request (R-).
- final Map<NetworkCallback, NetworkRequestInfo> mTrackingDefault = new ArrayMap<>();
+ final Map<NetworkCallback, Handler> mTrackingDefault = new ArrayMap<>();
final Map<NetworkCallback, NetworkRequestInfo> mListening = new ArrayMap<>();
final Map<NetworkCallback, NetworkRequestInfo> mRequested = new ArrayMap<>();
final Map<NetworkCallback, Integer> mLegacyTypeMap = new ArrayMap<>();
@@ -91,7 +94,7 @@
mContext = ctx;
}
- class NetworkRequestInfo {
+ static class NetworkRequestInfo {
public final NetworkRequest request;
public final Handler handler;
NetworkRequestInfo(NetworkRequest r, Handler h) {
@@ -145,15 +148,15 @@
private void sendDefaultNetworkCallbacks(TestNetworkAgent formerDefault,
TestNetworkAgent defaultNetwork) {
for (NetworkCallback cb : mTrackingDefault.keySet()) {
- final NetworkRequestInfo nri = mTrackingDefault.get(cb);
+ final Handler handler = mTrackingDefault.get(cb);
if (defaultNetwork != null) {
- nri.handler.post(() -> cb.onAvailable(defaultNetwork.networkId));
- nri.handler.post(() -> cb.onCapabilitiesChanged(
+ handler.post(() -> cb.onAvailable(defaultNetwork.networkId));
+ handler.post(() -> cb.onCapabilitiesChanged(
defaultNetwork.networkId, defaultNetwork.networkCapabilities));
- nri.handler.post(() -> cb.onLinkPropertiesChanged(
+ handler.post(() -> cb.onLinkPropertiesChanged(
defaultNetwork.networkId, defaultNetwork.linkProperties));
} else if (formerDefault != null) {
- nri.handler.post(() -> cb.onLost(formerDefault.networkId));
+ handler.post(() -> cb.onLost(formerDefault.networkId));
}
}
}
@@ -191,20 +194,33 @@
@Override
public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) {
- assertFalse(mAllCallbacks.containsKey(cb));
- mAllCallbacks.put(cb, new NetworkRequestInfo(req, h));
// For R- devices, Tethering will invoke this function in 2 cases, one is to request mobile
// network, the other is to track system default network.
if (looksLikeDefaultRequest(req)) {
- assertFalse(mTrackingDefault.containsKey(cb));
- mTrackingDefault.put(cb, new NetworkRequestInfo(req, h));
+ assertFalse(isAtLeastS());
+ addTrackDefaultCallback(cb, h);
} else {
+ assertFalse(mAllCallbacks.containsKey(cb));
+ mAllCallbacks.put(cb, h);
assertFalse(mRequested.containsKey(cb));
mRequested.put(cb, new NetworkRequestInfo(req, h));
}
}
@Override
+ public void registerSystemDefaultNetworkCallback(
+ @NonNull NetworkCallback cb, @NonNull Handler h) {
+ addTrackDefaultCallback(cb, h);
+ }
+
+ private void addTrackDefaultCallback(@NonNull NetworkCallback cb, @NonNull Handler h) {
+ assertFalse(mAllCallbacks.containsKey(cb));
+ mAllCallbacks.put(cb, h);
+ assertFalse(mTrackingDefault.containsKey(cb));
+ mTrackingDefault.put(cb, h);
+ }
+
+ @Override
public void requestNetwork(NetworkRequest req, NetworkCallback cb) {
fail("Should never be called.");
}
@@ -215,7 +231,7 @@
assertFalse(mAllCallbacks.containsKey(cb));
NetworkRequest newReq = new NetworkRequest(req.networkCapabilities, legacyType,
-1 /** testId */, req.type);
- mAllCallbacks.put(cb, new NetworkRequestInfo(newReq, h));
+ mAllCallbacks.put(cb, h);
assertFalse(mRequested.containsKey(cb));
mRequested.put(cb, new NetworkRequestInfo(newReq, h));
assertFalse(mLegacyTypeMap.containsKey(cb));
@@ -227,7 +243,7 @@
@Override
public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb, Handler h) {
assertFalse(mAllCallbacks.containsKey(cb));
- mAllCallbacks.put(cb, new NetworkRequestInfo(req, h));
+ mAllCallbacks.put(cb, h);
assertFalse(mListening.containsKey(cb));
mListening.put(cb, new NetworkRequestInfo(req, h));
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index d277e30..9e0c880 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -61,6 +61,7 @@
import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0;
@@ -760,10 +761,17 @@
}
private void verifyDefaultNetworkRequestFiled() {
- ArgumentCaptor<NetworkRequest> reqCaptor = ArgumentCaptor.forClass(NetworkRequest.class);
- verify(mCm, times(1)).requestNetwork(reqCaptor.capture(),
- any(NetworkCallback.class), any(Handler.class));
- assertTrue(TestConnectivityManager.looksLikeDefaultRequest(reqCaptor.getValue()));
+ if (isAtLeastS()) {
+ verify(mCm, times(1)).registerSystemDefaultNetworkCallback(
+ any(NetworkCallback.class), any(Handler.class));
+ } else {
+ ArgumentCaptor<NetworkRequest> reqCaptor = ArgumentCaptor.forClass(
+ NetworkRequest.class);
+ verify(mCm, times(1)).requestNetwork(reqCaptor.capture(),
+ any(NetworkCallback.class), any(Handler.class));
+ assertTrue(TestConnectivityManager.looksLikeDefaultRequest(reqCaptor.getValue()));
+ }
+
// The default network request is only ever filed once.
verifyNoMoreInteractions(mCm);
}
@@ -2602,12 +2610,57 @@
reset(mBluetoothAdapter, mBluetoothPan);
}
+ private void runDualStackUsbTethering(final String expectedIface) throws Exception {
+ when(mNetd.interfaceGetList()).thenReturn(new String[] {expectedIface});
+ when(mRouterAdvertisementDaemon.start())
+ .thenReturn(true);
+ final UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
+ runUsbTethering(upstreamState);
+
+ verify(mNetd).interfaceGetList();
+ verify(mNetd).tetherAddForward(expectedIface, TEST_MOBILE_IFNAME);
+ verify(mNetd).ipfwdAddInterfaceForward(expectedIface, TEST_MOBILE_IFNAME);
+
+ verify(mRouterAdvertisementDaemon).start();
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS)).startWithCallbacks(
+ any(), any());
+ sendIPv6TetherUpdates(upstreamState);
+ assertSetIfaceToDadProxy(1 /* numOfCalls */, TEST_MOBILE_IFNAME /* ifaceName */);
+ verify(mRouterAdvertisementDaemon).buildNewRa(any(), notNull());
+ verify(mNetd).tetherApplyDnsInterfaces();
+ }
+
+ private void forceUsbTetheringUse(final int function) {
+ Settings.Global.putInt(mContentResolver, TETHER_FORCE_USB_FUNCTIONS, function);
+ final ContentObserver observer = mTethering.getSettingsObserverForTest();
+ observer.onChange(false /* selfChange */);
+ mLooper.dispatchAll();
+ }
+
+ private void verifyUsbTetheringStopDueToSettingChange(final String iface) {
+ verify(mUsbManager, times(2)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+ mTethering.interfaceRemoved(iface);
+ sendUsbBroadcast(true, true, -1 /* no functions enabled */);
+ reset(mUsbManager, mNetd, mDhcpServer, mRouterAdvertisementDaemon,
+ mIPv6TetheringCoordinator, mDadProxy);
+ }
+
@Test
- public void testUsbTetheringWithNcmFunction() throws Exception {
- when(mResources.getInteger(R.integer.config_tether_usb_functions)).thenReturn(
- TetheringConfiguration.TETHER_USB_NCM_FUNCTION);
+ public void testUsbFunctionConfigurationChange() throws Exception {
+ // Run TETHERING_NCM.
+ runNcmTethering();
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+ any(), any());
+
+ // Change the USB tethering function to NCM. Because the USB tethering function was set to
+ // RNDIS (the default), tethering is stopped.
+ forceUsbTetheringUse(TETHER_USB_NCM_FUNCTION);
+ verifyUsbTetheringStopDueToSettingChange(TEST_NCM_IFNAME);
+
+ // TODO: move this into setup after allowing configure TEST_NCM_REGEX into
+ // config_tether_usb_regexs and config_tether_ncm_regexs at the same time.
when(mResources.getStringArray(R.array.config_tether_usb_regexs))
- .thenReturn(new String[] {TEST_NCM_REGEX});
+ .thenReturn(new String[] {TEST_RNDIS_REGEX, TEST_NCM_REGEX});
sendConfigurationChanged();
// If TETHERING_USB is forced to use ncm function, TETHERING_NCM would no longer be
@@ -2617,30 +2670,16 @@
mLooper.dispatchAll();
ncmResult.assertHasResult();
- final UpstreamNetworkState upstreamState = buildMobileDualStackUpstreamState();
- runUsbTethering(upstreamState);
+ // Run TETHERING_USB with ncm configuration.
+ runDualStackUsbTethering(TEST_NCM_IFNAME);
- verify(mNetd).interfaceGetList();
- verify(mNetd).tetherAddForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
- verify(mNetd).ipfwdAddInterfaceForward(TEST_NCM_IFNAME, TEST_MOBILE_IFNAME);
+ // Change configuration to rndis.
+ forceUsbTetheringUse(TETHER_USB_RNDIS_FUNCTION);
+ verifyUsbTetheringStopDueToSettingChange(TEST_NCM_IFNAME);
- verify(mRouterAdvertisementDaemon).start();
- verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS)).startWithCallbacks(
- any(), any());
- sendIPv6TetherUpdates(upstreamState);
- assertSetIfaceToDadProxy(1 /* numOfCalls */, TEST_MOBILE_IFNAME /* ifaceName */);
- verify(mRouterAdvertisementDaemon).buildNewRa(any(), notNull());
- verify(mNetd).tetherApplyDnsInterfaces();
-
- Settings.Global.putInt(mContentResolver, TETHER_FORCE_USB_FUNCTIONS,
- TETHER_USB_RNDIS_FUNCTION);
- final ContentObserver observer = mTethering.getSettingsObserverForTest();
- observer.onChange(false /* selfChange */);
- mLooper.dispatchAll();
- // stop TETHERING_USB and TETHERING_NCM
- verify(mUsbManager, times(2)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
- mTethering.interfaceRemoved(TEST_NCM_IFNAME);
- sendUsbBroadcast(true, true, -1 /* function */);
+ // Run TETHERING_USB with rndis configuration.
+ runDualStackUsbTethering(TEST_RNDIS_IFNAME);
+ runStopUSBTethering();
}
// TODO: Test that a request for hotspot mode doesn't interfere with an
// already operating tethering mode interface.
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
index ce4ba85..173679d 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
@@ -24,6 +24,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
import static com.android.networkstack.tethering.UpstreamNetworkMonitor.TYPE_NONE;
import static org.junit.Assert.assertEquals;
@@ -172,12 +173,17 @@
// Verify the fired default request matches expectation.
final ArgumentCaptor<NetworkRequest> requestCaptor =
ArgumentCaptor.forClass(NetworkRequest.class);
- verify(mCM, times(1)).requestNetwork(
- requestCaptor.capture(), any(NetworkCallback.class), any(Handler.class));
- // For R- devices, Tethering will invoke this function in 2 cases, one is to
- // request mobile network, the other is to track system default network. Verify
- // the request is the one tracks default network.
- assertTrue(TestConnectivityManager.looksLikeDefaultRequest(requestCaptor.getValue()));
+
+ if (isAtLeastS()) {
+ verify(mCM).registerSystemDefaultNetworkCallback(any(), any());
+ } else {
+ verify(mCM).requestNetwork(
+ requestCaptor.capture(), any(NetworkCallback.class), any(Handler.class));
+ // For R- devices, Tethering will invoke this function in 2 cases, one is to
+ // request mobile network, the other is to track system default network. Verify
+ // the request is the one tracks default network.
+ assertTrue(TestConnectivityManager.looksLikeDefaultRequest(requestCaptor.getValue()));
+ }
mUNM.startObserveAllNetworks();
verify(mCM, times(1)).registerNetworkCallback(
diff --git a/framework/Android.bp b/framework/Android.bp
index 93ef3bf..ee3a5d5 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -97,8 +97,6 @@
],
},
impl_only_libs: [
- // TODO (b/183097033) remove once module_current includes core_platform
- "stable.core.platform.api.stubs",
"framework-tethering.stubs.module_lib",
"framework-wifi.stubs.module_lib",
"net-utils-device-common",
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 7e2f688..14ec608 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -4939,7 +4939,7 @@
Log.e(TAG, "Can't set proxy properties", e);
}
// Must flush DNS cache as new network may have different DNS resolutions.
- InetAddressCompat.clearDnsCache();
+ InetAddress.clearDnsCache();
// Must flush socket pool as idle sockets will be bound to previous network and may
// cause subsequent fetches to be performed on old network.
NetworkEventDispatcher.getInstance().dispatchNetworkConfigurationChange();
diff --git a/framework/src/android/net/ConnectivitySettingsManager.java b/framework/src/android/net/ConnectivitySettingsManager.java
index 085de6b..8fc0065 100644
--- a/framework/src/android/net/ConnectivitySettingsManager.java
+++ b/framework/src/android/net/ConnectivitySettingsManager.java
@@ -29,6 +29,8 @@
import android.annotation.SystemApi;
import android.content.Context;
import android.net.ConnectivityManager.MultipathPreference;
+import android.os.Binder;
+import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.provider.Settings;
@@ -1039,6 +1041,15 @@
return getUidSetFromString(uidList);
}
+ private static boolean isCallingFromSystem() {
+ final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
+ if (uid == Process.SYSTEM_UID && pid == Process.myPid()) {
+ return true;
+ }
+ return false;
+ }
+
/**
* Set the list of uids(from {@link Settings}) that is allowed to use restricted networks.
*
@@ -1047,6 +1058,15 @@
*/
public static void setUidsAllowedOnRestrictedNetworks(@NonNull Context context,
@NonNull Set<Integer> uidList) {
+ final boolean calledFromSystem = isCallingFromSystem();
+ if (!calledFromSystem) {
+ // Enforce NETWORK_SETTINGS check if it's debug build. This is for MTS test only.
+ if (!Build.isDebuggable()) {
+ throw new SecurityException("Only system can set this setting.");
+ }
+ context.enforceCallingOrSelfPermission(android.Manifest.permission.NETWORK_SETTINGS,
+ "Requires NETWORK_SETTINGS permission");
+ }
final String uids = getUidStringFromSet(uidList);
Settings.Global.putString(context.getContentResolver(), UIDS_ALLOWED_ON_RESTRICTED_NETWORKS,
uids);
diff --git a/framework/src/android/net/InetAddressCompat.java b/framework/src/android/net/InetAddressCompat.java
deleted file mode 100644
index 6b7e75c..0000000
--- a/framework/src/android/net/InetAddressCompat.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2021 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 android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-/**
- * Compatibility utility for InetAddress core platform APIs.
- *
- * Connectivity has access to such APIs, but they are not part of the module_current stubs yet
- * (only core_current). Most stable core platform APIs are included manually in the connectivity
- * build rules, but because InetAddress is also part of the base java SDK that is earlier on the
- * classpath, the extra core platform APIs are not seen.
- *
- * TODO (b/183097033): remove this utility as soon as core_current is part of module_current
- * @hide
- */
-public class InetAddressCompat {
-
- /**
- * @see InetAddress#clearDnsCache()
- */
- public static void clearDnsCache() {
- try {
- InetAddress.class.getMethod("clearDnsCache").invoke(null);
- } catch (InvocationTargetException e) {
- if (e.getCause() instanceof RuntimeException) {
- throw (RuntimeException) e.getCause();
- }
- throw new IllegalStateException("Unknown InvocationTargetException", e.getCause());
- } catch (IllegalAccessException | NoSuchMethodException e) {
- Log.wtf(InetAddressCompat.class.getSimpleName(), "Error clearing DNS cache", e);
- }
- }
-
- /**
- * @see InetAddress#getAllByNameOnNet(String, int)
- */
- public static InetAddress[] getAllByNameOnNet(String host, int netId) throws
- UnknownHostException {
- return (InetAddress[]) callGetByNameMethod("getAllByNameOnNet", host, netId);
- }
-
- /**
- * @see InetAddress#getByNameOnNet(String, int)
- */
- public static InetAddress getByNameOnNet(String host, int netId) throws
- UnknownHostException {
- return (InetAddress) callGetByNameMethod("getByNameOnNet", host, netId);
- }
-
- private static Object callGetByNameMethod(String method, String host, int netId)
- throws UnknownHostException {
- try {
- return InetAddress.class.getMethod(method, String.class, int.class)
- .invoke(null, host, netId);
- } catch (InvocationTargetException e) {
- if (e.getCause() instanceof UnknownHostException) {
- throw (UnknownHostException) e.getCause();
- }
- if (e.getCause() instanceof RuntimeException) {
- throw (RuntimeException) e.getCause();
- }
- throw new IllegalStateException("Unknown InvocationTargetException", e.getCause());
- } catch (IllegalAccessException | NoSuchMethodException e) {
- Log.wtf(InetAddressCompat.class.getSimpleName(), "Error calling " + method, e);
- throw new IllegalStateException("Error querying via " + method, e);
- }
- }
-}
diff --git a/framework/src/android/net/Network.java b/framework/src/android/net/Network.java
index 1f49033..b3770ea 100644
--- a/framework/src/android/net/Network.java
+++ b/framework/src/android/net/Network.java
@@ -142,7 +142,7 @@
* @throws UnknownHostException if the address lookup fails.
*/
public InetAddress[] getAllByName(String host) throws UnknownHostException {
- return InetAddressCompat.getAllByNameOnNet(host, getNetIdForResolv());
+ return InetAddress.getAllByNameOnNet(host, getNetIdForResolv());
}
/**
@@ -155,7 +155,7 @@
* if the address lookup fails.
*/
public InetAddress getByName(String host) throws UnknownHostException {
- return InetAddressCompat.getByNameOnNet(host, getNetIdForResolv());
+ return InetAddress.getByNameOnNet(host, getNetIdForResolv());
}
/**
diff --git a/framework/src/android/net/util/MultinetworkPolicyTracker.java b/framework/src/android/net/util/MultinetworkPolicyTracker.java
index 7e62d28..9791cbf 100644
--- a/framework/src/android/net/util/MultinetworkPolicyTracker.java
+++ b/framework/src/android/net/util/MultinetworkPolicyTracker.java
@@ -182,7 +182,7 @@
public void setTestAllowBadWifiUntil(long timeMs) {
Log.d(TAG, "setTestAllowBadWifiUntil: " + mTestAllowBadWifiUntilMs);
mTestAllowBadWifiUntilMs = timeMs;
- updateAvoidBadWifi();
+ reevaluateInternal();
}
@VisibleForTesting
diff --git a/service/Android.bp b/service/Android.bp
index 841e189..848de12 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -57,9 +57,6 @@
":net-module-utils-srcs",
],
libs: [
- // TODO (b/183097033) remove once system_server_current includes core_current
- "stable.core.platform.api.stubs",
- "android_system_server_stubs_current",
"framework-annotations-lib",
"framework-connectivity.impl",
"framework-tethering.stubs.module_lib",
@@ -112,3 +109,9 @@
"com.android.tethering",
],
}
+
+filegroup {
+ name: "connectivity-jarjar-rules",
+ srcs: ["jarjar-rules.txt"],
+ visibility: ["//packages/modules/Connectivity:__subpackages__"],
+}
diff --git a/service/ServiceConnectivityResources/res/values/config.xml b/service/ServiceConnectivityResources/res/values/config.xml
index 078a9eb..bf32ad5 100644
--- a/service/ServiceConnectivityResources/res/values/config.xml
+++ b/service/ServiceConnectivityResources/res/values/config.xml
@@ -107,4 +107,11 @@
<string-array translatable="false" name="config_networkNotifySwitches">
</string-array>
+ <!-- Whether to use an ongoing notification for signing in to captive portals, instead of a
+ notification that can be dismissed. -->
+ <bool name="config_ongoingSignInNotification">false</bool>
+
+ <!-- Whether to cancel network notifications automatically when tapped -->
+ <bool name="config_autoCancelNetworkNotifications">true</bool>
+
</resources>
diff --git a/service/ServiceConnectivityResources/res/values/overlayable.xml b/service/ServiceConnectivityResources/res/values/overlayable.xml
index f0f4ae8..6ac6a0e 100644
--- a/service/ServiceConnectivityResources/res/values/overlayable.xml
+++ b/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -30,7 +30,10 @@
<item type="integer" name="config_networkWakeupPacketMask"/>
<item type="integer" name="config_networkNotifySwitchType"/>
<item type="array" name="config_networkNotifySwitches"/>
-
+ <item type="bool" name="config_ongoingSignInNotification"/>
+ <item type="bool" name="config_autoCancelNetworkNotifications"/>
+ <item type="drawable" name="stat_notify_wifi_in_range"/>
+ <item type="drawable" name="stat_notify_rssi_in_range"/>
</policy>
</overlayable>
</resources>
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index f76ed85..dfaa6b1 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -55,6 +55,7 @@
import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
+import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_SKIPPED;
import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
@@ -541,9 +542,9 @@
private static final int EVENT_SET_AVOID_UNVALIDATED = 35;
/**
- * used to trigger revalidation of a network.
+ * used to handle reported network connectivity. May trigger revalidation of a network.
*/
- private static final int EVENT_REVALIDATE_NETWORK = 36;
+ private static final int EVENT_REPORT_NETWORK_CONNECTIVITY = 36;
// Handle changes in Private DNS settings.
private static final int EVENT_PRIVATE_DNS_SETTINGS_CHANGED = 37;
@@ -3260,11 +3261,6 @@
}
case NetworkAgent.EVENT_UNDERLYING_NETWORKS_CHANGED: {
// TODO: prevent loops, e.g., if a network declares itself as underlying.
- if (!nai.supportsUnderlyingNetworks()) {
- Log.wtf(TAG, "Non-virtual networks cannot have underlying networks");
- break;
- }
-
final List<Network> underlying = (List<Network>) arg.second;
if (isLegacyLockdownNai(nai)
@@ -3541,8 +3537,14 @@
final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(mNetId);
if (nai == null) return;
+ // NetworkMonitor reports the network validation result as a bitmask while
+ // ConnectivityDiagnostics treats this value as an int. Convert the result to a single
+ // logical value for ConnectivityDiagnostics.
+ final int validationResult = networkMonitorValidationResultToConnDiagsValidationResult(
+ p.result);
+
final PersistableBundle extras = new PersistableBundle();
- extras.putInt(KEY_NETWORK_VALIDATION_RESULT, p.result);
+ extras.putInt(KEY_NETWORK_VALIDATION_RESULT, validationResult);
extras.putInt(KEY_NETWORK_PROBES_SUCCEEDED_BITMASK, p.probesSucceeded);
extras.putInt(KEY_NETWORK_PROBES_ATTEMPTED_BITMASK, p.probesAttempted);
@@ -3618,6 +3620,22 @@
}
}
+ /**
+ * Converts the given NetworkMonitor-specific validation result bitmask to a
+ * ConnectivityDiagnostics-specific validation result int.
+ */
+ private int networkMonitorValidationResultToConnDiagsValidationResult(int validationResult) {
+ if ((validationResult & NETWORK_VALIDATION_RESULT_SKIPPED) != 0) {
+ return ConnectivityReport.NETWORK_VALIDATION_RESULT_SKIPPED;
+ }
+ if ((validationResult & NETWORK_VALIDATION_RESULT_VALID) == 0) {
+ return ConnectivityReport.NETWORK_VALIDATION_RESULT_INVALID;
+ }
+ return (validationResult & NETWORK_VALIDATION_RESULT_PARTIAL) != 0
+ ? ConnectivityReport.NETWORK_VALIDATION_RESULT_PARTIALLY_VALID
+ : ConnectivityReport.NETWORK_VALIDATION_RESULT_VALID;
+ }
+
private void notifyDataStallSuspected(DataStallReportParcelable p, int netId) {
log("Data stall detected with methods: " + p.detectionMethod);
@@ -4867,8 +4885,9 @@
mKeepaliveTracker.handleStopKeepalive(nai, slot, reason);
break;
}
- case EVENT_REVALIDATE_NETWORK: {
- handleReportNetworkConnectivity((Network) msg.obj, msg.arg1, toBool(msg.arg2));
+ case EVENT_REPORT_NETWORK_CONNECTIVITY: {
+ handleReportNetworkConnectivity((NetworkAgentInfo) msg.obj, msg.arg1,
+ toBool(msg.arg2));
break;
}
case EVENT_PRIVATE_DNS_SETTINGS_CHANGED:
@@ -5031,41 +5050,34 @@
final int uid = mDeps.getCallingUid();
final int connectivityInfo = encodeBool(hasConnectivity);
- // Handle ConnectivityDiagnostics event before attempting to revalidate the network. This
- // forces an ordering of ConnectivityDiagnostics events in the case where hasConnectivity
- // does not match the known connectivity of the network - this causes NetworkMonitor to
- // revalidate the network and generate a ConnectivityDiagnostics ConnectivityReport event.
final NetworkAgentInfo nai;
if (network == null) {
nai = getDefaultNetwork();
} else {
nai = getNetworkAgentInfoForNetwork(network);
}
- if (nai != null) {
- mConnectivityDiagnosticsHandler.sendMessage(
- mConnectivityDiagnosticsHandler.obtainMessage(
- ConnectivityDiagnosticsHandler.EVENT_NETWORK_CONNECTIVITY_REPORTED,
- connectivityInfo, 0, nai));
- }
mHandler.sendMessage(
- mHandler.obtainMessage(EVENT_REVALIDATE_NETWORK, uid, connectivityInfo, network));
+ mHandler.obtainMessage(
+ EVENT_REPORT_NETWORK_CONNECTIVITY, uid, connectivityInfo, nai));
}
private void handleReportNetworkConnectivity(
- Network network, int uid, boolean hasConnectivity) {
- final NetworkAgentInfo nai;
- if (network == null) {
- nai = getDefaultNetwork();
- } else {
- nai = getNetworkAgentInfoForNetwork(network);
- }
- if (nai == null || nai.networkInfo.getState() == NetworkInfo.State.DISCONNECTING ||
- nai.networkInfo.getState() == NetworkInfo.State.DISCONNECTED) {
+ @Nullable NetworkAgentInfo nai, int uid, boolean hasConnectivity) {
+ // TODO(b/192611346): remove NetworkInfo.State.DISCONNECTING as it's not used
+ if (nai == null
+ || nai != getNetworkAgentInfoForNetwork(nai.network)
+ || nai.networkInfo.getState() == NetworkInfo.State.DISCONNECTING
+ || nai.networkInfo.getState() == NetworkInfo.State.DISCONNECTED) {
return;
}
// Revalidate if the app report does not match our current validated state.
if (hasConnectivity == nai.lastValidated) {
+ mConnectivityDiagnosticsHandler.sendMessage(
+ mConnectivityDiagnosticsHandler.obtainMessage(
+ ConnectivityDiagnosticsHandler.EVENT_NETWORK_CONNECTIVITY_REPORTED,
+ new ReportedNetworkConnectivityInfo(
+ hasConnectivity, false /* isNetworkRevalidating */, uid, nai)));
return;
}
if (DBG) {
@@ -5081,6 +5093,16 @@
if (isNetworkWithCapabilitiesBlocked(nc, uid, false)) {
return;
}
+
+ // Send CONNECTIVITY_REPORTED event before re-validating the Network to force an ordering of
+ // ConnDiags events. This ensures that #onNetworkConnectivityReported() will be called
+ // before #onConnectivityReportAvailable(), which is called once Network evaluation is
+ // completed.
+ mConnectivityDiagnosticsHandler.sendMessage(
+ mConnectivityDiagnosticsHandler.obtainMessage(
+ ConnectivityDiagnosticsHandler.EVENT_NETWORK_CONNECTIVITY_REPORTED,
+ new ReportedNetworkConnectivityInfo(
+ hasConnectivity, true /* isNetworkRevalidating */, uid, nai)));
nai.networkMonitor().forceReevaluation(uid);
}
@@ -5277,12 +5299,12 @@
* information, e.g underlying ifaces.
*/
private UnderlyingNetworkInfo createVpnInfo(NetworkAgentInfo nai) {
- if (!nai.isVPN()) return null;
-
Network[] underlyingNetworks = nai.declaredUnderlyingNetworks;
// see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
// the underlyingNetworks list.
- if (underlyingNetworks == null) {
+ // TODO: stop using propagateUnderlyingCapabilities here, for example, by always
+ // initializing NetworkAgentInfo#declaredUnderlyingNetworks to an empty array.
+ if (underlyingNetworks == null && nai.propagateUnderlyingCapabilities()) {
final NetworkAgentInfo defaultNai = getDefaultNetworkForUid(
nai.networkCapabilities.getOwnerUid());
if (defaultNai != null) {
@@ -5331,7 +5353,7 @@
private boolean hasUnderlyingNetwork(NetworkAgentInfo nai, Network network) {
// TODO: support more than one level of underlying networks, either via a fixed-depth search
// (e.g., 2 levels of underlying networks), or via loop detection, or....
- if (!nai.supportsUnderlyingNetworks()) return false;
+ if (!nai.propagateUnderlyingCapabilities()) return false;
final Network[] underlying = underlyingNetworksOrDefault(
nai.networkCapabilities.getOwnerUid(), nai.declaredUnderlyingNetworks);
return CollectionUtils.contains(underlying, network);
@@ -6280,7 +6302,8 @@
callingAttributionTag);
if (VDBG) log("pendingListenForNetwork for " + nri);
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_LISTENER, nri));
+ mHandler.sendMessage(mHandler.obtainMessage(
+ EVENT_REGISTER_NETWORK_LISTENER_WITH_INTENT, nri));
}
/** Returns the next Network provider ID. */
@@ -7303,7 +7326,7 @@
newNc.addCapability(NET_CAPABILITY_NOT_ROAMING);
}
- if (nai.supportsUnderlyingNetworks()) {
+ if (nai.propagateUnderlyingCapabilities()) {
applyUnderlyingCapabilities(nai.declaredUnderlyingNetworks, nai.declaredCapabilities,
newNc);
}
@@ -7597,9 +7620,16 @@
// 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());
+ // EXTRA_NETWORK_REQUEST is the active request, and whatever ID would be added would
+ // need to be sent as a separate extra.
+ final NetworkRequest req = nri.isMultilayerRequest()
+ ? nri.getActiveRequest()
+ // Non-multilayer listen requests do not have an active request
+ : nri.mRequests.get(0);
+ if (req == null) {
+ Log.wtf(TAG, "No request in NRI " + nri);
+ }
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, req);
nri.mPendingIntentSent = true;
sendIntent(nri.mPendingIntent, intent);
}
@@ -8438,7 +8468,7 @@
networkAgent.networkCapabilities.addCapability(NET_CAPABILITY_FOREGROUND);
if (!createNativeNetwork(networkAgent)) return;
- if (networkAgent.supportsUnderlyingNetworks()) {
+ if (networkAgent.propagateUnderlyingCapabilities()) {
// Initialize the network's capabilities to their starting values according to the
// underlying networks. This ensures that the capabilities are correct before
// anything happens to the network.
@@ -9032,8 +9062,7 @@
* the platform. This event will invoke {@link
* IConnectivityDiagnosticsCallback#onNetworkConnectivityReported} for permissioned
* callbacks.
- * obj = Network that was reported on
- * arg1 = boolint for the quality reported
+ * obj = ReportedNetworkConnectivityInfo with info on reported Network connectivity.
*/
private static final int EVENT_NETWORK_CONNECTIVITY_REPORTED = 5;
@@ -9071,7 +9100,7 @@
break;
}
case EVENT_NETWORK_CONNECTIVITY_REPORTED: {
- handleNetworkConnectivityReported((NetworkAgentInfo) msg.obj, toBool(msg.arg1));
+ handleNetworkConnectivityReported((ReportedNetworkConnectivityInfo) msg.obj);
break;
}
default: {
@@ -9141,6 +9170,28 @@
}
}
+ /**
+ * Class used for sending info for a call to {@link #reportNetworkConnectivity()} to {@link
+ * ConnectivityDiagnosticsHandler}.
+ */
+ private static class ReportedNetworkConnectivityInfo {
+ public final boolean hasConnectivity;
+ public final boolean isNetworkRevalidating;
+ public final int reporterUid;
+ @NonNull public final NetworkAgentInfo nai;
+
+ private ReportedNetworkConnectivityInfo(
+ boolean hasConnectivity,
+ boolean isNetworkRevalidating,
+ int reporterUid,
+ @NonNull NetworkAgentInfo nai) {
+ this.hasConnectivity = hasConnectivity;
+ this.isNetworkRevalidating = isNetworkRevalidating;
+ this.reporterUid = reporterUid;
+ this.nai = nai;
+ }
+ }
+
private void handleRegisterConnectivityDiagnosticsCallback(
@NonNull ConnectivityDiagnosticsCallbackInfo cbInfo) {
ensureRunningOnConnectivityServiceThread();
@@ -9248,13 +9299,14 @@
networkCapabilities,
extras);
nai.setConnectivityReport(report);
+
final List<IConnectivityDiagnosticsCallback> results =
- getMatchingPermissionedCallbacks(nai);
+ getMatchingPermissionedCallbacks(nai, Process.INVALID_UID);
for (final IConnectivityDiagnosticsCallback cb : results) {
try {
cb.onConnectivityReportAvailable(report);
} catch (RemoteException ex) {
- loge("Error invoking onConnectivityReport", ex);
+ loge("Error invoking onConnectivityReportAvailable", ex);
}
}
}
@@ -9273,7 +9325,7 @@
networkCapabilities,
extras);
final List<IConnectivityDiagnosticsCallback> results =
- getMatchingPermissionedCallbacks(nai);
+ getMatchingPermissionedCallbacks(nai, Process.INVALID_UID);
for (final IConnectivityDiagnosticsCallback cb : results) {
try {
cb.onDataStallSuspected(report);
@@ -9284,15 +9336,39 @@
}
private void handleNetworkConnectivityReported(
- @NonNull NetworkAgentInfo nai, boolean connectivity) {
+ @NonNull ReportedNetworkConnectivityInfo reportedNetworkConnectivityInfo) {
+ final NetworkAgentInfo nai = reportedNetworkConnectivityInfo.nai;
+ final ConnectivityReport cachedReport = nai.getConnectivityReport();
+
+ // If the Network is being re-validated as a result of this call to
+ // reportNetworkConnectivity(), notify all permissioned callbacks. Otherwise, only notify
+ // permissioned callbacks registered by the reporter.
final List<IConnectivityDiagnosticsCallback> results =
- getMatchingPermissionedCallbacks(nai);
+ getMatchingPermissionedCallbacks(
+ nai,
+ reportedNetworkConnectivityInfo.isNetworkRevalidating
+ ? Process.INVALID_UID
+ : reportedNetworkConnectivityInfo.reporterUid);
+
for (final IConnectivityDiagnosticsCallback cb : results) {
try {
- cb.onNetworkConnectivityReported(nai.network, connectivity);
+ cb.onNetworkConnectivityReported(
+ nai.network, reportedNetworkConnectivityInfo.hasConnectivity);
} catch (RemoteException ex) {
loge("Error invoking onNetworkConnectivityReported", ex);
}
+
+ // If the Network isn't re-validating, also provide the cached report. If there is no
+ // cached report, the Network is still being validated and a report will be sent once
+ // validation is complete. Note that networks which never undergo validation will still
+ // have a cached ConnectivityReport with RESULT_SKIPPED.
+ if (!reportedNetworkConnectivityInfo.isNetworkRevalidating && cachedReport != null) {
+ try {
+ cb.onConnectivityReportAvailable(cachedReport);
+ } catch (RemoteException ex) {
+ loge("Error invoking onConnectivityReportAvailable", ex);
+ }
+ }
}
}
@@ -9305,20 +9381,38 @@
return sanitized;
}
+ /**
+ * Gets a list of ConnectivityDiagnostics callbacks that match the specified Network and uid.
+ *
+ * <p>If Process.INVALID_UID is specified, all matching callbacks will be returned.
+ */
private List<IConnectivityDiagnosticsCallback> getMatchingPermissionedCallbacks(
- @NonNull NetworkAgentInfo nai) {
+ @NonNull NetworkAgentInfo nai, int uid) {
final List<IConnectivityDiagnosticsCallback> results = new ArrayList<>();
for (Entry<IBinder, ConnectivityDiagnosticsCallbackInfo> entry :
mConnectivityDiagnosticsCallbacks.entrySet()) {
final ConnectivityDiagnosticsCallbackInfo cbInfo = entry.getValue();
final NetworkRequestInfo nri = cbInfo.mRequestInfo;
+
// Connectivity Diagnostics rejects multilayer requests at registration hence get(0).
- if (nai.satisfies(nri.mRequests.get(0))) {
- if (checkConnectivityDiagnosticsPermissions(
- nri.mPid, nri.mUid, nai, cbInfo.mCallingPackageName)) {
- results.add(entry.getValue().mCb);
- }
+ if (!nai.satisfies(nri.mRequests.get(0))) {
+ continue;
}
+
+ // UID for this callback must either be:
+ // - INVALID_UID (which sends callbacks to all UIDs), or
+ // - The callback's owner (the owner called reportNetworkConnectivity() and is being
+ // notified as a result)
+ if (uid != Process.INVALID_UID && uid != nri.mUid) {
+ continue;
+ }
+
+ if (!checkConnectivityDiagnosticsPermissions(
+ nri.mPid, nri.mUid, nai, cbInfo.mCallingPackageName)) {
+ continue;
+ }
+
+ results.add(entry.getValue().mCb);
}
return results;
}
@@ -9347,7 +9441,7 @@
private boolean ownsVpnRunningOverNetwork(int uid, Network network) {
for (NetworkAgentInfo virtual : mNetworkAgentInfos) {
- if (virtual.supportsUnderlyingNetworks()
+ if (virtual.propagateUnderlyingCapabilities()
&& virtual.networkCapabilities.getOwnerUid() == uid
&& CollectionUtils.contains(virtual.declaredUnderlyingNetworks, network)) {
return true;
@@ -9946,7 +10040,7 @@
// - The request for the mobile network preferred.
// - The request for the default network, for fallback.
requests.add(createDefaultInternetRequestForTransport(
- TRANSPORT_CELLULAR, NetworkRequest.Type.LISTEN));
+ TRANSPORT_CELLULAR, NetworkRequest.Type.REQUEST));
requests.add(createDefaultInternetRequestForTransport(
TYPE_NONE, NetworkRequest.Type.TRACK_DEFAULT));
final Set<UidRange> ranges = new ArraySet<>();
diff --git a/service/src/com/android/server/connectivity/KeepaliveTracker.java b/service/src/com/android/server/connectivity/KeepaliveTracker.java
index acf39f0..ee1538a 100644
--- a/service/src/com/android/server/connectivity/KeepaliveTracker.java
+++ b/service/src/com/android/server/connectivity/KeepaliveTracker.java
@@ -373,12 +373,10 @@
Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
}
}
- // Ignore the case when the network disconnects immediately after stop() has been
- // called and the keepalive code is waiting for the response from the modem. This
- // might happen when the caller listens for a lower-layer network disconnect
- // callback and stop the keepalive at that time. But the stop() races with the
- // stop() generated in ConnectivityService network disconnection code path.
- if (mStartedState == STOPPING && reason == ERROR_INVALID_NETWORK) return;
+ // To prevent races from re-entrance of stop(), return if the state is already stopping.
+ // This might happen if multiple event sources stop keepalive in a short time. Such as
+ // network disconnect after user calls stop(), or tear down socket after binder died.
+ if (mStartedState == STOPPING) return;
// Store the reason of stopping, and report it after the keepalive is fully stopped.
if (mStopReason != ERROR_STOP_REASON_UNINITIALIZED) {
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index 18becd4..bbf523a 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -157,8 +157,8 @@
@NonNull public NetworkCapabilities networkCapabilities;
@NonNull public final NetworkAgentConfig networkAgentConfig;
- // Underlying networks declared by the agent. Only set if supportsUnderlyingNetworks is true.
- // The networks in this list might be declared by a VPN app using setUnderlyingNetworks and are
+ // Underlying networks declared by the agent.
+ // The networks in this list might be declared by a VPN using setUnderlyingNetworks and are
// not guaranteed to be current or correct, or even to exist.
//
// This array is read and iterated on multiple threads with no locking so its contents must
@@ -168,7 +168,7 @@
// The capabilities originally announced by the NetworkAgent, regardless of any capabilities
// that were added or removed due to this network's underlying networks.
- // Only set if #supportsUnderlyingNetworks is true.
+ // Only set if #propagateUnderlyingCapabilities is true.
public @Nullable NetworkCapabilities declaredCapabilities;
// Indicates if netd has been told to create this Network. From this point on the appropriate
@@ -898,8 +898,11 @@
return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
}
- /** Whether this network might have underlying networks. Currently only true for VPNs. */
- public boolean supportsUnderlyingNetworks() {
+ /**
+ * Whether this network should propagate the capabilities from its underlying networks.
+ * Currently only true for VPNs.
+ */
+ public boolean propagateUnderlyingCapabilities() {
return isVPN();
}
diff --git a/service/src/com/android/server/connectivity/NetworkNotificationManager.java b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
index b57ad5d..ae98d92 100644
--- a/service/src/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
@@ -274,13 +274,17 @@
.setWhen(System.currentTimeMillis())
.setShowWhen(notifyType == NotificationType.NETWORK_SWITCH)
.setSmallIcon(icon)
- .setAutoCancel(true)
+ .setAutoCancel(r.getBoolean(R.bool.config_autoCancelNetworkNotifications))
.setTicker(title)
.setColor(mContext.getColor(android.R.color.system_notification_accent_color))
.setContentTitle(title)
.setContentIntent(intent)
.setLocalOnly(true)
- .setOnlyAlertOnce(true);
+ .setOnlyAlertOnce(true)
+ // TODO: consider having action buttons to disconnect on the sign-in notification
+ // especially if it is ongoing
+ .setOngoing(notifyType == NotificationType.SIGN_IN
+ && r.getBoolean(R.bool.config_ongoingSignInNotification));
if (notifyType == NotificationType.NETWORK_SWITCH) {
builder.setStyle(new Notification.BigTextStyle().bigText(details));
diff --git a/service/src/com/android/server/connectivity/NetworkRanker.java b/service/src/com/android/server/connectivity/NetworkRanker.java
index e839837..d7eb9c8 100644
--- a/service/src/com/android/server/connectivity/NetworkRanker.java
+++ b/service/src/com/android/server/connectivity/NetworkRanker.java
@@ -108,7 +108,58 @@
}
}
- @Nullable private <T extends Scoreable> T getBestNetworkByPolicy(
+ private <T extends Scoreable> boolean isBadWiFi(@NonNull final T candidate) {
+ return candidate.getScore().hasPolicy(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD)
+ && candidate.getCapsNoCopy().hasTransport(TRANSPORT_WIFI);
+ }
+
+ /**
+ * Apply the "yield to bad WiFi" policy.
+ *
+ * This function must run immediately after the validation policy.
+ *
+ * If any of the accepted networks has the "yield to bad WiFi" policy AND there are some
+ * bad WiFis in the rejected list, then move the networks with the policy to the rejected
+ * list. If this leaves no accepted network, then move the bad WiFis back to the accepted list.
+ *
+ * This function returns nothing, but will have updated accepted and rejected in-place.
+ *
+ * @param accepted networks accepted by the validation policy
+ * @param rejected networks rejected by the validation policy
+ */
+ private <T extends Scoreable> void applyYieldToBadWifiPolicy(@NonNull ArrayList<T> accepted,
+ @NonNull ArrayList<T> rejected) {
+ if (!CollectionUtils.any(accepted, n -> n.getScore().hasPolicy(POLICY_YIELD_TO_BAD_WIFI))) {
+ // No network with the policy : do nothing.
+ return;
+ }
+ if (!CollectionUtils.any(rejected, n -> isBadWiFi(n))) {
+ // No bad WiFi : do nothing.
+ return;
+ }
+ if (CollectionUtils.all(accepted, n -> n.getScore().hasPolicy(POLICY_YIELD_TO_BAD_WIFI))) {
+ // All validated networks yield to bad WiFis : keep bad WiFis alongside with the
+ // yielders. This is important because the yielders need to be compared to the bad
+ // wifis by the following policies (e.g. exiting).
+ final ArrayList<T> acceptedYielders = new ArrayList<>(accepted);
+ final ArrayList<T> rejectedWithBadWiFis = new ArrayList<>(rejected);
+ partitionInto(rejectedWithBadWiFis, n -> isBadWiFi(n), accepted, rejected);
+ accepted.addAll(acceptedYielders);
+ return;
+ }
+ // Only some of the validated networks yield to bad WiFi : keep only the ones who don't.
+ final ArrayList<T> acceptedWithYielders = new ArrayList<>(accepted);
+ partitionInto(acceptedWithYielders, n -> !n.getScore().hasPolicy(POLICY_YIELD_TO_BAD_WIFI),
+ accepted, rejected);
+ }
+
+ /**
+ * Get the best network among a list of candidates according to policy.
+ * @param candidates the candidates
+ * @param currentSatisfier the current satisfier, or null if none
+ * @return the best network
+ */
+ @Nullable public <T extends Scoreable> T getBestNetworkByPolicy(
@NonNull List<T> candidates,
@Nullable final T currentSatisfier) {
// Used as working areas.
@@ -148,24 +199,15 @@
if (accepted.size() == 1) return accepted.get(0);
if (accepted.size() > 0 && rejected.size() > 0) candidates = new ArrayList<>(accepted);
- // Yield to bad wifi policy : if any wifi has ever been validated (even if it's now
- // unvalidated), and unless it's been explicitly avoided when bad in UI, then keep only
- // networks that don't yield to such a wifi network.
- final boolean anyWiFiEverValidated = CollectionUtils.any(candidates,
- nai -> nai.getScore().hasPolicy(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD)
- && nai.getCapsNoCopy().hasTransport(TRANSPORT_WIFI));
- if (anyWiFiEverValidated) {
- partitionInto(candidates, nai -> !nai.getScore().hasPolicy(POLICY_YIELD_TO_BAD_WIFI),
- accepted, rejected);
- if (accepted.size() == 1) return accepted.get(0);
- if (accepted.size() > 0 && rejected.size() > 0) candidates = new ArrayList<>(accepted);
- }
-
// If any network is validated (or should be accepted even if it's not validated), then
// don't choose one that isn't.
partitionInto(candidates, nai -> nai.getScore().hasPolicy(POLICY_IS_VALIDATED)
|| nai.getScore().hasPolicy(POLICY_ACCEPT_UNVALIDATED),
accepted, rejected);
+ // Yield to bad wifi policy : if any network has the "yield to bad WiFi" policy and
+ // there are bad WiFis connected, then accept the bad WiFis and reject the networks with
+ // the policy.
+ applyYieldToBadWifiPolicy(accepted, rejected);
if (accepted.size() == 1) return accepted.get(0);
if (accepted.size() > 0 && rejected.size() > 0) candidates = new ArrayList<>(accepted);
@@ -194,16 +236,26 @@
// subscription with the same transport.
partitionInto(candidates, nai -> nai.getScore().hasPolicy(POLICY_TRANSPORT_PRIMARY),
accepted, rejected);
- for (final Scoreable defaultSubNai : accepted) {
- // Remove all networks without the DEFAULT_SUBSCRIPTION policy and the same transports
- // as a network that has it.
- final int[] transports = defaultSubNai.getCapsNoCopy().getTransportTypes();
- candidates.removeIf(nai -> !nai.getScore().hasPolicy(POLICY_TRANSPORT_PRIMARY)
- && Arrays.equals(transports, nai.getCapsNoCopy().getTransportTypes()));
+ if (accepted.size() > 0) {
+ // Some networks are primary for their transport. For each transport, keep only the
+ // primary, but also keep all networks for which there isn't a primary (which are now
+ // in the |rejected| array).
+ // So for each primary network, remove from |rejected| all networks with the same
+ // transports as one of the primary networks. The remaining networks should be accepted.
+ for (final T defaultSubNai : accepted) {
+ final int[] transports = defaultSubNai.getCapsNoCopy().getTransportTypes();
+ rejected.removeIf(
+ nai -> Arrays.equals(transports, nai.getCapsNoCopy().getTransportTypes()));
+ }
+ // Now the |rejected| list contains networks with transports for which there isn't
+ // a primary network. Add them back to the candidates.
+ accepted.addAll(rejected);
+ candidates = new ArrayList<>(accepted);
}
if (1 == candidates.size()) return candidates.get(0);
- // It's guaranteed candidates.size() > 0 because there is at least one with the
- // TRANSPORT_PRIMARY policy and only those without it were removed.
+ // If there were no primary network, then candidates.size() > 0 because it didn't
+ // change from the previous result. If there were, it's guaranteed candidates.size() > 0
+ // because accepted.size() > 0 above.
// If some of the networks have a better transport than others, keep only the ones with
// the best transports.
diff --git a/service/src/com/android/server/connectivity/OsCompat.java b/service/src/com/android/server/connectivity/OsCompat.java
deleted file mode 100644
index 57e3dcd..0000000
--- a/service/src/com/android/server/connectivity/OsCompat.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.connectivity;
-
-import android.system.ErrnoException;
-import android.system.Os;
-
-import java.io.FileDescriptor;
-
-/**
- * Compatibility utility for android.system.Os core platform APIs.
- *
- * Connectivity has access to such APIs, but they are not part of the module_current stubs yet
- * (only core_current). Most stable core platform APIs are included manually in the connectivity
- * build rules, but because Os is also part of the base java SDK that is earlier on the
- * classpath, the extra core platform APIs are not seen.
- *
- * TODO (b/157639992, b/183097033): remove as soon as core_current is part of system_server_current
- * @hide
- */
-public class OsCompat {
- // This value should be correct on all architectures supported by Android, but hardcoding ioctl
- // numbers should be avoided.
- /**
- * @see android.system.OsConstants#TIOCOUTQ
- */
- public static final int TIOCOUTQ = 0x5411;
-
- /**
- * @see android.system.Os#getsockoptInt(FileDescriptor, int, int)
- */
- public static int getsockoptInt(FileDescriptor fd, int level, int option) throws
- ErrnoException {
- try {
- return (int) Os.class.getMethod(
- "getsockoptInt", FileDescriptor.class, int.class, int.class)
- .invoke(null, fd, level, option);
- } catch (ReflectiveOperationException e) {
- if (e.getCause() instanceof ErrnoException) {
- throw (ErrnoException) e.getCause();
- }
- throw new IllegalStateException("Error calling getsockoptInt", e);
- }
- }
-
- /**
- * @see android.system.Os#ioctlInt(FileDescriptor, int)
- */
- public static int ioctlInt(FileDescriptor fd, int cmd) throws
- ErrnoException {
- try {
- return (int) Os.class.getMethod(
- "ioctlInt", FileDescriptor.class, int.class).invoke(null, fd, cmd);
- } catch (ReflectiveOperationException e) {
- if (e.getCause() instanceof ErrnoException) {
- throw (ErrnoException) e.getCause();
- }
- throw new IllegalStateException("Error calling ioctlInt", e);
- }
- }
-}
diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java
old mode 100644
new mode 100755
index 32e06e5..512d767
--- a/service/src/com/android/server/connectivity/PermissionMonitor.java
+++ b/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -121,15 +121,23 @@
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
- final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
- final Uri packageData = intent.getData();
- final String packageName =
- packageData != null ? packageData.getSchemeSpecificPart() : null;
if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+ final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ final Uri packageData = intent.getData();
+ final String packageName =
+ packageData != null ? packageData.getSchemeSpecificPart() : null;
onPackageAdded(packageName, uid);
} else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+ final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ final Uri packageData = intent.getData();
+ final String packageName =
+ packageData != null ? packageData.getSchemeSpecificPart() : null;
onPackageRemoved(packageName, uid);
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+ final String[] pkgList =
+ intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ onExternalApplicationsAvailable(pkgList);
} else {
Log.wtf(TAG, "received unexpected intent: " + action);
}
@@ -194,6 +202,12 @@
mIntentReceiver, intentFilter, null /* broadcastPermission */,
null /* scheduler */);
+ final IntentFilter externalIntentFilter =
+ new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ userAllContext.registerReceiver(
+ mIntentReceiver, externalIntentFilter, null /* broadcastPermission */,
+ null /* scheduler */);
+
// Register UIDS_ALLOWED_ON_RESTRICTED_NETWORKS setting observer
mDeps.registerContentObserver(
userAllContext,
@@ -230,11 +244,11 @@
boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
if (isNetwork || hasRestrictedPermission) {
- Boolean permission = mApps.get(uid);
+ Boolean permission = mApps.get(UserHandle.getAppId(uid));
// If multiple packages share a UID (cf: android:sharedUserId) and ask for different
// permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
if (permission == null || permission == NETWORK) {
- mApps.put(uid, hasRestrictedPermission);
+ mApps.put(UserHandle.getAppId(uid), hasRestrictedPermission);
}
}
@@ -325,14 +339,14 @@
// networks. mApps contains the result of checks for both hasNetworkPermission and
// hasRestrictedNetworkPermission. If uid is in the mApps list that means uid has one of
// permissions at least.
- return mApps.containsKey(uid);
+ return mApps.containsKey(UserHandle.getAppId(uid));
}
/**
* Returns whether the given uid has permission to use restricted networks.
*/
public synchronized boolean hasRestrictedNetworksPermission(int uid) {
- return Boolean.TRUE.equals(mApps.get(uid));
+ return Boolean.TRUE.equals(mApps.get(UserHandle.getAppId(uid)));
}
private void update(Set<UserHandle> users, Map<Integer, Boolean> apps, boolean add) {
@@ -452,12 +466,13 @@
// If multiple packages share a UID (cf: android:sharedUserId) and ask for different
// permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
- final Boolean permission = highestPermissionForUid(mApps.get(uid), packageName);
- if (permission != mApps.get(uid)) {
- mApps.put(uid, permission);
+ final int appId = UserHandle.getAppId(uid);
+ final Boolean permission = highestPermissionForUid(mApps.get(appId), packageName);
+ if (permission != mApps.get(appId)) {
+ mApps.put(appId, permission);
Map<Integer, Boolean> apps = new HashMap<>();
- apps.put(uid, permission);
+ apps.put(appId, permission);
update(mUsers, apps, true);
}
@@ -472,7 +487,7 @@
updateVpnUids(vpn.getKey(), changedUids, true);
}
}
- mAllApps.add(UserHandle.getAppId(uid));
+ mAllApps.add(appId);
}
private Boolean highestUidNetworkPermission(int uid) {
@@ -529,16 +544,17 @@
return;
}
- if (permission == mApps.get(uid)) {
+ final int appId = UserHandle.getAppId(uid);
+ if (permission == mApps.get(appId)) {
// The permissions of this UID have not changed. Nothing to do.
return;
} else if (permission != null) {
- mApps.put(uid, permission);
- apps.put(uid, permission);
+ mApps.put(appId, permission);
+ apps.put(appId, permission);
update(mUsers, apps, true);
} else {
- mApps.remove(uid);
- apps.put(uid, NETWORK); // doesn't matter which permission we pick here
+ mApps.remove(appId);
+ apps.put(appId, NETWORK); // doesn't matter which permission we pick here
update(mUsers, apps, false);
}
}
@@ -653,7 +669,7 @@
*/
private void removeBypassingUids(Set<Integer> uids, int vpnAppUid) {
uids.remove(vpnAppUid);
- uids.removeIf(uid -> mApps.getOrDefault(uid, NETWORK) == SYSTEM);
+ uids.removeIf(uid -> mApps.getOrDefault(UserHandle.getAppId(uid), NETWORK) == SYSTEM);
}
/**
@@ -795,12 +811,13 @@
for (Integer uid : uidsToUpdate) {
final Boolean permission = highestUidNetworkPermission(uid);
+ final int appId = UserHandle.getAppId(uid);
if (null == permission) {
- removedUids.put(uid, NETWORK); // Doesn't matter which permission is set here.
- mApps.remove(uid);
+ removedUids.put(appId, NETWORK); // Doesn't matter which permission is set here.
+ mApps.remove(appId);
} else {
- updatedUids.put(uid, permission);
- mApps.put(uid, permission);
+ updatedUids.put(appId, permission);
+ mApps.put(appId, permission);
}
}
@@ -809,6 +826,21 @@
update(mUsers, removedUids, false /* add */);
}
+ private synchronized void onExternalApplicationsAvailable(String[] pkgList) {
+ if (CollectionUtils.isEmpty(pkgList)) {
+ Log.e(TAG, "No available external application.");
+ return;
+ }
+
+ for (String app : pkgList) {
+ final PackageInfo info = getPackageInfo(app);
+ if (info == null || info.applicationInfo == null) continue;
+
+ final int appId = info.applicationInfo.uid;
+ onPackageAdded(app, appId); // Use onPackageAdded to add package one by one.
+ }
+ }
+
/** Dump info to dumpsys */
public void dump(IndentingPrintWriter pw) {
pw.println("Interface filtering rules:");
diff --git a/service/src/com/android/server/connectivity/TcpKeepaliveController.java b/service/src/com/android/server/connectivity/TcpKeepaliveController.java
index 73f3475..c480594 100644
--- a/service/src/com/android/server/connectivity/TcpKeepaliveController.java
+++ b/service/src/com/android/server/connectivity/TcpKeepaliveController.java
@@ -27,8 +27,7 @@
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IP_TOS;
import static android.system.OsConstants.IP_TTL;
-
-import static com.android.server.connectivity.OsCompat.TIOCOUTQ;
+import static android.system.OsConstants.TIOCOUTQ;
import android.annotation.NonNull;
import android.net.InvalidPacketException;
@@ -176,10 +175,10 @@
}
// Query write sequence number from SEND_QUEUE.
Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_SEND_QUEUE);
- tcpDetails.seq = OsCompat.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
+ tcpDetails.seq = Os.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
// Query read sequence number from RECV_QUEUE.
Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_RECV_QUEUE);
- tcpDetails.ack = OsCompat.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
+ tcpDetails.ack = Os.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
// Switch to NO_QUEUE to prevent illegal socket read/write in repair mode.
Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_NO_QUEUE);
// Finally, check if socket is still idle. TODO : this check needs to move to
@@ -199,9 +198,9 @@
tcpDetails.rcvWndScale = trw.rcvWndScale;
if (tcpDetails.srcAddress.length == 4 /* V4 address length */) {
// Query TOS.
- tcpDetails.tos = OsCompat.getsockoptInt(fd, IPPROTO_IP, IP_TOS);
+ tcpDetails.tos = Os.getsockoptInt(fd, IPPROTO_IP, IP_TOS);
// Query TTL.
- tcpDetails.ttl = OsCompat.getsockoptInt(fd, IPPROTO_IP, IP_TTL);
+ tcpDetails.ttl = Os.getsockoptInt(fd, IPPROTO_IP, IP_TTL);
}
} catch (ErrnoException e) {
Log.e(TAG, "Exception reading TCP state from socket", e);
@@ -306,7 +305,7 @@
private static boolean isReceiveQueueEmpty(FileDescriptor fd)
throws ErrnoException {
- final int result = OsCompat.ioctlInt(fd, SIOCINQ);
+ final int result = Os.ioctlInt(fd, SIOCINQ);
if (result != 0) {
Log.e(TAG, "Read queue has data");
return false;
@@ -316,7 +315,7 @@
private static boolean isSendQueueEmpty(FileDescriptor fd)
throws ErrnoException {
- final int result = OsCompat.ioctlInt(fd, SIOCOUTQ);
+ final int result = Os.ioctlInt(fd, SIOCOUTQ);
if (result != 0) {
Log.e(TAG, "Write queue has data");
return false;
diff --git a/tests/common/Android.bp b/tests/common/Android.bp
index 28339f1..e1fab09 100644
--- a/tests/common/Android.bp
+++ b/tests/common/Android.bp
@@ -43,13 +43,30 @@
],
}
+// Connectivity coverage tests combines Tethering and Connectivity tests, each with their
+// respective jarjar rules applied.
+// Some tests may be duplicated (in particular static lib tests), as they need to be run under both
+// jarjared packages to cover both usages.
+android_library {
+ name: "ConnectivityCoverageTestsLib",
+ min_sdk_version: "30",
+ static_libs: [
+ "FrameworksNetTestsLib",
+ "NetdStaticLibTestsLib",
+ "NetworkStaticLibTestsLib",
+ ],
+ jarjar_rules: ":connectivity-jarjar-rules",
+ manifest: "AndroidManifest_coverage.xml",
+ visibility: ["//visibility:private"],
+}
+
android_test {
name: "ConnectivityCoverageTests",
// Tethering started on SDK 30
min_sdk_version: "30",
// TODO: change to 31 as soon as it is available
target_sdk_version: "30",
- test_suites: ["device-tests", "mts"],
+ test_suites: ["general-tests", "mts"],
defaults: [
"framework-connectivity-test-defaults",
"FrameworksNetTests-jni-defaults",
@@ -62,13 +79,9 @@
// (some tests would fail).
// TODO: consider removing extended mockito usage in tests that use it, for performance
"mockito-target-extended-minus-junit4",
- "FrameworksNetTestsLib",
"modules-utils-native-coverage-listener",
- "NetdStaticLibTestsLib",
- "NetworkStaticLibTestsLib",
- "NetworkStackTestsLib",
- "TetheringTestsLatestSdkLib",
- "TetheringIntegrationTestsLatestSdkLib",
+ "ConnectivityCoverageTestsLib",
+ "TetheringCoverageTestsLib",
],
jni_libs: [
// For mockito extended
diff --git a/tests/common/AndroidTest_Coverage.xml b/tests/common/AndroidTest_Coverage.xml
index 577f36a..7c8e710 100644
--- a/tests/common/AndroidTest_Coverage.xml
+++ b/tests/common/AndroidTest_Coverage.xml
@@ -18,6 +18,7 @@
</target_preparer>
<option name="test-tag" value="ConnectivityCoverageTests" />
+ <option name="config-descriptor:metadata" key="mainline-param" value="CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.connectivity.tests.coverage" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
diff --git a/tests/common/java/android/net/metrics/IpConnectivityLogTest.java b/tests/common/java/android/net/metrics/IpConnectivityLogTest.java
index d4780d3..ab97f2d 100644
--- a/tests/common/java/android/net/metrics/IpConnectivityLogTest.java
+++ b/tests/common/java/android/net/metrics/IpConnectivityLogTest.java
@@ -19,6 +19,8 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static com.android.net.module.util.NetworkCapabilitiesUtils.unpackBits;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.timeout;
@@ -31,8 +33,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.util.BitUtils;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,7 +49,7 @@
@SmallTest
public class IpConnectivityLogTest {
private static final int FAKE_NET_ID = 100;
- private static final int[] FAKE_TRANSPORT_TYPES = BitUtils.unpackBits(TRANSPORT_WIFI);
+ private static final int[] FAKE_TRANSPORT_TYPES = unpackBits(TRANSPORT_WIFI);
private static final long FAKE_TIME_STAMP = System.currentTimeMillis();
private static final String FAKE_INTERFACE_NAME = "test";
private static final IpReachabilityEvent FAKE_EV =
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyManagerTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyManagerTest.java
index ddc5fd4..ad7ec9e 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyManagerTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyManagerTest.java
@@ -23,6 +23,8 @@
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isUidNetworkingBlocked;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isUidRestrictedOnMeteredNetworks;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -79,6 +81,7 @@
assertFalse(isUidNetworkingBlocked(mUid, NON_METERED)); // Match NTWK_ALLOWED_NON_METERED
}
+ @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE})
@Test
public void testIsUidNetworkingBlocked_withSystemUid() throws Exception {
// Refer to NetworkPolicyManagerService#isUidNetworkingBlockedInternal(), this test is to
@@ -103,6 +106,7 @@
}
}
+ @RequiredProperties({DATA_SAVER_MODE})
@Test
public void testIsUidNetworkingBlocked_withDataSaverMode() throws Exception {
// Refer to NetworkPolicyManagerService#isUidNetworkingBlockedInternal(), this test is to
@@ -182,6 +186,7 @@
}
}
+ @RequiredProperties({BATTERY_SAVER_MODE})
@Test
public void testIsUidNetworkingBlocked_withPowerSaverMode() throws Exception {
// Refer to NetworkPolicyManagerService#isUidNetworkingBlockedInternal(), this test is to
@@ -209,6 +214,7 @@
}
}
+ @RequiredProperties({DATA_SAVER_MODE})
@Test
public void testIsUidRestrictedOnMeteredNetworks() throws Exception {
try {
diff --git a/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java b/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
new file mode 100644
index 0000000..86642ea
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2021 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.cts;
+
+import static android.Manifest.permission.UPDATE_DEVICE_STATS;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.testutils.MiscAsserts.assertThrows;
+import static com.android.testutils.TestPermissionUtil.runAsShell;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.cts.util.CtsNetUtils;
+import android.os.BatteryStatsManager;
+import android.os.Build;
+import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.WifiBatteryStats;
+import android.platform.test.annotations.AppModeFull;
+import android.util.Log;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.SkipPresubmit;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+/**
+ * Test for BatteryStatsManager.
+ */
+@RunWith(AndroidJUnit4.class)
+public class BatteryStatsManagerTest{
+ @Rule
+ public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+ private static final String TAG = BatteryStatsManagerTest.class.getSimpleName();
+ private static final String TEST_URL = "https://connectivitycheck.gstatic.com/generate_204";
+ // This value should be the same as BatteryStatsManager.BATTERY_STATUS_DISCHARGING.
+ // TODO: Use the constant once it's available in all branches
+ private static final int BATTERY_STATUS_DISCHARGING = 3;
+
+ private Context mContext;
+ private BatteryStatsManager mBsm;
+ private ConnectivityManager mCm;
+ private CtsNetUtils mCtsNetUtils;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = getContext();
+ mBsm = mContext.getSystemService(BatteryStatsManager.class);
+ mCm = mContext.getSystemService(ConnectivityManager.class);
+ mCtsNetUtils = new CtsNetUtils(mContext);
+ }
+
+ @Test
+ @AppModeFull(reason = "Cannot get CHANGE_NETWORK_STATE to request wifi/cell in instant mode")
+ @SkipPresubmit(reason = "Virtual hardware does not support wifi battery stats")
+ public void testReportNetworkInterfaceForTransports() throws Exception {
+ try {
+ final Network cellNetwork = mCtsNetUtils.connectToCell();
+ final URL url = new URL(TEST_URL);
+
+ // Make sure wifi is disabled.
+ mCtsNetUtils.ensureWifiDisconnected(null /* wifiNetworkToCheck */);
+ // Simulate the device being unplugged from charging.
+ executeShellCommand("dumpsys battery unplug");
+ executeShellCommand("dumpsys battery set status " + BATTERY_STATUS_DISCHARGING);
+ executeShellCommand("dumpsys batterystats enable pretend-screen-off");
+
+ // Get cellular battery stats
+ CellularBatteryStats cellularStatsBefore = runAsShell(UPDATE_DEVICE_STATS,
+ mBsm::getCellularBatteryStats);
+
+ // Generate traffic on cellular network.
+ generateNetworkTraffic(cellNetwork, url);
+
+ // The mobile battery stats are updated when a network stops being the default network.
+ // ConnectivityService will call BatteryStatsManager.reportMobileRadioPowerState when
+ // removing data activity tracking.
+ final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
+
+ // Check cellular battery stats are updated.
+ runAsShell(UPDATE_DEVICE_STATS,
+ () -> assertStatsEventually(mBsm::getCellularBatteryStats,
+ cellularStatsAfter -> cellularBatteryStatsIncreased(
+ cellularStatsBefore, cellularStatsAfter)));
+
+ WifiBatteryStats wifiStatsBefore = runAsShell(UPDATE_DEVICE_STATS,
+ mBsm::getWifiBatteryStats);
+
+ // Generate traffic on wifi network.
+ generateNetworkTraffic(wifiNetwork, url);
+ // Wifi battery stats are updated when wifi on.
+ mCtsNetUtils.toggleWifi();
+
+ // Check wifi battery stats are updated.
+ runAsShell(UPDATE_DEVICE_STATS,
+ () -> assertStatsEventually(mBsm::getWifiBatteryStats,
+ wifiStatsAfter -> wifiBatteryStatsIncreased(wifiStatsBefore,
+ wifiStatsAfter)));
+ } finally {
+ // Reset battery settings.
+ executeShellCommand("dumpsys battery reset");
+ executeShellCommand("dumpsys batterystats disable pretend-screen-off");
+ }
+ }
+
+ @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+ @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+ @Test
+ public void testReportNetworkInterfaceForTransports_throwsSecurityException()
+ throws Exception {
+ Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
+ final String iface = mCm.getLinkProperties(wifiNetwork).getInterfaceName();
+ final int[] transportType = mCm.getNetworkCapabilities(wifiNetwork).getTransportTypes();
+ assertThrows(SecurityException.class,
+ () -> mBsm.reportNetworkInterfaceForTransports(iface, transportType));
+ }
+
+ private void generateNetworkTraffic(Network network, URL url) throws IOException {
+ HttpURLConnection connection = null;
+ try {
+ connection = (HttpURLConnection) network.openConnection(url);
+ assertEquals(204, connection.getResponseCode());
+ } catch (IOException e) {
+ Log.e(TAG, "Generate traffic failed with exception " + e);
+ } finally {
+ if (connection != null) {
+ connection.disconnect();
+ }
+ }
+ }
+
+ private static <T> void assertStatsEventually(Supplier<T> statsGetter,
+ Predicate<T> statsChecker) throws Exception {
+ // Wait for updating mobile/wifi stats, and check stats every 10ms.
+ final int maxTries = 1000;
+ T result = null;
+ for (int i = 1; i <= maxTries; i++) {
+ result = statsGetter.get();
+ if (statsChecker.test(result)) return;
+ Thread.sleep(10);
+ }
+ final String stats = result instanceof CellularBatteryStats
+ ? "Cellular" : "Wifi";
+ fail(stats + " battery stats did not increase.");
+ }
+
+ private static boolean cellularBatteryStatsIncreased(CellularBatteryStats before,
+ CellularBatteryStats after) {
+ return (after.getNumBytesTx() > before.getNumBytesTx())
+ && (after.getNumBytesRx() > before.getNumBytesRx())
+ && (after.getNumPacketsTx() > before.getNumPacketsTx())
+ && (after.getNumPacketsRx() > before.getNumPacketsRx());
+ }
+
+ private static boolean wifiBatteryStatsIncreased(WifiBatteryStats before,
+ WifiBatteryStats after) {
+ return (after.getNumBytesTx() > before.getNumBytesTx())
+ && (after.getNumBytesRx() > before.getNumBytesRx())
+ && (after.getNumPacketsTx() > before.getNumPacketsTx())
+ && (after.getNumPacketsRx() > before.getNumPacketsRx());
+ }
+
+ private static String executeShellCommand(String command) {
+ final String result = runShellCommand(command).trim();
+ Log.d(TAG, "Output of '" + command + "': '" + result + "'");
+ return result;
+ }
+}
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
index ccbdbd3..a40c92d 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
@@ -22,6 +22,7 @@
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_SUCCEEDED_BITMASK;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_VALIDATION_RESULT;
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.NETWORK_VALIDATION_RESULT_SKIPPED;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.NETWORK_VALIDATION_RESULT_VALID;
import static android.net.ConnectivityDiagnosticsManager.DataStallReport;
import static android.net.ConnectivityDiagnosticsManager.DataStallReport.DETECTION_METHOD_DNS_EVENTS;
@@ -78,6 +79,7 @@
import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.util.ArrayUtils;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.ArrayTrackRecord;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -292,7 +294,7 @@
final String interfaceName =
mConnectivityManager.getLinkProperties(network).getInterfaceName();
connDiagsCallback.expectOnConnectivityReportAvailable(
- network, interfaceName, TRANSPORT_CELLULAR);
+ network, interfaceName, TRANSPORT_CELLULAR, NETWORK_VALIDATION_RESULT_VALID);
connDiagsCallback.assertNoCallback();
}
@@ -423,9 +425,10 @@
cb.expectOnNetworkConnectivityReported(mTestNetwork, hasConnectivity);
- // if hasConnectivity does not match the network's known connectivity, it will be
- // revalidated which will trigger another onConnectivityReportAvailable callback.
- if (!hasConnectivity) {
+ // All calls to #onNetworkConnectivityReported are expected to be accompanied by a call to
+ // #onConnectivityReportAvailable for S+ (for R, ConnectivityReports were only sent when the
+ // Network was re-validated - when reported connectivity != known connectivity).
+ if (SdkLevel.isAtLeastS() || !hasConnectivity) {
cb.expectOnConnectivityReportAvailable(mTestNetwork, interfaceName);
}
@@ -479,11 +482,19 @@
public void expectOnConnectivityReportAvailable(
@NonNull Network network, @NonNull String interfaceName) {
- expectOnConnectivityReportAvailable(network, interfaceName, TRANSPORT_TEST);
+ // Test Networks both do not require validation and are not tested for validation. This
+ // results in the validation result being reported as SKIPPED for S+ (for R, the
+ // platform marked these Networks as VALID).
+ final int expectedNetworkValidationResult =
+ SdkLevel.isAtLeastS()
+ ? NETWORK_VALIDATION_RESULT_SKIPPED
+ : NETWORK_VALIDATION_RESULT_VALID;
+ expectOnConnectivityReportAvailable(
+ network, interfaceName, TRANSPORT_TEST, expectedNetworkValidationResult);
}
- public void expectOnConnectivityReportAvailable(
- @NonNull Network network, @NonNull String interfaceName, int transportType) {
+ public void expectOnConnectivityReportAvailable(@NonNull Network network,
+ @NonNull String interfaceName, int transportType, int expectedValidationResult) {
final ConnectivityReport result =
(ConnectivityReport) mHistory.poll(CALLBACK_TIMEOUT_MILLIS, x -> true);
assertEquals(network, result.getNetwork());
@@ -496,9 +507,9 @@
final PersistableBundle extras = result.getAdditionalInfo();
assertTrue(extras.containsKey(KEY_NETWORK_VALIDATION_RESULT));
- final int validationResult = extras.getInt(KEY_NETWORK_VALIDATION_RESULT);
- assertEquals("Network validation result is not 'valid'",
- NETWORK_VALIDATION_RESULT_VALID, validationResult);
+ final int actualValidationResult = extras.getInt(KEY_NETWORK_VALIDATION_RESULT);
+ assertEquals("Network validation result is incorrect",
+ expectedValidationResult, actualValidationResult);
assertTrue(extras.containsKey(KEY_NETWORK_PROBES_SUCCEEDED_BITMASK));
final int probesSucceeded = extras.getInt(KEY_NETWORK_VALIDATION_RESULT);
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 3220565..527897e 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -55,16 +55,12 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
-import static android.net.TetheringManager.TETHERING_WIFI;
-import static android.net.TetheringManager.TetheringRequest;
import static android.net.cts.util.CtsNetUtils.ConnectivityActionReceiver;
import static android.net.cts.util.CtsNetUtils.HTTP_PORT;
import static android.net.cts.util.CtsNetUtils.NETWORK_CALLBACK_ACTION;
import static android.net.cts.util.CtsNetUtils.TEST_HOST;
import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
-import static android.net.cts.util.CtsTetheringUtils.StartTetheringCallback;
import static android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback;
-import static android.net.cts.util.CtsTetheringUtils.isWifiTetheringSupported;
import static android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTPS_URL;
import static android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTP_URL;
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
@@ -133,9 +129,9 @@
import android.net.TelephonyNetworkSpecifier;
import android.net.TestNetworkInterface;
import android.net.TestNetworkManager;
-import android.net.TetheringManager;
import android.net.Uri;
import android.net.cts.util.CtsNetUtils;
+import android.net.cts.util.CtsTetheringUtils;
import android.net.util.KeepaliveUtils;
import android.net.wifi.WifiManager;
import android.os.Binder;
@@ -285,7 +281,6 @@
private final ArraySet<Integer> mNetworkTypes = new ArraySet<>();
private UiAutomation mUiAutomation;
private CtsNetUtils mCtsNetUtils;
- private TetheringManager mTm;
// Used for cleanup purposes.
private final List<Range<Integer>> mVpnRequiredUidRanges = new ArrayList<>();
@@ -301,7 +296,6 @@
mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
mPackageManager = mContext.getPackageManager();
mCtsNetUtils = new CtsNetUtils(mContext);
- mTm = mContext.getSystemService(TetheringManager.class);
if (DevSdkIgnoreRuleKt.isDevSdkInRange(null /* minExclusive */,
Build.VERSION_CODES.R /* maxInclusive */)) {
@@ -506,6 +500,7 @@
@Test
public void testGetAllNetworkStateSnapshots()
throws InterruptedException {
+ assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
// Make sure cell is active to retrieve IMSI for verification in later step.
final Network cellNetwork = mCtsNetUtils.connectToCell();
final String subscriberId = getSubscriberIdForCellNetwork(cellNetwork);
@@ -536,8 +531,10 @@
Objects.requireNonNull(mCm.getNetworkCapabilities(network));
// Redact specifier of the capabilities of the snapshot before comparing since
// the result returned from getNetworkCapabilities always get redacted.
+ final NetworkSpecifier snapshotCapSpecifier =
+ snapshot.getNetworkCapabilities().getNetworkSpecifier();
final NetworkSpecifier redactedSnapshotCapSpecifier =
- snapshot.getNetworkCapabilities().getNetworkSpecifier().redact();
+ snapshotCapSpecifier == null ? null : snapshotCapSpecifier.redact();
assertEquals("", caps.describeImmutableDifferences(
snapshot.getNetworkCapabilities()
.setNetworkSpecifier(redactedSnapshotCapSpecifier)));
@@ -734,6 +731,8 @@
.isPrivateDnsBroken()) && networkForPrivateDns.equals(entry.getNetwork()));
} finally {
mCtsNetUtils.restorePrivateDnsSetting();
+ // Toggle wifi to make sure it is re-validated
+ reconnectWifi();
}
}
@@ -939,8 +938,8 @@
// noticeably flaky.
Thread.sleep(NO_CALLBACK_TIMEOUT_MS);
- // TODO: BUG (b/189868426): this should also apply to listens
- if (!useListen) {
+ // For R- frameworks, listens will receive duplicated callbacks. See b/189868426.
+ if (isAtLeastS() || !useListen) {
assertEquals("PendingIntent should only be received once", 1, receivedCount.get());
}
} finally {
@@ -953,12 +952,10 @@
private void assertPendingIntentRequestMatches(NetworkRequest broadcasted, NetworkRequest filed,
boolean useListen) {
- // TODO: BUG (b/191713869): on S the request extra is null on listens
- if (isAtLeastS() && useListen && broadcasted == null) return;
assertArrayEquals(filed.networkCapabilities.getCapabilities(),
broadcasted.networkCapabilities.getCapabilities());
- // TODO: BUG (b/189868426): this should also apply to listens
- if (useListen) return;
+ // For R- frameworks, listens will receive duplicated callbacks. See b/189868426.
+ if (!isAtLeastS() && useListen) return;
assertArrayEquals(filed.networkCapabilities.getTransportTypes(),
broadcasted.networkCapabilities.getTransportTypes());
}
@@ -1082,7 +1079,7 @@
final Matcher m = Pattern.compile("^" + ssid + ";(true|false|none)$",
Pattern.MULTILINE | Pattern.UNIX_LINES).matcher(policyString);
if (!m.find()) {
- fail("Unexpected format from cmd netpolicy");
+ fail("Unexpected format from cmd netpolicy, policyString = " + policyString);
}
return m.group(1);
}
@@ -2232,14 +2229,15 @@
ConnectivitySettingsManager.getNetworkAvoidBadWifi(mContext);
final int curPrivateDnsMode = ConnectivitySettingsManager.getPrivateDnsMode(mContext);
- final TestTetheringEventCallback tetherEventCallback = new TestTetheringEventCallback();
+ TestTetheringEventCallback tetherEventCallback = null;
+ final CtsTetheringUtils tetherUtils = new CtsTetheringUtils(mContext);
try {
- mTm.registerTetheringEventCallback(c -> c.run() /* executor */, tetherEventCallback);
+ tetherEventCallback = tetherUtils.registerTetheringEventCallback();
// Adopt for NETWORK_SETTINGS permission.
mUiAutomation.adoptShellPermissionIdentity();
// start tethering
tetherEventCallback.assumeWifiTetheringSupported(mContext);
- startWifiTethering(tetherEventCallback);
+ tetherUtils.startWifiTethering(tetherEventCallback);
// Update setting to verify the behavior.
mCm.setAirplaneMode(true);
ConnectivitySettingsManager.setPrivateDnsMode(mContext,
@@ -2260,8 +2258,10 @@
mCm.setAirplaneMode(false);
ConnectivitySettingsManager.setNetworkAvoidBadWifi(mContext, curAvoidBadWifi);
ConnectivitySettingsManager.setPrivateDnsMode(mContext, curPrivateDnsMode);
- mTm.unregisterTetheringEventCallback(tetherEventCallback);
- mTm.stopAllTethering();
+ if (tetherEventCallback != null) {
+ tetherUtils.unregisterTetheringEventCallback(tetherEventCallback);
+ }
+ tetherUtils.stopAllTethering();
mUiAutomation.dropShellPermissionIdentity();
}
}
@@ -2308,19 +2308,6 @@
ConnectivitySettingsManager.getNetworkAvoidBadWifi(mContext));
}
- private void startWifiTethering(final TestTetheringEventCallback callback) throws Exception {
- if (!isWifiTetheringSupported(mContext, callback)) return;
-
- final List<String> wifiRegexs =
- callback.getTetheringInterfaceRegexps().getTetherableWifiRegexs();
- final StartTetheringCallback startTetheringCallback = new StartTetheringCallback();
- final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI)
- .setShouldShowEntitlementUi(false).build();
- mTm.startTethering(request, c -> c.run() /* executor */, startTetheringCallback);
- startTetheringCallback.verifyTetheringStarted();
- callback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI);
- }
-
/**
* Verify that per-app OEM network preference functions as expected for network preference TEST.
* For specified apps, validate networks are prioritized in order: unmetered, TEST transport,
@@ -2889,6 +2876,10 @@
public void testUidsAllowedOnRestrictedNetworks() throws Exception {
assumeTrue(TestUtils.shouldTestSApis());
+ // TODO (b/175199465): figure out a reasonable permission check for
+ // setUidsAllowedOnRestrictedNetworks that allows tests but not system-external callers.
+ assumeTrue(Build.isDebuggable());
+
final int uid = mPackageManager.getPackageUid(mContext.getPackageName(), 0 /* flag */);
final Set<Integer> originalUidsAllowedOnRestrictedNetworks =
ConnectivitySettingsManager.getUidsAllowedOnRestrictedNetworks(mContext);
@@ -2896,8 +2887,9 @@
// because it has been just installed to device. In case the uid is existed in setting
// mistakenly, try to remove the uid and set correct uids to setting.
originalUidsAllowedOnRestrictedNetworks.remove(uid);
- ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(mContext,
- originalUidsAllowedOnRestrictedNetworks);
+ runWithShellPermissionIdentity(() ->
+ ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(
+ mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
final Handler h = new Handler(Looper.getMainLooper());
final TestableNetworkCallback testNetworkCb = new TestableNetworkCallback();
@@ -2944,8 +2936,9 @@
final Set<Integer> newUidsAllowedOnRestrictedNetworks =
new ArraySet<>(originalUidsAllowedOnRestrictedNetworks);
newUidsAllowedOnRestrictedNetworks.add(uid);
- ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(mContext,
- newUidsAllowedOnRestrictedNetworks);
+ runWithShellPermissionIdentity(() ->
+ ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(
+ mContext, newUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
// Wait a while for sending allowed uids on the restricted network to netd.
// TODD: Have a significant signal to know the uids has been send to netd.
assertBindSocketToNetworkSuccess(network);
@@ -2954,8 +2947,9 @@
agent.unregister();
// Restore setting.
- ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(mContext,
- originalUidsAllowedOnRestrictedNetworks);
+ runWithShellPermissionIdentity(() ->
+ ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(
+ mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
}
}
}
diff --git a/tests/cts/net/src/android/net/cts/IpSecBaseTest.java b/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
index c54ee91..7f710d7 100644
--- a/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
@@ -23,6 +23,7 @@
import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA384;
import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA512;
import static android.net.IpSecAlgorithm.CRYPT_AES_CBC;
+import static android.system.OsConstants.FIONREAD;
import static org.junit.Assert.assertArrayEquals;
@@ -32,8 +33,10 @@
import android.net.IpSecManager;
import android.net.IpSecTransform;
import android.platform.test.annotations.AppModeFull;
+import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
+import android.system.StructTimeval;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -46,15 +49,21 @@
import org.junit.runner.RunWith;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
-import java.net.ServerSocket;
import java.net.Socket;
+import java.net.SocketAddress;
import java.net.SocketException;
+import java.net.SocketImpl;
+import java.net.SocketOptions;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@@ -232,6 +241,12 @@
public NativeTcpSocket(FileDescriptor fd) {
super(fd);
}
+
+ public JavaTcpSocket acceptToJavaSocket() throws Exception {
+ InetSocketAddress peer = new InetSocketAddress(0);
+ FileDescriptor newFd = Os.accept(mFd, peer);
+ return new JavaTcpSocket(new AcceptedTcpFileDescriptorSocket(newFd, peer, getPort()));
+ }
}
public static class NativeUdpSocket extends NativeSocket implements GenericUdpSocket {
@@ -357,6 +372,137 @@
}
}
+ private static class AcceptedTcpFileDescriptorSocket extends Socket {
+
+ AcceptedTcpFileDescriptorSocket(FileDescriptor fd, InetSocketAddress remote,
+ int localPort) throws IOException {
+ super(new FileDescriptorSocketImpl(fd, remote, localPort));
+ connect(remote);
+ }
+
+ private static class FileDescriptorSocketImpl extends SocketImpl {
+
+ private FileDescriptorSocketImpl(FileDescriptor fd, InetSocketAddress remote,
+ int localPort) {
+ this.fd = fd;
+ this.address = remote.getAddress();
+ this.port = remote.getPort();
+ this.localport = localPort;
+ }
+
+ @Override
+ protected void create(boolean stream) throws IOException {
+ // The socket has been created.
+ }
+
+ @Override
+ protected void connect(String host, int port) throws IOException {
+ // The socket has connected.
+ }
+
+ @Override
+ protected void connect(InetAddress address, int port) throws IOException {
+ // The socket has connected.
+ }
+
+ @Override
+ protected void connect(SocketAddress address, int timeout) throws IOException {
+ // The socket has connected.
+ }
+
+ @Override
+ protected void bind(InetAddress host, int port) throws IOException {
+ // The socket is bounded.
+ }
+
+ @Override
+ protected void listen(int backlog) throws IOException {
+ throw new UnsupportedOperationException("listen");
+ }
+
+ @Override
+ protected void accept(SocketImpl s) throws IOException {
+ throw new UnsupportedOperationException("accept");
+ }
+
+ @Override
+ protected InputStream getInputStream() throws IOException {
+ return new FileInputStream(fd);
+ }
+
+ @Override
+ protected OutputStream getOutputStream() throws IOException {
+ return new FileOutputStream(fd);
+ }
+
+ @Override
+ protected int available() throws IOException {
+ try {
+ return Os.ioctlInt(fd, FIONREAD);
+ } catch (ErrnoException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ protected void close() throws IOException {
+ try {
+ Os.close(fd);
+ } catch (ErrnoException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ protected void sendUrgentData(int data) throws IOException {
+ throw new UnsupportedOperationException("sendUrgentData");
+ }
+
+ @Override
+ public void setOption(int optID, Object value) throws SocketException {
+ try {
+ setOptionInternal(optID, value);
+ } catch (ErrnoException e) {
+ throw new SocketException(e.getMessage());
+ }
+ }
+
+ private void setOptionInternal(int optID, Object value) throws ErrnoException,
+ SocketException {
+ switch(optID) {
+ case SocketOptions.SO_TIMEOUT:
+ int millis = (Integer) value;
+ StructTimeval tv = StructTimeval.fromMillis(millis);
+ Os.setsockoptTimeval(fd, OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO,
+ tv);
+ return;
+ default:
+ throw new SocketException("Unknown socket option: " + optID);
+ }
+ }
+
+ @Override
+ public Object getOption(int optID) throws SocketException {
+ try {
+ return getOptionInternal(optID);
+ } catch (ErrnoException e) {
+ throw new SocketException(e.getMessage());
+ }
+ }
+
+ private Object getOptionInternal(int optID) throws ErrnoException, SocketException {
+ switch (optID) {
+ case SocketOptions.SO_LINGER:
+ // Returns an arbitrary value because IpSecManager doesn't actually
+ // use this value.
+ return 10;
+ default:
+ throw new SocketException("Unknown socket option: " + optID);
+ }
+ }
+ }
+ }
+
public static class SocketPair<T> {
public final T mLeftSock;
public final T mRightSock;
@@ -441,8 +587,6 @@
public static SocketPair<JavaTcpSocket> getJavaTcpSocketPair(
InetAddress localAddr, IpSecManager ism, IpSecTransform transform) throws Exception {
JavaTcpSocket clientSock = new JavaTcpSocket(new Socket());
- ServerSocket serverSocket = new ServerSocket();
- serverSocket.bind(new InetSocketAddress(localAddr, 0));
// While technically the client socket does not need to be bound, the OpenJDK implementation
// of Socket only allocates an FD when bind() or connect() or other similar methods are
@@ -451,16 +595,19 @@
clientSock.mSocket.bind(new InetSocketAddress(localAddr, 0));
// IpSecService doesn't support serverSockets at the moment; workaround using FD
- FileDescriptor serverFd = serverSocket.getImpl().getFD$();
+ NativeTcpSocket server = new NativeTcpSocket(
+ Os.socket(getDomain(localAddr), OsConstants.SOCK_STREAM, OsConstants.IPPROTO_TCP));
+ Os.bind(server.mFd, localAddr, 0);
- applyTransformBidirectionally(ism, transform, new NativeTcpSocket(serverFd));
+ applyTransformBidirectionally(ism, transform, server);
applyTransformBidirectionally(ism, transform, clientSock);
- clientSock.mSocket.connect(new InetSocketAddress(localAddr, serverSocket.getLocalPort()));
- JavaTcpSocket acceptedSock = new JavaTcpSocket(serverSocket.accept());
+ Os.listen(server.mFd, 10 /* backlog */);
+ clientSock.mSocket.connect(new InetSocketAddress(localAddr, server.getPort()));
+ JavaTcpSocket acceptedSock = server.acceptToJavaSocket();
applyTransformBidirectionally(ism, transform, acceptedSock);
- serverSocket.close();
+ server.close();
return new SocketPair<>(clientSock, acceptedSock);
}
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index ccc9416..7c380e3 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -101,6 +101,7 @@
import com.android.testutils.TestableNetworkCallback
import org.junit.After
import org.junit.Assert.assertArrayEquals
+import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -1034,6 +1035,9 @@
@Test
fun testQosCallbackRegisterWithUnregister() {
+ // Instant apps can't bind sockets to localhost
+ // TODO: use @AppModeFull when supported by DevSdkIgnoreRunner
+ assumeFalse(realContext.packageManager.isInstantApp())
val (agent, socket) = setupForQosCallbackTesting()
val qosCallback = TestableQosCallback()
@@ -1060,6 +1064,9 @@
@Test
fun testQosCallbackOnQosSession() {
+ // Instant apps can't bind sockets to localhost
+ // TODO: use @AppModeFull when supported by DevSdkIgnoreRunner
+ assumeFalse(realContext.packageManager.isInstantApp())
val (agent, socket) = setupForQosCallbackTesting()
val qosCallback = TestableQosCallback()
Executors.newSingleThreadExecutor().let { executor ->
@@ -1104,6 +1111,9 @@
@Test
fun testQosCallbackOnError() {
+ // Instant apps can't bind sockets to localhost
+ // TODO: use @AppModeFull when supported by DevSdkIgnoreRunner
+ assumeFalse(realContext.packageManager.isInstantApp())
val (agent, socket) = setupForQosCallbackTesting()
val qosCallback = TestableQosCallback()
Executors.newSingleThreadExecutor().let { executor ->
@@ -1142,6 +1152,9 @@
@Test
fun testQosCallbackIdsAreMappedCorrectly() {
+ // Instant apps can't bind sockets to localhost
+ // TODO: use @AppModeFull when supported by DevSdkIgnoreRunner
+ assumeFalse(realContext.packageManager.isInstantApp())
val (agent, socket) = setupForQosCallbackTesting()
val qosCallback1 = TestableQosCallback()
val qosCallback2 = TestableQosCallback()
@@ -1182,6 +1195,9 @@
@Test
fun testQosCallbackWhenNetworkReleased() {
+ // Instant apps can't bind sockets to localhost
+ // TODO: use @AppModeFull when supported by DevSdkIgnoreRunner
+ assumeFalse(realContext.packageManager.isInstantApp())
val (agent, socket) = setupForQosCallbackTesting()
Executors.newSingleThreadExecutor().let { executor ->
try {
diff --git a/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt b/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
new file mode 100644
index 0000000..8f17199
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/NetworkScoreTest.kt
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2021 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.cts
+
+import android.Manifest.permission.MANAGE_TEST_NETWORKS
+import android.content.Context
+import android.net.ConnectivityManager
+import android.net.LinkProperties
+import android.net.NetworkAgent
+import android.net.NetworkAgentConfig
+import android.net.NetworkCapabilities
+import android.net.NetworkProvider
+import android.net.NetworkRequest
+import android.net.NetworkScore
+import android.net.VpnManager
+import android.net.VpnTransportInfo
+import android.os.Build
+import android.os.Handler
+import android.os.HandlerThread
+import androidx.test.InstrumentationRegistry
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import com.android.testutils.TestableNetworkCallback
+import com.android.testutils.TestableNetworkCallback.HasNetwork
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+// This test doesn't really have a constraint on how fast the methods should return. If it's
+// going to fail, it will simply wait forever, so setting a high timeout lowers the flake ratio
+// without affecting the run time of successful runs. Thus, set a very high timeout.
+private const val TIMEOUT_MS = 30_000L
+// When waiting for a NetworkCallback to determine there was no timeout, waiting is the
+// only possible thing (the relevant handler is the one in the real ConnectivityService,
+// and then there is the Binder call), so have a short timeout for this as it will be
+// exhausted every time.
+private const val NO_CALLBACK_TIMEOUT = 200L
+
+private val testContext: Context
+ get() = InstrumentationRegistry.getContext()
+
+private fun score(exiting: Boolean = false, primary: Boolean = false) =
+ NetworkScore.Builder().setExiting(exiting).setTransportPrimary(primary)
+ // TODO : have a constant KEEP_CONNECTED_FOR_TEST ?
+ .setKeepConnectedReason(NetworkScore.KEEP_CONNECTED_FOR_HANDOVER)
+ .build()
+
+@IgnoreUpTo(Build.VERSION_CODES.R)
+@RunWith(DevSdkIgnoreRunner::class)
+class NetworkScoreTest {
+ private val mCm = testContext.getSystemService(ConnectivityManager::class.java)
+ private val mHandlerThread = HandlerThread("${javaClass.simpleName} handler thread")
+ private val mHandler by lazy { Handler(mHandlerThread.looper) }
+ private val agentsToCleanUp = mutableListOf<NetworkAgent>()
+ private val callbacksToCleanUp = mutableListOf<TestableNetworkCallback>()
+
+ @Before
+ fun setUp() {
+ mHandlerThread.start()
+ }
+
+ @After
+ fun tearDown() {
+ agentsToCleanUp.forEach { it.unregister() }
+ mHandlerThread.quitSafely()
+ callbacksToCleanUp.forEach { mCm.unregisterNetworkCallback(it) }
+ }
+
+ // Returns a networkCallback that sends onAvailable on the best network with TRANSPORT_TEST.
+ private fun makeTestNetworkCallback() = TestableNetworkCallback(TIMEOUT_MS).also { cb ->
+ mCm.registerBestMatchingNetworkCallback(NetworkRequest.Builder().clearCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_TEST).build(), cb, mHandler)
+ callbacksToCleanUp.add(cb)
+ }
+
+ // TestNetworkCallback is made to interact with a wrapper of NetworkAgent, because it's
+ // made for ConnectivityServiceTest.
+ // TODO : have TestNetworkCallback work for NetworkAgent too and remove this class.
+ private class AgentWrapper(val agent: NetworkAgent) : HasNetwork {
+ override val network = agent.network
+ fun sendNetworkScore(s: NetworkScore) = agent.sendNetworkScore(s)
+ }
+
+ private fun createTestNetworkAgent(
+ // The network always has TRANSPORT_TEST, plus optional transports
+ optionalTransports: IntArray = IntArray(size = 0),
+ everUserSelected: Boolean = false,
+ acceptUnvalidated: Boolean = false,
+ isExiting: Boolean = false,
+ isPrimary: Boolean = false
+ ): AgentWrapper {
+ val nc = NetworkCapabilities.Builder().apply {
+ addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+ optionalTransports.forEach { addTransportType(it) }
+ // Add capabilities that are common, just for realism. It's not strictly necessary
+ addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
+ addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
+ // Remove capabilities that a test network agent shouldn't have and that are not
+ // needed for the purposes of this test.
+ removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ if (optionalTransports.contains(NetworkCapabilities.TRANSPORT_VPN)) {
+ addTransportType(NetworkCapabilities.TRANSPORT_VPN)
+ removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+ setTransportInfo(VpnTransportInfo(VpnManager.TYPE_VPN_SERVICE, null))
+ }
+ addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
+ }.build()
+ val config = NetworkAgentConfig.Builder()
+ .setExplicitlySelected(everUserSelected)
+ .setUnvalidatedConnectivityAcceptable(acceptUnvalidated)
+ .build()
+ val score = score(exiting = isExiting, primary = isPrimary)
+ val context = testContext
+ val looper = mHandlerThread.looper
+ val agent = object : NetworkAgent(context, looper, "NetworkScore test agent", nc,
+ LinkProperties(), score, config, NetworkProvider(context, looper,
+ "NetworkScore test provider")) {}.also {
+ agentsToCleanUp.add(it)
+ }
+ runWithShellPermissionIdentity({ agent.register() }, MANAGE_TEST_NETWORKS)
+ agent.markConnected()
+ return AgentWrapper(agent)
+ }
+
+ @Test
+ fun testExitingLosesAndOldSatisfierWins() {
+ val cb = makeTestNetworkCallback()
+ val agent1 = createTestNetworkAgent()
+ cb.expectAvailableThenValidatedCallbacks(agent1)
+ val agent2 = createTestNetworkAgent()
+ // Because the existing network must win, the callback stays on agent1.
+ cb.assertNoCallback(NO_CALLBACK_TIMEOUT)
+ agent1.sendNetworkScore(score(exiting = true))
+ // Now that agent1 is exiting, the callback is satisfied by agent2.
+ cb.expectAvailableCallbacks(agent2.network)
+ agent1.sendNetworkScore(score(exiting = false))
+ // Agent1 is no longer exiting, but agent2 is the current satisfier.
+ cb.assertNoCallback(NO_CALLBACK_TIMEOUT)
+ }
+
+ @Test
+ fun testVpnWins() {
+ val cb = makeTestNetworkCallback()
+ val agent1 = createTestNetworkAgent()
+ cb.expectAvailableThenValidatedCallbacks(agent1.network)
+ val agent2 = createTestNetworkAgent(intArrayOf(NetworkCapabilities.TRANSPORT_VPN))
+ // VPN wins out against agent1 even before it's validated (hence the "then validated",
+ // because it becomes the best network for this callback before it validates)
+ cb.expectAvailableThenValidatedCallbacks(agent2.network)
+ }
+
+ @Test
+ fun testEverUserSelectedAcceptUnvalidatedWins() {
+ val cb = makeTestNetworkCallback()
+ val agent1 = createTestNetworkAgent()
+ cb.expectAvailableThenValidatedCallbacks(agent1.network)
+ val agent2 = createTestNetworkAgent(everUserSelected = true, acceptUnvalidated = true)
+ // agent2 wins out against agent1 even before it's validated, because user-selected and
+ // accept unvalidated networks should win against even networks that are validated.
+ cb.expectAvailableThenValidatedCallbacks(agent2.network)
+ }
+
+ @Test
+ fun testPreferredTransportOrder() {
+ val cb = makeTestNetworkCallback()
+ val agentCell = createTestNetworkAgent(intArrayOf(NetworkCapabilities.TRANSPORT_CELLULAR))
+ cb.expectAvailableThenValidatedCallbacks(agentCell.network)
+ val agentWifi = createTestNetworkAgent(intArrayOf(NetworkCapabilities.TRANSPORT_WIFI))
+ // In the absence of other discriminating factors, agentWifi wins against agentCell because
+ // of its better transport, but only after it validates.
+ cb.expectAvailableDoubleValidatedCallbacks(agentWifi)
+ val agentEth = createTestNetworkAgent(intArrayOf(NetworkCapabilities.TRANSPORT_ETHERNET))
+ // Likewise, agentEth wins against agentWifi after validation because of its better
+ // transport.
+ cb.expectAvailableCallbacksValidated(agentEth)
+ }
+
+ @Test
+ fun testTransportPrimary() {
+ val cb = makeTestNetworkCallback()
+ val agent1 = createTestNetworkAgent()
+ cb.expectAvailableThenValidatedCallbacks(agent1)
+ val agent2 = createTestNetworkAgent()
+ // Because the existing network must win, the callback stays on agent1.
+ cb.assertNoCallback(NO_CALLBACK_TIMEOUT)
+ agent2.sendNetworkScore(score(primary = true))
+ // Now that agent2 is primary, the callback is satisfied by agent2.
+ cb.expectAvailableCallbacks(agent2.network)
+ agent1.sendNetworkScore(score(primary = true))
+ // Agent1 is primary too, but agent2 is the current satisfier
+ cb.assertNoCallback(NO_CALLBACK_TIMEOUT)
+ agent2.sendNetworkScore(score(primary = false))
+ // Now agent1 is primary and agent2 isn't
+ cb.expectAvailableCallbacks(agent1.network)
+ }
+
+ // TODO (b/187929636) : add a test making sure that validated networks win over unvalidated
+ // ones. Right now this is not possible because this CTS can't directly manipulate the
+ // validation state of a network.
+}
diff --git a/tests/cts/net/src/android/net/cts/PacProxyManagerTest.java b/tests/cts/net/src/android/net/cts/PacProxyManagerTest.java
index 7d5e9ff..a20f1cc 100644
--- a/tests/cts/net/src/android/net/cts/PacProxyManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/PacProxyManagerTest.java
@@ -35,6 +35,7 @@
import android.net.ProxyInfo;
import android.net.Uri;
import android.os.Build;
+import android.platform.test.annotations.AppModeFull;
import android.util.Log;
import android.util.Range;
@@ -145,6 +146,7 @@
}
}
+ @AppModeFull(reason = "Instant apps can't bind sockets to localhost for a test proxy server")
@Test
public void testSetCurrentProxyScriptUrl() throws Exception {
// Register a PacProxyInstalledListener
diff --git a/tests/cts/net/src/android/net/ipv6/cts/PingTest.java b/tests/cts/net/src/android/net/ipv6/cts/PingTest.java
index 146fd83..8665fc8 100644
--- a/tests/cts/net/src/android/net/ipv6/cts/PingTest.java
+++ b/tests/cts/net/src/android/net/ipv6/cts/PingTest.java
@@ -115,7 +115,7 @@
// Receive the response.
if (useRecvfrom) {
- InetSocketAddress from = new InetSocketAddress();
+ InetSocketAddress from = new InetSocketAddress(0);
bytesRead = Os.recvfrom(s, responseBuffer, 0, from);
// Check the source address and scope ID.
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
index c220326..8c5372d 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
@@ -440,12 +440,6 @@
return callback.getTetheringInterfaceRegexps().getTetherableWifiRegexs();
}
- public static boolean isWifiTetheringSupported(final Context ctx,
- final TestTetheringEventCallback callback) throws Exception {
- return !getWifiTetherableInterfaceRegexps(callback).isEmpty()
- && isPortableHotspotSupported(ctx);
- }
-
/* Returns if wifi supports hotspot. */
private static boolean isPortableHotspotSupported(final Context ctx) throws Exception {
final PackageManager pm = ctx.getPackageManager();
@@ -522,4 +516,8 @@
callback.expectNoTetheringActive();
callback.expectOneOfOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED);
}
+
+ public void stopAllTethering() {
+ mTm.stopAllTethering();
+ }
}
diff --git a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
index 0a5e506..bd1b74a 100644
--- a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
+++ b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
@@ -361,6 +361,7 @@
@Test
public void testRequestLatestEntitlementResult() throws Exception {
assumeTrue(mTM.isTetheringSupported());
+ assumeTrue(mPm.hasSystemFeature(FEATURE_TELEPHONY));
// Verify that requestLatestTetheringEntitlementResult() can get entitlement
// result(TETHER_ERROR_ENTITLEMENT_UNKNOWN due to invalid downstream type) via listener.
assertEntitlementResult(listener -> mTM.requestLatestTetheringEntitlementResult(
diff --git a/tests/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/integration/util/com/android/server/NetworkAgentWrapper.java
index 17db179..95ea401 100644
--- a/tests/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -55,6 +55,7 @@
import com.android.testutils.HandlerUtils;
import com.android.testutils.TestableNetworkCallback;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -255,6 +256,15 @@
}
}
+ public void setUnderlyingNetworks(List<Network> underlyingNetworks) {
+ mNetworkAgent.setUnderlyingNetworks(underlyingNetworks);
+ }
+
+ public void setOwnerUid(int uid) {
+ mNetworkCapabilities.setOwnerUid(uid);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
public void connect() {
if (!mConnected.compareAndSet(false /* expect */, true /* update */)) {
// compareAndSet returns false when the value couldn't be updated because it did not
diff --git a/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java b/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
index 6bd2bd5..08a3007 100644
--- a/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
+++ b/tests/unit/java/android/app/usage/NetworkStatsManagerTest.java
@@ -36,11 +36,14 @@
import android.net.NetworkStats.Entry;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
+import android.os.Build;
import android.os.RemoteException;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -49,8 +52,9 @@
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class NetworkStatsManagerTest {
private static final String TEST_SUBSCRIBER_ID = "subid";
diff --git a/tests/unit/java/android/net/ConnectivityManagerTest.java b/tests/unit/java/android/net/ConnectivityManagerTest.java
index 07f22a2..e7873af 100644
--- a/tests/unit/java/android/net/ConnectivityManagerTest.java
+++ b/tests/unit/java/android/net/ConnectivityManagerTest.java
@@ -68,7 +68,9 @@
import android.os.Process;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -77,8 +79,9 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(VERSION_CODES.R)
public class ConnectivityManagerTest {
@Mock Context mCtx;
@@ -320,26 +323,34 @@
NetworkCallback nullCallback = null;
PendingIntent nullIntent = null;
- mustFail(() -> { manager.requestNetwork(null, callback); });
- mustFail(() -> { manager.requestNetwork(request, nullCallback); });
- mustFail(() -> { manager.requestNetwork(request, callback, null); });
- mustFail(() -> { manager.requestNetwork(request, callback, -1); });
- mustFail(() -> { manager.requestNetwork(request, nullIntent); });
+ mustFail(() -> manager.requestNetwork(null, callback));
+ mustFail(() -> manager.requestNetwork(request, nullCallback));
+ mustFail(() -> manager.requestNetwork(request, callback, null));
+ mustFail(() -> manager.requestNetwork(request, callback, -1));
+ mustFail(() -> manager.requestNetwork(request, nullIntent));
- mustFail(() -> { manager.registerNetworkCallback(null, callback, handler); });
- mustFail(() -> { manager.registerNetworkCallback(request, null, handler); });
- mustFail(() -> { manager.registerNetworkCallback(request, callback, null); });
- mustFail(() -> { manager.registerNetworkCallback(request, nullIntent); });
+ mustFail(() -> manager.requestBackgroundNetwork(null, callback, handler));
+ mustFail(() -> manager.requestBackgroundNetwork(request, null, handler));
+ mustFail(() -> manager.requestBackgroundNetwork(request, callback, null));
- mustFail(() -> { manager.registerDefaultNetworkCallback(null, handler); });
- mustFail(() -> { manager.registerDefaultNetworkCallback(callback, null); });
+ mustFail(() -> manager.registerNetworkCallback(null, callback, handler));
+ mustFail(() -> manager.registerNetworkCallback(request, null, handler));
+ mustFail(() -> manager.registerNetworkCallback(request, callback, null));
+ mustFail(() -> manager.registerNetworkCallback(request, nullIntent));
- mustFail(() -> { manager.registerSystemDefaultNetworkCallback(null, handler); });
- mustFail(() -> { manager.registerSystemDefaultNetworkCallback(callback, null); });
+ mustFail(() -> manager.registerDefaultNetworkCallback(null, handler));
+ mustFail(() -> manager.registerDefaultNetworkCallback(callback, null));
- mustFail(() -> { manager.unregisterNetworkCallback(nullCallback); });
- mustFail(() -> { manager.unregisterNetworkCallback(nullIntent); });
- mustFail(() -> { manager.releaseNetworkRequest(nullIntent); });
+ mustFail(() -> manager.registerSystemDefaultNetworkCallback(null, handler));
+ mustFail(() -> manager.registerSystemDefaultNetworkCallback(callback, null));
+
+ mustFail(() -> manager.registerBestMatchingNetworkCallback(null, callback, handler));
+ mustFail(() -> manager.registerBestMatchingNetworkCallback(request, null, handler));
+ mustFail(() -> manager.registerBestMatchingNetworkCallback(request, callback, null));
+
+ mustFail(() -> manager.unregisterNetworkCallback(nullCallback));
+ mustFail(() -> manager.unregisterNetworkCallback(nullIntent));
+ mustFail(() -> manager.releaseNetworkRequest(nullIntent));
}
static void mustFail(Runnable fn) {
diff --git a/tests/unit/java/android/net/Ikev2VpnProfileTest.java b/tests/unit/java/android/net/Ikev2VpnProfileTest.java
index 0707ef3..56e5c62 100644
--- a/tests/unit/java/android/net/Ikev2VpnProfileTest.java
+++ b/tests/unit/java/android/net/Ikev2VpnProfileTest.java
@@ -23,14 +23,16 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.os.Build;
import android.test.mock.MockContext;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.net.VpnProfile;
import com.android.internal.org.bouncycastle.x509.X509V1CertificateGenerator;
import com.android.net.module.util.ProxyUtils;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -51,7 +53,8 @@
/** Unit tests for {@link Ikev2VpnProfile.Builder}. */
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class Ikev2VpnProfileTest {
private static final String SERVER_ADDR_STRING = "1.2.3.4";
private static final String IDENTITY_STRING = "Identity";
diff --git a/tests/unit/java/android/net/IpMemoryStoreTest.java b/tests/unit/java/android/net/IpMemoryStoreTest.java
index 0b13800..0b82759 100644
--- a/tests/unit/java/android/net/IpMemoryStoreTest.java
+++ b/tests/unit/java/android/net/IpMemoryStoreTest.java
@@ -36,10 +36,13 @@
import android.net.ipmemorystore.NetworkAttributesParcelable;
import android.net.ipmemorystore.Status;
import android.net.networkstack.ModuleNetworkStackClient;
+import android.os.Build;
import android.os.RemoteException;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -53,8 +56,9 @@
import java.net.UnknownHostException;
import java.util.Arrays;
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class IpMemoryStoreTest {
private static final String TAG = IpMemoryStoreTest.class.getSimpleName();
private static final String TEST_CLIENT_ID = "testClientId";
diff --git a/tests/unit/java/android/net/IpSecAlgorithmTest.java b/tests/unit/java/android/net/IpSecAlgorithmTest.java
index 3a8d600..cac8c2d 100644
--- a/tests/unit/java/android/net/IpSecAlgorithmTest.java
+++ b/tests/unit/java/android/net/IpSecAlgorithmTest.java
@@ -29,9 +29,10 @@
import android.os.Parcel;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.CollectionUtils;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -45,7 +46,8 @@
/** Unit tests for {@link IpSecAlgorithm}. */
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class IpSecAlgorithmTest {
private static final byte[] KEY_MATERIAL;
diff --git a/tests/unit/java/android/net/IpSecConfigTest.java b/tests/unit/java/android/net/IpSecConfigTest.java
index 25e225e..b87cb48 100644
--- a/tests/unit/java/android/net/IpSecConfigTest.java
+++ b/tests/unit/java/android/net/IpSecConfigTest.java
@@ -23,15 +23,20 @@
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
+import android.os.Build;
+
import androidx.test.filters.SmallTest;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
/** Unit tests for {@link IpSecConfig}. */
@SmallTest
-@RunWith(JUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class IpSecConfigTest {
@Test
diff --git a/tests/unit/java/android/net/IpSecManagerTest.java b/tests/unit/java/android/net/IpSecManagerTest.java
index 730e2d5..cda8eb7 100644
--- a/tests/unit/java/android/net/IpSecManagerTest.java
+++ b/tests/unit/java/android/net/IpSecManagerTest.java
@@ -31,13 +31,15 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.os.Build;
import android.system.Os;
import android.test.mock.MockContext;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.server.IpSecService;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -49,7 +51,8 @@
/** Unit tests for {@link IpSecManager}. */
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class IpSecManagerTest {
private static final int TEST_UDP_ENCAP_PORT = 34567;
diff --git a/tests/unit/java/android/net/IpSecTransformTest.java b/tests/unit/java/android/net/IpSecTransformTest.java
index 424f23d..81375f1 100644
--- a/tests/unit/java/android/net/IpSecTransformTest.java
+++ b/tests/unit/java/android/net/IpSecTransformTest.java
@@ -19,15 +19,20 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import android.os.Build;
+
import androidx.test.filters.SmallTest;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
/** Unit tests for {@link IpSecTransform}. */
@SmallTest
-@RunWith(JUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class IpSecTransformTest {
@Test
diff --git a/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java b/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java
index bc6dbf2..ed4f61d 100644
--- a/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java
+++ b/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java
@@ -24,18 +24,22 @@
import static org.junit.Assert.fail;
import android.net.util.KeepalivePacketDataUtil;
+import android.os.Build;
import android.util.Log;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
import java.net.InetAddress;
import java.nio.ByteBuffer;
-@RunWith(JUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public final class KeepalivePacketDataUtilTest {
private static final byte[] IPV4_KEEPALIVE_SRC_ADDR = {10, 0, 0, 1};
private static final byte[] IPV4_KEEPALIVE_DST_ADDR = {10, 0, 0, 5};
diff --git a/tests/unit/java/android/net/MacAddressTest.java b/tests/unit/java/android/net/MacAddressTest.java
index 6de31f6..ae7deaa 100644
--- a/tests/unit/java/android/net/MacAddressTest.java
+++ b/tests/unit/java/android/net/MacAddressTest.java
@@ -22,10 +22,13 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.os.Build;
+
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.net.module.util.MacAddressUtils;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -35,7 +38,8 @@
import java.util.Random;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class MacAddressTest {
static class AddrTypeTestCase {
diff --git a/tests/unit/java/android/net/NetworkIdentityTest.kt b/tests/unit/java/android/net/NetworkIdentityTest.kt
index eb2b85c..f963593 100644
--- a/tests/unit/java/android/net/NetworkIdentityTest.kt
+++ b/tests/unit/java/android/net/NetworkIdentityTest.kt
@@ -20,12 +20,15 @@
import android.net.NetworkIdentity.OEM_PAID
import android.net.NetworkIdentity.OEM_PRIVATE
import android.net.NetworkIdentity.getOemBitfield
+import android.os.Build
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import kotlin.test.assertEquals
-@RunWith(JUnit4::class)
+@RunWith(DevSdkIgnoreRunner::class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
class NetworkIdentityTest {
@Test
fun testGetOemBitfield() {
diff --git a/tests/unit/java/android/net/NetworkStatsHistoryTest.java b/tests/unit/java/android/net/NetworkStatsHistoryTest.java
index 13558cd..c5f8c00 100644
--- a/tests/unit/java/android/net/NetworkStatsHistoryTest.java
+++ b/tests/unit/java/android/net/NetworkStatsHistoryTest.java
@@ -38,13 +38,15 @@
import static org.junit.Assert.assertTrue;
import android.content.Context;
+import android.os.Build;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.tests.net.R;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.After;
import org.junit.Test;
@@ -56,8 +58,9 @@
import java.io.DataOutputStream;
import java.util.Random;
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class NetworkStatsHistoryTest {
private static final String TAG = "NetworkStatsHistoryTest";
diff --git a/tests/unit/java/android/net/NetworkStatsTest.java b/tests/unit/java/android/net/NetworkStatsTest.java
index 23d5a7e..c971da1 100644
--- a/tests/unit/java/android/net/NetworkStatsTest.java
+++ b/tests/unit/java/android/net/NetworkStatsTest.java
@@ -39,11 +39,14 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import android.os.Build;
import android.os.Process;
import android.util.ArrayMap;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import com.google.android.collect.Sets;
@@ -53,8 +56,9 @@
import java.util.Arrays;
import java.util.HashSet;
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class NetworkStatsTest {
private static final String TEST_IFACE = "test0";
@@ -730,6 +734,56 @@
ROAMING_ALL, DEFAULT_NETWORK_ALL, 50500L, 27L, 100200L, 55, 0);
}
+ // Tests a case where an PlatformVpn is used, where the entire datapath is in the kernel,
+ // including all encapsulation/decapsulation.
+ @Test
+ public void testMigrateTun_platformVpn() {
+ final int ownerUid = Process.SYSTEM_UID;
+ final String tunIface = "ipsec1";
+ final String underlyingIface = "wlan0";
+ NetworkStats delta = new NetworkStats(TEST_START, 9)
+ // 2 different apps sent/receive data via ipsec1.
+ .insertEntry(tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L)
+ .insertEntry(tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L)
+ // Owner (system) sends data through the tunnel
+ .insertEntry(tunIface, ownerUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 2000L, 20L, 3000L, 30L, 0L)
+ // 1 app already has some traffic on the underlying interface, the other doesn't yet
+ .insertEntry(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 1000L, 10L, 2000L, 20L, 0L);
+
+ delta.migrateTun(ownerUid, tunIface, Arrays.asList(underlyingIface));
+ assertEquals(9, delta.size()); // 3 DBG entries + 1 entry per app per interface
+
+ // tunIface entries should not be changed.
+ assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+ assertValues(delta, 1, tunIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L);
+ assertValues(delta, 2, tunIface, ownerUid, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO,
+ DEFAULT_NETWORK_NO, 2000L, 20L, 3000L, 30L, 0L);
+
+ // Existing underlying Iface entries are updated to include usage over ipsec1
+ assertValues(delta, 3, underlyingIface, 10100, SET_DEFAULT, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 51000L, 35L, 102000L, 70L, 0L);
+
+ // New entries are added on underlying Iface traffic
+ assertContains(delta, underlyingIface, ownerUid, SET_DEFAULT, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 2000L, 20L, 3000L, 30L, 0L);
+ assertContains(delta, underlyingIface, 20100, SET_DEFAULT, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 500L, 2L, 200L, 5L, 0L);
+
+ // New entries are added for debug purpose
+ assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 50000L, 25L, 100000L, 50L, 0L);
+ assertContains(delta, underlyingIface, 20100, SET_DBG_VPN_IN, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 500, 2L, 200L, 5L, 0L);
+ assertContains(delta, underlyingIface, ownerUid, SET_DBG_VPN_IN, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, 2000L, 20L, 3000L, 30L, 0L);
+ }
+
@Test
public void testFilter_NoFilter() {
NetworkStats.Entry entry1 = new NetworkStats.Entry(
diff --git a/tests/unit/java/android/net/NetworkTemplateTest.kt b/tests/unit/java/android/net/NetworkTemplateTest.kt
index cb39a0c..49c7271 100644
--- a/tests/unit/java/android/net/NetworkTemplateTest.kt
+++ b/tests/unit/java/android/net/NetworkTemplateTest.kt
@@ -19,10 +19,10 @@
import android.content.Context
import android.net.ConnectivityManager.TYPE_MOBILE
import android.net.ConnectivityManager.TYPE_WIFI
-import android.net.NetworkIdentity.SUBTYPE_COMBINED
import android.net.NetworkIdentity.OEM_NONE
import android.net.NetworkIdentity.OEM_PAID
import android.net.NetworkIdentity.OEM_PRIVATE
+import android.net.NetworkIdentity.SUBTYPE_COMBINED
import android.net.NetworkIdentity.buildNetworkIdentity
import android.net.NetworkStats.DEFAULT_NETWORK_ALL
import android.net.NetworkStats.METERED_ALL
@@ -31,23 +31,25 @@
import android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD
import android.net.NetworkTemplate.MATCH_WIFI
import android.net.NetworkTemplate.MATCH_WIFI_WILDCARD
-import android.net.NetworkTemplate.WIFI_NETWORKID_ALL
import android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA
import android.net.NetworkTemplate.NETWORK_TYPE_ALL
import android.net.NetworkTemplate.OEM_MANAGED_ALL
import android.net.NetworkTemplate.OEM_MANAGED_NO
import android.net.NetworkTemplate.OEM_MANAGED_YES
import android.net.NetworkTemplate.SUBSCRIBER_ID_MATCH_RULE_EXACT
-import android.net.NetworkTemplate.buildTemplateWifi
-import android.net.NetworkTemplate.buildTemplateWifiWildcard
+import android.net.NetworkTemplate.WIFI_NETWORKID_ALL
import android.net.NetworkTemplate.buildTemplateCarrierMetered
import android.net.NetworkTemplate.buildTemplateMobileWithRatType
+import android.net.NetworkTemplate.buildTemplateWifi
+import android.net.NetworkTemplate.buildTemplateWifiWildcard
+import android.os.Build
import android.telephony.TelephonyManager
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.assertParcelSane
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
import kotlin.test.assertEquals
@@ -60,7 +62,8 @@
private const val TEST_SSID1 = "ssid1"
private const val TEST_SSID2 = "ssid2"
-@RunWith(JUnit4::class)
+@RunWith(DevSdkIgnoreRunner::class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
class NetworkTemplateTest {
private val mockContext = mock(Context::class.java)
diff --git a/tests/unit/java/android/net/NetworkUtilsTest.java b/tests/unit/java/android/net/NetworkUtilsTest.java
index 7748288..a28245d 100644
--- a/tests/unit/java/android/net/NetworkUtilsTest.java
+++ b/tests/unit/java/android/net/NetworkUtilsTest.java
@@ -18,7 +18,12 @@
import static junit.framework.Assert.assertEquals;
-import androidx.test.runner.AndroidJUnit4;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -26,8 +31,9 @@
import java.math.BigInteger;
import java.util.TreeSet;
-@RunWith(AndroidJUnit4.class)
-@androidx.test.filters.SmallTest
+@RunWith(DevSdkIgnoreRunner.class)
+@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class NetworkUtilsTest {
@Test
public void testRoutedIPv4AddressCount() {
diff --git a/tests/unit/java/android/net/QosSocketFilterTest.java b/tests/unit/java/android/net/QosSocketFilterTest.java
index 40f8f1b..91f2cdd 100644
--- a/tests/unit/java/android/net/QosSocketFilterTest.java
+++ b/tests/unit/java/android/net/QosSocketFilterTest.java
@@ -19,7 +19,12 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import androidx.test.runner.AndroidJUnit4;
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -27,8 +32,9 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
-@RunWith(AndroidJUnit4.class)
-@androidx.test.filters.SmallTest
+@RunWith(DevSdkIgnoreRunner.class)
+@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class QosSocketFilterTest {
@Test
diff --git a/tests/unit/java/android/net/TelephonyNetworkSpecifierTest.java b/tests/unit/java/android/net/TelephonyNetworkSpecifierTest.java
index 6714bb1..ead964e 100644
--- a/tests/unit/java/android/net/TelephonyNetworkSpecifierTest.java
+++ b/tests/unit/java/android/net/TelephonyNetworkSpecifierTest.java
@@ -23,16 +23,20 @@
import static org.junit.Assert.assertTrue;
import android.net.wifi.WifiNetworkSpecifier;
+import android.os.Build;
import android.telephony.SubscriptionManager;
import androidx.test.filters.SmallTest;
+import com.android.testutils.DevSdkIgnoreRule;
+
import org.junit.Test;
/**
* Unit test for {@link android.net.TelephonyNetworkSpecifier}.
*/
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class TelephonyNetworkSpecifierTest {
private static final int TEST_SUBID = 5;
private static final String TEST_SSID = "Test123";
diff --git a/tests/unit/java/android/net/VpnManagerTest.java b/tests/unit/java/android/net/VpnManagerTest.java
index 3135062..532081a 100644
--- a/tests/unit/java/android/net/VpnManagerTest.java
+++ b/tests/unit/java/android/net/VpnManagerTest.java
@@ -27,14 +27,16 @@
import android.content.ComponentName;
import android.content.Intent;
+import android.os.Build;
import android.test.mock.MockContext;
import android.util.SparseArray;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.MessageUtils;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -42,7 +44,8 @@
/** Unit tests for {@link VpnManager}. */
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class VpnManagerTest {
private static final String PKG_NAME = "fooPackage";
diff --git a/tests/unit/java/android/net/VpnTransportInfoTest.java b/tests/unit/java/android/net/VpnTransportInfoTest.java
index ccaa5cf..b4c7ac4 100644
--- a/tests/unit/java/android/net/VpnTransportInfoTest.java
+++ b/tests/unit/java/android/net/VpnTransportInfoTest.java
@@ -24,14 +24,19 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
+import android.os.Build;
+
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class VpnTransportInfoTest {
@Test
diff --git a/tests/unit/java/android/net/ipmemorystore/ParcelableTests.java b/tests/unit/java/android/net/ipmemorystore/ParcelableTests.java
index 603c875..5d0b783 100644
--- a/tests/unit/java/android/net/ipmemorystore/ParcelableTests.java
+++ b/tests/unit/java/android/net/ipmemorystore/ParcelableTests.java
@@ -21,11 +21,14 @@
import android.net.networkstack.aidl.quirks.IPv6ProvisioningLossQuirk;
import android.net.networkstack.aidl.quirks.IPv6ProvisioningLossQuirkParcelable;
+import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -36,8 +39,9 @@
import java.util.Arrays;
import java.util.Collections;
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class ParcelableTests {
@Test
public void testNetworkAttributesParceling() throws Exception {
diff --git a/tests/unit/java/android/net/nsd/NsdManagerTest.java b/tests/unit/java/android/net/nsd/NsdManagerTest.java
index 370179c..31c8927 100644
--- a/tests/unit/java/android/net/nsd/NsdManagerTest.java
+++ b/tests/unit/java/android/net/nsd/NsdManagerTest.java
@@ -28,6 +28,7 @@
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -35,9 +36,10 @@
import android.os.Messenger;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.AsyncChannel;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils;
import org.junit.After;
@@ -47,8 +49,9 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class NsdManagerTest {
static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD;
diff --git a/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java b/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
index 94dfc75..ca8cf07 100644
--- a/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
+++ b/tests/unit/java/android/net/nsd/NsdServiceInfoTest.java
@@ -21,12 +21,15 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.StrictMode;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -36,8 +39,9 @@
import java.util.Arrays;
import java.util.Map;
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class NsdServiceInfoTest {
public final static InetAddress LOCALHOST;
diff --git a/tests/unit/java/android/net/util/DnsUtilsTest.java b/tests/unit/java/android/net/util/DnsUtilsTest.java
index b626db8..660d516 100644
--- a/tests/unit/java/android/net/util/DnsUtilsTest.java
+++ b/tests/unit/java/android/net/util/DnsUtilsTest.java
@@ -25,9 +25,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.InetAddresses;
+import android.os.Build;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -37,8 +40,9 @@
import java.util.Collections;
import java.util.List;
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class DnsUtilsTest {
private InetAddress stringToAddress(@NonNull String addr) {
return InetAddresses.parseNumericAddress(addr);
diff --git a/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt b/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt
index b62bdbc..40f39a4 100644
--- a/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt
+++ b/tests/unit/java/android/net/util/KeepaliveUtilsTest.kt
@@ -25,15 +25,17 @@
import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
import android.net.NetworkCapabilities.TRANSPORT_VPN
import android.net.NetworkCapabilities.TRANSPORT_WIFI
+import android.os.Build
import androidx.test.filters.SmallTest
import com.android.internal.R
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
import org.junit.After
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.fail
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito.any
import org.mockito.Mockito.doReturn
@@ -45,8 +47,9 @@
* Build, install and run with:
* atest android.net.util.KeepaliveUtilsTest
*/
-@RunWith(JUnit4::class)
+@RunWith(DevSdkIgnoreRunner::class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
class KeepaliveUtilsTest {
// Prepare mocked context with given resource strings.
diff --git a/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt b/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt
index 78c8fa4..576b8d3 100644
--- a/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt
+++ b/tests/unit/java/android/net/util/MultinetworkPolicyTrackerTest.kt
@@ -25,15 +25,17 @@
import android.net.ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI
import android.net.ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE
import android.net.util.MultinetworkPolicyTracker.ActiveDataSubscriptionIdListener
+import android.os.Build
import android.provider.Settings
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.test.mock.MockContentResolver
import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
import com.android.connectivity.resources.R
import com.android.internal.util.test.FakeSettingsProvider
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
@@ -57,8 +59,9 @@
* Build, install and run with:
* atest android.net.util.MultinetworkPolicyTrackerTest
*/
-@RunWith(AndroidJUnit4::class)
+@RunWith(DevSdkIgnoreRunner::class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
class MultinetworkPolicyTrackerTest {
private val resources = mock(Resources::class.java).also {
doReturn(R.integer.config_networkAvoidBadWifi).`when`(it).getIdentifier(
diff --git a/tests/unit/java/com/android/internal/net/NetworkUtilsInternalTest.java b/tests/unit/java/com/android/internal/net/NetworkUtilsInternalTest.java
index 3cfecd5..51388d4 100644
--- a/tests/unit/java/com/android/internal/net/NetworkUtilsInternalTest.java
+++ b/tests/unit/java/com/android/internal/net/NetworkUtilsInternalTest.java
@@ -27,18 +27,23 @@
import static org.junit.Assert.fail;
+import android.os.Build;
import android.system.ErrnoException;
import android.system.Os;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import libcore.io.IoUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
-@RunWith(AndroidJUnit4.class)
-@androidx.test.filters.SmallTest
+@RunWith(DevSdkIgnoreRunner.class)
+@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class NetworkUtilsInternalTest {
private static void expectSocketSuccess(String msg, int domain, int type) {
diff --git a/tests/unit/java/com/android/internal/net/VpnProfileTest.java b/tests/unit/java/com/android/internal/net/VpnProfileTest.java
index cb0f071..a945a1f 100644
--- a/tests/unit/java/com/android/internal/net/VpnProfileTest.java
+++ b/tests/unit/java/com/android/internal/net/VpnProfileTest.java
@@ -25,12 +25,15 @@
import static org.junit.Assert.assertTrue;
import android.net.IpSecAlgorithm;
+import android.os.Build;
import androidx.test.filters.SmallTest;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
import java.util.ArrayList;
import java.util.Arrays;
@@ -38,7 +41,8 @@
/** Unit tests for {@link VpnProfile}. */
@SmallTest
-@RunWith(JUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class VpnProfileTest {
private static final String DUMMY_PROFILE_KEY = "Test";
diff --git a/tests/unit/java/com/android/internal/util/BitUtilsTest.java b/tests/unit/java/com/android/internal/util/BitUtilsTest.java
deleted file mode 100644
index d2fbdce..0000000
--- a/tests/unit/java/com/android/internal/util/BitUtilsTest.java
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.util;
-
-import static com.android.internal.util.BitUtils.bytesToBEInt;
-import static com.android.internal.util.BitUtils.bytesToLEInt;
-import static com.android.internal.util.BitUtils.getUint16;
-import static com.android.internal.util.BitUtils.getUint32;
-import static com.android.internal.util.BitUtils.getUint8;
-import static com.android.internal.util.BitUtils.packBits;
-import static com.android.internal.util.BitUtils.uint16;
-import static com.android.internal.util.BitUtils.uint32;
-import static com.android.internal.util.BitUtils.uint8;
-import static com.android.internal.util.BitUtils.unpackBits;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.Random;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class BitUtilsTest {
-
- @Test
- public void testUnsignedByteWideningConversions() {
- byte b0 = 0;
- byte b1 = 1;
- byte bm1 = -1;
- assertEquals(0, uint8(b0));
- assertEquals(1, uint8(b1));
- assertEquals(127, uint8(Byte.MAX_VALUE));
- assertEquals(128, uint8(Byte.MIN_VALUE));
- assertEquals(255, uint8(bm1));
- assertEquals(255, uint8((byte)255));
- }
-
- @Test
- public void testUnsignedShortWideningConversions() {
- short s0 = 0;
- short s1 = 1;
- short sm1 = -1;
- assertEquals(0, uint16(s0));
- assertEquals(1, uint16(s1));
- assertEquals(32767, uint16(Short.MAX_VALUE));
- assertEquals(32768, uint16(Short.MIN_VALUE));
- assertEquals(65535, uint16(sm1));
- assertEquals(65535, uint16((short)65535));
- }
-
- @Test
- public void testUnsignedShortComposition() {
- byte b0 = 0;
- byte b1 = 1;
- byte b2 = 2;
- byte b10 = 10;
- byte b16 = 16;
- byte b128 = -128;
- byte b224 = -32;
- byte b255 = -1;
- assertEquals(0x0000, uint16(b0, b0));
- assertEquals(0xffff, uint16(b255, b255));
- assertEquals(0x0a01, uint16(b10, b1));
- assertEquals(0x8002, uint16(b128, b2));
- assertEquals(0x01ff, uint16(b1, b255));
- assertEquals(0x80ff, uint16(b128, b255));
- assertEquals(0xe010, uint16(b224, b16));
- }
-
- @Test
- public void testUnsignedIntWideningConversions() {
- assertEquals(0, uint32(0));
- assertEquals(1, uint32(1));
- assertEquals(2147483647L, uint32(Integer.MAX_VALUE));
- assertEquals(2147483648L, uint32(Integer.MIN_VALUE));
- assertEquals(4294967295L, uint32(-1));
- assertEquals(4294967295L, uint32((int)4294967295L));
- }
-
- @Test
- public void testBytesToInt() {
- assertEquals(0x00000000, bytesToBEInt(bytes(0, 0, 0, 0)));
- assertEquals(0xffffffff, bytesToBEInt(bytes(255, 255, 255, 255)));
- assertEquals(0x0a000001, bytesToBEInt(bytes(10, 0, 0, 1)));
- assertEquals(0x0a000002, bytesToBEInt(bytes(10, 0, 0, 2)));
- assertEquals(0x0a001fff, bytesToBEInt(bytes(10, 0, 31, 255)));
- assertEquals(0xe0000001, bytesToBEInt(bytes(224, 0, 0, 1)));
-
- assertEquals(0x00000000, bytesToLEInt(bytes(0, 0, 0, 0)));
- assertEquals(0x01020304, bytesToLEInt(bytes(4, 3, 2, 1)));
- assertEquals(0xffff0000, bytesToLEInt(bytes(0, 0, 255, 255)));
- }
-
- @Test
- public void testUnsignedGetters() {
- ByteBuffer b = ByteBuffer.allocate(4);
- b.putInt(0xffff);
-
- assertEquals(0x0, getUint8(b, 0));
- assertEquals(0x0, getUint8(b, 1));
- assertEquals(0xff, getUint8(b, 2));
- assertEquals(0xff, getUint8(b, 3));
-
- assertEquals(0x0, getUint16(b, 0));
- assertEquals(0xffff, getUint16(b, 2));
-
- b.rewind();
- b.putInt(0xffffffff);
- assertEquals(0xffffffffL, getUint32(b, 0));
- }
-
- @Test
- public void testBitsPacking() {
- BitPackingTestCase[] testCases = {
- new BitPackingTestCase(0, ints()),
- new BitPackingTestCase(1, ints(0)),
- new BitPackingTestCase(2, ints(1)),
- new BitPackingTestCase(3, ints(0, 1)),
- new BitPackingTestCase(4, ints(2)),
- new BitPackingTestCase(6, ints(1, 2)),
- new BitPackingTestCase(9, ints(0, 3)),
- new BitPackingTestCase(~Long.MAX_VALUE, ints(63)),
- new BitPackingTestCase(~Long.MAX_VALUE + 1, ints(0, 63)),
- new BitPackingTestCase(~Long.MAX_VALUE + 2, ints(1, 63)),
- };
- for (BitPackingTestCase tc : testCases) {
- int[] got = unpackBits(tc.packedBits);
- assertTrue(
- "unpackBits("
- + tc.packedBits
- + "): expected "
- + Arrays.toString(tc.bits)
- + " but got "
- + Arrays.toString(got),
- Arrays.equals(tc.bits, got));
- }
- for (BitPackingTestCase tc : testCases) {
- long got = packBits(tc.bits);
- assertEquals(
- "packBits("
- + Arrays.toString(tc.bits)
- + "): expected "
- + tc.packedBits
- + " but got "
- + got,
- tc.packedBits,
- got);
- }
-
- long[] moreTestCases = {
- 0, 1, -1, 23895, -908235, Long.MAX_VALUE, Long.MIN_VALUE, new Random().nextLong(),
- };
- for (long l : moreTestCases) {
- assertEquals(l, packBits(unpackBits(l)));
- }
- }
-
- static byte[] bytes(int b1, int b2, int b3, int b4) {
- return new byte[] {b(b1), b(b2), b(b3), b(b4)};
- }
-
- static byte b(int i) {
- return (byte) i;
- }
-
- static int[] ints(int... array) {
- return array;
- }
-
- static class BitPackingTestCase {
- final int[] bits;
- final long packedBits;
-
- BitPackingTestCase(long packedBits, int[] bits) {
- this.bits = bits;
- this.packedBits = packedBits;
- }
- }
-}
diff --git a/tests/unit/java/com/android/internal/util/RingBufferTest.java b/tests/unit/java/com/android/internal/util/RingBufferTest.java
deleted file mode 100644
index d06095a..0000000
--- a/tests/unit/java/com/android/internal/util/RingBufferTest.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.util;
-
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class RingBufferTest {
-
- @Test
- public void testEmptyRingBuffer() {
- RingBuffer<String> buffer = new RingBuffer<>(String.class, 100);
-
- assertArrayEquals(new String[0], buffer.toArray());
- }
-
- @Test
- public void testIncorrectConstructorArguments() {
- try {
- RingBuffer<String> buffer = new RingBuffer<>(String.class, -10);
- fail("Should not be able to create a negative capacity RingBuffer");
- } catch (IllegalArgumentException expected) {
- }
-
- try {
- RingBuffer<String> buffer = new RingBuffer<>(String.class, 0);
- fail("Should not be able to create a 0 capacity RingBuffer");
- } catch (IllegalArgumentException expected) {
- }
- }
-
- @Test
- public void testRingBufferWithNoWrapping() {
- RingBuffer<String> buffer = new RingBuffer<>(String.class, 100);
-
- buffer.append("a");
- buffer.append("b");
- buffer.append("c");
- buffer.append("d");
- buffer.append("e");
-
- String[] expected = {"a", "b", "c", "d", "e"};
- assertArrayEquals(expected, buffer.toArray());
- }
-
- @Test
- public void testRingBufferWithCapacity1() {
- RingBuffer<String> buffer = new RingBuffer<>(String.class, 1);
-
- buffer.append("a");
- assertArrayEquals(new String[]{"a"}, buffer.toArray());
-
- buffer.append("b");
- assertArrayEquals(new String[]{"b"}, buffer.toArray());
-
- buffer.append("c");
- assertArrayEquals(new String[]{"c"}, buffer.toArray());
-
- buffer.append("d");
- assertArrayEquals(new String[]{"d"}, buffer.toArray());
-
- buffer.append("e");
- assertArrayEquals(new String[]{"e"}, buffer.toArray());
- }
-
- @Test
- public void testRingBufferWithWrapping() {
- int capacity = 100;
- RingBuffer<String> buffer = new RingBuffer<>(String.class, capacity);
-
- buffer.append("a");
- buffer.append("b");
- buffer.append("c");
- buffer.append("d");
- buffer.append("e");
-
- String[] expected1 = {"a", "b", "c", "d", "e"};
- assertArrayEquals(expected1, buffer.toArray());
-
- String[] expected2 = new String[capacity];
- int firstIndex = 0;
- int lastIndex = capacity - 1;
-
- expected2[firstIndex] = "e";
- for (int i = 1; i < capacity; i++) {
- buffer.append("x");
- expected2[i] = "x";
- }
- assertArrayEquals(expected2, buffer.toArray());
-
- buffer.append("x");
- expected2[firstIndex] = "x";
- assertArrayEquals(expected2, buffer.toArray());
-
- for (int i = 0; i < 10; i++) {
- for (String s : expected2) {
- buffer.append(s);
- }
- }
- assertArrayEquals(expected2, buffer.toArray());
-
- buffer.append("a");
- expected2[lastIndex] = "a";
- assertArrayEquals(expected2, buffer.toArray());
- }
-
- @Test
- public void testGetNextSlot() {
- int capacity = 100;
- RingBuffer<DummyClass1> buffer = new RingBuffer<>(DummyClass1.class, capacity);
-
- final DummyClass1[] actual = new DummyClass1[capacity];
- final DummyClass1[] expected = new DummyClass1[capacity];
- for (int i = 0; i < capacity; ++i) {
- final DummyClass1 obj = buffer.getNextSlot();
- obj.x = capacity * i;
- actual[i] = obj;
- expected[i] = new DummyClass1();
- expected[i].x = capacity * i;
- }
- assertArrayEquals(expected, buffer.toArray());
-
- for (int i = 0; i < capacity; ++i) {
- if (actual[i] != buffer.getNextSlot()) {
- fail("getNextSlot() should re-use objects if available");
- }
- }
-
- RingBuffer<DummyClass2> buffer2 = new RingBuffer<>(DummyClass2.class, capacity);
- assertNull("getNextSlot() should return null if the object can't be initiated "
- + "(No nullary constructor)", buffer2.getNextSlot());
-
- RingBuffer<DummyClass3> buffer3 = new RingBuffer<>(DummyClass3.class, capacity);
- assertNull("getNextSlot() should return null if the object can't be initiated "
- + "(Inaccessible class)", buffer3.getNextSlot());
- }
-
- public static final class DummyClass1 {
- int x;
-
- public boolean equals(Object o) {
- if (o instanceof DummyClass1) {
- final DummyClass1 other = (DummyClass1) o;
- return other.x == this.x;
- }
- return false;
- }
- }
-
- public static final class DummyClass2 {
- public DummyClass2(int x) {}
- }
-
- private static final class DummyClass3 {}
-}
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 6c484cc..cec3882 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -301,7 +301,6 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.connectivity.resources.R;
import com.android.internal.app.IBatteryStats;
@@ -325,6 +324,8 @@
import com.android.server.connectivity.Vpn;
import com.android.server.connectivity.VpnProfileStore;
import com.android.server.net.NetworkPinner;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.ExceptionUtils;
import com.android.testutils.HandlerUtils;
import com.android.testutils.RecorderCallback.CallbackEntry;
@@ -388,8 +389,9 @@
* Build, install and run with:
* runtest frameworks-net -c com.android.server.ConnectivityServiceTest
*/
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class ConnectivityServiceTest {
private static final String TAG = "ConnectivityServiceTest";
@@ -5448,6 +5450,8 @@
// the follow-up network disconnection will be processed first.
mWiFiNetworkAgent.setKeepaliveResponseDelay(3 * TIMEOUT_MS);
ka.stop();
+ // Call stop() twice shouldn't result in crash, b/182586681.
+ ka.stop();
// Make sure the stop has been processed. Wait for executor idle is needed to prevent
// flaky since the actual stop call to the service is delegated to executor thread.
@@ -5745,37 +5749,59 @@
@Test
public void testNetworkCallbackMaximum() throws Exception {
final int MAX_REQUESTS = 100;
- final int CALLBACKS = 89;
- final int INTENTS = 11;
+ final int CALLBACKS = 87;
+ final int DIFF_INTENTS = 10;
+ final int SAME_INTENTS = 10;
final int SYSTEM_ONLY_MAX_REQUESTS = 250;
- assertEquals(MAX_REQUESTS, CALLBACKS + INTENTS);
+ // Assert 1 (Default request filed before testing) + CALLBACKS + DIFF_INTENTS +
+ // 1 (same intent) = MAX_REQUESTS - 1, since the capacity is MAX_REQUEST - 1.
+ assertEquals(MAX_REQUESTS - 1, 1 + CALLBACKS + DIFF_INTENTS + 1);
NetworkRequest networkRequest = new NetworkRequest.Builder().build();
ArrayList<Object> registered = new ArrayList<>();
- int j = 0;
- while (j++ < CALLBACKS / 2) {
- NetworkCallback cb = new NetworkCallback();
- mCm.requestNetwork(networkRequest, cb);
+ for (int j = 0; j < CALLBACKS; j++) {
+ final NetworkCallback cb = new NetworkCallback();
+ if (j < CALLBACKS / 2) {
+ mCm.requestNetwork(networkRequest, cb);
+ } else {
+ mCm.registerNetworkCallback(networkRequest, cb);
+ }
registered.add(cb);
}
- while (j++ < CALLBACKS) {
- NetworkCallback cb = new NetworkCallback();
- mCm.registerNetworkCallback(networkRequest, cb);
- registered.add(cb);
+
+ // Since ConnectivityService will de-duplicate the request with the same intent,
+ // register multiple times does not really increase multiple requests.
+ final PendingIntent same_pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
+ new Intent("same"), FLAG_IMMUTABLE);
+ for (int j = 0; j < SAME_INTENTS; j++) {
+ mCm.registerNetworkCallback(networkRequest, same_pi);
+ // Wait for the requests with the same intent to be de-duplicated. Because
+ // ConnectivityService side incrementCountOrThrow in binder, decrementCount in handler
+ // thread, waitForIdle is needed to ensure decrementCount being invoked for same intent
+ // requests before doing further tests.
+ waitForIdle();
}
- j = 0;
- while (j++ < INTENTS / 2) {
- final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
- new Intent("a" + j), FLAG_IMMUTABLE);
- mCm.requestNetwork(networkRequest, pi);
- registered.add(pi);
+ for (int j = 0; j < SAME_INTENTS; j++) {
+ mCm.requestNetwork(networkRequest, same_pi);
+ // Wait for the requests with the same intent to be de-duplicated.
+ // Refer to the reason above.
+ waitForIdle();
}
- while (j++ < INTENTS) {
- final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
- new Intent("b" + j), FLAG_IMMUTABLE);
- mCm.registerNetworkCallback(networkRequest, pi);
- registered.add(pi);
+ registered.add(same_pi);
+
+ for (int j = 0; j < DIFF_INTENTS; j++) {
+ if (j < DIFF_INTENTS / 2) {
+ final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
+ new Intent("a" + j), FLAG_IMMUTABLE);
+ mCm.requestNetwork(networkRequest, pi);
+ registered.add(pi);
+ } else {
+ final PendingIntent pi = PendingIntent.getBroadcast(mContext, 0 /* requestCode */,
+ new Intent("b" + j), FLAG_IMMUTABLE);
+ mCm.registerNetworkCallback(networkRequest, pi);
+ registered.add(pi);
+ }
}
// Test that the limit is enforced when MAX_REQUESTS simultaneous requests are added.
@@ -5825,10 +5851,10 @@
for (Object o : registered) {
if (o instanceof NetworkCallback) {
- mCm.unregisterNetworkCallback((NetworkCallback)o);
+ mCm.unregisterNetworkCallback((NetworkCallback) o);
}
if (o instanceof PendingIntent) {
- mCm.unregisterNetworkCallback((PendingIntent)o);
+ mCm.unregisterNetworkCallback((PendingIntent) o);
}
}
waitForIdle();
@@ -6003,7 +6029,7 @@
verify(mStatsManager, atLeastOnce()).notifyNetworkStatus(networksCaptor.capture(),
any(List.class), eq(defaultIface), vpnInfosCaptor.capture());
- assertSameElements(networksCaptor.getValue(), networks);
+ assertSameElements(networks, networksCaptor.getValue());
List<UnderlyingNetworkInfo> infos = vpnInfosCaptor.getValue();
if (vpnUid != null) {
@@ -6208,6 +6234,77 @@
}
@Test
+ public void testNonVpnUnderlyingNetworks() throws Exception {
+ // Ensure wifi and cellular are not torn down.
+ for (int transport : new int[]{TRANSPORT_CELLULAR, TRANSPORT_WIFI}) {
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .addTransportType(transport)
+ .removeCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+ .build();
+ mCm.requestNetwork(request, new NetworkCallback());
+ }
+
+ // Connect a VCN-managed wifi network.
+ final LinkProperties wifiLp = new LinkProperties();
+ wifiLp.setInterfaceName(WIFI_IFNAME);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
+ mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
+ mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ mWiFiNetworkAgent.connect(true /* validated */);
+
+ final List<Network> none = List.of();
+ expectNotifyNetworkStatus(none, null); // Wifi is not the default network
+
+ // Create a virtual network based on the wifi network.
+ final int ownerUid = 10042;
+ NetworkCapabilities nc = new NetworkCapabilities.Builder()
+ .setOwnerUid(ownerUid)
+ .setAdministratorUids(new int[]{ownerUid})
+ .build();
+ final String vcnIface = "ipsec42";
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(vcnIface);
+ final TestNetworkAgentWrapper vcn = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, lp, nc);
+ vcn.setUnderlyingNetworks(List.of(mWiFiNetworkAgent.getNetwork()));
+ vcn.connect(false /* validated */);
+
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(callback);
+ callback.expectAvailableCallbacksUnvalidated(vcn);
+
+ // The underlying wifi network's capabilities are not propagated to the virtual network,
+ // but NetworkStatsService is informed of the underlying interface.
+ nc = mCm.getNetworkCapabilities(vcn.getNetwork());
+ assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+ assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
+ final List<Network> onlyVcn = List.of(vcn.getNetwork());
+ expectNotifyNetworkStatus(onlyVcn, vcnIface, ownerUid, vcnIface, List.of(WIFI_IFNAME));
+
+ // Add NOT_METERED to the underlying network, check that it is not propagated.
+ mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ callback.assertNoCallback();
+ nc = mCm.getNetworkCapabilities(vcn.getNetwork());
+ assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
+
+ // Switch underlying networks.
+ final LinkProperties cellLp = new LinkProperties();
+ cellLp.setInterfaceName(MOBILE_IFNAME);
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
+ mCellNetworkAgent.removeCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
+ mCellNetworkAgent.addCapability(NET_CAPABILITY_NOT_ROAMING);
+ mCellNetworkAgent.connect(false /* validated */);
+ vcn.setUnderlyingNetworks(List.of(mCellNetworkAgent.getNetwork()));
+
+ // The underlying capability changes do not propagate to the virtual network, but
+ // NetworkStatsService is informed of the new underlying interface.
+ callback.assertNoCallback();
+ nc = mCm.getNetworkCapabilities(vcn.getNetwork());
+ assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+ assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+ expectNotifyNetworkStatus(onlyVcn, vcnIface, ownerUid, vcnIface, List.of(MOBILE_IFNAME));
+ }
+
+ @Test
public void testBasicDnsConfigurationPushed() throws Exception {
setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
@@ -10195,6 +10292,9 @@
public void testConnectivityDiagnosticsCallbackOnConnectivityReported() throws Exception {
setUpConnectivityDiagnosticsCallback();
+ // reset to ignore callbacks from setup
+ reset(mConnectivityDiagnosticsCallback);
+
final Network n = mCellNetworkAgent.getNetwork();
final boolean hasConnectivity = true;
mService.reportNetworkConnectivity(n, hasConnectivity);
@@ -10205,6 +10305,8 @@
// Verify onNetworkConnectivityReported fired
verify(mConnectivityDiagnosticsCallback)
.onNetworkConnectivityReported(eq(n), eq(hasConnectivity));
+ verify(mConnectivityDiagnosticsCallback).onConnectivityReportAvailable(
+ argThat(report -> areConnDiagCapsRedacted(report.getNetworkCapabilities())));
final boolean noConnectivity = false;
mService.reportNetworkConnectivity(n, noConnectivity);
@@ -10215,6 +10317,54 @@
// Wait for onNetworkConnectivityReported to fire
verify(mConnectivityDiagnosticsCallback)
.onNetworkConnectivityReported(eq(n), eq(noConnectivity));
+
+ // Also expect a ConnectivityReport after NetworkMonitor asynchronously re-validates
+ verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS).times(2))
+ .onConnectivityReportAvailable(
+ argThat(report ->
+ areConnDiagCapsRedacted(report.getNetworkCapabilities())));
+ }
+
+ @Test
+ public void testConnectivityDiagnosticsCallbackOnConnectivityReportedSeparateUid()
+ throws Exception {
+ setUpConnectivityDiagnosticsCallback();
+
+ // reset to ignore callbacks from setup
+ reset(mConnectivityDiagnosticsCallback);
+
+ // report known Connectivity from a different uid. Verify that network is not re-validated
+ // and this callback is not notified.
+ final Network n = mCellNetworkAgent.getNetwork();
+ final boolean hasConnectivity = true;
+ doAsUid(Process.myUid() + 1, () -> mService.reportNetworkConnectivity(n, hasConnectivity));
+
+ // Block until all other events are done processing.
+ HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+ // Verify onNetworkConnectivityReported did not fire
+ verify(mConnectivityDiagnosticsCallback, never())
+ .onNetworkConnectivityReported(any(), anyBoolean());
+ verify(mConnectivityDiagnosticsCallback, never())
+ .onConnectivityReportAvailable(any());
+
+ // report different Connectivity from a different uid. Verify that network is re-validated
+ // and that this callback is notified.
+ final boolean noConnectivity = false;
+ doAsUid(Process.myUid() + 1, () -> mService.reportNetworkConnectivity(n, noConnectivity));
+
+ // Block until all other events are done processing.
+ HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
+
+ // Wait for onNetworkConnectivityReported to fire
+ verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
+ .onNetworkConnectivityReported(eq(n), eq(noConnectivity));
+
+ // Also expect a ConnectivityReport after NetworkMonitor asynchronously re-validates
+ verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
+ .onConnectivityReportAvailable(
+ argThat(report ->
+ areConnDiagCapsRedacted(report.getNetworkCapabilities())));
}
@Test
@@ -13359,43 +13509,43 @@
}
@Test
- public void testSetMobileDataPreferredUids_noIssueToFactory() throws Exception {
- // First set mobile data preferred uid to create a multi-layer requests: 1. listen for
+ public void testMultilayerRequestsOfSetMobileDataPreferredUids() throws Exception {
+ // First set mobile data preferred uid to create a multi-layer requests: 1. request for
// cellular, 2. track the default network for fallback.
setAndUpdateMobileDataPreferredUids(
Set.of(PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID)));
final HandlerThread handlerThread = new HandlerThread("MockFactory");
handlerThread.start();
- NetworkCapabilities internetFilter = new NetworkCapabilities()
+ final NetworkCapabilities cellFilter = new NetworkCapabilities()
+ .addTransportType(TRANSPORT_CELLULAR)
.addCapability(NET_CAPABILITY_INTERNET)
.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
- final MockNetworkFactory internetFactory = new MockNetworkFactory(handlerThread.getLooper(),
- mServiceContext, "internetFactory", internetFilter, mCsHandlerThread);
- internetFactory.setScoreFilter(40);
+ final MockNetworkFactory cellFactory = new MockNetworkFactory(handlerThread.getLooper(),
+ mServiceContext, "cellFactory", cellFilter, mCsHandlerThread);
+ cellFactory.setScoreFilter(40);
try {
- internetFactory.register();
- // Default internet request only. The first request is listen for cellular network,
- // which is never sent to factories (it's a LISTEN, not requestable). The second
- // fallback request is TRACK_DEFAULT which is also not sent to factories.
- internetFactory.expectRequestAdds(1);
- internetFactory.assertRequestCountEquals(1);
+ cellFactory.register();
+ // Default internet request and the mobile data preferred request.
+ cellFactory.expectRequestAdds(2);
+ cellFactory.assertRequestCountEquals(2);
- mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- mCellNetworkAgent.connect(true);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
- // The internet factory however is outscored, and should lose its requests.
- internetFactory.expectRequestRemove();
- internetFactory.assertRequestCountEquals(0);
+ // The cellFactory however is outscored, and should lose default internet request.
+ // But it should still see mobile data preferred request.
+ cellFactory.expectRequestRemove();
+ cellFactory.assertRequestCountEquals(1);
- mCellNetworkAgent.disconnect();
+ mWiFiNetworkAgent.disconnect();
// The network satisfying the default internet request has disconnected, so the
- // internetFactory sees the default request again.
- internetFactory.expectRequestAdds(1);
- internetFactory.assertRequestCountEquals(1);
+ // cellFactory sees the default internet requests again.
+ cellFactory.expectRequestAdd();
+ cellFactory.assertRequestCountEquals(2);
} finally {
- internetFactory.terminate();
+ cellFactory.terminate();
handlerThread.quitSafely();
}
}
diff --git a/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
index cf2c9c7..5bbbe40 100644
--- a/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -58,6 +58,7 @@
import android.net.LinkProperties;
import android.net.Network;
import android.os.Binder;
+import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.system.Os;
import android.test.mock.MockContext;
@@ -66,9 +67,11 @@
import androidx.test.filters.SmallTest;
import com.android.server.IpSecService.TunnelInterfaceRecord;
+import com.android.testutils.DevSdkIgnoreRule;
import org.junit.Before;
import org.junit.Ignore;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -83,6 +86,9 @@
@SmallTest
@RunWith(Parameterized.class)
public class IpSecServiceParameterizedTest {
+ @Rule
+ public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(
+ Build.VERSION_CODES.R /* ignoreClassUpTo */);
private static final int TEST_SPI = 0xD1201D;
diff --git a/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java b/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java
index 22a2c94..6957d51 100644
--- a/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java
+++ b/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java
@@ -30,14 +30,16 @@
import android.content.Context;
import android.os.Binder;
+import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.server.IpSecService.IResource;
import com.android.server.IpSecService.RefcountedResource;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -51,7 +53,8 @@
/** Unit tests for {@link IpSecService.RefcountedResource}. */
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class IpSecServiceRefcountedResourceTest {
Context mMockContext;
IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
diff --git a/tests/unit/java/com/android/server/IpSecServiceTest.java b/tests/unit/java/com/android/server/IpSecServiceTest.java
index 6232423..fabd6f1 100644
--- a/tests/unit/java/com/android/server/IpSecServiceTest.java
+++ b/tests/unit/java/com/android/server/IpSecServiceTest.java
@@ -43,6 +43,7 @@
import android.net.IpSecSpiResponse;
import android.net.IpSecUdpEncapResponse;
import android.os.Binder;
+import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.system.ErrnoException;
@@ -51,7 +52,9 @@
import android.util.Range;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import dalvik.system.SocketTagger;
@@ -70,7 +73,8 @@
/** Unit tests for {@link IpSecService}. */
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class IpSecServiceTest {
private static final int DROID_SPI = 0xD1201D;
diff --git a/tests/unit/java/com/android/server/LegacyTypeTrackerTest.kt b/tests/unit/java/com/android/server/LegacyTypeTrackerTest.kt
index 5ec1119..64736f2 100644
--- a/tests/unit/java/com/android/server/LegacyTypeTrackerTest.kt
+++ b/tests/unit/java/com/android/server/LegacyTypeTrackerTest.kt
@@ -43,11 +43,13 @@
import android.net.EthernetManager
import android.net.NetworkInfo.DetailedState.CONNECTED
import android.net.NetworkInfo.DetailedState.DISCONNECTED
+import android.os.Build
import android.telephony.TelephonyManager
import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
import com.android.server.ConnectivityService.LegacyTypeTracker
import com.android.server.connectivity.NetworkAgentInfo
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertSame
@@ -64,8 +66,9 @@
const val UNSUPPORTED_TYPE = TYPE_WIMAX
-@RunWith(AndroidJUnit4::class)
+@RunWith(DevSdkIgnoreRunner::class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
class LegacyTypeTrackerTest {
private val supportedTypes = arrayOf(TYPE_WIFI, TYPE_WIFI_P2P, TYPE_ETHERNET, TYPE_MOBILE,
TYPE_MOBILE_SUPL, TYPE_MOBILE_MMS, TYPE_MOBILE_SUPL, TYPE_MOBILE_DUN, TYPE_MOBILE_HIPRI,
diff --git a/tests/unit/java/com/android/server/NetIdManagerTest.kt b/tests/unit/java/com/android/server/NetIdManagerTest.kt
index 6f5e740..811134e 100644
--- a/tests/unit/java/com/android/server/NetIdManagerTest.kt
+++ b/tests/unit/java/com/android/server/NetIdManagerTest.kt
@@ -16,17 +16,20 @@
package com.android.server
+import android.os.Build
import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
import com.android.server.NetIdManager.MIN_NET_ID
-import com.android.testutils.assertThrows
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.ExceptionUtils.ThrowingRunnable
+import com.android.testutils.assertThrows
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.test.assertEquals
-@RunWith(AndroidJUnit4::class)
+@RunWith(DevSdkIgnoreRunner::class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
class NetIdManagerTest {
@Test
fun testReserveReleaseNetId() {
diff --git a/tests/unit/java/com/android/server/NetworkManagementServiceTest.java b/tests/unit/java/com/android/server/NetworkManagementServiceTest.java
index 13516d7..ea29da0 100644
--- a/tests/unit/java/com/android/server/NetworkManagementServiceTest.java
+++ b/tests/unit/java/com/android/server/NetworkManagementServiceTest.java
@@ -38,17 +38,18 @@
import android.net.NetworkPolicyManager;
import android.os.BatteryStats;
import android.os.Binder;
+import android.os.Build;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.ArrayMap;
-import androidx.test.runner.AndroidJUnit4;
-
import com.android.internal.app.IBatteryStats;
import com.android.server.NetworkManagementService.Dependencies;
import com.android.server.net.BaseNetworkObserver;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.After;
import org.junit.Before;
@@ -64,8 +65,9 @@
/**
* Tests for {@link NetworkManagementService}.
*/
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class NetworkManagementServiceTest {
private NetworkManagementService mNMService;
@Mock private Context mContext;
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index 1eac4ea..e80a938 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -31,17 +31,19 @@
import android.content.Context;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.server.NsdService.DaemonConnection;
import com.android.server.NsdService.DaemonConnectionSupplier;
import com.android.server.NsdService.NativeCallbackReceiver;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils;
import org.junit.After;
@@ -56,8 +58,9 @@
// TODOs:
// - test client can send requests and receive replies
// - test NSD_ON ENABLE/DISABLED listening
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class NsdServiceTest {
static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD;
diff --git a/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java b/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
index 0ffeec9..9ef558f 100644
--- a/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
@@ -55,15 +55,17 @@
import android.net.ResolverParamsParcel;
import android.net.RouteInfo;
import android.net.shared.PrivateDnsConfig;
+import android.os.Build;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.util.SparseArray;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.MessageUtils;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import libcore.net.InetAddressUtils;
@@ -83,8 +85,9 @@
* Build, install and run with:
* runtest frameworks-net -c com.android.server.connectivity.DnsManagerTest
*/
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class DnsManagerTest {
static final String TEST_IFACENAME = "test_wlan0";
static final int TEST_NETID = 100;
diff --git a/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt b/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt
index 45b575a..785153a 100644
--- a/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt
@@ -19,27 +19,28 @@
import android.net.NetworkAgentConfig
import android.net.NetworkCapabilities
import android.net.NetworkScore.KEEP_CONNECTED_NONE
+import android.os.Build
import android.text.TextUtils
import android.util.ArraySet
import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
import com.android.server.connectivity.FullScore.MAX_CS_MANAGED_POLICY
import com.android.server.connectivity.FullScore.POLICY_ACCEPT_UNVALIDATED
import com.android.server.connectivity.FullScore.POLICY_EVER_USER_SELECTED
import com.android.server.connectivity.FullScore.POLICY_IS_VALIDATED
import com.android.server.connectivity.FullScore.POLICY_IS_VPN
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
import org.junit.Test
import org.junit.runner.RunWith
-import kotlin.collections.minOfOrNull
-import kotlin.collections.maxOfOrNull
import kotlin.reflect.full.staticProperties
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertTrue
-@RunWith(AndroidJUnit4::class)
+@RunWith(DevSdkIgnoreRunner::class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
class FullScoreTest {
// Convenience methods
fun FullScore.withPolicies(
diff --git a/tests/unit/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/unit/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index 70495cc..52b05aa 100644
--- a/tests/unit/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/unit/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -42,11 +42,12 @@
import android.net.metrics.RaEvent;
import android.net.metrics.ValidationProbeEvent;
import android.net.metrics.WakeupStats;
+import android.os.Build;
import android.test.suitebuilder.annotation.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -55,8 +56,9 @@
import java.util.List;
// TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class IpConnectivityEventBuilderTest {
@Test
diff --git a/tests/unit/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/unit/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 8b072c4..063ccd3 100644
--- a/tests/unit/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/unit/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -45,15 +45,16 @@
import android.net.metrics.IpReachabilityEvent;
import android.net.metrics.RaEvent;
import android.net.metrics.ValidationProbeEvent;
+import android.os.Build;
import android.os.Parcelable;
import android.system.OsConstants;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Base64;
-import androidx.test.runner.AndroidJUnit4;
-
import com.android.internal.util.BitUtils;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -65,8 +66,9 @@
import java.io.PrintWriter;
import java.io.StringWriter;
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class IpConnectivityMetricsTest {
static final IpReachabilityEvent FAKE_EV =
new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED);
diff --git a/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java
index 36e229d..58a7c89 100644
--- a/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -43,14 +43,16 @@
import android.net.NetworkProvider;
import android.net.NetworkScore;
import android.os.Binder;
+import android.os.Build;
import android.text.format.DateUtils;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.connectivity.resources.R;
import com.android.server.ConnectivityService;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.After;
import org.junit.Before;
@@ -59,8 +61,9 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class LingerMonitorTest {
static final String CELLULAR = "CELLULAR";
static final String WIFI = "WIFI";
diff --git a/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java b/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
index 4c80f6a..e2ad00d 100644
--- a/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
@@ -28,8 +28,7 @@
import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH;
import static com.android.server.net.NetworkPolicyManagerService.OPPORTUNISTIC_QUOTA_UNKNOWN;
-import static junit.framework.TestCase.assertNotNull;
-
+import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
@@ -54,6 +53,7 @@
import android.net.NetworkPolicyManager;
import android.net.NetworkTemplate;
import android.net.TelephonyNetworkSpecifier;
+import android.os.Build;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
@@ -63,13 +63,14 @@
import android.util.RecurrenceRule;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.LocalServices;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.net.NetworkStatsManagerInternal;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.After;
import org.junit.Before;
@@ -87,8 +88,9 @@
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class MultipathPolicyTrackerTest {
private static final Network TEST_NETWORK = new Network(123);
private static final int POLICY_SNOOZED = -100;
diff --git a/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java
index 9b2a638..f358726 100644
--- a/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -40,13 +40,15 @@
import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
+import android.os.Build;
import android.os.Handler;
import android.os.test.TestLooper;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.server.ConnectivityService;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -56,8 +58,9 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class Nat464XlatTest {
static final String BASE_IFACE = "test0";
diff --git a/tests/unit/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/unit/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index 50aaaee..7d6c3ae 100644
--- a/tests/unit/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/unit/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -31,14 +31,15 @@
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.os.Build;
import android.system.OsConstants;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Base64;
-import androidx.test.runner.AndroidJUnit4;
-
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -52,8 +53,9 @@
import java.util.Comparator;
import java.util.List;
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class NetdEventListenerServiceTest {
private static final String EXAMPLE_IPV4 = "192.0.2.1";
private static final String EXAMPLE_IPV6 = "2001:db8:1200::2:1";
diff --git a/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index 3adf08c..07deeef 100644
--- a/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -16,8 +16,17 @@
package com.android.server.connectivity;
-import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.*;
+import static android.app.Notification.FLAG_AUTO_CANCEL;
+import static android.app.Notification.FLAG_ONGOING_EVENT;
+import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.LOST_INTERNET;
+import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.NETWORK_SWITCH;
+import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.NO_INTERNET;
+import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.PARTIAL_CONNECTIVITY;
+import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.PRIVATE_DNS_BROKEN;
+import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.SIGN_IN;
+
+import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.clearInvocations;
@@ -39,19 +48,19 @@
import android.net.ConnectivityResources;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
+import android.os.Build;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.connectivity.resources.R;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.After;
-import org.junit.AfterClass;
import org.junit.Before;
-import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -65,12 +74,15 @@
import java.util.Collections;
import java.util.List;
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class NetworkNotificationManagerTest {
private static final String TEST_SSID = "Test SSID";
private static final String TEST_EXTRA_INFO = "extra";
+ private static final int TEST_NOTIF_ID = 101;
+ private static final String TEST_NOTIF_TAG = NetworkNotificationManager.tagFor(TEST_NOTIF_ID);
static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities();
static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities();
static final NetworkCapabilities VPN_CAPABILITIES = new NetworkCapabilities();
@@ -132,6 +144,7 @@
}
when(mResources.getStringArray(R.array.network_switch_type_name))
.thenReturn(transportNames);
+ when(mResources.getBoolean(R.bool.config_autoCancelNetworkNotifications)).thenReturn(true);
mManager = new NetworkNotificationManager(mCtx, mTelephonyManager);
}
@@ -228,29 +241,65 @@
verify(mNotificationManager, never()).notify(any(), anyInt(), any());
}
+ private void assertNotification(NotificationType type, boolean ongoing, boolean autoCancel) {
+ final ArgumentCaptor<Notification> noteCaptor = ArgumentCaptor.forClass(Notification.class);
+ mManager.showNotification(TEST_NOTIF_ID, type, mWifiNai, mCellNai, null, false);
+ verify(mNotificationManager, times(1)).notify(eq(TEST_NOTIF_TAG), eq(type.eventId),
+ noteCaptor.capture());
+
+ assertEquals("Notification ongoing flag should be " + (ongoing ? "set" : "unset"),
+ ongoing, (noteCaptor.getValue().flags & FLAG_ONGOING_EVENT) != 0);
+ assertEquals("Notification autocancel flag should be " + (autoCancel ? "set" : "unset"),
+ autoCancel, (noteCaptor.getValue().flags & FLAG_AUTO_CANCEL) != 0);
+ }
+
@Test
public void testDuplicatedNotificationsNoInternetThenSignIn() {
- final int id = 101;
- final String tag = NetworkNotificationManager.tagFor(id);
-
// Show first NO_INTERNET
- mManager.showNotification(id, NO_INTERNET, mWifiNai, mCellNai, null, false);
- verify(mNotificationManager, times(1)).notify(eq(tag), eq(NO_INTERNET.eventId), any());
+ assertNotification(NO_INTERNET, false /* ongoing */, true /* autoCancel */);
// Captive portal detection triggers SIGN_IN a bit later, clearing the previous NO_INTERNET
- mManager.showNotification(id, SIGN_IN, mWifiNai, mCellNai, null, false);
- verify(mNotificationManager, times(1)).cancel(eq(tag), eq(NO_INTERNET.eventId));
- verify(mNotificationManager, times(1)).notify(eq(tag), eq(SIGN_IN.eventId), any());
+ assertNotification(SIGN_IN, false /* ongoing */, true /* autoCancel */);
+ verify(mNotificationManager, times(1)).cancel(eq(TEST_NOTIF_TAG), eq(NO_INTERNET.eventId));
// Network disconnects
- mManager.clearNotification(id);
- verify(mNotificationManager, times(1)).cancel(eq(tag), eq(SIGN_IN.eventId));
+ mManager.clearNotification(TEST_NOTIF_ID);
+ verify(mNotificationManager, times(1)).cancel(eq(TEST_NOTIF_TAG), eq(SIGN_IN.eventId));
+ }
+
+ @Test
+ public void testOngoingSignInNotification() {
+ doReturn(true).when(mResources).getBoolean(R.bool.config_ongoingSignInNotification);
+
+ // Show first NO_INTERNET
+ assertNotification(NO_INTERNET, false /* ongoing */, true /* autoCancel */);
+
+ // Captive portal detection triggers SIGN_IN a bit later, clearing the previous NO_INTERNET
+ assertNotification(SIGN_IN, true /* ongoing */, true /* autoCancel */);
+ verify(mNotificationManager, times(1)).cancel(eq(TEST_NOTIF_TAG), eq(NO_INTERNET.eventId));
+
+ // Network disconnects
+ mManager.clearNotification(TEST_NOTIF_ID);
+ verify(mNotificationManager, times(1)).cancel(eq(TEST_NOTIF_TAG), eq(SIGN_IN.eventId));
+ }
+
+ @Test
+ public void testNoAutoCancelNotification() {
+ doReturn(false).when(mResources).getBoolean(R.bool.config_autoCancelNetworkNotifications);
+
+ // Show NO_INTERNET, then SIGN_IN
+ assertNotification(NO_INTERNET, false /* ongoing */, false /* autoCancel */);
+ assertNotification(SIGN_IN, false /* ongoing */, false /* autoCancel */);
+ verify(mNotificationManager, times(1)).cancel(eq(TEST_NOTIF_TAG), eq(NO_INTERNET.eventId));
+
+ mManager.clearNotification(TEST_NOTIF_ID);
+ verify(mNotificationManager, times(1)).cancel(eq(TEST_NOTIF_TAG), eq(SIGN_IN.eventId));
}
@Test
public void testDuplicatedNotificationsSignInThenNoInternet() {
- final int id = 101;
- final String tag = NetworkNotificationManager.tagFor(id);
+ final int id = TEST_NOTIF_ID;
+ final String tag = TEST_NOTIF_TAG;
// Show first SIGN_IN
mManager.showNotification(id, SIGN_IN, mWifiNai, mCellNai, null, false);
@@ -269,8 +318,8 @@
@Test
public void testClearNotificationByType() {
- final int id = 101;
- final String tag = NetworkNotificationManager.tagFor(id);
+ final int id = TEST_NOTIF_ID;
+ final String tag = TEST_NOTIF_TAG;
// clearNotification(int id, NotificationType notifyType) will check if given type is equal
// to previous type or not. If they are equal then clear the notification; if they are not
diff --git a/tests/unit/java/com/android/server/connectivity/NetworkOfferTest.kt b/tests/unit/java/com/android/server/connectivity/NetworkOfferTest.kt
index 409f8c3..d03c567 100644
--- a/tests/unit/java/com/android/server/connectivity/NetworkOfferTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/NetworkOfferTest.kt
@@ -20,8 +20,10 @@
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.net.NetworkScore.KEEP_CONNECTED_NONE
+import android.os.Build
import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.eq
@@ -32,8 +34,9 @@
const val POLICY_NONE = 0L
-@RunWith(AndroidJUnit4::class)
+@RunWith(DevSdkIgnoreRunner::class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
class NetworkOfferTest {
val mockCallback = mock(INetworkOfferCallback::class.java)
diff --git a/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt b/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt
index 551b94c..4408958 100644
--- a/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/NetworkRankerTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -17,74 +17,156 @@
package com.android.server.connectivity
import android.net.NetworkCapabilities
-import android.net.NetworkRequest
+import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.net.NetworkCapabilities.TRANSPORT_WIFI
import android.net.NetworkScore.KEEP_CONNECTED_NONE
+import android.net.NetworkScore.POLICY_EXITING
+import android.net.NetworkScore.POLICY_TRANSPORT_PRIMARY
+import android.net.NetworkScore.POLICY_YIELD_TO_BAD_WIFI
+import android.os.Build
import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
+import com.android.server.connectivity.FullScore.POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD
+import com.android.server.connectivity.FullScore.POLICY_IS_VALIDATED
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.Mockito.doReturn
-import org.mockito.Mockito.mock
import kotlin.test.assertEquals
-import kotlin.test.assertNull
-@RunWith(AndroidJUnit4::class)
+private fun score(vararg policies: Int) = FullScore(0,
+ policies.fold(0L) { acc, e -> acc or (1L shl e) }, KEEP_CONNECTED_NONE)
+private fun caps(transport: Int) = NetworkCapabilities.Builder().addTransportType(transport).build()
+
@SmallTest
+@RunWith(DevSdkIgnoreRunner::class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
class NetworkRankerTest {
- private val ranker = NetworkRanker()
+ private val mRanker = NetworkRanker()
- private fun makeNai(satisfy: Boolean, legacyScore: Int) =
- mock(NetworkAgentInfo::class.java).also {
- doReturn(satisfy).`when`(it).satisfies(any())
- val fs = FullScore(legacyScore, 0 /* policies */, KEEP_CONNECTED_NONE)
- doReturn(fs).`when`(it).getScore()
- val nc = NetworkCapabilities.Builder().build()
- doReturn(nc).`when`(it).getCapsNoCopy()
- }
-
- @Test
- fun testGetBestNetwork() {
- val scores = listOf(20, 50, 90, 60, 23, 68)
- val nais = scores.map { makeNai(true, it) }
- val bestNetwork = nais[2] // The one with the top score
- val someRequest = mock(NetworkRequest::class.java)
- assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais, bestNetwork))
+ private class TestScore(private val sc: FullScore, private val nc: NetworkCapabilities)
+ : NetworkRanker.Scoreable {
+ override fun getScore() = sc
+ override fun getCapsNoCopy(): NetworkCapabilities = nc
}
@Test
- fun testIgnoreNonSatisfying() {
- val nais = listOf(makeNai(true, 20), makeNai(true, 50), makeNai(false, 90),
- makeNai(false, 60), makeNai(true, 23), makeNai(false, 68))
- val bestNetwork = nais[1] // Top score that's satisfying
- val someRequest = mock(NetworkRequest::class.java)
- assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais, nais[1]))
+ fun testYieldToBadWiFiOneCell() {
+ // Only cell, it wins
+ val winner = TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
+ caps(TRANSPORT_CELLULAR))
+ val scores = listOf(winner)
+ assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
}
@Test
- fun testNoMatch() {
- val nais = listOf(makeNai(false, 20), makeNai(false, 50), makeNai(false, 90))
- val someRequest = mock(NetworkRequest::class.java)
- assertNull(ranker.getBestNetwork(someRequest, nais, null))
+ fun testYieldToBadWiFiOneCellOneBadWiFi() {
+ // Bad wifi wins against yielding validated cell
+ val winner = TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD),
+ caps(TRANSPORT_WIFI))
+ val scores = listOf(
+ winner,
+ TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
+ caps(TRANSPORT_CELLULAR))
+ )
+ assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
}
@Test
- fun testEmpty() {
- val someRequest = mock(NetworkRequest::class.java)
- assertNull(ranker.getBestNetwork(someRequest, emptyList(), null))
+ fun testYieldToBadWiFiOneCellTwoBadWiFi() {
+ // Bad wifi wins against yielding validated cell. Prefer the one that's primary.
+ val winner = TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD,
+ POLICY_TRANSPORT_PRIMARY), caps(TRANSPORT_WIFI))
+ val scores = listOf(
+ winner,
+ TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD),
+ caps(TRANSPORT_WIFI)),
+ TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
+ caps(TRANSPORT_CELLULAR))
+ )
+ assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
}
- // Make sure the ranker is "stable" (as in stable sort), that is, it always returns the FIRST
- // network satisfying the request if multiple of them have the same score.
@Test
- fun testStable() {
- val nais1 = listOf(makeNai(true, 30), makeNai(true, 30), makeNai(true, 30),
- makeNai(true, 30), makeNai(true, 30), makeNai(true, 30))
- val someRequest = mock(NetworkRequest::class.java)
- assertEquals(nais1[0], ranker.getBestNetwork(someRequest, nais1, nais1[0]))
+ fun testYieldToBadWiFiOneCellTwoBadWiFiOneNotAvoided() {
+ // Bad wifi ever validated wins against bad wifi that never was validated (or was
+ // avoided when bad).
+ val winner = TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD),
+ caps(TRANSPORT_WIFI))
+ val scores = listOf(
+ winner,
+ TestScore(score(), caps(TRANSPORT_WIFI)),
+ TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
+ caps(TRANSPORT_CELLULAR))
+ )
+ assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+ }
- val nais2 = listOf(makeNai(true, 30), makeNai(true, 50), makeNai(true, 20),
- makeNai(true, 50), makeNai(true, 50), makeNai(true, 40))
- assertEquals(nais2[1], ranker.getBestNetwork(someRequest, nais2, nais2[1]))
+ @Test
+ fun testYieldToBadWiFiOneCellOneBadWiFiOneGoodWiFi() {
+ // Good wifi wins
+ val winner = TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD,
+ POLICY_IS_VALIDATED), caps(TRANSPORT_WIFI))
+ val scores = listOf(
+ winner,
+ TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD,
+ POLICY_TRANSPORT_PRIMARY), caps(TRANSPORT_WIFI)),
+ TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
+ caps(TRANSPORT_CELLULAR))
+ )
+ assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+ }
+
+ @Test
+ fun testYieldToBadWiFiTwoCellsOneBadWiFi() {
+ // Cell that doesn't yield wins over cell that yields and bad wifi
+ val winner = TestScore(score(POLICY_IS_VALIDATED), caps(TRANSPORT_CELLULAR))
+ val scores = listOf(
+ winner,
+ TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD,
+ POLICY_TRANSPORT_PRIMARY), caps(TRANSPORT_WIFI)),
+ TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
+ caps(TRANSPORT_CELLULAR))
+ )
+ assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+ }
+
+ @Test
+ fun testYieldToBadWiFiTwoCellsOneBadWiFiOneGoodWiFi() {
+ // Good wifi wins over cell that doesn't yield and cell that yields
+ val winner = TestScore(score(POLICY_IS_VALIDATED), caps(TRANSPORT_WIFI))
+ val scores = listOf(
+ winner,
+ TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD,
+ POLICY_TRANSPORT_PRIMARY), caps(TRANSPORT_WIFI)),
+ TestScore(score(POLICY_IS_VALIDATED), caps(TRANSPORT_CELLULAR)),
+ TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
+ caps(TRANSPORT_CELLULAR))
+ )
+ assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+ }
+
+ @Test
+ fun testYieldToBadWiFiOneExitingGoodWiFi() {
+ // Yielding cell wins over good exiting wifi
+ val winner = TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
+ caps(TRANSPORT_CELLULAR))
+ val scores = listOf(
+ winner,
+ TestScore(score(POLICY_IS_VALIDATED, POLICY_EXITING), caps(TRANSPORT_WIFI))
+ )
+ assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
+ }
+
+ @Test
+ fun testYieldToBadWiFiOneExitingBadWiFi() {
+ // Yielding cell wins over bad exiting wifi
+ val winner = TestScore(score(POLICY_YIELD_TO_BAD_WIFI, POLICY_IS_VALIDATED),
+ caps(TRANSPORT_CELLULAR))
+ val scores = listOf(
+ winner,
+ TestScore(score(POLICY_EVER_VALIDATED_NOT_AVOIDED_WHEN_BAD,
+ POLICY_EXITING), caps(TRANSPORT_WIFI))
+ )
+ assertEquals(winner, mRanker.getBestNetworkByPolicy(scores, null))
}
}
diff --git a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
index 8b45755..8f46508 100644
--- a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -80,7 +80,9 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -96,10 +98,12 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class PermissionMonitorTest {
private static final UserHandle MOCK_USER1 = UserHandle.of(0);
private static final UserHandle MOCK_USER2 = UserHandle.of(1);
@@ -208,16 +212,12 @@
return packageInfo;
}
- private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid,
- UserHandle user) {
+ private static PackageInfo buildPackageInfo(String packageName, int uid,
+ String... permissions) {
final PackageInfo pkgInfo;
- if (hasSystemPermission) {
- pkgInfo = systemPackageInfoWithPermissions(
- CHANGE_NETWORK_STATE, NETWORK_STACK, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
- } else {
- pkgInfo = packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED, new String[] {}, "");
- }
- pkgInfo.applicationInfo.uid = user.getUid(UserHandle.getAppId(uid));
+ pkgInfo = systemPackageInfoWithPermissions(permissions);
+ pkgInfo.packageName = packageName;
+ pkgInfo.applicationInfo.uid = uid;
return pkgInfo;
}
@@ -526,13 +526,13 @@
// MOCK_UID1: MOCK_PACKAGE1 only has network permission.
// SYSTEM_UID: SYSTEM_PACKAGE1 has system permission.
// SYSTEM_UID: SYSTEM_PACKAGE2 only has network permission.
- doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(eq(SYSTEM), anyString());
doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(any(),
eq(SYSTEM_PACKAGE1));
doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
eq(SYSTEM_PACKAGE2));
doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
eq(MOCK_PACKAGE1));
+ doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(eq(SYSTEM), anyString());
// Add SYSTEM_PACKAGE2, expect only have network permission.
mPermissionMonitor.onUserAdded(MOCK_USER1);
@@ -547,6 +547,21 @@
netdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
new int[]{SYSTEM_UID});
+ // Remove SYSTEM_PACKAGE2, expect keep system permission.
+ when(mPackageManager.getPackagesForUid(MOCK_USER1.getUid(SYSTEM_UID)))
+ .thenReturn(new String[]{SYSTEM_PACKAGE1});
+ when(mPackageManager.getPackagesForUid(MOCK_USER2.getUid(SYSTEM_UID)))
+ .thenReturn(new String[]{SYSTEM_PACKAGE1});
+ removePackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2},
+ SYSTEM_PACKAGE2, SYSTEM_UID);
+ netdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
+ new int[]{SYSTEM_UID});
+
+ // Add SYSTEM_PACKAGE2, expect keep system permission.
+ addPackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, SYSTEM_PACKAGE2, SYSTEM_UID);
+ netdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
+ new int[]{SYSTEM_UID});
+
addPackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1);
netdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
new int[]{SYSTEM_UID});
@@ -554,6 +569,10 @@
new int[]{MOCK_UID1});
// Remove MOCK_UID1, expect no permission left for all user.
+ when(mPackageManager.getPackagesForUid(MOCK_USER1.getUid(MOCK_UID1)))
+ .thenReturn(new String[]{});
+ when(mPackageManager.getPackagesForUid(MOCK_USER2.getUid(MOCK_UID1)))
+ .thenReturn(new String[]{});
mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
removePackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1);
netdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1, MOCK_USER2},
@@ -584,15 +603,12 @@
@Test
public void testUidFilteringDuringVpnConnectDisconnectAndUidUpdates() throws Exception {
when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
- Arrays.asList(new PackageInfo[] {
- buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1),
- buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1),
- buildPackageInfo(false /* hasSystemPermission */, MOCK_UID2, MOCK_USER1),
- buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1)
- }));
- when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1),
- eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
- buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1));
+ List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_UID1, CHANGE_NETWORK_STATE,
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS),
+ buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1),
+ buildPackageInfo(MOCK_PACKAGE2, MOCK_UID2),
+ buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)));
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1);
mPermissionMonitor.startMonitoring();
// Every app on user 0 except MOCK_UID2 are under VPN.
final Set<UidRange> vpnRange1 = new HashSet<>(Arrays.asList(new UidRange[] {
@@ -636,13 +652,10 @@
@Test
public void testUidFilteringDuringPackageInstallAndUninstall() throws Exception {
when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
- Arrays.asList(new PackageInfo[] {
- buildPackageInfo(true /* hasSystemPermission */, SYSTEM_UID1, MOCK_USER1),
- buildPackageInfo(false /* hasSystemPermission */, VPN_UID, MOCK_USER1)
- }));
- when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1),
- eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
- buildPackageInfo(false /* hasSystemPermission */, MOCK_UID1, MOCK_USER1));
+ List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_UID1, CHANGE_NETWORK_STATE,
+ NETWORK_STACK, CONNECTIVITY_USE_RESTRICTED_NETWORKS),
+ buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)));
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1);
mPermissionMonitor.startMonitoring();
final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(MOCK_USER1));
@@ -879,13 +892,26 @@
new int[]{ MOCK_UID2 });
}
+ private BroadcastReceiver expectBroadcastReceiver(String... actions) {
+ final ArgumentCaptor<BroadcastReceiver> receiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mContext, times(1)).registerReceiver(receiverCaptor.capture(),
+ argThat(filter -> {
+ for (String action : actions) {
+ if (!filter.hasAction(action)) {
+ return false;
+ }
+ }
+ return true;
+ }), any(), any());
+ return receiverCaptor.getValue();
+ }
+
@Test
public void testIntentReceiver() throws Exception {
final NetdServiceMonitor netdServiceMonitor = new NetdServiceMonitor(mNetdService);
- final ArgumentCaptor<BroadcastReceiver> receiverCaptor =
- ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(mContext, times(1)).registerReceiver(receiverCaptor.capture(), any(), any(), any());
- final BroadcastReceiver receiver = receiverCaptor.getValue();
+ final BroadcastReceiver receiver = expectBroadcastReceiver(
+ Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_REMOVED);
// Verify receiving PACKAGE_ADDED intent.
final Intent addedIntent = new Intent(Intent.ACTION_PACKAGE_ADDED,
@@ -1039,4 +1065,136 @@
netdMonitor.expectNoPermission(
new UserHandle[]{MOCK_USER2}, new int[]{ MOCK_UID1, MOCK_UID2 });
}
+
+ @Test
+ public void testOnExternalApplicationsAvailable() throws Exception {
+ final NetdServiceMonitor netdServiceMonitor = new NetdServiceMonitor(mNetdService);
+ final NetdMonitor netdMonitor = new NetdMonitor(mNetdService);
+ final BroadcastReceiver receiver = expectBroadcastReceiver(
+ Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+
+ // Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external
+ // and have different uids. There has no permission for both uids.
+ when(mUserManager.getUserHandles(eq(true))).thenReturn(List.of(MOCK_USER1));
+ when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
+ List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1),
+ buildPackageInfo(MOCK_PACKAGE2, MOCK_UID2)));
+ mPermissionMonitor.startMonitoring();
+ netdMonitor.expectNoPermission(
+ new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1, MOCK_UID2});
+ netdServiceMonitor.expectPermission(
+ INetd.PERMISSION_NONE, new int[]{MOCK_UID1, MOCK_UID2});
+
+ // Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd.
+ final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST,
+ new String[] { MOCK_PACKAGE1 , MOCK_PACKAGE2});
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1,
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS, INTERNET);
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID2, CHANGE_NETWORK_STATE,
+ UPDATE_DEVICE_STATS);
+ receiver.onReceive(mContext, externalIntent);
+ netdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
+ netdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID2});
+ netdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[] { MOCK_UID1 });
+ netdServiceMonitor.expectPermission(
+ INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID2});
+ }
+
+ @Test
+ public void testOnExternalApplicationsAvailable_AppsNotRegisteredOnStartMonitoring()
+ throws Exception {
+ final NetdServiceMonitor netdServiceMonitor = new NetdServiceMonitor(mNetdService);
+ final NetdMonitor netdMonitor = new NetdMonitor(mNetdService);
+ final BroadcastReceiver receiver = expectBroadcastReceiver(
+ Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+
+ // One user MOCK_USER1
+ mPermissionMonitor.onUserAdded(MOCK_USER1);
+
+ // Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external
+ // and have different uids. There has no permission for both uids.
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1,
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS, INTERNET);
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID2, CHANGE_NETWORK_STATE,
+ UPDATE_DEVICE_STATS);
+
+ // Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd.
+ final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST,
+ new String[] { MOCK_PACKAGE1 , MOCK_PACKAGE2});
+ receiver.onReceive(mContext, externalIntent);
+ netdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
+ netdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID2});
+ netdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[] { MOCK_UID1 });
+ netdServiceMonitor.expectPermission(
+ INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID2});
+ }
+
+ @Test
+ public void testOnExternalApplicationsAvailableWithSharedUid()
+ throws Exception {
+ final NetdServiceMonitor netdServiceMonitor = new NetdServiceMonitor(mNetdService);
+ final NetdMonitor netdMonitor = new NetdMonitor(mNetdService);
+ final BroadcastReceiver receiver = expectBroadcastReceiver(
+ Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+
+ // Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external
+ // storage and shared on MOCK_UID1. There has no permission for MOCK_UID1.
+ when(mUserManager.getUserHandles(eq(true))).thenReturn(List.of(MOCK_USER1));
+ when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
+ List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1),
+ buildPackageInfo(MOCK_PACKAGE2, MOCK_UID1)));
+ mPermissionMonitor.startMonitoring();
+ netdMonitor.expectNoPermission(new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
+ netdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[] {MOCK_UID1});
+
+ // Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd.
+ final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[] {MOCK_PACKAGE1});
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE);
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID1, UPDATE_DEVICE_STATS);
+ when(mPackageManager.getPackagesForUid(MOCK_UID1))
+ .thenReturn(new String[]{MOCK_PACKAGE1, MOCK_PACKAGE2});
+ receiver.onReceive(mContext, externalIntent);
+ netdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
+ netdServiceMonitor.expectPermission(
+ INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[] {MOCK_UID1});
+ }
+
+ @Test
+ public void testOnExternalApplicationsAvailableWithSharedUid_DifferentStorage()
+ throws Exception {
+ final NetdServiceMonitor netdServiceMonitor = new NetdServiceMonitor(mNetdService);
+ final NetdMonitor netdMonitor = new NetdMonitor(mNetdService);
+ final BroadcastReceiver receiver = expectBroadcastReceiver(
+ Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+
+ // Initial the permission state. MOCK_PACKAGE1 is installed on external storage and
+ // MOCK_PACKAGE2 is installed on device. These two packages are shared on MOCK_UID1.
+ // MOCK_UID1 has NETWORK and INTERNET permissions.
+ when(mUserManager.getUserHandles(eq(true))).thenReturn(List.of(MOCK_USER1));
+ when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
+ List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1),
+ buildPackageInfo(MOCK_PACKAGE2, MOCK_UID1, CHANGE_NETWORK_STATE,
+ INTERNET)));
+ mPermissionMonitor.startMonitoring();
+ netdMonitor.expectPermission(NETWORK, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
+ netdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[] {MOCK_UID1});
+
+ // Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd.
+ final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[] {MOCK_PACKAGE1});
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1,
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS, UPDATE_DEVICE_STATS);
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID1, CHANGE_NETWORK_STATE,
+ INTERNET);
+ when(mPackageManager.getPackagesForUid(MOCK_UID1))
+ .thenReturn(new String[]{MOCK_PACKAGE1, MOCK_PACKAGE2});
+ receiver.onReceive(mContext, externalIntent);
+ netdMonitor.expectPermission(SYSTEM, new UserHandle[]{MOCK_USER1}, new int[]{MOCK_UID1});
+ netdServiceMonitor.expectPermission(
+ INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS,
+ new int[] {MOCK_UID1});
+ }
}
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 6ff47ae..b706090 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -97,13 +97,14 @@
import android.util.Range;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.R;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
import com.android.server.IpSecService;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -138,8 +139,9 @@
* Build, install and run with:
* runtest frameworks-net -c com.android.server.connectivity.VpnTest
*/
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(VERSION_CODES.R)
public class VpnTest {
private static final String TAG = "VpnTest";
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java b/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java
index 8b730af..03d9404 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java
@@ -25,12 +25,14 @@
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.Build;
import android.telephony.TelephonyManager;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.server.LocalServices;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.After;
import org.junit.Before;
@@ -39,8 +41,9 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class NetworkStatsAccessTest {
private static final String TEST_PKG = "com.example.test";
private static final int TEST_UID = 12345;
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java
index 505ff9b..e771558 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java
@@ -42,6 +42,7 @@
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
+import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import android.telephony.SubscriptionPlan;
@@ -51,9 +52,10 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.tests.net.R;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -79,8 +81,9 @@
/**
* Tests for {@link NetworkStatsCollection}.
*/
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class NetworkStatsCollectionTest {
private static final String TEST_FILE = "test.bin";
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
index 40d4446..8d7aa4e 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -37,12 +37,14 @@
import android.net.NetworkStats;
import android.net.TrafficStats;
import android.net.UnderlyingNetworkInfo;
+import android.os.Build;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.tests.net.R;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -60,8 +62,9 @@
import java.io.OutputStream;
/** Tests for {@link NetworkStatsFactory}. */
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class NetworkStatsFactoryTest extends NetworkStatsBaseTest {
private static final String CLAT_PREFIX = "v4-";
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
index 9fa1c50..e35104e 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -39,6 +39,7 @@
import android.net.NetworkIdentity;
import android.net.NetworkStats;
import android.net.NetworkTemplate;
+import android.os.Build;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
@@ -51,9 +52,10 @@
import android.util.ArrayMap;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.server.net.NetworkStatsServiceTest.LatchedHandler;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils;
import org.junit.Before;
@@ -68,8 +70,9 @@
/**
* Tests for {@link NetworkStatsObservers}.
*/
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class NetworkStatsObserversTest {
private static final String TEST_IFACE = "test0";
private static final String TEST_IFACE2 = "test1";
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index ee94ae9..ab76460 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -98,6 +98,7 @@
import android.net.TelephonyNetworkSpecifier;
import android.net.UnderlyingNetworkInfo;
import android.net.netstats.provider.INetworkStatsProviderCallback;
+import android.os.Build;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
@@ -114,12 +115,13 @@
import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils;
import com.android.testutils.TestableNetworkStatsProviderBinder;
@@ -146,8 +148,9 @@
* TODO: This test used to be really brittle because it used Easymock - it uses Mockito now, but
* still uses the Easymock structure, which could be simplified.
*/
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
private static final String TAG = "NetworkStatsServiceTest";
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
index 6d2c7dc..2bc385c 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
@@ -34,6 +34,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.net.NetworkTemplate;
+import android.os.Build;
import android.os.test.TestLooper;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.PhoneStateListener;
@@ -43,11 +44,12 @@
import com.android.internal.util.CollectionUtils;
import com.android.server.net.NetworkStatsSubscriptionsMonitor.RatTypeListener;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -57,7 +59,8 @@
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
-@RunWith(JUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public final class NetworkStatsSubscriptionsMonitorTest {
private static final int TEST_SUBID1 = 3;
private static final int TEST_SUBID2 = 5;
diff --git a/tests/unit/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java b/tests/unit/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
index ebbc0ef..5f3efed 100644
--- a/tests/unit/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
+++ b/tests/unit/java/com/android/server/net/ipmemorystore/NetworkAttributesTest.java
@@ -20,9 +20,12 @@
import android.net.ipmemorystore.NetworkAttributes;
import android.net.networkstack.aidl.quirks.IPv6ProvisioningLossQuirk;
+import android.os.Build;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -34,7 +37,8 @@
/** Unit tests for {@link NetworkAttributes}. */
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(DevSdkIgnoreRunner.class)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class NetworkAttributesTest {
private static final String WEIGHT_FIELD_NAME_PREFIX = "WEIGHT_";
private static final float EPSILON = 0.0001f;