docs: changes to broadcast documentation am: e1682a08b7
am: 538a5cf796
Change-Id: I7dcb1bc57fb62f2b177c7ad15a9236da98fb749f
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 35e3065..a677d73 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -52,6 +52,17 @@
public native static void attachRaFilter(FileDescriptor fd, int packetType) throws SocketException;
/**
+ * Attaches a socket filter that accepts L2-L4 signaling traffic required for IP connectivity.
+ *
+ * This includes: all ARP, ICMPv6 RS/RA/NS/NA messages, and DHCPv4 exchanges.
+ *
+ * @param fd the socket's {@link FileDescriptor}.
+ * @param packetType the hardware address type, one of ARPHRD_*.
+ */
+ public native static void attachControlPacketFilter(FileDescriptor fd, int packetType)
+ throws SocketException;
+
+ /**
* Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
* @param fd the socket's {@link FileDescriptor}.
* @param ifIndex the interface index.
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 679e882..eb105d2 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -47,28 +47,33 @@
namespace android {
+static const uint32_t kEtherTypeOffset = offsetof(ether_header, ether_type);
+static const uint32_t kEtherHeaderLen = sizeof(ether_header);
+static const uint32_t kIPv4Protocol = kEtherHeaderLen + offsetof(iphdr, protocol);
+static const uint32_t kIPv4FlagsOffset = kEtherHeaderLen + offsetof(iphdr, frag_off);
+static const uint32_t kIPv6NextHeader = kEtherHeaderLen + offsetof(ip6_hdr, ip6_nxt);
+static const uint32_t kIPv6PayloadStart = kEtherHeaderLen + sizeof(ip6_hdr);
+static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type);
+static const uint32_t kUDPSrcPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, source);
+static const uint32_t kUDPDstPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, dest);
static const uint16_t kDhcpClientPort = 68;
static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd)
{
- uint32_t ip_offset = sizeof(ether_header);
- uint32_t proto_offset = ip_offset + offsetof(iphdr, protocol);
- uint32_t flags_offset = ip_offset + offsetof(iphdr, frag_off);
- uint32_t dport_indirect_offset = ip_offset + offsetof(udphdr, dest);
struct sock_filter filter_code[] = {
// Check the protocol is UDP.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, proto_offset),
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv4Protocol),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 6),
// Check this is not a fragment.
- BPF_STMT(BPF_LD | BPF_H | BPF_ABS, flags_offset),
- BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, 0x1fff, 4, 0),
+ BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kIPv4FlagsOffset),
+ BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, IP_OFFMASK, 4, 0),
// Get the IP header length.
- BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, ip_offset),
+ BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, kEtherHeaderLen),
// Check the destination port.
- BPF_STMT(BPF_LD | BPF_H | BPF_IND, dport_indirect_offset),
+ BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPDstPortIndirectOffset),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 0, 1),
// Accept or reject.
@@ -96,17 +101,13 @@
return;
}
- uint32_t ipv6_offset = sizeof(ether_header);
- uint32_t ipv6_next_header_offset = ipv6_offset + offsetof(ip6_hdr, ip6_nxt);
- uint32_t icmp6_offset = ipv6_offset + sizeof(ip6_hdr);
- uint32_t icmp6_type_offset = icmp6_offset + offsetof(icmp6_hdr, icmp6_type);
struct sock_filter filter_code[] = {
// Check IPv6 Next Header is ICMPv6.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, ipv6_next_header_offset),
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeader),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3),
// Check ICMPv6 type is Router Advertisement.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, icmp6_type_offset),
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset),
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_ADVERT, 0, 1),
// Accept or reject.
@@ -125,6 +126,81 @@
}
}
+// TODO: Move all this filter code into libnetutils.
+static void android_net_utils_attachControlPacketFilter(
+ JNIEnv *env, jobject clazz, jobject javaFd, jint hardwareAddressType) {
+ if (hardwareAddressType != ARPHRD_ETHER) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "attachControlPacketFilter only supports ARPHRD_ETHER");
+ return;
+ }
+
+ // Capture all:
+ // - ARPs
+ // - DHCPv4 packets
+ // - Router Advertisements & Solicitations
+ // - Neighbor Advertisements & Solicitations
+ //
+ // tcpdump:
+ // arp or
+ // '(ip and udp port 68)' or
+ // '(icmp6 and ip6[40] >= 133 and ip6[40] <= 136)'
+ struct sock_filter filter_code[] = {
+ // Load the link layer next payload field.
+ BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kEtherTypeOffset),
+
+ // Accept all ARP.
+ // TODO: Figure out how to better filter ARPs on noisy networks.
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_ARP, 16, 0),
+
+ // If IPv4:
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IP, 0, 9),
+
+ // Check the protocol is UDP.
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv4Protocol),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 14),
+
+ // Check this is not a fragment.
+ BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kIPv4FlagsOffset),
+ BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, IP_OFFMASK, 12, 0),
+
+ // Get the IP header length.
+ BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, kEtherHeaderLen),
+
+ // Check the source port.
+ BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPSrcPortIndirectOffset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 8, 0),
+
+ // Check the destination port.
+ BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPDstPortIndirectOffset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 6, 7),
+
+ // IPv6 ...
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IPV6, 0, 6),
+ // ... check IPv6 Next Header is ICMPv6 (ignore fragments), ...
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeader),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 4),
+ // ... and check the ICMPv6 type is one of RS/RA/NS/NA.
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset),
+ BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, ND_ROUTER_SOLICIT, 0, 2),
+ BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, ND_NEIGHBOR_ADVERT, 1, 0),
+
+ // Accept or reject.
+ BPF_STMT(BPF_RET | BPF_K, 0xffff),
+ BPF_STMT(BPF_RET | BPF_K, 0)
+ };
+ struct sock_fprog filter = {
+ sizeof(filter_code) / sizeof(filter_code[0]),
+ filter_code,
+ };
+
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
+ }
+}
+
static void android_net_utils_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
jint ifIndex)
{
@@ -266,6 +342,7 @@
{ "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
{ "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
{ "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter },
+ { "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachControlPacketFilter },
{ "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket },
};
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 614a94f..2693272 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -864,6 +864,7 @@
mAvoidBadWifiTracker = createAvoidBadWifiTracker(
mContext, mHandler, () -> rematchForAvoidBadWifiUpdate());
+ mAvoidBadWifiTracker.start();
}
private NetworkRequest createInternetRequestForTransport(
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index f7b01be..c6bf4c5 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -114,7 +114,7 @@
}
if (DBG) {
- Slog.d(TAG, "showNotification " + notifyType
+ Slog.d(TAG, "showNotification id=" + id + " " + notifyType
+ " transportType=" + getTransportName(transportType)
+ " extraInfo=" + extraInfo + " highPriority=" + highPriority);
}
@@ -187,7 +187,7 @@
try {
mNotificationManager.notifyAsUser(NOTIFICATION_ID, id, notification, UserHandle.ALL);
} catch (NullPointerException npe) {
- Slog.d(TAG, "setNotificationVisible: visible notificationManager npe=" + npe);
+ Slog.d(TAG, "setNotificationVisible: visible notificationManager error", npe);
}
}
@@ -198,7 +198,7 @@
try {
mNotificationManager.cancelAsUser(NOTIFICATION_ID, id, UserHandle.ALL);
} catch (NullPointerException npe) {
- Slog.d(TAG, "setNotificationVisible: cancel notificationManager npe=" + npe);
+ Slog.d(TAG, "setNotificationVisible: cancel notificationManager error", npe);
}
}
diff --git a/services/net/java/android/net/util/AvoidBadWifiTracker.java b/services/net/java/android/net/util/AvoidBadWifiTracker.java
index c14e811..2abaeb1 100644
--- a/services/net/java/android/net/util/AvoidBadWifiTracker.java
+++ b/services/net/java/android/net/util/AvoidBadWifiTracker.java
@@ -57,7 +57,11 @@
private final Context mContext;
private final Handler mHandler;
private final Runnable mReevaluateRunnable;
+ private final Uri mUri;
+ private final ContentResolver mResolver;
private final SettingObserver mSettingObserver;
+ private final BroadcastReceiver mBroadcastReceiver;
+
private volatile boolean mAvoidBadWifi = true;
public AvoidBadWifiTracker(Context ctx, Handler handler) {
@@ -68,19 +72,36 @@
mContext = ctx;
mHandler = handler;
mReevaluateRunnable = () -> { if (update() && cb != null) cb.run(); };
+ mUri = Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI);
+ mResolver = mContext.getContentResolver();
mSettingObserver = new SettingObserver();
-
- final IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- mContext.registerReceiverAsUser(new BroadcastReceiver() {
+ mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
public void onReceive(Context context, Intent intent) {
reevaluate();
}
- }, UserHandle.ALL, intentFilter, null, null);
+ };
update();
}
+ public void start() {
+ mResolver.registerContentObserver(mUri, false, mSettingObserver);
+
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+ mContext.registerReceiverAsUser(
+ mBroadcastReceiver, UserHandle.ALL, intentFilter, null, null);
+
+ reevaluate();
+ }
+
+ public void shutdown() {
+ mResolver.unregisterContentObserver(mSettingObserver);
+
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ }
+
public boolean currentValue() {
return mAvoidBadWifi;
}
@@ -100,8 +121,7 @@
}
public String getSettingsValue() {
- final ContentResolver resolver = mContext.getContentResolver();
- return Settings.Global.getString(resolver, NETWORK_AVOID_BAD_WIFI);
+ return Settings.Global.getString(mResolver, NETWORK_AVOID_BAD_WIFI);
}
@VisibleForTesting
@@ -117,12 +137,8 @@
}
private class SettingObserver extends ContentObserver {
- private final Uri mUri = Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI);
-
public SettingObserver() {
super(null);
- final ContentResolver resolver = mContext.getContentResolver();
- resolver.registerContentObserver(mUri, false, this);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index aed3635..84f0f90 100644
--- a/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -71,7 +71,8 @@
" transport_types: 3",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -93,7 +94,8 @@
" state_transition: \"SomeState\"",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -114,7 +116,8 @@
" state_transition: \"\"",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -160,7 +163,8 @@
" return_codes: 178",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -181,7 +185,8 @@
" latency_ms: 5678",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -200,7 +205,8 @@
" if_name: \"wlan0\"",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -223,7 +229,8 @@
" >",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -248,7 +255,8 @@
" probe_result: 204",
" probe_type: 1",
" >",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -274,7 +282,8 @@
" program_length: 2048",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -305,7 +314,8 @@
" zero_lifetime_ras: 1",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
@@ -332,7 +342,8 @@
" router_lifetime: 2000",
" >",
" time_ms: 1",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, ev);
}
diff --git a/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityMetricsTest.java b/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 3fc89b9..aa491bb 100644
--- a/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/services/tests/servicestests/src/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.net.ConnectivityMetricsEvent;
import android.net.IIpConnectivityMetrics;
+import android.net.metrics.ApfProgramEvent;
import android.net.metrics.ApfStats;
import android.net.metrics.DefaultNetworkEvent;
import android.net.metrics.DhcpClientEvent;
@@ -57,7 +58,7 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
- mService = new IpConnectivityMetrics(mCtx);
+ mService = new IpConnectivityMetrics(mCtx, (ctx) -> 2000);
}
public void testLoggingEvents() throws Exception {
@@ -112,6 +113,27 @@
assertEquals("", output3);
}
+ public void testRateLimiting() {
+ final IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
+ final ApfProgramEvent ev = new ApfProgramEvent(0, 0, 0, 0, 0);
+ final long fakeTimestamp = 1;
+
+ int attempt = 100; // More than burst quota, but less than buffer size.
+ for (int i = 0; i < attempt; i++) {
+ logger.log(ev);
+ }
+
+ String output1 = getdump("flush");
+ assertFalse("".equals(output1));
+
+ for (int i = 0; i < attempt; i++) {
+ assertFalse("expected event to be dropped", logger.log(fakeTimestamp, ev));
+ }
+
+ String output2 = getdump("flush");
+ assertEquals("", output2);
+ }
+
public void testEndToEndLogging() {
IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
@@ -204,7 +226,8 @@
" router_lifetime: 2000",
" >",
" time_ms: 700",
- ">");
+ ">",
+ "version: 2");
verifySerialization(want, getdump("flush"));
}
diff --git a/services/tests/servicestests/src/com/android/server/connectivity/DnsEventListenerServiceTest.java b/services/tests/servicestests/src/com/android/server/connectivity/NetdEventListenerServiceTest.java
similarity index 89%
rename from services/tests/servicestests/src/com/android/server/connectivity/DnsEventListenerServiceTest.java
rename to services/tests/servicestests/src/com/android/server/connectivity/NetdEventListenerServiceTest.java
index 033b2c9..af4a374 100644
--- a/services/tests/servicestests/src/com/android/server/connectivity/DnsEventListenerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -20,8 +20,9 @@
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.metrics.DnsEvent;
-import android.net.metrics.IDnsEventListener;
+import android.net.metrics.INetdEventListener;
import android.net.metrics.IpConnectivityLog;
+import android.os.RemoteException;
import junit.framework.TestCase;
import org.junit.Before;
@@ -47,12 +48,12 @@
import java.util.OptionalInt;
import java.util.stream.IntStream;
-public class DnsEventListenerServiceTest extends TestCase {
+public class NetdEventListenerServiceTest extends TestCase {
- // TODO: read from DnsEventListenerService after this constant is read from system property
+ // TODO: read from NetdEventListenerService after this constant is read from system property
static final int BATCH_SIZE = 100;
- static final int EVENT_TYPE = IDnsEventListener.EVENT_GETADDRINFO;
- // TODO: read from IDnsEventListener
+ static final int EVENT_TYPE = INetdEventListener.EVENT_GETADDRINFO;
+ // TODO: read from INetdEventListener
static final int RETURN_CODE = 1;
static final byte[] EVENT_TYPES = new byte[BATCH_SIZE];
@@ -66,7 +67,7 @@
}
}
- DnsEventListenerService mDnsService;
+ NetdEventListenerService mNetdEventListenerService;
@Mock ConnectivityManager mCm;
@Mock IpConnectivityLog mLog;
@@ -77,7 +78,7 @@
MockitoAnnotations.initMocks(this);
mCallbackCaptor = ArgumentCaptor.forClass(NetworkCallback.class);
mEvCaptor = ArgumentCaptor.forClass(DnsEvent.class);
- mDnsService = new DnsEventListenerService(mCm, mLog);
+ mNetdEventListenerService = new NetdEventListenerService(mCm, mLog);
verify(mCm, times(1)).registerNetworkCallback(any(), mCallbackCaptor.capture());
}
@@ -131,7 +132,7 @@
new Thread() {
public void run() {
while (System.currentTimeMillis() < stop) {
- mDnsService.dump(pw);
+ mNetdEventListenerService.dump(pw);
}
}
}.start();
@@ -157,8 +158,13 @@
}
void log(int netId, int[] latencies) {
- for (int l : latencies) {
- mDnsService.onDnsEvent(netId, EVENT_TYPE, RETURN_CODE, l);
+ try {
+ for (int l : latencies) {
+ mNetdEventListenerService.onDnsEvent(netId, EVENT_TYPE, RETURN_CODE, l, null, null,
+ 0, 0);
+ }
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
}
}